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