1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * This file containts all the functions required for interactions of
31 * event sources with the event port file system.
32 */
33
34 #include <sys/types.h>
35 #include <sys/conf.h>
36 #include <sys/stat.h>
37 #include <sys/errno.h>
38 #include <sys/kmem.h>
39 #include <sys/debug.h>
40 #include <sys/file.h>
41 #include <sys/sysmacros.h>
42 #include <sys/systm.h>
43 #include <sys/bitmap.h>
44 #include <sys/rctl.h>
45 #include <sys/atomic.h>
46 #include <sys/poll_impl.h>
47 #include <sys/port_impl.h>
48
49 /*
50 * Maximum number of elements allowed to be passed in a single call of a
51 * port function (port_sendn(), port_getn(). We need to allocate kernel memory
52 * for all of them at once, so we can't let it scale without limit.
53 */
54 uint_t port_max_list = PORT_MAX_LIST;
55 port_control_t port_control; /* Event port framework main structure */
56
57 /*
58 * Block other threads from using a port.
59 * We enter holding portq->portq_mutex but
60 * we may drop and reacquire this lock.
61 * Callers must deal with this fact.
62 */
63 void
port_block(port_queue_t * portq)64 port_block(port_queue_t *portq)
65 {
66 ASSERT(MUTEX_HELD(&portq->portq_mutex));
67
68 while (portq->portq_flags & PORTQ_BLOCKED)
69 cv_wait(&portq->portq_block_cv, &portq->portq_mutex);
70 portq->portq_flags |= PORTQ_BLOCKED;
71 }
72
73 /*
74 * Undo port_block(portq).
75 */
76 void
port_unblock(port_queue_t * portq)77 port_unblock(port_queue_t *portq)
78 {
79 ASSERT(MUTEX_HELD(&portq->portq_mutex));
80
81 portq->portq_flags &= ~PORTQ_BLOCKED;
82 cv_signal(&portq->portq_block_cv);
83 }
84
85 /*
86 * Called from pollwakeup(PORT_SOURCE_FD source) to determine
87 * if the port's fd needs to be notified of poll events. If yes,
88 * we mark the port indicating that pollwakeup() is referring
89 * it so that the port_t does not disappear. pollwakeup()
90 * calls port_pollwkdone() after notifying. In port_pollwkdone(),
91 * we clear the hold on the port_t (clear PORTQ_POLLWK_PEND).
92 */
93 int
port_pollwkup(port_t * pp)94 port_pollwkup(port_t *pp)
95 {
96 int events = 0;
97 port_queue_t *portq;
98 portq = &pp->port_queue;
99 mutex_enter(&portq->portq_mutex);
100
101 /*
102 * Normally, we should not have a situation where PORTQ_POLLIN
103 * and PORTQ_POLLWK_PEND are set at the same time, but it is
104 * possible. So, in pollwakeup() we ensure that no new fd's get
105 * added to the pollhead between the time it notifies poll events
106 * and calls poll_wkupdone() where we clear the PORTQ_POLLWK_PEND flag.
107 */
108 if (portq->portq_flags & PORTQ_POLLIN &&
109 !(portq->portq_flags & PORTQ_POLLWK_PEND)) {
110 portq->portq_flags &= ~PORTQ_POLLIN;
111 portq->portq_flags |= PORTQ_POLLWK_PEND;
112 events = POLLIN;
113 }
114 mutex_exit(&portq->portq_mutex);
115 return (events);
116 }
117
118 void
port_pollwkdone(port_t * pp)119 port_pollwkdone(port_t *pp)
120 {
121 port_queue_t *portq;
122 portq = &pp->port_queue;
123 ASSERT(portq->portq_flags & PORTQ_POLLWK_PEND);
124 mutex_enter(&portq->portq_mutex);
125 portq->portq_flags &= ~PORTQ_POLLWK_PEND;
126 cv_signal(&pp->port_cv);
127 mutex_exit(&portq->portq_mutex);
128 }
129
130
131 /*
132 * The port_send_event() function is used by all event sources to submit
133 * trigerred events to a port. All the data required for the event management
134 * is already stored in the port_kevent_t structure.
135 * The event port internal data is stored in the port_kevent_t structure
136 * during the allocation time (see port_alloc_event()). The data related to
137 * the event itself and to the event source management is stored in the
138 * port_kevent_t structure between the allocation time and submit time
139 * (see port_init_event()).
140 *
141 * This function is often called from interrupt level.
142 */
143 void
port_send_event(port_kevent_t * pkevp)144 port_send_event(port_kevent_t *pkevp)
145 {
146 port_queue_t *portq;
147
148 portq = &pkevp->portkev_port->port_queue;
149 mutex_enter(&portq->portq_mutex);
150
151 if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
152 /* Event already in the port queue */
153 if (pkevp->portkev_source == PORT_SOURCE_FD) {
154 mutex_exit(&pkevp->portkev_lock);
155 }
156 mutex_exit(&portq->portq_mutex);
157 return;
158 }
159
160 /* put event in the port queue */
161 list_insert_tail(&portq->portq_list, pkevp);
162 portq->portq_nent++;
163
164 /*
165 * Remove the PORTQ_WAIT_EVENTS flag to indicate
166 * that new events are available.
167 */
168 portq->portq_flags &= ~PORTQ_WAIT_EVENTS;
169 pkevp->portkev_flags |= PORT_KEV_DONEQ; /* event enqueued */
170
171 if (pkevp->portkev_source == PORT_SOURCE_FD) {
172 mutex_exit(&pkevp->portkev_lock);
173 }
174
175 /* Check if thread is in port_close() waiting for outstanding events */
176 if (portq->portq_flags & PORTQ_CLOSE) {
177 /* Check if all outstanding events are already in port queue */
178 if (pkevp->portkev_port->port_curr <= portq->portq_nent)
179 cv_signal(&portq->portq_closecv);
180 }
181
182 if (portq->portq_getn == 0) {
183 /*
184 * No thread retrieving events -> check if enough events are
185 * available to satify waiting threads.
186 */
187 if (portq->portq_thread &&
188 (portq->portq_nent >= portq->portq_nget))
189 cv_signal(&portq->portq_thread->portget_cv);
190 }
191
192 /*
193 * If some thread is polling the port's fd, then notify it.
194 * For PORT_SOURCE_FD source, we don't need to call pollwakeup()
195 * here as it will result in a recursive call(PORT_SOURCE_FD source
196 * is pollwakeup()). Therefore pollwakeup() itself will notify the
197 * ports if being polled.
198 */
199 if (pkevp->portkev_source != PORT_SOURCE_FD &&
200 portq->portq_flags & PORTQ_POLLIN) {
201 port_t *pp;
202
203 portq->portq_flags &= ~PORTQ_POLLIN;
204 /*
205 * Need to save port_t for calling pollwakeup since port_getn()
206 * may end up freeing pkevp once portq_mutex is dropped.
207 */
208 pp = pkevp->portkev_port;
209 mutex_exit(&portq->portq_mutex);
210 pollwakeup(&pp->port_pollhd, POLLIN);
211 } else {
212 mutex_exit(&portq->portq_mutex);
213 }
214 }
215
216 /*
217 * The port_alloc_event() function has to be used by all event sources
218 * to request an slot for event notification.
219 * The slot reservation could be denied because of lack of resources.
220 * For that reason the event source should allocate an event slot as early
221 * as possible and be prepared to get an error code instead of the
222 * port event pointer.
223 * Al current event sources allocate an event slot during a system call
224 * entry. They return an error code to the application if an event slot
225 * could not be reserved.
226 * It is also recommended to associate the event source with the port
227 * before some other port function is used.
228 * The port argument is a file descriptor obtained by the application as
229 * a return value of port_create().
230 * Possible values of flags are:
231 * PORT_ALLOC_DEFAULT
232 * This is the standard type of port events. port_get(n) will free this
233 * type of event structures as soon as the events are delivered to the
234 * application.
235 * PORT_ALLOC_PRIVATE
236 * This type of event will be use for private use of the event source.
237 * The port_get(n) function will deliver events of such an structure to
238 * the application but it will not free the event structure itself.
239 * The event source must free this structure using port_free_event().
240 * PORT_ALLOC_CACHED
241 * This type of events is used when the event source helds an own
242 * cache.
243 * The port_get(n) function will deliver events of such an structure to
244 * the application but it will not free the event structure itself.
245 * The event source must free this structure using port_free_event().
246 */
247 int
port_alloc_event(int port,int flags,int source,port_kevent_t ** pkevpp)248 port_alloc_event(int port, int flags, int source, port_kevent_t **pkevpp)
249 {
250 port_t *pp;
251 file_t *fp;
252 port_kevent_t *pkevp;
253
254 if ((fp = getf(port)) == NULL)
255 return (EBADF);
256
257 if (fp->f_vnode->v_type != VPORT) {
258 releasef(port);
259 return (EBADFD);
260 }
261
262 pkevp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP);
263 if (pkevp == NULL) {
264 releasef(port);
265 return (ENOMEM);
266 }
267
268 /*
269 * port_max_events is controlled by the resource control
270 * process.port-max-events
271 */
272 pp = VTOEP(fp->f_vnode);
273 mutex_enter(&pp->port_queue.portq_mutex);
274 if (pp->port_curr >= pp->port_max_events) {
275 mutex_exit(&pp->port_queue.portq_mutex);
276 kmem_cache_free(port_control.pc_cache, pkevp);
277 releasef(port);
278 return (EAGAIN);
279 }
280 pp->port_curr++;
281 mutex_exit(&pp->port_queue.portq_mutex);
282
283 bzero(pkevp, sizeof (port_kevent_t));
284 mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
285 pkevp->portkev_source = source;
286 pkevp->portkev_flags = flags;
287 pkevp->portkev_pid = curproc->p_pid;
288 pkevp->portkev_port = pp;
289 *pkevpp = pkevp;
290 releasef(port);
291 return (0);
292 }
293
294 /*
295 * This function is faster than the standard port_alloc_event() and
296 * can be used when the event source already allocated an event from
297 * a port.
298 */
299 int
port_dup_event(port_kevent_t * pkevp,port_kevent_t ** pkevdupp,int flags)300 port_dup_event(port_kevent_t *pkevp, port_kevent_t **pkevdupp, int flags)
301 {
302 int error;
303
304 error = port_alloc_event_local(pkevp->portkev_port,
305 pkevp->portkev_source, flags, pkevdupp);
306 if (error == 0)
307 (*pkevdupp)->portkev_pid = pkevp->portkev_pid;
308 return (error);
309 }
310
311 /*
312 * port_alloc_event_local() is reserved for internal use only.
313 * It is doing the same job as port_alloc_event() but with the event port
314 * pointer as the first argument.
315 * The check of the validity of the port file descriptor is skipped here.
316 */
317 int
port_alloc_event_local(port_t * pp,int source,int flags,port_kevent_t ** pkevpp)318 port_alloc_event_local(port_t *pp, int source, int flags,
319 port_kevent_t **pkevpp)
320 {
321 port_kevent_t *pkevp;
322
323 pkevp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP);
324 if (pkevp == NULL)
325 return (ENOMEM);
326
327 mutex_enter(&pp->port_queue.portq_mutex);
328 if (pp->port_curr >= pp->port_max_events) {
329 mutex_exit(&pp->port_queue.portq_mutex);
330 kmem_cache_free(port_control.pc_cache, pkevp);
331 return (EAGAIN);
332 }
333 pp->port_curr++;
334 mutex_exit(&pp->port_queue.portq_mutex);
335
336 bzero(pkevp, sizeof (port_kevent_t));
337 mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
338 pkevp->portkev_flags = flags;
339 pkevp->portkev_port = pp;
340 pkevp->portkev_source = source;
341 pkevp->portkev_pid = curproc->p_pid;
342 *pkevpp = pkevp;
343 return (0);
344 }
345
346 /*
347 * port_alloc_event_block() has the same functionality of port_alloc_event() +
348 * - it blocks if not enough event slots are available and
349 * - it blocks if not enough memory is available.
350 * Currently port_dispatch() is using this function to increase the
351 * reliability of event delivery for library event sources.
352 */
353 int
port_alloc_event_block(port_t * pp,int source,int flags,port_kevent_t ** pkevpp)354 port_alloc_event_block(port_t *pp, int source, int flags,
355 port_kevent_t **pkevpp)
356 {
357 port_kevent_t *pkevp =
358 kmem_cache_alloc(port_control.pc_cache, KM_SLEEP);
359
360 mutex_enter(&pp->port_queue.portq_mutex);
361 while (pp->port_curr >= pp->port_max_events) {
362 if (!cv_wait_sig(&pp->port_cv, &pp->port_queue.portq_mutex)) {
363 /* signal detected */
364 mutex_exit(&pp->port_queue.portq_mutex);
365 kmem_cache_free(port_control.pc_cache, pkevp);
366 return (EINTR);
367 }
368 }
369 pp->port_curr++;
370 mutex_exit(&pp->port_queue.portq_mutex);
371
372 bzero(pkevp, sizeof (port_kevent_t));
373 mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
374 pkevp->portkev_flags = flags;
375 pkevp->portkev_port = pp;
376 pkevp->portkev_source = source;
377 pkevp->portkev_pid = curproc->p_pid;
378 *pkevpp = pkevp;
379 return (0);
380 }
381
382 /*
383 * Take an event out of the port queue
384 */
385 static void
port_remove_event_doneq(port_kevent_t * pkevp,port_queue_t * portq)386 port_remove_event_doneq(port_kevent_t *pkevp, port_queue_t *portq)
387 {
388 ASSERT(MUTEX_HELD(&portq->portq_mutex));
389 list_remove(&portq->portq_list, pkevp);
390 portq->portq_nent--;
391 pkevp->portkev_flags &= ~PORT_KEV_DONEQ;
392 }
393
394 /*
395 * The port_remove_done_event() function takes a fired event out of the
396 * port queue.
397 * Currently this function is required to cancel a fired event because
398 * the application is delivering new association data (see port_associate_fd()).
399 */
400 int
port_remove_done_event(port_kevent_t * pkevp)401 port_remove_done_event(port_kevent_t *pkevp)
402 {
403 port_queue_t *portq;
404 int removed = 0;
405
406 portq = &pkevp->portkev_port->port_queue;
407 mutex_enter(&portq->portq_mutex);
408 /* wait for port_get() or port_getn() */
409 port_block(portq);
410 if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
411 /* event still in port queue */
412 if (portq->portq_getn) {
413 /*
414 * There could be still fired events in the temp queue;
415 * push those events back to the port queue and
416 * remove requested event afterwards.
417 */
418 port_push_eventq(portq);
419 }
420 /* now remove event from the port queue */
421 port_remove_event_doneq(pkevp, portq);
422 removed = 1;
423 }
424 port_unblock(portq);
425 mutex_exit(&portq->portq_mutex);
426 return (removed);
427 }
428
429 /*
430 * Return port event back to the kmem_cache.
431 * If the event is currently in the port queue the event itself will only
432 * be set as invalid. The port_get(n) function will not deliver such events
433 * to the application and it will return them back to the kmem_cache.
434 */
435 void
port_free_event(port_kevent_t * pkevp)436 port_free_event(port_kevent_t *pkevp)
437 {
438 port_queue_t *portq;
439 port_t *pp;
440
441 pp = pkevp->portkev_port;
442 if (pp == NULL)
443 return;
444 if (pkevp->portkev_flags & PORT_ALLOC_PRIVATE) {
445 port_free_event_local(pkevp, 0);
446 return;
447 }
448
449 portq = &pp->port_queue;
450 mutex_enter(&portq->portq_mutex);
451 port_block(portq);
452 if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
453 pkevp->portkev_flags |= PORT_KEV_FREE;
454 pkevp->portkev_callback = NULL;
455 port_unblock(portq);
456 mutex_exit(&portq->portq_mutex);
457 return;
458 }
459 port_unblock(portq);
460
461 if (pkevp->portkev_flags & PORT_KEV_CACHED) {
462 mutex_exit(&portq->portq_mutex);
463 return;
464 }
465
466 if (--pp->port_curr < pp->port_max_events)
467 cv_signal(&pp->port_cv);
468 if (portq->portq_flags & PORTQ_CLOSE) {
469 /*
470 * Another thread is closing the event port.
471 * That thread will sleep until all allocated event
472 * structures returned to the event port framework.
473 * The portq_mutex is used to synchronize the status
474 * of the allocated event structures (port_curr).
475 */
476 if (pp->port_curr <= portq->portq_nent)
477 cv_signal(&portq->portq_closecv);
478 }
479 mutex_exit(&portq->portq_mutex);
480 port_free_event_local(pkevp, 1);
481 }
482
483 /*
484 * This event port internal function is used by port_free_event() and
485 * other port internal functions to return event structures back to the
486 * kmem_cache.
487 */
488 void
port_free_event_local(port_kevent_t * pkevp,int counter)489 port_free_event_local(port_kevent_t *pkevp, int counter)
490 {
491 port_t *pp = pkevp->portkev_port;
492 port_queue_t *portq = &pp->port_queue;
493 int wakeup;
494
495 pkevp->portkev_callback = NULL;
496 pkevp->portkev_flags = 0;
497 pkevp->portkev_port = NULL;
498 mutex_destroy(&pkevp->portkev_lock);
499 kmem_cache_free(port_control.pc_cache, pkevp);
500
501 mutex_enter(&portq->portq_mutex);
502 if (counter == 0) {
503 if (--pp->port_curr < pp->port_max_events)
504 cv_signal(&pp->port_cv);
505 }
506 wakeup = (portq->portq_flags & PORTQ_POLLOUT);
507 portq->portq_flags &= ~PORTQ_POLLOUT;
508 mutex_exit(&portq->portq_mutex);
509
510 /* Submit a POLLOUT event if requested */
511 if (wakeup)
512 pollwakeup(&pp->port_pollhd, POLLOUT);
513 }
514
515 /*
516 * port_init_event(port_event_t *pev, uintptr_t object, void *user,
517 * int (*port_callback)(void *, int *, pid_t, int, void *), void *sysarg);
518 * This function initializes most of the "wired" elements of the port
519 * event structure. This is normally being used just after the allocation
520 * of the port event structure.
521 * pkevp : pointer to the port event structure
522 * object : object associated with this event structure
523 * user : user defined pointer delivered with the association function
524 * port_callback:
525 * Address of the callback function which will be called
526 * - just before the event is delivered to the application.
527 * The callback function is called in user context and can be
528 * used for copyouts, e.g.
529 * - on close() or dissociation of the event. The sub-system
530 * must remove immediately every existing association of
531 * some object with this event.
532 * sysarg : event source propietary data
533 */
534 void
port_init_event(port_kevent_t * pkevp,uintptr_t object,void * user,int (* port_callback)(void *,int *,pid_t,int,void *),void * sysarg)535 port_init_event(port_kevent_t *pkevp, uintptr_t object, void *user,
536 int (*port_callback)(void *, int *, pid_t, int, void *),
537 void *sysarg)
538 {
539 pkevp->portkev_object = object;
540 pkevp->portkev_user = user;
541 pkevp->portkev_callback = port_callback;
542 pkevp->portkev_arg = sysarg;
543 }
544
545 /*
546 * This routine removes a portfd_t from the fd cache's hash table.
547 */
548 void
port_pcache_remove_fd(port_fdcache_t * pcp,portfd_t * pfd)549 port_pcache_remove_fd(port_fdcache_t *pcp, portfd_t *pfd)
550 {
551 polldat_t *lpdp;
552 polldat_t *cpdp;
553 portfd_t **bucket;
554 polldat_t *pdp = PFTOD(pfd);
555
556 ASSERT(MUTEX_HELD(&pcp->pc_lock));
557 bucket = PORT_FD_BUCKET(pcp, pdp->pd_fd);
558 cpdp = PFTOD(*bucket);
559 if (pdp == cpdp) {
560 *bucket = PDTOF(pdp->pd_hashnext);
561 if (--pcp->pc_fdcount == 0) {
562 /*
563 * signal the thread which may have blocked in
564 * port_close_sourcefd() on lastclose waiting
565 * for pc_fdcount to drop to 0.
566 */
567 cv_signal(&pcp->pc_lclosecv);
568 }
569 kmem_free(pfd, sizeof (portfd_t));
570 return;
571 }
572
573 while (cpdp != NULL) {
574 lpdp = cpdp;
575 cpdp = cpdp->pd_hashnext;
576 if (cpdp == pdp) {
577 /* polldat struct found */
578 lpdp->pd_hashnext = pdp->pd_hashnext;
579 if (--pcp->pc_fdcount == 0) {
580 /*
581 * signal the thread which may have blocked in
582 * port_close_sourcefd() on lastclose waiting
583 * for pc_fdcount to drop to 0.
584 */
585 cv_signal(&pcp->pc_lclosecv);
586 }
587 break;
588 }
589 }
590 ASSERT(cpdp != NULL);
591 kmem_free(pfd, sizeof (portfd_t));
592 }
593
594 /*
595 * The port_push_eventq() function is used to move all remaining events
596 * from the temporary queue used in port_get(n)() to the standard port
597 * queue.
598 */
599 void
port_push_eventq(port_queue_t * portq)600 port_push_eventq(port_queue_t *portq)
601 {
602 /*
603 * Append temporary portq_get_list to the port queue. On return
604 * the temporary portq_get_list is empty.
605 */
606 list_move_tail(&portq->portq_list, &portq->portq_get_list);
607 portq->portq_nent += portq->portq_tnent;
608 portq->portq_tnent = 0;
609 }
610
611 /*
612 * The port_remove_fd_object() function frees all resources associated with
613 * delivered portfd_t structure. Returns 1 if the port_kevent was found
614 * and removed from the port queue.
615 */
616 int
port_remove_fd_object(portfd_t * pfd,port_t * pp,port_fdcache_t * pcp)617 port_remove_fd_object(portfd_t *pfd, port_t *pp, port_fdcache_t *pcp)
618 {
619 port_queue_t *portq;
620 polldat_t *pdp = PFTOD(pfd);
621 port_kevent_t *pkevp;
622 int error;
623 int removed = 0;
624
625 ASSERT(MUTEX_HELD(&pcp->pc_lock));
626 if (pdp->pd_php != NULL) {
627 pollhead_delete(pdp->pd_php, pdp);
628 pdp->pd_php = NULL;
629 }
630 pkevp = pdp->pd_portev;
631 portq = &pp->port_queue;
632 mutex_enter(&portq->portq_mutex);
633 port_block(portq);
634 if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
635 if (portq->portq_getn && portq->portq_tnent) {
636 /*
637 * move events from the temporary "get" queue
638 * back to the port queue
639 */
640 port_push_eventq(portq);
641 }
642 /* cleanup merged port queue */
643 port_remove_event_doneq(pkevp, portq);
644 removed = 1;
645 }
646 port_unblock(portq);
647 mutex_exit(&portq->portq_mutex);
648 if (pkevp->portkev_callback) {
649 (void) (*pkevp->portkev_callback)(pkevp->portkev_arg,
650 &error, pkevp->portkev_pid, PORT_CALLBACK_DISSOCIATE,
651 pkevp);
652 }
653 port_free_event_local(pkevp, 0);
654
655 /* remove polldat struct */
656 port_pcache_remove_fd(pcp, pfd);
657 return (removed);
658 }
659
660 /*
661 * The port_close_fd() function dissociates a file descriptor from a port
662 * and removes all allocated resources.
663 * close(2) detects in the uf_entry_t structure that the fd is associated
664 * with a port (at least one port).
665 * The fd can be associated with several ports.
666 */
667 void
port_close_pfd(portfd_t * pfd)668 port_close_pfd(portfd_t *pfd)
669 {
670 port_t *pp;
671 port_fdcache_t *pcp;
672
673 /*
674 * the portfd_t passed in should be for this proc.
675 */
676 ASSERT(curproc->p_pid == PFTOD(pfd)->pd_portev->portkev_pid);
677 pp = PFTOD(pfd)->pd_portev->portkev_port;
678 pcp = pp->port_queue.portq_pcp;
679 mutex_enter(&pcp->pc_lock);
680 (void) port_remove_fd_object(pfd, pp, pcp);
681 mutex_exit(&pcp->pc_lock);
682 }
683
684 /*
685 * The port_associate_ksource() function associates an event source with a port.
686 * On port_close() all associated sources are requested to free all local
687 * resources associated with the event port.
688 * The association of a source with a port can only be done one time. Further
689 * calls of this function will only increment the reference counter.
690 * The allocated port_source_t structure is removed from the port as soon as
691 * the reference counter becomes 0.
692 */
693 /* ARGSUSED */
694 int
port_associate_ksource(int port,int source,port_source_t ** portsrc,void (* port_src_close)(void *,int,pid_t,int),void * arg,int (* port_src_associate)(port_kevent_t *,int,int,uintptr_t,void *))695 port_associate_ksource(int port, int source, port_source_t **portsrc,
696 void (*port_src_close)(void *, int, pid_t, int), void *arg,
697 int (*port_src_associate)(port_kevent_t *, int, int, uintptr_t, void *))
698 {
699 port_t *pp;
700 file_t *fp;
701 port_source_t **ps;
702 port_source_t *pse;
703
704 if ((fp = getf(port)) == NULL)
705 return (EBADF);
706
707 if (fp->f_vnode->v_type != VPORT) {
708 releasef(port);
709 return (EBADFD);
710 }
711 pp = VTOEP(fp->f_vnode);
712
713 mutex_enter(&pp->port_queue.portq_source_mutex);
714 ps = &pp->port_queue.portq_scache[PORT_SHASH(source)];
715 for (pse = *ps; pse != NULL; pse = pse->portsrc_next) {
716 if (pse->portsrc_source == source)
717 break;
718 }
719
720 if (pse == NULL) {
721 /* Create association of the event source with the port */
722 pse = kmem_zalloc(sizeof (port_source_t), KM_NOSLEEP);
723 if (pse == NULL) {
724 mutex_exit(&pp->port_queue.portq_source_mutex);
725 releasef(port);
726 return (ENOMEM);
727 }
728 pse->portsrc_source = source;
729 pse->portsrc_close = port_src_close;
730 pse->portsrc_closearg = arg;
731 pse->portsrc_cnt = 1;
732 if (*ps)
733 pse->portsrc_next = (*ps)->portsrc_next;
734 *ps = pse;
735 } else {
736 /* entry already available, source is only requesting count */
737 pse->portsrc_cnt++;
738 }
739 mutex_exit(&pp->port_queue.portq_source_mutex);
740 releasef(port);
741 if (portsrc)
742 *portsrc = pse;
743 return (0);
744 }
745
746 /*
747 * The port_dissociate_ksource() function dissociates an event source from
748 * a port.
749 */
750 int
port_dissociate_ksource(int port,int source,port_source_t * ps)751 port_dissociate_ksource(int port, int source, port_source_t *ps)
752 {
753 port_t *pp;
754 file_t *fp;
755 port_source_t **psh;
756
757 if (ps == NULL)
758 return (EINVAL);
759
760 if ((fp = getf(port)) == NULL)
761 return (EBADF);
762
763 if (fp->f_vnode->v_type != VPORT) {
764 releasef(port);
765 return (EBADFD);
766 }
767 pp = VTOEP(fp->f_vnode);
768
769 mutex_enter(&pp->port_queue.portq_source_mutex);
770 if (--ps->portsrc_cnt == 0) {
771 /* last association removed -> free source structure */
772 if (ps->portsrc_prev == NULL) {
773 /* first entry */
774 psh = &pp->port_queue.portq_scache[PORT_SHASH(source)];
775 *psh = ps->portsrc_next;
776 if (ps->portsrc_next)
777 ps->portsrc_next->portsrc_prev = NULL;
778 } else {
779 ps->portsrc_prev->portsrc_next = ps->portsrc_next;
780 if (ps->portsrc_next)
781 ps->portsrc_next->portsrc_prev =
782 ps->portsrc_prev;
783 }
784 kmem_free(ps, sizeof (port_source_t));
785 }
786 mutex_exit(&pp->port_queue.portq_source_mutex);
787 releasef(port);
788 return (0);
789 }
790
791 void
free_fopdata(vnode_t * vp)792 free_fopdata(vnode_t *vp)
793 {
794 portfop_vp_t *pvp;
795 pvp = vp->v_fopdata;
796 ASSERT(pvp->pvp_femp == NULL);
797 mutex_destroy(&pvp->pvp_mutex);
798 list_destroy(&pvp->pvp_pfoplist);
799 kmem_free(pvp, sizeof (*pvp));
800 vp->v_fopdata = NULL;
801 }
802