17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 534709573Sraf * Common Development and Distribution License (the "License"). 634709573Sraf * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 2134709573Sraf 227c478bd9Sstevel@tonic-gate /* 23*3348528fSdm * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <sys/types.h> 307c478bd9Sstevel@tonic-gate #include <sys/systm.h> 317c478bd9Sstevel@tonic-gate #include <sys/cred.h> 327c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 337c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 34aa59c4cbSrsb #include <sys/vfs_opreg.h> 357c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 367c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 377c478bd9Sstevel@tonic-gate #include <sys/stat.h> 387c478bd9Sstevel@tonic-gate #include <sys/errno.h> 397c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 407c478bd9Sstevel@tonic-gate #include <sys/file.h> 417c478bd9Sstevel@tonic-gate #include <sys/kstat.h> 427c478bd9Sstevel@tonic-gate #include <sys/port_impl.h> 437c478bd9Sstevel@tonic-gate #include <sys/task.h> 447c478bd9Sstevel@tonic-gate #include <sys/project.h> 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate /* 477c478bd9Sstevel@tonic-gate * Event Ports can be shared across threads or across processes. 487c478bd9Sstevel@tonic-gate * Every thread/process can use an own event port or a group of them 497c478bd9Sstevel@tonic-gate * can use a single port. A major request was also to get the ability 507c478bd9Sstevel@tonic-gate * to submit user-defined events to a port. The idea of the 517c478bd9Sstevel@tonic-gate * user-defined events is to use the event ports for communication between 527c478bd9Sstevel@tonic-gate * threads/processes (like message queues). User defined-events are queued 537c478bd9Sstevel@tonic-gate * in a port with the same priority as other event types. 547c478bd9Sstevel@tonic-gate * 557c478bd9Sstevel@tonic-gate * Events are delivered only once. The thread/process which is waiting 567c478bd9Sstevel@tonic-gate * for events with the "highest priority" (priority here is related to the 577c478bd9Sstevel@tonic-gate * internal strategy to wakeup waiting threads) will retrieve the event, 587c478bd9Sstevel@tonic-gate * all other threads/processes will not be notified. There is also 597c478bd9Sstevel@tonic-gate * the requirement to have events which should be submitted immediately 607c478bd9Sstevel@tonic-gate * to all "waiting" threads. That is the main task of the alert event. 617c478bd9Sstevel@tonic-gate * The alert event is submitted by the application to a port. The port 627c478bd9Sstevel@tonic-gate * changes from a standard mode to the alert mode. Now all waiting threads 637c478bd9Sstevel@tonic-gate * will be awaken immediately and they will return with the alert event. 647c478bd9Sstevel@tonic-gate * Threads trying to retrieve events from a port in alert mode will 657c478bd9Sstevel@tonic-gate * return immediately with the alert event. 667c478bd9Sstevel@tonic-gate * 677c478bd9Sstevel@tonic-gate * 687c478bd9Sstevel@tonic-gate * An event port is like a kernel queue, which accept events submitted from 697c478bd9Sstevel@tonic-gate * user level as well as events submitted from kernel sub-systems. Sub-systems 707c478bd9Sstevel@tonic-gate * able to submit events to a port are the so-called "event sources". 717c478bd9Sstevel@tonic-gate * Current event sources: 727c478bd9Sstevel@tonic-gate * PORT_SOURCE_AIO : events submitted per transaction completion from 737c478bd9Sstevel@tonic-gate * POSIX-I/O framework. 747c478bd9Sstevel@tonic-gate * PORT_SOURCE_TIMER : events submitted when a timer fires 757c478bd9Sstevel@tonic-gate * (see timer_create(3RT)). 767c478bd9Sstevel@tonic-gate * PORT_SOURCE_FD : events submitted per file descriptor (see poll(2)). 777c478bd9Sstevel@tonic-gate * PORT_SOURCE_ALERT : events submitted from user. This is not really a 787c478bd9Sstevel@tonic-gate * single event, this is actually a port mode 797c478bd9Sstevel@tonic-gate * (see port_alert(3c)). 807c478bd9Sstevel@tonic-gate * PORT_SOURCE_USER : events submitted by applications with 817c478bd9Sstevel@tonic-gate * port_send(3c) or port_sendn(3c). 827c478bd9Sstevel@tonic-gate * 837c478bd9Sstevel@tonic-gate * There is a user API implemented in the libc library as well as a 847c478bd9Sstevel@tonic-gate * kernel API implemented in port_subr.c in genunix. 857c478bd9Sstevel@tonic-gate * The available user API functions are: 867c478bd9Sstevel@tonic-gate * port_create() : create a port as a file descriptor of portfs file system 877c478bd9Sstevel@tonic-gate * The standard close(2) function closes a port. 887c478bd9Sstevel@tonic-gate * port_associate() : associate a file descriptor with a port to be able to 897c478bd9Sstevel@tonic-gate * retrieve events from that file descriptor. 907c478bd9Sstevel@tonic-gate * port_dissociate(): remove the association of a file descriptor with a port. 917c478bd9Sstevel@tonic-gate * port_alert() : set/unset a port in alert mode 927c478bd9Sstevel@tonic-gate * port_send() : send an event of type PORT_SOURCE_USER to a port 937c478bd9Sstevel@tonic-gate * port_sendn() : send an event of type PORT_SOURCE_USER to a list of ports 947c478bd9Sstevel@tonic-gate * port_get() : retrieve a single event from a port 957c478bd9Sstevel@tonic-gate * port_getn() : retrieve a list of events from a port 967c478bd9Sstevel@tonic-gate * 977c478bd9Sstevel@tonic-gate * The available kernel API functions are: 987c478bd9Sstevel@tonic-gate * port_allocate_event(): allocate an event slot/structure of/from a port 997c478bd9Sstevel@tonic-gate * port_init_event() : set event data in the event structure 1007c478bd9Sstevel@tonic-gate * port_send_event() : send event to a port 1017c478bd9Sstevel@tonic-gate * port_free_event() : deliver allocated slot/structure back to a port 1027c478bd9Sstevel@tonic-gate * port_associate_ksource(): associate a kernel event source with a port 1037c478bd9Sstevel@tonic-gate * port_dissociate_ksource(): dissociate a kernel event source from a port 1047c478bd9Sstevel@tonic-gate * 1057c478bd9Sstevel@tonic-gate * The libc implementation consists of small functions which pass the 1067c478bd9Sstevel@tonic-gate * arguments to the kernel using the "portfs" system call. It means, all the 1077c478bd9Sstevel@tonic-gate * synchronisation work is being done in the kernel. The "portfs" system 1087c478bd9Sstevel@tonic-gate * call loads the portfs file system into the kernel. 1097c478bd9Sstevel@tonic-gate * 1107c478bd9Sstevel@tonic-gate * PORT CREATION 1117c478bd9Sstevel@tonic-gate * The first function to be used is port_create() which internally creates 1127c478bd9Sstevel@tonic-gate * a vnode and a portfs node. The portfs node is represented by the port_t 1137c478bd9Sstevel@tonic-gate * structure, which again includes all the data necessary to control a port. 1147c478bd9Sstevel@tonic-gate * port_create() returns a file descriptor, which needs to be used in almost 1157c478bd9Sstevel@tonic-gate * all other event port functions. 1167c478bd9Sstevel@tonic-gate * The maximum number of ports per system is controlled by the resource 1177c478bd9Sstevel@tonic-gate * control: project:port-max-ids. 1187c478bd9Sstevel@tonic-gate * 1197c478bd9Sstevel@tonic-gate * EVENT GENERATION 1207c478bd9Sstevel@tonic-gate * The second step is the triggering of events, which could be sent to a port. 1217c478bd9Sstevel@tonic-gate * Every event source implements an own method to generate events for a port: 1227c478bd9Sstevel@tonic-gate * PORT_SOURCE_AIO: 1237c478bd9Sstevel@tonic-gate * The sigevent structure of the standard POSIX-IO functions 1247c478bd9Sstevel@tonic-gate * was extended by an additional notification type. 1257c478bd9Sstevel@tonic-gate * Standard notification types: 1267c478bd9Sstevel@tonic-gate * SIGEV_NONE, SIGEV_SIGNAL and SIGEV_THREAD 1277c478bd9Sstevel@tonic-gate * Event ports introduced now SIGEV_PORT. 1287c478bd9Sstevel@tonic-gate * The notification type SIGEV_PORT specifies that a structure 1297c478bd9Sstevel@tonic-gate * of type port_notify_t has to be attached to the sigev_value. 1307c478bd9Sstevel@tonic-gate * The port_notify_t structure contains the event port file 1317c478bd9Sstevel@tonic-gate * descriptor and a user-defined pointer. 1327c478bd9Sstevel@tonic-gate * Internally the AIO implementation will use the kernel API 1337c478bd9Sstevel@tonic-gate * functions to allocate an event port slot per transaction (aiocb) 1347c478bd9Sstevel@tonic-gate * and sent the event to the port as soon as the transaction completes. 1357c478bd9Sstevel@tonic-gate * All the events submitted per transaction are of type 1367c478bd9Sstevel@tonic-gate * PORT_SOURCE_AIO. 1377c478bd9Sstevel@tonic-gate * PORT_SOURCE_TIMER: 1387c478bd9Sstevel@tonic-gate * The timer_create() function uses the same method as the 1397c478bd9Sstevel@tonic-gate * PORT_SOURCE_AIO event source. It also uses the sigevent structure 1407c478bd9Sstevel@tonic-gate * to deliver the port information. 1417c478bd9Sstevel@tonic-gate * Internally the timer code will allocate a single event slot/struct 1427c478bd9Sstevel@tonic-gate * per timer and it will send the timer event as soon as the timer 1437c478bd9Sstevel@tonic-gate * fires. If the timer-fired event is not delivered to the application 1447c478bd9Sstevel@tonic-gate * before the next period elapsed, then an overrun counter will be 1457c478bd9Sstevel@tonic-gate * incremented. The timer event source uses a callback function to 1467c478bd9Sstevel@tonic-gate * detect the delivery of the event to the application. At that time 1477c478bd9Sstevel@tonic-gate * the timer callback function will update the event overrun counter. 1487c478bd9Sstevel@tonic-gate * PORT_SOURCE_FD: 1497c478bd9Sstevel@tonic-gate * This event source uses the port_associate() function to allocate 1507c478bd9Sstevel@tonic-gate * an event slot/struct from a port. The application defines in the 1517c478bd9Sstevel@tonic-gate * events argument of port_associate() the type of events which it is 1527c478bd9Sstevel@tonic-gate * interested on. 1537c478bd9Sstevel@tonic-gate * The internal pollwakeup() function is used by all the file 1547c478bd9Sstevel@tonic-gate * systems --which are supporting the VOP_POLL() interface- to notify 1557c478bd9Sstevel@tonic-gate * the upper layer (poll(2), devpoll(7d) and now event ports) about 1567c478bd9Sstevel@tonic-gate * the event triggered (see valid events in poll(2)). 1577c478bd9Sstevel@tonic-gate * The pollwakeup() function forwards the event to the layer registered 1587c478bd9Sstevel@tonic-gate * to receive the current event. 1597c478bd9Sstevel@tonic-gate * The port_dissociate() function can be used to free the allocated 1607c478bd9Sstevel@tonic-gate * event slot from the port. Anyway, file descriptors deliver events 1617c478bd9Sstevel@tonic-gate * only one time and remain deactivated until the application 1627c478bd9Sstevel@tonic-gate * reactivates the association of a file descriptor with port_associate(). 1637c478bd9Sstevel@tonic-gate * If an associated file descriptor is closed then the file descriptor 1647c478bd9Sstevel@tonic-gate * will be dissociated automatically from the port. 1657c478bd9Sstevel@tonic-gate * 1667c478bd9Sstevel@tonic-gate * PORT_SOURCE_ALERT: 1677c478bd9Sstevel@tonic-gate * This event type is generated when the port was previously set in 1687c478bd9Sstevel@tonic-gate * alert mode using the port_alert() function. 1697c478bd9Sstevel@tonic-gate * A single alert event is delivered to every thread which tries to 1707c478bd9Sstevel@tonic-gate * retrieve events from a port. 1717c478bd9Sstevel@tonic-gate * PORT_SOURCE_USER: 1727c478bd9Sstevel@tonic-gate * This type of event is generated from user level using the port_send() 1737c478bd9Sstevel@tonic-gate * function to send a user event to a port or the port_sendn() function 1747c478bd9Sstevel@tonic-gate * to send an event to a list of ports. 1757c478bd9Sstevel@tonic-gate * 1767c478bd9Sstevel@tonic-gate * EVENT DELIVERY / RETRIEVING EVENTS 1777c478bd9Sstevel@tonic-gate * Events remain in the port queue until: 1787c478bd9Sstevel@tonic-gate * - the application uses port_get() or port_getn() to retrieve events, 1797c478bd9Sstevel@tonic-gate * - the event source cancel the event, 1807c478bd9Sstevel@tonic-gate * - the event port is closed or 1817c478bd9Sstevel@tonic-gate * - the process exits. 1827c478bd9Sstevel@tonic-gate * The maximal number of events in a port queue is the maximal number 1837c478bd9Sstevel@tonic-gate * of event slots/structures which can be allocated by event sources. 1847c478bd9Sstevel@tonic-gate * The allocation of event slots/structures is controlled by the resource 1857c478bd9Sstevel@tonic-gate * control: process.port-max-events. 1867c478bd9Sstevel@tonic-gate * The port_get() function retrieves a single event and the port_getn() 1877c478bd9Sstevel@tonic-gate * function retrieves a list of events. 1887c478bd9Sstevel@tonic-gate * Events are classified as shareable and non-shareable events across processes. 1897c478bd9Sstevel@tonic-gate * Non-shareable events are invisible for the port_get(n)() functions of 1907c478bd9Sstevel@tonic-gate * processes other than the owner of the event. 1917c478bd9Sstevel@tonic-gate * Shareable event types are: 1927c478bd9Sstevel@tonic-gate * PORT_SOURCE_USER events 1937c478bd9Sstevel@tonic-gate * This type of event is unconditionally shareable and without 1947c478bd9Sstevel@tonic-gate * limitations. If the parent process sends a user event and closes 1957c478bd9Sstevel@tonic-gate * the port afterwards, the event remains in the port and the child 1967c478bd9Sstevel@tonic-gate * process will still be able to retrieve the user event. 1977c478bd9Sstevel@tonic-gate * PORT_SOURCE_ALERT events 1987c478bd9Sstevel@tonic-gate * This type of event is shareable between processes. 1997c478bd9Sstevel@tonic-gate * Limitation: The alert mode of the port is removed if the owner 2007c478bd9Sstevel@tonic-gate * (process which set the port in alert mode) of the 2017c478bd9Sstevel@tonic-gate * alert event closes the port. 2027c478bd9Sstevel@tonic-gate * PORT_SOURCE_FD events 2037c478bd9Sstevel@tonic-gate * This type of event is conditional shareable between processes. 2047c478bd9Sstevel@tonic-gate * After fork(2) all forked file descriptors are shareable between 2057c478bd9Sstevel@tonic-gate * the processes. The child process is allowed to retrieve events 2067c478bd9Sstevel@tonic-gate * from the associated file descriptors and it can also re-associate 2077c478bd9Sstevel@tonic-gate * the fd with the port. 2087c478bd9Sstevel@tonic-gate * Limitations: The child process is not allowed to dissociate 2097c478bd9Sstevel@tonic-gate * the file descriptor from the port. Only the 2107c478bd9Sstevel@tonic-gate * owner (process) of the association is allowed to 2117c478bd9Sstevel@tonic-gate * dissociate the file descriptor from the port. 2127c478bd9Sstevel@tonic-gate * If the owner of the association closes the port 2137c478bd9Sstevel@tonic-gate * the association will be removed. 2147c478bd9Sstevel@tonic-gate * PORT_SOURCE_AIO events 2157c478bd9Sstevel@tonic-gate * This type of event is not shareable between processes. 2167c478bd9Sstevel@tonic-gate * PORT_SOURCE_TIMER events 2177c478bd9Sstevel@tonic-gate * This type of event is not shareable between processes. 2187c478bd9Sstevel@tonic-gate * 2197c478bd9Sstevel@tonic-gate * FORK BEHAVIOUR 2207c478bd9Sstevel@tonic-gate * On fork(2) the child process inherits all opened file descriptors from 2217c478bd9Sstevel@tonic-gate * the parent process. This is also valid for port file descriptors. 2227c478bd9Sstevel@tonic-gate * Associated file descriptors with a port maintain the association across the 2237c478bd9Sstevel@tonic-gate * fork(2). It means, the child process gets full access to the port and 2247c478bd9Sstevel@tonic-gate * it can retrieve events from all common associated file descriptors. 2257c478bd9Sstevel@tonic-gate * Events of file descriptors created and associated with a port after the 2267c478bd9Sstevel@tonic-gate * fork(2) are non-shareable and can only be retrieved by the same process. 2277c478bd9Sstevel@tonic-gate * 2287c478bd9Sstevel@tonic-gate * If the parent or the child process closes an exported port (using fork(2) 2297c478bd9Sstevel@tonic-gate * or I_SENDFD) all the file descriptors associated with the port by the 2307c478bd9Sstevel@tonic-gate * process will be dissociated from the port. Events of dissociated file 2317c478bd9Sstevel@tonic-gate * descriptors as well as all non-shareable events will be discarded. 2327c478bd9Sstevel@tonic-gate * The other process can continue working with the port as usual. 2337c478bd9Sstevel@tonic-gate * 2347c478bd9Sstevel@tonic-gate * CLOSING A PORT 2357c478bd9Sstevel@tonic-gate * close(2) has to be used to close a port. See FORK BEHAVIOUR for details. 2367c478bd9Sstevel@tonic-gate * 2377c478bd9Sstevel@tonic-gate * PORT EVENT STRUCTURES 2387c478bd9Sstevel@tonic-gate * The global control structure of the event ports framework is port_control_t. 2397c478bd9Sstevel@tonic-gate * port_control_t keeps track of the number of created ports in the system. 2407c478bd9Sstevel@tonic-gate * The cache of the port event structures is also located in port_control_t. 2417c478bd9Sstevel@tonic-gate * 2427c478bd9Sstevel@tonic-gate * On port_create() the vnode and the portfs node is also created. 2437c478bd9Sstevel@tonic-gate * The portfs node is represented by the port_t structure. 2447c478bd9Sstevel@tonic-gate * The port_t structure manages all port specific tasks: 2457c478bd9Sstevel@tonic-gate * - management of resource control values 2467c478bd9Sstevel@tonic-gate * - port VOP_POLL interface 2477c478bd9Sstevel@tonic-gate * - creation time 2487c478bd9Sstevel@tonic-gate * - uid and gid of the port 2497c478bd9Sstevel@tonic-gate * 2507c478bd9Sstevel@tonic-gate * The port_t structure contains the port_queue_t structure. 2517c478bd9Sstevel@tonic-gate * The port_queue_t structure contains all the data necessary for the 2527c478bd9Sstevel@tonic-gate * queue management: 2537c478bd9Sstevel@tonic-gate * - locking 2547c478bd9Sstevel@tonic-gate * - condition variables 2557c478bd9Sstevel@tonic-gate * - event counters 2567c478bd9Sstevel@tonic-gate * - submitted events (represented by port_kevent_t structures) 2577c478bd9Sstevel@tonic-gate * - threads waiting for event delivery (check portget_t structure) 2587c478bd9Sstevel@tonic-gate * - PORT_SOURCE_FD cache (managed by the port_fdcache_t structure) 2597c478bd9Sstevel@tonic-gate * - event source management (managed by the port_source_t structure) 2607c478bd9Sstevel@tonic-gate * - alert mode management (check port_alert_t structure) 2617c478bd9Sstevel@tonic-gate * 2627c478bd9Sstevel@tonic-gate * EVENT MANAGEMENT 2637c478bd9Sstevel@tonic-gate * The event port file system creates a kmem_cache for internal allocation of 2647c478bd9Sstevel@tonic-gate * event port structures. 2657c478bd9Sstevel@tonic-gate * 2667c478bd9Sstevel@tonic-gate * 1. Event source association with a port: 2677c478bd9Sstevel@tonic-gate * The first step to do for event sources is to get associated with a port 2687c478bd9Sstevel@tonic-gate * using the port_associate_ksource() function or adding an entry to the 2697c478bd9Sstevel@tonic-gate * port_ksource_tab[]. An event source can get dissociated from a port 2707c478bd9Sstevel@tonic-gate * using the port_dissociate_ksource() function. An entry in the 2717c478bd9Sstevel@tonic-gate * port_ksource_tab[] implies that the source will be associated 2727c478bd9Sstevel@tonic-gate * automatically with every new created port. 2737c478bd9Sstevel@tonic-gate * The event source can deliver a callback function, which is used by the 2747c478bd9Sstevel@tonic-gate * port to notify the event source about close(2). The idea is that 2757c478bd9Sstevel@tonic-gate * in such a case the event source should free all allocated resources 2767c478bd9Sstevel@tonic-gate * and it must return to the port all allocated slots/structures. 2777c478bd9Sstevel@tonic-gate * The port_close() function will wait until all allocated event 2787c478bd9Sstevel@tonic-gate * structures/slots are returned to the port. 2797c478bd9Sstevel@tonic-gate * The callback function is not necessary when the event source does not 2807c478bd9Sstevel@tonic-gate * maintain local resources, a second condition is that the event source 2817c478bd9Sstevel@tonic-gate * can guarantee that allocated event slots will be returned without 2827c478bd9Sstevel@tonic-gate * delay to the port (it will not block and sleep somewhere). 2837c478bd9Sstevel@tonic-gate * 2847c478bd9Sstevel@tonic-gate * 2. Reservation of an event slot / event structure 2857c478bd9Sstevel@tonic-gate * The event port reliability is based on the reservation of an event "slot" 2867c478bd9Sstevel@tonic-gate * (allocation of an event structure) by the event source as part of the 2877c478bd9Sstevel@tonic-gate * application call. If the maximal number of event slots is exhausted then 2887c478bd9Sstevel@tonic-gate * the event source can return a corresponding error code to the application. 2897c478bd9Sstevel@tonic-gate * 2907c478bd9Sstevel@tonic-gate * The port_alloc_event() function has to be used by event sources to 2917c478bd9Sstevel@tonic-gate * allocate an event slot (reserve an event structure). The port_alloc_event() 2927c478bd9Sstevel@tonic-gate * doesn not block and it will return a 0 value on success or an error code 2937c478bd9Sstevel@tonic-gate * if it fails. 2947c478bd9Sstevel@tonic-gate * An argument of port_alloc_event() is a flag which determines the behavior 2957c478bd9Sstevel@tonic-gate * of the event after it was delivered to the application: 2967c478bd9Sstevel@tonic-gate * PORT_ALLOC_DEFAULT : event slot becomes free after delivery to the 2977c478bd9Sstevel@tonic-gate * application. 2987c478bd9Sstevel@tonic-gate * PORT_ALLOC_PRIVATE : event slot remains under the control of the event 2997c478bd9Sstevel@tonic-gate * source. This kind of slots can not be used for 3007c478bd9Sstevel@tonic-gate * event delivery and should only be used internally 3017c478bd9Sstevel@tonic-gate * by the event source. 3027c478bd9Sstevel@tonic-gate * PORT_KEV_CACHED : event slot remains under the control of an event 3037c478bd9Sstevel@tonic-gate * port cache. It does not become free after delivery 3047c478bd9Sstevel@tonic-gate * to the application. 3057c478bd9Sstevel@tonic-gate * PORT_ALLOC_SCACHED : event slot remains under the control of the event 3067c478bd9Sstevel@tonic-gate * source. The event source takes the control over 3077c478bd9Sstevel@tonic-gate * the slot after the event is delivered to the 3087c478bd9Sstevel@tonic-gate * application. 3097c478bd9Sstevel@tonic-gate * 3107c478bd9Sstevel@tonic-gate * 3. Delivery of events to the event port 3117c478bd9Sstevel@tonic-gate * Earlier allocated event structure/slot has to be used to deliver 3127c478bd9Sstevel@tonic-gate * event data to the port. Event source has to use the function 3137c478bd9Sstevel@tonic-gate * port_send_event(). The single argument is a pointer to the previously 3147c478bd9Sstevel@tonic-gate * reserved event structure/slot. 3157c478bd9Sstevel@tonic-gate * The portkev_events field of the port_kevent_t structure can be updated/set 3167c478bd9Sstevel@tonic-gate * in two ways: 3177c478bd9Sstevel@tonic-gate * 1. using the port_set_event() function, or 3187c478bd9Sstevel@tonic-gate * 2. updating the portkev_events field out of the callback function: 3197c478bd9Sstevel@tonic-gate * The event source can deliver a callback function to the port as an 3207c478bd9Sstevel@tonic-gate * argument of port_init_event(). 3217c478bd9Sstevel@tonic-gate * One of the arguments of the callback function is a pointer to the 3227c478bd9Sstevel@tonic-gate * events field, which will be delivered to the application. 3237c478bd9Sstevel@tonic-gate * (see Delivery of events to the application). 3247c478bd9Sstevel@tonic-gate * Event structures/slots can be delivered to the event port only one time, 3257c478bd9Sstevel@tonic-gate * they remain blocked until the data is delivered to the application and the 3267c478bd9Sstevel@tonic-gate * slot becomes free or it is delivered back to the event source 3277c478bd9Sstevel@tonic-gate * (PORT_ALLOC_SCACHED). The activation of the callback function mentioned above 3287c478bd9Sstevel@tonic-gate * is at the same time the indicator for the event source that the event 3297c478bd9Sstevel@tonic-gate * structure/slot is free for reuse. 3307c478bd9Sstevel@tonic-gate * 3317c478bd9Sstevel@tonic-gate * 4. Delivery of events to the application 3327c478bd9Sstevel@tonic-gate * The events structures/slots delivered by event sources remain in the 3337c478bd9Sstevel@tonic-gate * port queue until they are retrieved by the application or the port 3347c478bd9Sstevel@tonic-gate * is closed (exit(2) also closes all opened file descriptors).. 3357c478bd9Sstevel@tonic-gate * The application uses port_get() or port_getn() to retrieve events from 3367c478bd9Sstevel@tonic-gate * a port. port_get() retrieves a single event structure/slot and port_getn() 3377c478bd9Sstevel@tonic-gate * retrieves a list of event structures/slots. 3387c478bd9Sstevel@tonic-gate * Both functions are able to poll for events and return immediately or they 3397c478bd9Sstevel@tonic-gate * can specify a timeout value. 3407c478bd9Sstevel@tonic-gate * Before the events are delivered to the application they are moved to a 3417c478bd9Sstevel@tonic-gate * second temporary internal queue. The idea is to avoid lock collisions or 3427c478bd9Sstevel@tonic-gate * contentions of the global queue lock. 3437c478bd9Sstevel@tonic-gate * The global queue lock is used every time when an event source delivers 3447c478bd9Sstevel@tonic-gate * new events to the port. 3457c478bd9Sstevel@tonic-gate * The port_get() and port_getn() functions 3467c478bd9Sstevel@tonic-gate * a) retrieve single events from the temporary queue, 3477c478bd9Sstevel@tonic-gate * b) prepare the data to be passed to the application memory, 3487c478bd9Sstevel@tonic-gate * c) activate the callback function of the event sources: 3497c478bd9Sstevel@tonic-gate * - to get the latest event data, 3507c478bd9Sstevel@tonic-gate * - the event source can free all allocated resources associated with the 3517c478bd9Sstevel@tonic-gate * current event, 3527c478bd9Sstevel@tonic-gate * - the event source can re-use the current event slot/structure 3537c478bd9Sstevel@tonic-gate * - the event source can deny the delivery of the event to the application 3547c478bd9Sstevel@tonic-gate * (e.g. because of the wrong process). 3557c478bd9Sstevel@tonic-gate * d) put the event back to the temporary queue if the event delivery was denied 3567c478bd9Sstevel@tonic-gate * e) repeat a) until d) as long as there are events in the queue and 3577c478bd9Sstevel@tonic-gate * there is enough user space available. 3587c478bd9Sstevel@tonic-gate * 3597c478bd9Sstevel@tonic-gate * The loop described above could block for a very long time the global mutex, 3607c478bd9Sstevel@tonic-gate * to avoid that a second mutex was introduced to synchronized concurrent 3617c478bd9Sstevel@tonic-gate * threads accessing the temporary queue. 3627c478bd9Sstevel@tonic-gate */ 3637c478bd9Sstevel@tonic-gate 3647c478bd9Sstevel@tonic-gate static int64_t portfs(int, uintptr_t, uintptr_t, uintptr_t, uintptr_t, 3657c478bd9Sstevel@tonic-gate uintptr_t); 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate static struct sysent port_sysent = { 3687c478bd9Sstevel@tonic-gate 6, 3697c478bd9Sstevel@tonic-gate SE_ARGC | SE_64RVAL | SE_NOUNLOAD, 3707c478bd9Sstevel@tonic-gate (int (*)())portfs, 3717c478bd9Sstevel@tonic-gate }; 3727c478bd9Sstevel@tonic-gate 3737c478bd9Sstevel@tonic-gate static struct modlsys modlsys = { 3747c478bd9Sstevel@tonic-gate &mod_syscallops, "event ports", &port_sysent 3757c478bd9Sstevel@tonic-gate }; 3767c478bd9Sstevel@tonic-gate 3777c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 3787c478bd9Sstevel@tonic-gate 3797c478bd9Sstevel@tonic-gate static int64_t 3807c478bd9Sstevel@tonic-gate portfs32(uint32_t arg1, int32_t arg2, uint32_t arg3, uint32_t arg4, 3817c478bd9Sstevel@tonic-gate uint32_t arg5, uint32_t arg6); 3827c478bd9Sstevel@tonic-gate 3837c478bd9Sstevel@tonic-gate static struct sysent port_sysent32 = { 3847c478bd9Sstevel@tonic-gate 6, 3857c478bd9Sstevel@tonic-gate SE_ARGC | SE_64RVAL | SE_NOUNLOAD, 3867c478bd9Sstevel@tonic-gate (int (*)())portfs32, 3877c478bd9Sstevel@tonic-gate }; 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate static struct modlsys modlsys32 = { 3907c478bd9Sstevel@tonic-gate &mod_syscallops32, 3917c478bd9Sstevel@tonic-gate "32-bit event ports syscalls", 3927c478bd9Sstevel@tonic-gate &port_sysent32 3937c478bd9Sstevel@tonic-gate }; 3947c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */ 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 3977c478bd9Sstevel@tonic-gate MODREV_1, 3987c478bd9Sstevel@tonic-gate &modlsys, 3997c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 4007c478bd9Sstevel@tonic-gate &modlsys32, 4017c478bd9Sstevel@tonic-gate #endif 4027c478bd9Sstevel@tonic-gate NULL 4037c478bd9Sstevel@tonic-gate }; 4047c478bd9Sstevel@tonic-gate 4057c478bd9Sstevel@tonic-gate port_kstat_t port_kstat = { 4067c478bd9Sstevel@tonic-gate { "ports", KSTAT_DATA_UINT32 } 4077c478bd9Sstevel@tonic-gate }; 4087c478bd9Sstevel@tonic-gate 4097c478bd9Sstevel@tonic-gate dev_t portdev; 4107c478bd9Sstevel@tonic-gate struct vnodeops *port_vnodeops; 4117c478bd9Sstevel@tonic-gate struct vfs port_vfs; 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate extern rctl_hndl_t rc_process_portev; 4147c478bd9Sstevel@tonic-gate extern rctl_hndl_t rc_project_portids; 4157c478bd9Sstevel@tonic-gate extern void aio_close_port(void *, int, pid_t, int); 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate /* 4187c478bd9Sstevel@tonic-gate * This table contains a list of event sources which need a static 4197c478bd9Sstevel@tonic-gate * association with a port (every port). 4207c478bd9Sstevel@tonic-gate * The last NULL entry in the table is required to detect "end of table". 4217c478bd9Sstevel@tonic-gate */ 4227c478bd9Sstevel@tonic-gate struct port_ksource port_ksource_tab[] = { 4237c478bd9Sstevel@tonic-gate {PORT_SOURCE_AIO, aio_close_port, NULL, NULL}, 4247c478bd9Sstevel@tonic-gate {0, NULL, NULL, NULL} 4257c478bd9Sstevel@tonic-gate }; 4267c478bd9Sstevel@tonic-gate 4277c478bd9Sstevel@tonic-gate /* local functions */ 4287c478bd9Sstevel@tonic-gate static int port_getn(port_t *, port_event_t *, uint_t, uint_t *, 4297c478bd9Sstevel@tonic-gate port_gettimer_t *); 4307c478bd9Sstevel@tonic-gate static int port_sendn(int [], int [], uint_t, int, void *, uint_t *); 4317c478bd9Sstevel@tonic-gate static int port_alert(port_t *, int, int, void *); 4327c478bd9Sstevel@tonic-gate static int port_dispatch_event(port_t *, int, int, int, uintptr_t, void *); 4337c478bd9Sstevel@tonic-gate static int port_send(port_t *, int, int, void *); 4347c478bd9Sstevel@tonic-gate static int port_create(int *); 4357c478bd9Sstevel@tonic-gate static int port_get_alert(port_alert_t *, port_event_t *); 4367c478bd9Sstevel@tonic-gate static int port_copy_event(port_event_t *, port_kevent_t *, list_t *); 4377c478bd9Sstevel@tonic-gate static int *port_errorn(int *, int, int, int); 4387c478bd9Sstevel@tonic-gate static int port_noshare(void *, int *, pid_t, int, void *); 4397c478bd9Sstevel@tonic-gate static int port_get_timeout(timespec_t *, timespec_t *, timespec_t **, int *, 4407c478bd9Sstevel@tonic-gate int); 4417c478bd9Sstevel@tonic-gate static void port_init(port_t *); 4427c478bd9Sstevel@tonic-gate static void port_remove_alert(port_queue_t *); 4437c478bd9Sstevel@tonic-gate static void port_add_ksource_local(port_t *, port_ksource_t *); 4447c478bd9Sstevel@tonic-gate static void port_check_return_cond(port_queue_t *); 4457c478bd9Sstevel@tonic-gate static void port_dequeue_thread(port_queue_t *, portget_t *); 4467c478bd9Sstevel@tonic-gate static portget_t *port_queue_thread(port_queue_t *, uint_t); 4477c478bd9Sstevel@tonic-gate static void port_kstat_init(void); 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 4507c478bd9Sstevel@tonic-gate static int port_copy_event32(port_event32_t *, port_kevent_t *, list_t *); 4517c478bd9Sstevel@tonic-gate #endif 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate int 4547c478bd9Sstevel@tonic-gate _init(void) 4557c478bd9Sstevel@tonic-gate { 4567c478bd9Sstevel@tonic-gate static const fs_operation_def_t port_vfsops_template[] = { 4577c478bd9Sstevel@tonic-gate NULL, NULL 4587c478bd9Sstevel@tonic-gate }; 4597c478bd9Sstevel@tonic-gate extern const fs_operation_def_t port_vnodeops_template[]; 4607c478bd9Sstevel@tonic-gate vfsops_t *port_vfsops; 4617c478bd9Sstevel@tonic-gate int error; 4627c478bd9Sstevel@tonic-gate major_t major; 4637c478bd9Sstevel@tonic-gate 4647c478bd9Sstevel@tonic-gate if ((major = getudev()) == (major_t)-1) 4657c478bd9Sstevel@tonic-gate return (ENXIO); 4667c478bd9Sstevel@tonic-gate portdev = makedevice(major, 0); 4677c478bd9Sstevel@tonic-gate 4687c478bd9Sstevel@tonic-gate /* Create a dummy vfs */ 4697c478bd9Sstevel@tonic-gate error = vfs_makefsops(port_vfsops_template, &port_vfsops); 4707c478bd9Sstevel@tonic-gate if (error) { 4717c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "port init: bad vfs ops"); 4727c478bd9Sstevel@tonic-gate return (error); 4737c478bd9Sstevel@tonic-gate } 4747c478bd9Sstevel@tonic-gate vfs_setops(&port_vfs, port_vfsops); 4757c478bd9Sstevel@tonic-gate port_vfs.vfs_flag = VFS_RDONLY; 4767c478bd9Sstevel@tonic-gate port_vfs.vfs_dev = portdev; 4777c478bd9Sstevel@tonic-gate vfs_make_fsid(&(port_vfs.vfs_fsid), portdev, 0); 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate error = vn_make_ops("portfs", port_vnodeops_template, &port_vnodeops); 4807c478bd9Sstevel@tonic-gate if (error) { 4817c478bd9Sstevel@tonic-gate vfs_freevfsops(port_vfsops); 4827c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "port init: bad vnode ops"); 4837c478bd9Sstevel@tonic-gate return (error); 4847c478bd9Sstevel@tonic-gate } 4857c478bd9Sstevel@tonic-gate 4867c478bd9Sstevel@tonic-gate mutex_init(&port_control.pc_mutex, NULL, MUTEX_DEFAULT, NULL); 4877c478bd9Sstevel@tonic-gate port_control.pc_nents = 0; /* number of active ports */ 4887c478bd9Sstevel@tonic-gate 4897c478bd9Sstevel@tonic-gate /* create kmem_cache for port event structures */ 4907c478bd9Sstevel@tonic-gate port_control.pc_cache = kmem_cache_create("port_cache", 4917c478bd9Sstevel@tonic-gate sizeof (port_kevent_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate port_kstat_init(); /* init port kstats */ 4947c478bd9Sstevel@tonic-gate return (mod_install(&modlinkage)); 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate 4977c478bd9Sstevel@tonic-gate int 4987c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 4997c478bd9Sstevel@tonic-gate { 5007c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 5017c478bd9Sstevel@tonic-gate } 5027c478bd9Sstevel@tonic-gate 5037c478bd9Sstevel@tonic-gate /* 5047c478bd9Sstevel@tonic-gate * System call wrapper for all port related system calls from 32-bit programs. 5057c478bd9Sstevel@tonic-gate */ 5067c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 5077c478bd9Sstevel@tonic-gate static int64_t 5087c478bd9Sstevel@tonic-gate portfs32(uint32_t opcode, int32_t a0, uint32_t a1, uint32_t a2, uint32_t a3, 5097c478bd9Sstevel@tonic-gate uint32_t a4) 5107c478bd9Sstevel@tonic-gate { 5117c478bd9Sstevel@tonic-gate int64_t error; 5127c478bd9Sstevel@tonic-gate 5137c478bd9Sstevel@tonic-gate switch (opcode & PORT_CODE_MASK) { 5147c478bd9Sstevel@tonic-gate case PORT_GET: 5157c478bd9Sstevel@tonic-gate error = portfs(PORT_GET, a0, a1, (int)a2, (int)a3, a4); 5167c478bd9Sstevel@tonic-gate break; 5177c478bd9Sstevel@tonic-gate case PORT_SENDN: 5187c478bd9Sstevel@tonic-gate error = portfs(opcode, (uint32_t)a0, a1, a2, a3, a4); 5197c478bd9Sstevel@tonic-gate break; 5207c478bd9Sstevel@tonic-gate default: 5217c478bd9Sstevel@tonic-gate error = portfs(opcode, a0, a1, a2, a3, a4); 5227c478bd9Sstevel@tonic-gate break; 5237c478bd9Sstevel@tonic-gate } 5247c478bd9Sstevel@tonic-gate return (error); 5257c478bd9Sstevel@tonic-gate } 5267c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */ 5277c478bd9Sstevel@tonic-gate 5287c478bd9Sstevel@tonic-gate /* 5297c478bd9Sstevel@tonic-gate * System entry point for port functions. 5307c478bd9Sstevel@tonic-gate * a0 is a port file descriptor (except for PORT_SENDN and PORT_CREATE). 5317c478bd9Sstevel@tonic-gate * The libc uses PORT_SYS_NOPORT in functions which do not deliver a 5327c478bd9Sstevel@tonic-gate * port file descriptor as first argument. 5337c478bd9Sstevel@tonic-gate */ 5347c478bd9Sstevel@tonic-gate static int64_t 5357c478bd9Sstevel@tonic-gate portfs(int opcode, uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, 5367c478bd9Sstevel@tonic-gate uintptr_t a4) 5377c478bd9Sstevel@tonic-gate { 5387c478bd9Sstevel@tonic-gate rval_t r; 5397c478bd9Sstevel@tonic-gate port_t *pp; 5407c478bd9Sstevel@tonic-gate int error = 0; 5417c478bd9Sstevel@tonic-gate uint_t nget; 5427c478bd9Sstevel@tonic-gate file_t *fp; 5437c478bd9Sstevel@tonic-gate port_gettimer_t port_timer; 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate r.r_vals = 0; 5467c478bd9Sstevel@tonic-gate if (opcode & PORT_SYS_NOPORT) { 5477c478bd9Sstevel@tonic-gate opcode &= PORT_CODE_MASK; 5487c478bd9Sstevel@tonic-gate if (opcode == PORT_SENDN) { 5497c478bd9Sstevel@tonic-gate error = port_sendn((int *)a0, (int *)a1, (uint_t)a2, 5507c478bd9Sstevel@tonic-gate (int)a3, (void *)a4, (uint_t *)&r.r_val1); 5517c478bd9Sstevel@tonic-gate if (error && (error != EIO)) 5527c478bd9Sstevel@tonic-gate return ((int64_t)set_errno(error)); 5537c478bd9Sstevel@tonic-gate return (r.r_vals); 5547c478bd9Sstevel@tonic-gate } 5557c478bd9Sstevel@tonic-gate 5567c478bd9Sstevel@tonic-gate if (opcode == PORT_CREATE) { 5577c478bd9Sstevel@tonic-gate error = port_create(&r.r_val1); 5587c478bd9Sstevel@tonic-gate if (error) 5597c478bd9Sstevel@tonic-gate return ((int64_t)set_errno(error)); 5607c478bd9Sstevel@tonic-gate return (r.r_vals); 5617c478bd9Sstevel@tonic-gate } 5627c478bd9Sstevel@tonic-gate } 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate /* opcodes using port as first argument (a0) */ 5657c478bd9Sstevel@tonic-gate 5667c478bd9Sstevel@tonic-gate if ((fp = getf((int)a0)) == NULL) 5677c478bd9Sstevel@tonic-gate return ((uintptr_t)set_errno(EBADF)); 5687c478bd9Sstevel@tonic-gate 5697c478bd9Sstevel@tonic-gate if (fp->f_vnode->v_type != VPORT) { 5707c478bd9Sstevel@tonic-gate releasef((int)a0); 5717c478bd9Sstevel@tonic-gate return ((uintptr_t)set_errno(EBADFD)); 5727c478bd9Sstevel@tonic-gate } 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate pp = VTOEP(fp->f_vnode); 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate switch (opcode & PORT_CODE_MASK) { 5777c478bd9Sstevel@tonic-gate case PORT_GET: 5787c478bd9Sstevel@tonic-gate { 5797c478bd9Sstevel@tonic-gate /* see PORT_GETN description */ 5807c478bd9Sstevel@tonic-gate struct timespec timeout; 5817c478bd9Sstevel@tonic-gate 5827c478bd9Sstevel@tonic-gate port_timer.pgt_flags = PORTGET_ONE; 5837c478bd9Sstevel@tonic-gate port_timer.pgt_loop = 0; 5847c478bd9Sstevel@tonic-gate port_timer.pgt_rqtp = NULL; 5857c478bd9Sstevel@tonic-gate if (a4 != NULL) { 5867c478bd9Sstevel@tonic-gate port_timer.pgt_timeout = &timeout; 5877c478bd9Sstevel@tonic-gate timeout.tv_sec = (time_t)a2; 5887c478bd9Sstevel@tonic-gate timeout.tv_nsec = (long)a3; 5897c478bd9Sstevel@tonic-gate } else { 5907c478bd9Sstevel@tonic-gate port_timer.pgt_timeout = NULL; 5917c478bd9Sstevel@tonic-gate } 5927c478bd9Sstevel@tonic-gate do { 5937c478bd9Sstevel@tonic-gate nget = 1; 5947c478bd9Sstevel@tonic-gate error = port_getn(pp, (port_event_t *)a1, 1, 5957c478bd9Sstevel@tonic-gate (uint_t *)&nget, &port_timer); 5967c478bd9Sstevel@tonic-gate } while (nget == 0 && error == 0 && port_timer.pgt_loop); 5977c478bd9Sstevel@tonic-gate break; 5987c478bd9Sstevel@tonic-gate } 5997c478bd9Sstevel@tonic-gate case PORT_GETN: 6007c478bd9Sstevel@tonic-gate { 6017c478bd9Sstevel@tonic-gate /* 6027c478bd9Sstevel@tonic-gate * port_getn() can only retrieve own or shareable events from 6037c478bd9Sstevel@tonic-gate * other processes. The port_getn() function remains in the 6047c478bd9Sstevel@tonic-gate * kernel until own or shareable events are available or the 6057c478bd9Sstevel@tonic-gate * timeout elapses. 6067c478bd9Sstevel@tonic-gate */ 6077c478bd9Sstevel@tonic-gate port_timer.pgt_flags = 0; 6087c478bd9Sstevel@tonic-gate port_timer.pgt_loop = 0; 6097c478bd9Sstevel@tonic-gate port_timer.pgt_rqtp = NULL; 6107c478bd9Sstevel@tonic-gate port_timer.pgt_timeout = (struct timespec *)a4; 6117c478bd9Sstevel@tonic-gate do { 6127c478bd9Sstevel@tonic-gate nget = a3; 6137c478bd9Sstevel@tonic-gate error = port_getn(pp, (port_event_t *)a1, (uint_t)a2, 6147c478bd9Sstevel@tonic-gate (uint_t *)&nget, &port_timer); 6157c478bd9Sstevel@tonic-gate } while (nget == 0 && error == 0 && port_timer.pgt_loop); 6167c478bd9Sstevel@tonic-gate r.r_val1 = nget; 6177c478bd9Sstevel@tonic-gate r.r_val2 = error; 6187c478bd9Sstevel@tonic-gate releasef((int)a0); 6197c478bd9Sstevel@tonic-gate if (error && error != ETIME) 6207c478bd9Sstevel@tonic-gate return ((int64_t)set_errno(error)); 6217c478bd9Sstevel@tonic-gate return (r.r_vals); 6227c478bd9Sstevel@tonic-gate } 6237c478bd9Sstevel@tonic-gate case PORT_ASSOCIATE: 6247c478bd9Sstevel@tonic-gate { 6257c478bd9Sstevel@tonic-gate /* currently only PORT_SOURCE_FD is implemented */ 6267c478bd9Sstevel@tonic-gate if ((int)a1 != PORT_SOURCE_FD) { 6277c478bd9Sstevel@tonic-gate error = EINVAL; 6287c478bd9Sstevel@tonic-gate break; 6297c478bd9Sstevel@tonic-gate } 6307c478bd9Sstevel@tonic-gate error = port_associate_fd(pp, (int)a1, (uintptr_t)a2, (int)a3, 6317c478bd9Sstevel@tonic-gate (void *)a4); 6327c478bd9Sstevel@tonic-gate break; 6337c478bd9Sstevel@tonic-gate } 6347c478bd9Sstevel@tonic-gate case PORT_SEND: 6357c478bd9Sstevel@tonic-gate { 6367c478bd9Sstevel@tonic-gate /* user-defined events */ 6377c478bd9Sstevel@tonic-gate error = port_send(pp, PORT_SOURCE_USER, (int)a1, (void *)a2); 6387c478bd9Sstevel@tonic-gate break; 6397c478bd9Sstevel@tonic-gate } 6407c478bd9Sstevel@tonic-gate case PORT_DISPATCH: 6417c478bd9Sstevel@tonic-gate { 6427c478bd9Sstevel@tonic-gate /* 6437c478bd9Sstevel@tonic-gate * library events, blocking 64434709573Sraf * Only events of type PORT_SOURCE_AIO or PORT_SOURCE_MQ 64534709573Sraf * are currently allowed. 6467c478bd9Sstevel@tonic-gate */ 64734709573Sraf if ((int)a1 != PORT_SOURCE_AIO && (int)a1 != PORT_SOURCE_MQ) { 6487c478bd9Sstevel@tonic-gate error = EINVAL; 6497c478bd9Sstevel@tonic-gate break; 6507c478bd9Sstevel@tonic-gate } 6517c478bd9Sstevel@tonic-gate error = port_dispatch_event(pp, (int)opcode, (int)a1, (int)a2, 6527c478bd9Sstevel@tonic-gate (uintptr_t)a3, (void *)a4); 6537c478bd9Sstevel@tonic-gate break; 6547c478bd9Sstevel@tonic-gate } 6557c478bd9Sstevel@tonic-gate case PORT_DISSOCIATE: 6567c478bd9Sstevel@tonic-gate { 6577c478bd9Sstevel@tonic-gate /* currently only PORT_SOURCE_FD is implemented */ 6587c478bd9Sstevel@tonic-gate if ((int)a1 != PORT_SOURCE_FD) { 6597c478bd9Sstevel@tonic-gate error = EINVAL; 6607c478bd9Sstevel@tonic-gate break; 6617c478bd9Sstevel@tonic-gate } 6627c478bd9Sstevel@tonic-gate error = port_dissociate_fd(pp, (uintptr_t)a2); 6637c478bd9Sstevel@tonic-gate break; 6647c478bd9Sstevel@tonic-gate } 6657c478bd9Sstevel@tonic-gate case PORT_ALERT: 6667c478bd9Sstevel@tonic-gate { 6677c478bd9Sstevel@tonic-gate if ((int)a2) /* a2 = events */ 6687c478bd9Sstevel@tonic-gate error = port_alert(pp, (int)a1, (int)a2, (void *)a3); 6697c478bd9Sstevel@tonic-gate else 6707c478bd9Sstevel@tonic-gate port_remove_alert(&pp->port_queue); 6717c478bd9Sstevel@tonic-gate break; 6727c478bd9Sstevel@tonic-gate } 6737c478bd9Sstevel@tonic-gate default: 6747c478bd9Sstevel@tonic-gate error = EINVAL; 6757c478bd9Sstevel@tonic-gate break; 6767c478bd9Sstevel@tonic-gate } 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate releasef((int)a0); 6797c478bd9Sstevel@tonic-gate if (error) 6807c478bd9Sstevel@tonic-gate return ((int64_t)set_errno(error)); 6817c478bd9Sstevel@tonic-gate return (r.r_vals); 6827c478bd9Sstevel@tonic-gate } 6837c478bd9Sstevel@tonic-gate 6847c478bd9Sstevel@tonic-gate /* 6857c478bd9Sstevel@tonic-gate * System call to create a port. 6867c478bd9Sstevel@tonic-gate * 6877c478bd9Sstevel@tonic-gate * The port_create() function creates a vnode of type VPORT per port. 6887c478bd9Sstevel@tonic-gate * The port control data is associated with the vnode as vnode private data. 6897c478bd9Sstevel@tonic-gate * The port_create() function returns an event port file descriptor. 6907c478bd9Sstevel@tonic-gate */ 6917c478bd9Sstevel@tonic-gate static int 6927c478bd9Sstevel@tonic-gate port_create(int *fdp) 6937c478bd9Sstevel@tonic-gate { 6947c478bd9Sstevel@tonic-gate port_t *pp; 6957c478bd9Sstevel@tonic-gate vnode_t *vp; 6967c478bd9Sstevel@tonic-gate struct file *fp; 6977c478bd9Sstevel@tonic-gate proc_t *p = curproc; 6987c478bd9Sstevel@tonic-gate 6997c478bd9Sstevel@tonic-gate /* initialize vnode and port private data */ 7007c478bd9Sstevel@tonic-gate pp = kmem_zalloc(sizeof (port_t), KM_SLEEP); 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate pp->port_vnode = vn_alloc(KM_SLEEP); 7037c478bd9Sstevel@tonic-gate vp = EPTOV(pp); 7047c478bd9Sstevel@tonic-gate vn_setops(vp, port_vnodeops); 7057c478bd9Sstevel@tonic-gate vp->v_type = VPORT; 7067c478bd9Sstevel@tonic-gate vp->v_vfsp = &port_vfs; 7077c478bd9Sstevel@tonic-gate vp->v_data = (caddr_t)pp; 7087c478bd9Sstevel@tonic-gate 7097c478bd9Sstevel@tonic-gate mutex_enter(&port_control.pc_mutex); 7107c478bd9Sstevel@tonic-gate /* 7117c478bd9Sstevel@tonic-gate * Retrieve the maximal number of event ports allowed per system from 7127c478bd9Sstevel@tonic-gate * the resource control: project.port-max-ids. 7137c478bd9Sstevel@tonic-gate */ 7147c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 7157c478bd9Sstevel@tonic-gate if (rctl_test(rc_project_portids, p->p_task->tk_proj->kpj_rctls, p, 7167c478bd9Sstevel@tonic-gate port_control.pc_nents + 1, RCA_SAFE) & RCT_DENY) { 7177c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 7187c478bd9Sstevel@tonic-gate vn_free(vp); 7197c478bd9Sstevel@tonic-gate kmem_free(pp, sizeof (port_t)); 7207c478bd9Sstevel@tonic-gate mutex_exit(&port_control.pc_mutex); 7217c478bd9Sstevel@tonic-gate return (EAGAIN); 7227c478bd9Sstevel@tonic-gate } 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate /* 7257c478bd9Sstevel@tonic-gate * Retrieve the maximal number of events allowed per port from 7267c478bd9Sstevel@tonic-gate * the resource control: process.port-max-events. 7277c478bd9Sstevel@tonic-gate */ 7287c478bd9Sstevel@tonic-gate pp->port_max_events = rctl_enforced_value(rc_process_portev, 7297c478bd9Sstevel@tonic-gate p->p_rctls, p); 7307c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 7317c478bd9Sstevel@tonic-gate 7327c478bd9Sstevel@tonic-gate /* allocate a new user file descriptor and a file structure */ 7337c478bd9Sstevel@tonic-gate if (falloc(vp, 0, &fp, fdp)) { 7347c478bd9Sstevel@tonic-gate /* 7357c478bd9Sstevel@tonic-gate * If the file table is full, free allocated resources. 7367c478bd9Sstevel@tonic-gate */ 7377c478bd9Sstevel@tonic-gate vn_free(vp); 7387c478bd9Sstevel@tonic-gate kmem_free(pp, sizeof (port_t)); 7397c478bd9Sstevel@tonic-gate mutex_exit(&port_control.pc_mutex); 7407c478bd9Sstevel@tonic-gate return (EMFILE); 7417c478bd9Sstevel@tonic-gate } 7427c478bd9Sstevel@tonic-gate 7437c478bd9Sstevel@tonic-gate mutex_exit(&fp->f_tlock); 7447c478bd9Sstevel@tonic-gate 7457c478bd9Sstevel@tonic-gate pp->port_fd = *fdp; 7467c478bd9Sstevel@tonic-gate port_control.pc_nents++; 7477c478bd9Sstevel@tonic-gate p->p_portcnt++; 7487c478bd9Sstevel@tonic-gate port_kstat.pks_ports.value.ui32++; 7497c478bd9Sstevel@tonic-gate mutex_exit(&port_control.pc_mutex); 7507c478bd9Sstevel@tonic-gate 7517c478bd9Sstevel@tonic-gate /* initializes port private data */ 7527c478bd9Sstevel@tonic-gate port_init(pp); 75361b4b1efSpraks /* set user file pointer */ 75461b4b1efSpraks setf(*fdp, fp); 7557c478bd9Sstevel@tonic-gate return (0); 7567c478bd9Sstevel@tonic-gate } 7577c478bd9Sstevel@tonic-gate 7587c478bd9Sstevel@tonic-gate /* 7597c478bd9Sstevel@tonic-gate * port_init() initializes event port specific data 7607c478bd9Sstevel@tonic-gate */ 7617c478bd9Sstevel@tonic-gate static void 7627c478bd9Sstevel@tonic-gate port_init(port_t *pp) 7637c478bd9Sstevel@tonic-gate { 7647c478bd9Sstevel@tonic-gate port_queue_t *portq; 7657c478bd9Sstevel@tonic-gate port_ksource_t *pks; 7667c478bd9Sstevel@tonic-gate 7677c478bd9Sstevel@tonic-gate mutex_init(&pp->port_mutex, NULL, MUTEX_DEFAULT, NULL); 7687c478bd9Sstevel@tonic-gate portq = &pp->port_queue; 7697c478bd9Sstevel@tonic-gate mutex_init(&portq->portq_mutex, NULL, MUTEX_DEFAULT, NULL); 7707c478bd9Sstevel@tonic-gate pp->port_flags |= PORT_INIT; 7717c478bd9Sstevel@tonic-gate 7727c478bd9Sstevel@tonic-gate /* 7737c478bd9Sstevel@tonic-gate * If it is not enough memory available to satisfy a user 7747c478bd9Sstevel@tonic-gate * request using a single port_getn() call then port_getn() 7757c478bd9Sstevel@tonic-gate * will reduce the size of the list to PORT_MAX_LIST. 7767c478bd9Sstevel@tonic-gate */ 7777c478bd9Sstevel@tonic-gate pp->port_max_list = port_max_list; 7787c478bd9Sstevel@tonic-gate 7797c478bd9Sstevel@tonic-gate /* Set timestamp entries required for fstat(2) requests */ 7807c478bd9Sstevel@tonic-gate gethrestime(&pp->port_ctime); 7817c478bd9Sstevel@tonic-gate pp->port_uid = crgetuid(curproc->p_cred); 7827c478bd9Sstevel@tonic-gate pp->port_gid = crgetgid(curproc->p_cred); 7837c478bd9Sstevel@tonic-gate 7847c478bd9Sstevel@tonic-gate /* initialize port queue structs */ 7857c478bd9Sstevel@tonic-gate list_create(&portq->portq_list, sizeof (port_kevent_t), 7867c478bd9Sstevel@tonic-gate offsetof(port_kevent_t, portkev_node)); 7877c478bd9Sstevel@tonic-gate list_create(&portq->portq_get_list, sizeof (port_kevent_t), 7887c478bd9Sstevel@tonic-gate offsetof(port_kevent_t, portkev_node)); 7897c478bd9Sstevel@tonic-gate portq->portq_flags = 0; 7907c478bd9Sstevel@tonic-gate pp->port_pid = curproc->p_pid; 7917c478bd9Sstevel@tonic-gate 7927c478bd9Sstevel@tonic-gate /* Allocate cache skeleton for PORT_SOURCE_FD events */ 7937c478bd9Sstevel@tonic-gate portq->portq_pcp = kmem_zalloc(sizeof (port_fdcache_t), KM_SLEEP); 7947c478bd9Sstevel@tonic-gate mutex_init(&portq->portq_pcp->pc_lock, NULL, MUTEX_DEFAULT, NULL); 7957c478bd9Sstevel@tonic-gate 7967c478bd9Sstevel@tonic-gate /* 7977c478bd9Sstevel@tonic-gate * Allocate cache skeleton for association of event sources. 7987c478bd9Sstevel@tonic-gate */ 7997c478bd9Sstevel@tonic-gate mutex_init(&portq->portq_source_mutex, NULL, MUTEX_DEFAULT, NULL); 8007c478bd9Sstevel@tonic-gate portq->portq_scache = kmem_zalloc( 8017c478bd9Sstevel@tonic-gate PORT_SCACHE_SIZE * sizeof (port_source_t *), KM_SLEEP); 8027c478bd9Sstevel@tonic-gate 8037c478bd9Sstevel@tonic-gate /* 8047c478bd9Sstevel@tonic-gate * pre-associate some kernel sources with this port. 8057c478bd9Sstevel@tonic-gate * The pre-association is required to create port_source_t 8067c478bd9Sstevel@tonic-gate * structures for object association. 8077c478bd9Sstevel@tonic-gate * Some sources can not get associated with a port before the first 8087c478bd9Sstevel@tonic-gate * object association is requested. Another reason to pre_associate 8097c478bd9Sstevel@tonic-gate * a particular source with a port is because of performance. 8107c478bd9Sstevel@tonic-gate */ 8117c478bd9Sstevel@tonic-gate 8127c478bd9Sstevel@tonic-gate for (pks = port_ksource_tab; pks->pks_source != 0; pks++) 8137c478bd9Sstevel@tonic-gate port_add_ksource_local(pp, pks); 8147c478bd9Sstevel@tonic-gate } 8157c478bd9Sstevel@tonic-gate 8167c478bd9Sstevel@tonic-gate /* 8177c478bd9Sstevel@tonic-gate * The port_add_ksource_local() function is being used to associate 8187c478bd9Sstevel@tonic-gate * event sources with every new port. 8197c478bd9Sstevel@tonic-gate * The event sources need to be added to port_ksource_tab[]. 8207c478bd9Sstevel@tonic-gate */ 8217c478bd9Sstevel@tonic-gate static void 8227c478bd9Sstevel@tonic-gate port_add_ksource_local(port_t *pp, port_ksource_t *pks) 8237c478bd9Sstevel@tonic-gate { 8247c478bd9Sstevel@tonic-gate port_source_t *pse; 8257c478bd9Sstevel@tonic-gate port_source_t **ps; 8267c478bd9Sstevel@tonic-gate 8277c478bd9Sstevel@tonic-gate mutex_enter(&pp->port_queue.portq_source_mutex); 8287c478bd9Sstevel@tonic-gate ps = &pp->port_queue.portq_scache[PORT_SHASH(pks->pks_source)]; 8297c478bd9Sstevel@tonic-gate for (pse = *ps; pse != NULL; pse = pse->portsrc_next) { 8307c478bd9Sstevel@tonic-gate if (pse->portsrc_source == pks->pks_source) 8317c478bd9Sstevel@tonic-gate break; 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate 8347c478bd9Sstevel@tonic-gate if (pse == NULL) { 8357c478bd9Sstevel@tonic-gate /* associate new source with the port */ 8367c478bd9Sstevel@tonic-gate pse = kmem_zalloc(sizeof (port_source_t), KM_SLEEP); 8377c478bd9Sstevel@tonic-gate pse->portsrc_source = pks->pks_source; 8387c478bd9Sstevel@tonic-gate pse->portsrc_close = pks->pks_close; 8397c478bd9Sstevel@tonic-gate pse->portsrc_closearg = pks->pks_closearg; 8407c478bd9Sstevel@tonic-gate pse->portsrc_cnt = 1; 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate pks->pks_portsrc = pse; 8437c478bd9Sstevel@tonic-gate if (*ps != NULL) 8447c478bd9Sstevel@tonic-gate pse->portsrc_next = (*ps)->portsrc_next; 8457c478bd9Sstevel@tonic-gate *ps = pse; 8467c478bd9Sstevel@tonic-gate } 8477c478bd9Sstevel@tonic-gate mutex_exit(&pp->port_queue.portq_source_mutex); 8487c478bd9Sstevel@tonic-gate } 8497c478bd9Sstevel@tonic-gate 8507c478bd9Sstevel@tonic-gate /* 8517c478bd9Sstevel@tonic-gate * The port_send() function sends an event of type "source" to a 8527c478bd9Sstevel@tonic-gate * port. This function is non-blocking. An event can be sent to 8537c478bd9Sstevel@tonic-gate * a port as long as the number of events per port does not achieve the 8547c478bd9Sstevel@tonic-gate * maximal allowed number of events. The max. number of events per port is 8557c478bd9Sstevel@tonic-gate * defined by the resource control process.max-port-events. 8567c478bd9Sstevel@tonic-gate * This function is used by the port library function port_send() 8577c478bd9Sstevel@tonic-gate * and port_dispatch(). The port_send(3c) function is part of the 8587c478bd9Sstevel@tonic-gate * event ports API and submits events of type PORT_SOURCE_USER. The 8597c478bd9Sstevel@tonic-gate * port_dispatch() function is project private and it is used by library 8607c478bd9Sstevel@tonic-gate * functions to submit events of other types than PORT_SOURCE_USER 8617c478bd9Sstevel@tonic-gate * (e.g. PORT_SOURCE_AIO). 8627c478bd9Sstevel@tonic-gate */ 8637c478bd9Sstevel@tonic-gate static int 8647c478bd9Sstevel@tonic-gate port_send(port_t *pp, int source, int events, void *user) 8657c478bd9Sstevel@tonic-gate { 8667c478bd9Sstevel@tonic-gate port_kevent_t *pev; 8677c478bd9Sstevel@tonic-gate int error; 8687c478bd9Sstevel@tonic-gate 8697c478bd9Sstevel@tonic-gate error = port_alloc_event_local(pp, source, PORT_ALLOC_DEFAULT, &pev); 8707c478bd9Sstevel@tonic-gate if (error) 8717c478bd9Sstevel@tonic-gate return (error); 8727c478bd9Sstevel@tonic-gate 8737c478bd9Sstevel@tonic-gate pev->portkev_object = 0; 8747c478bd9Sstevel@tonic-gate pev->portkev_events = events; 8757c478bd9Sstevel@tonic-gate pev->portkev_user = user; 8767c478bd9Sstevel@tonic-gate pev->portkev_callback = NULL; 8777c478bd9Sstevel@tonic-gate pev->portkev_arg = NULL; 8787c478bd9Sstevel@tonic-gate pev->portkev_flags = 0; 8797c478bd9Sstevel@tonic-gate 88034709573Sraf port_send_event(pev); 8817c478bd9Sstevel@tonic-gate return (0); 8827c478bd9Sstevel@tonic-gate } 8837c478bd9Sstevel@tonic-gate 8847c478bd9Sstevel@tonic-gate /* 8857c478bd9Sstevel@tonic-gate * The port_noshare() function returns 0 if the current event was generated 8867c478bd9Sstevel@tonic-gate * by the same process. Otherwise is returns a value other than 0 and the 8877c478bd9Sstevel@tonic-gate * event should not be delivered to the current processe. 8887c478bd9Sstevel@tonic-gate * The port_noshare() function is normally used by the port_dispatch() 8897c478bd9Sstevel@tonic-gate * function. The port_dispatch() function is project private and can only be 8907c478bd9Sstevel@tonic-gate * used within the event port project. 8917c478bd9Sstevel@tonic-gate * Currently the libaio uses the port_dispatch() function to deliver events 8927c478bd9Sstevel@tonic-gate * of types PORT_SOURCE_AIO. 8937c478bd9Sstevel@tonic-gate */ 8947c478bd9Sstevel@tonic-gate /* ARGSUSED */ 8957c478bd9Sstevel@tonic-gate static int 8967c478bd9Sstevel@tonic-gate port_noshare(void *arg, int *events, pid_t pid, int flag, void *evp) 8977c478bd9Sstevel@tonic-gate { 8987c478bd9Sstevel@tonic-gate if (flag == PORT_CALLBACK_DEFAULT && curproc->p_pid != pid) 8997c478bd9Sstevel@tonic-gate return (1); 9007c478bd9Sstevel@tonic-gate return (0); 9017c478bd9Sstevel@tonic-gate } 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate /* 9047c478bd9Sstevel@tonic-gate * The port_dispatch_event() function is project private and it is used by 9057c478bd9Sstevel@tonic-gate * libraries involved in the project to deliver events to the port. 9067c478bd9Sstevel@tonic-gate * port_dispatch will sleep and wait for enough resources to satisfy the 9077c478bd9Sstevel@tonic-gate * request, if necessary. 9087c478bd9Sstevel@tonic-gate * The library can specify if the delivered event is shareable with other 9097c478bd9Sstevel@tonic-gate * processes (see PORT_SYS_NOSHARE flag). 9107c478bd9Sstevel@tonic-gate */ 9117c478bd9Sstevel@tonic-gate static int 9127c478bd9Sstevel@tonic-gate port_dispatch_event(port_t *pp, int opcode, int source, int events, 9137c478bd9Sstevel@tonic-gate uintptr_t object, void *user) 9147c478bd9Sstevel@tonic-gate { 9157c478bd9Sstevel@tonic-gate port_kevent_t *pev; 9167c478bd9Sstevel@tonic-gate int error; 9177c478bd9Sstevel@tonic-gate 9187c478bd9Sstevel@tonic-gate error = port_alloc_event_block(pp, source, PORT_ALLOC_DEFAULT, &pev); 9197c478bd9Sstevel@tonic-gate if (error) 9207c478bd9Sstevel@tonic-gate return (error); 9217c478bd9Sstevel@tonic-gate 9227c478bd9Sstevel@tonic-gate pev->portkev_object = object; 9237c478bd9Sstevel@tonic-gate pev->portkev_events = events; 9247c478bd9Sstevel@tonic-gate pev->portkev_user = user; 9257c478bd9Sstevel@tonic-gate pev->portkev_arg = NULL; 9267c478bd9Sstevel@tonic-gate if (opcode & PORT_SYS_NOSHARE) { 9277c478bd9Sstevel@tonic-gate pev->portkev_flags = PORT_KEV_NOSHARE; 9287c478bd9Sstevel@tonic-gate pev->portkev_callback = port_noshare; 9297c478bd9Sstevel@tonic-gate } else { 9307c478bd9Sstevel@tonic-gate pev->portkev_flags = 0; 9317c478bd9Sstevel@tonic-gate pev->portkev_callback = NULL; 9327c478bd9Sstevel@tonic-gate } 9337c478bd9Sstevel@tonic-gate 93434709573Sraf port_send_event(pev); 9357c478bd9Sstevel@tonic-gate return (0); 9367c478bd9Sstevel@tonic-gate } 9377c478bd9Sstevel@tonic-gate 9387c478bd9Sstevel@tonic-gate 9397c478bd9Sstevel@tonic-gate /* 9407c478bd9Sstevel@tonic-gate * The port_sendn() function is the kernel implementation of the event 9417c478bd9Sstevel@tonic-gate * port API function port_sendn(3c). 9427c478bd9Sstevel@tonic-gate * This function is able to send an event to a list of event ports. 9437c478bd9Sstevel@tonic-gate */ 9447c478bd9Sstevel@tonic-gate static int 9457c478bd9Sstevel@tonic-gate port_sendn(int ports[], int errors[], uint_t nent, int events, void *user, 9467c478bd9Sstevel@tonic-gate uint_t *nget) 9477c478bd9Sstevel@tonic-gate { 9487c478bd9Sstevel@tonic-gate port_kevent_t *pev; 9497c478bd9Sstevel@tonic-gate int errorcnt = 0; 9507c478bd9Sstevel@tonic-gate int error = 0; 9517c478bd9Sstevel@tonic-gate int count; 9527c478bd9Sstevel@tonic-gate int port; 9537c478bd9Sstevel@tonic-gate int *plist; 9547c478bd9Sstevel@tonic-gate int *elist = NULL; 9557c478bd9Sstevel@tonic-gate file_t *fp; 9567c478bd9Sstevel@tonic-gate port_t *pp; 9577c478bd9Sstevel@tonic-gate 9587c478bd9Sstevel@tonic-gate if (nent == 0 || nent > port_max_list) 9597c478bd9Sstevel@tonic-gate return (EINVAL); 9607c478bd9Sstevel@tonic-gate 9617c478bd9Sstevel@tonic-gate plist = kmem_alloc(nent * sizeof (int), KM_SLEEP); 9627c478bd9Sstevel@tonic-gate if (copyin((void *)ports, plist, nent * sizeof (int))) { 9637c478bd9Sstevel@tonic-gate kmem_free(plist, nent * sizeof (int)); 9647c478bd9Sstevel@tonic-gate return (EFAULT); 9657c478bd9Sstevel@tonic-gate } 9667c478bd9Sstevel@tonic-gate 9677c478bd9Sstevel@tonic-gate /* 9687c478bd9Sstevel@tonic-gate * Scan the list for event port file descriptors and send the 9697c478bd9Sstevel@tonic-gate * attached user event data embedded in a event of type 9707c478bd9Sstevel@tonic-gate * PORT_SOURCE_USER to every event port in the list. 9717c478bd9Sstevel@tonic-gate * If a list entry is not a valid event port then the corresponding 9727c478bd9Sstevel@tonic-gate * error code will be stored in the errors[] list with the same 9737c478bd9Sstevel@tonic-gate * list offset as in the ports[] list. 9747c478bd9Sstevel@tonic-gate */ 9757c478bd9Sstevel@tonic-gate 9767c478bd9Sstevel@tonic-gate for (count = 0; count < nent; count++) { 9777c478bd9Sstevel@tonic-gate port = plist[count]; 9787c478bd9Sstevel@tonic-gate if ((fp = getf(port)) == NULL) { 9797c478bd9Sstevel@tonic-gate elist = port_errorn(elist, nent, EBADF, count); 9807c478bd9Sstevel@tonic-gate errorcnt++; 9817c478bd9Sstevel@tonic-gate continue; 9827c478bd9Sstevel@tonic-gate } 9837c478bd9Sstevel@tonic-gate 9847c478bd9Sstevel@tonic-gate pp = VTOEP(fp->f_vnode); 9857c478bd9Sstevel@tonic-gate if (fp->f_vnode->v_type != VPORT) { 9867c478bd9Sstevel@tonic-gate releasef(port); 9877c478bd9Sstevel@tonic-gate elist = port_errorn(elist, nent, EBADFD, count); 9887c478bd9Sstevel@tonic-gate errorcnt++; 9897c478bd9Sstevel@tonic-gate continue; 9907c478bd9Sstevel@tonic-gate } 9917c478bd9Sstevel@tonic-gate 9927c478bd9Sstevel@tonic-gate error = port_alloc_event_local(pp, PORT_SOURCE_USER, 9937c478bd9Sstevel@tonic-gate PORT_ALLOC_DEFAULT, &pev); 9947c478bd9Sstevel@tonic-gate if (error) { 9957c478bd9Sstevel@tonic-gate releasef(port); 9967c478bd9Sstevel@tonic-gate elist = port_errorn(elist, nent, error, count); 9977c478bd9Sstevel@tonic-gate errorcnt++; 9987c478bd9Sstevel@tonic-gate continue; 9997c478bd9Sstevel@tonic-gate } 10007c478bd9Sstevel@tonic-gate 10017c478bd9Sstevel@tonic-gate pev->portkev_object = 0; 10027c478bd9Sstevel@tonic-gate pev->portkev_events = events; 10037c478bd9Sstevel@tonic-gate pev->portkev_user = user; 10047c478bd9Sstevel@tonic-gate pev->portkev_callback = NULL; 10057c478bd9Sstevel@tonic-gate pev->portkev_arg = NULL; 10067c478bd9Sstevel@tonic-gate pev->portkev_flags = 0; 10077c478bd9Sstevel@tonic-gate 100834709573Sraf port_send_event(pev); 10097c478bd9Sstevel@tonic-gate releasef(port); 10107c478bd9Sstevel@tonic-gate } 10117c478bd9Sstevel@tonic-gate if (errorcnt) { 10127c478bd9Sstevel@tonic-gate error = EIO; 10137c478bd9Sstevel@tonic-gate if (copyout(elist, (void *)errors, nent * sizeof (int))) 10147c478bd9Sstevel@tonic-gate error = EFAULT; 10157c478bd9Sstevel@tonic-gate kmem_free(elist, nent * sizeof (int)); 10167c478bd9Sstevel@tonic-gate } 10177c478bd9Sstevel@tonic-gate *nget = nent - errorcnt; 10187c478bd9Sstevel@tonic-gate kmem_free(plist, nent * sizeof (int)); 10197c478bd9Sstevel@tonic-gate return (error); 10207c478bd9Sstevel@tonic-gate } 10217c478bd9Sstevel@tonic-gate 10227c478bd9Sstevel@tonic-gate static int * 10237c478bd9Sstevel@tonic-gate port_errorn(int *elist, int nent, int error, int index) 10247c478bd9Sstevel@tonic-gate { 10257c478bd9Sstevel@tonic-gate if (elist == NULL) 10267c478bd9Sstevel@tonic-gate elist = kmem_zalloc(nent * sizeof (int), KM_SLEEP); 10277c478bd9Sstevel@tonic-gate elist[index] = error; 10287c478bd9Sstevel@tonic-gate return (elist); 10297c478bd9Sstevel@tonic-gate } 10307c478bd9Sstevel@tonic-gate 10317c478bd9Sstevel@tonic-gate /* 10327c478bd9Sstevel@tonic-gate * port_alert() 10337c478bd9Sstevel@tonic-gate * The port_alert() funcion is a high priority event and it is always set 10347c478bd9Sstevel@tonic-gate * on top of the queue. It is also delivered as single event. 10357c478bd9Sstevel@tonic-gate * flags: 10367c478bd9Sstevel@tonic-gate * - SET :overwrite current alert data 10377c478bd9Sstevel@tonic-gate * - UPDATE:set alert data or return EBUSY if alert mode is already set 10387c478bd9Sstevel@tonic-gate * 10397c478bd9Sstevel@tonic-gate * - set the ALERT flag 10407c478bd9Sstevel@tonic-gate * - wakeup all sleeping threads 10417c478bd9Sstevel@tonic-gate */ 10427c478bd9Sstevel@tonic-gate static int 10437c478bd9Sstevel@tonic-gate port_alert(port_t *pp, int flags, int events, void *user) 10447c478bd9Sstevel@tonic-gate { 10457c478bd9Sstevel@tonic-gate port_queue_t *portq; 10467c478bd9Sstevel@tonic-gate portget_t *pgetp; 10477c478bd9Sstevel@tonic-gate port_alert_t *pa; 10487c478bd9Sstevel@tonic-gate 10497c478bd9Sstevel@tonic-gate if ((flags & PORT_ALERT_INVALID) == PORT_ALERT_INVALID) 10507c478bd9Sstevel@tonic-gate return (EINVAL); 10517c478bd9Sstevel@tonic-gate 10527c478bd9Sstevel@tonic-gate portq = &pp->port_queue; 10537c478bd9Sstevel@tonic-gate pa = &portq->portq_alert; 10547c478bd9Sstevel@tonic-gate mutex_enter(&portq->portq_mutex); 10557c478bd9Sstevel@tonic-gate 10567c478bd9Sstevel@tonic-gate /* check alert conditions */ 10577c478bd9Sstevel@tonic-gate if (flags == PORT_ALERT_UPDATE) { 10587c478bd9Sstevel@tonic-gate if (portq->portq_flags & PORTQ_ALERT) { 10597c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 10607c478bd9Sstevel@tonic-gate return (EBUSY); 10617c478bd9Sstevel@tonic-gate } 10627c478bd9Sstevel@tonic-gate } 10637c478bd9Sstevel@tonic-gate 10647c478bd9Sstevel@tonic-gate /* 10657c478bd9Sstevel@tonic-gate * Store alert data in the port to be delivered to threads 10667c478bd9Sstevel@tonic-gate * which are using port_get(n) to retrieve events. 10677c478bd9Sstevel@tonic-gate */ 10687c478bd9Sstevel@tonic-gate 10697c478bd9Sstevel@tonic-gate portq->portq_flags |= PORTQ_ALERT; 10707c478bd9Sstevel@tonic-gate pa->portal_events = events; /* alert info */ 10717c478bd9Sstevel@tonic-gate pa->portal_pid = curproc->p_pid; /* process owner */ 10727c478bd9Sstevel@tonic-gate pa->portal_object = 0; /* no object */ 10737c478bd9Sstevel@tonic-gate pa->portal_user = user; /* user alert data */ 10747c478bd9Sstevel@tonic-gate 10757c478bd9Sstevel@tonic-gate /* alert and deliver alert data to waiting threads */ 10767c478bd9Sstevel@tonic-gate pgetp = portq->portq_thread; 10777c478bd9Sstevel@tonic-gate if (pgetp == NULL) { 10787c478bd9Sstevel@tonic-gate /* no threads waiting for events */ 10797c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 10807c478bd9Sstevel@tonic-gate return (0); 10817c478bd9Sstevel@tonic-gate } 10827c478bd9Sstevel@tonic-gate 10837c478bd9Sstevel@tonic-gate /* 10847c478bd9Sstevel@tonic-gate * Set waiting threads in alert mode (PORTGET_ALERT).. 10857c478bd9Sstevel@tonic-gate * Every thread waiting for events already allocated a portget_t 10867c478bd9Sstevel@tonic-gate * structure to sleep on. 10877c478bd9Sstevel@tonic-gate * The port alert arguments are stored in the portget_t structure. 10887c478bd9Sstevel@tonic-gate * The PORTGET_ALERT flag is set to indicate the thread to return 10897c478bd9Sstevel@tonic-gate * immediately with the alert event. 10907c478bd9Sstevel@tonic-gate */ 10917c478bd9Sstevel@tonic-gate do { 10927c478bd9Sstevel@tonic-gate if ((pgetp->portget_state & PORTGET_ALERT) == 0) { 10937c478bd9Sstevel@tonic-gate pa = &pgetp->portget_alert; 10947c478bd9Sstevel@tonic-gate pa->portal_events = events; 10957c478bd9Sstevel@tonic-gate pa->portal_object = 0; 10967c478bd9Sstevel@tonic-gate pa->portal_user = user; 10977c478bd9Sstevel@tonic-gate pgetp->portget_state |= PORTGET_ALERT; 10987c478bd9Sstevel@tonic-gate cv_signal(&pgetp->portget_cv); 10997c478bd9Sstevel@tonic-gate } 11007c478bd9Sstevel@tonic-gate } while ((pgetp = pgetp->portget_next) != portq->portq_thread); 11017c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 11027c478bd9Sstevel@tonic-gate return (0); 11037c478bd9Sstevel@tonic-gate } 11047c478bd9Sstevel@tonic-gate 11057c478bd9Sstevel@tonic-gate /* 11067c478bd9Sstevel@tonic-gate * Clear alert state of the port 11077c478bd9Sstevel@tonic-gate */ 11087c478bd9Sstevel@tonic-gate static void 11097c478bd9Sstevel@tonic-gate port_remove_alert(port_queue_t *portq) 11107c478bd9Sstevel@tonic-gate { 11117c478bd9Sstevel@tonic-gate mutex_enter(&portq->portq_mutex); 11127c478bd9Sstevel@tonic-gate portq->portq_flags &= ~PORTQ_ALERT; 11137c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 11147c478bd9Sstevel@tonic-gate } 11157c478bd9Sstevel@tonic-gate 11167c478bd9Sstevel@tonic-gate /* 11177c478bd9Sstevel@tonic-gate * The port_getn() function is used to retrieve events from a port. 11187c478bd9Sstevel@tonic-gate * 11197c478bd9Sstevel@tonic-gate * The port_getn() function returns immediately if there are enough events 11207c478bd9Sstevel@tonic-gate * available in the port to satisfy the request or if the port is in alert 11217c478bd9Sstevel@tonic-gate * mode (see port_alert(3c)). 11227c478bd9Sstevel@tonic-gate * The timeout argument of port_getn(3c) -which is embedded in the 11237c478bd9Sstevel@tonic-gate * port_gettimer_t structure- specifies if the system call should block or if it 11247c478bd9Sstevel@tonic-gate * should return immediately depending on the number of events available. 11257c478bd9Sstevel@tonic-gate * This function is internally used by port_getn(3c) as well as by 11267c478bd9Sstevel@tonic-gate * port_get(3c). 11277c478bd9Sstevel@tonic-gate */ 11287c478bd9Sstevel@tonic-gate static int 11297c478bd9Sstevel@tonic-gate port_getn(port_t *pp, port_event_t *uevp, uint_t max, uint_t *nget, 11307c478bd9Sstevel@tonic-gate port_gettimer_t *pgt) 11317c478bd9Sstevel@tonic-gate { 11327c478bd9Sstevel@tonic-gate port_queue_t *portq; 11337c478bd9Sstevel@tonic-gate port_kevent_t *pev; 11347c478bd9Sstevel@tonic-gate port_kevent_t *lev; 11357c478bd9Sstevel@tonic-gate int error = 0; 11367c478bd9Sstevel@tonic-gate uint_t nmax; 11377c478bd9Sstevel@tonic-gate uint_t nevents; 11387c478bd9Sstevel@tonic-gate uint_t eventsz; 11397c478bd9Sstevel@tonic-gate port_event_t *kevp; 11407c478bd9Sstevel@tonic-gate list_t *glist; 11417c478bd9Sstevel@tonic-gate uint_t tnent; 11427c478bd9Sstevel@tonic-gate int rval; 11437c478bd9Sstevel@tonic-gate int blocking = -1; 1144*3348528fSdm int timecheck; 11457c478bd9Sstevel@tonic-gate int flag; 11467c478bd9Sstevel@tonic-gate timespec_t rqtime; 11477c478bd9Sstevel@tonic-gate timespec_t *rqtp = NULL; 11487c478bd9Sstevel@tonic-gate portget_t *pgetp; 11497c478bd9Sstevel@tonic-gate void *results; 11507c478bd9Sstevel@tonic-gate model_t model = get_udatamodel(); 11517c478bd9Sstevel@tonic-gate 11527c478bd9Sstevel@tonic-gate flag = pgt->pgt_flags; 11537c478bd9Sstevel@tonic-gate 11547c478bd9Sstevel@tonic-gate if (*nget > max && max > 0) 11557c478bd9Sstevel@tonic-gate return (EINVAL); 11567c478bd9Sstevel@tonic-gate 11577c478bd9Sstevel@tonic-gate portq = &pp->port_queue; 11587c478bd9Sstevel@tonic-gate mutex_enter(&portq->portq_mutex); 11597c478bd9Sstevel@tonic-gate if (max == 0) { 11607c478bd9Sstevel@tonic-gate /* 116134709573Sraf * Return number of objects with events. 116234709573Sraf * The port_block() call is required to synchronize this 11637c478bd9Sstevel@tonic-gate * thread with another possible thread, which could be 11647c478bd9Sstevel@tonic-gate * retrieving events from the port queue. 11657c478bd9Sstevel@tonic-gate */ 116634709573Sraf port_block(portq); 11677c478bd9Sstevel@tonic-gate /* 11687c478bd9Sstevel@tonic-gate * Check if a second thread is currently retrieving events 11697c478bd9Sstevel@tonic-gate * and it is using the temporary event queue. 11707c478bd9Sstevel@tonic-gate */ 11717c478bd9Sstevel@tonic-gate if (portq->portq_tnent) { 11727c478bd9Sstevel@tonic-gate /* put remaining events back to the port queue */ 11737c478bd9Sstevel@tonic-gate port_push_eventq(portq); 11747c478bd9Sstevel@tonic-gate } 11757c478bd9Sstevel@tonic-gate *nget = portq->portq_nent; 117634709573Sraf port_unblock(portq); 11777c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 11787c478bd9Sstevel@tonic-gate return (0); 11797c478bd9Sstevel@tonic-gate } 11807c478bd9Sstevel@tonic-gate 11817c478bd9Sstevel@tonic-gate if (uevp == NULL) { 11827c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 11837c478bd9Sstevel@tonic-gate return (EFAULT); 11847c478bd9Sstevel@tonic-gate } 11857c478bd9Sstevel@tonic-gate if (*nget == 0) { /* no events required */ 11867c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 11877c478bd9Sstevel@tonic-gate return (0); 11887c478bd9Sstevel@tonic-gate } 11897c478bd9Sstevel@tonic-gate 11907c478bd9Sstevel@tonic-gate /* port is being closed ... */ 11917c478bd9Sstevel@tonic-gate if (portq->portq_flags & PORTQ_CLOSE) { 11927c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 11937c478bd9Sstevel@tonic-gate return (EBADFD); 11947c478bd9Sstevel@tonic-gate } 11957c478bd9Sstevel@tonic-gate 11967c478bd9Sstevel@tonic-gate /* return immediately if port in alert mode */ 11977c478bd9Sstevel@tonic-gate if (portq->portq_flags & PORTQ_ALERT) { 11987c478bd9Sstevel@tonic-gate error = port_get_alert(&portq->portq_alert, uevp); 11997c478bd9Sstevel@tonic-gate if (error == 0) 12007c478bd9Sstevel@tonic-gate *nget = 1; 12017c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 12027c478bd9Sstevel@tonic-gate return (error); 12037c478bd9Sstevel@tonic-gate } 12047c478bd9Sstevel@tonic-gate 12057c478bd9Sstevel@tonic-gate portq->portq_thrcnt++; 12067c478bd9Sstevel@tonic-gate 12077c478bd9Sstevel@tonic-gate /* 12087c478bd9Sstevel@tonic-gate * Now check if the completed events satisfy the 12097c478bd9Sstevel@tonic-gate * "wait" requirements of the current thread: 12107c478bd9Sstevel@tonic-gate */ 12117c478bd9Sstevel@tonic-gate 12127c478bd9Sstevel@tonic-gate if (pgt->pgt_loop) { 12137c478bd9Sstevel@tonic-gate /* 12147c478bd9Sstevel@tonic-gate * loop entry of same thread 12157c478bd9Sstevel@tonic-gate * pgt_loop is set when the current thread returns 12167c478bd9Sstevel@tonic-gate * prematurely from this function. That could happen 12177c478bd9Sstevel@tonic-gate * when a port is being shared between processes and 12187c478bd9Sstevel@tonic-gate * this thread could not find events to return. 12197c478bd9Sstevel@tonic-gate * It is not allowed to a thread to retrieve non-shareable 12207c478bd9Sstevel@tonic-gate * events generated in other processes. 12217c478bd9Sstevel@tonic-gate * PORTQ_WAIT_EVENTS is set when a thread already 12227c478bd9Sstevel@tonic-gate * checked the current event queue and no new events 12237c478bd9Sstevel@tonic-gate * are added to the queue. 12247c478bd9Sstevel@tonic-gate */ 12257c478bd9Sstevel@tonic-gate if (((portq->portq_flags & PORTQ_WAIT_EVENTS) == 0) && 12267c478bd9Sstevel@tonic-gate (portq->portq_nent >= *nget)) { 12277c478bd9Sstevel@tonic-gate /* some new events arrived ...check them */ 12287c478bd9Sstevel@tonic-gate goto portnowait; 12297c478bd9Sstevel@tonic-gate } 12307c478bd9Sstevel@tonic-gate rqtp = pgt->pgt_rqtp; 1231*3348528fSdm timecheck = pgt->pgt_timecheck; 12327c478bd9Sstevel@tonic-gate pgt->pgt_flags |= PORTGET_WAIT_EVENTS; 12337c478bd9Sstevel@tonic-gate } else { 12347c478bd9Sstevel@tonic-gate /* check if enough events are available ... */ 12357c478bd9Sstevel@tonic-gate if (portq->portq_nent >= *nget) 12367c478bd9Sstevel@tonic-gate goto portnowait; 12377c478bd9Sstevel@tonic-gate /* 12387c478bd9Sstevel@tonic-gate * There are not enough events available to satisfy 12397c478bd9Sstevel@tonic-gate * the request, check timeout value and wait for 12407c478bd9Sstevel@tonic-gate * incoming events. 12417c478bd9Sstevel@tonic-gate */ 12427c478bd9Sstevel@tonic-gate error = port_get_timeout(pgt->pgt_timeout, &rqtime, &rqtp, 12437c478bd9Sstevel@tonic-gate &blocking, flag); 12447c478bd9Sstevel@tonic-gate if (error) { 12457c478bd9Sstevel@tonic-gate port_check_return_cond(portq); 12467c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 12477c478bd9Sstevel@tonic-gate return (error); 12487c478bd9Sstevel@tonic-gate } 12497c478bd9Sstevel@tonic-gate 12507c478bd9Sstevel@tonic-gate if (blocking == 0) /* don't block, check fired events */ 12517c478bd9Sstevel@tonic-gate goto portnowait; 12527c478bd9Sstevel@tonic-gate 12537c478bd9Sstevel@tonic-gate if (rqtp != NULL) { 12547c478bd9Sstevel@tonic-gate timespec_t now; 1255*3348528fSdm timecheck = timechanged; 12567c478bd9Sstevel@tonic-gate gethrestime(&now); 12577c478bd9Sstevel@tonic-gate timespecadd(rqtp, &now); 12587c478bd9Sstevel@tonic-gate } 12597c478bd9Sstevel@tonic-gate } 12607c478bd9Sstevel@tonic-gate 12617c478bd9Sstevel@tonic-gate /* enqueue thread in the list of waiting threads */ 12627c478bd9Sstevel@tonic-gate pgetp = port_queue_thread(portq, *nget); 12637c478bd9Sstevel@tonic-gate 12647c478bd9Sstevel@tonic-gate 12657c478bd9Sstevel@tonic-gate /* Wait here until return conditions met */ 12667c478bd9Sstevel@tonic-gate for (;;) { 12677c478bd9Sstevel@tonic-gate if (pgetp->portget_state & PORTGET_ALERT) { 12687c478bd9Sstevel@tonic-gate /* reap alert event and return */ 12697c478bd9Sstevel@tonic-gate error = port_get_alert(&pgetp->portget_alert, uevp); 12707c478bd9Sstevel@tonic-gate if (error) 12717c478bd9Sstevel@tonic-gate *nget = 0; 12727c478bd9Sstevel@tonic-gate else 12737c478bd9Sstevel@tonic-gate *nget = 1; 12747c478bd9Sstevel@tonic-gate port_dequeue_thread(&pp->port_queue, pgetp); 12757c478bd9Sstevel@tonic-gate portq->portq_thrcnt--; 12767c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 12777c478bd9Sstevel@tonic-gate return (error); 12787c478bd9Sstevel@tonic-gate } 12797c478bd9Sstevel@tonic-gate 12807c478bd9Sstevel@tonic-gate /* 12817c478bd9Sstevel@tonic-gate * Check if some other thread is already retrieving 12827c478bd9Sstevel@tonic-gate * events (portq_getn > 0). 12837c478bd9Sstevel@tonic-gate */ 12847c478bd9Sstevel@tonic-gate 12857c478bd9Sstevel@tonic-gate if ((portq->portq_getn == 0) && 12867c478bd9Sstevel@tonic-gate ((portq)->portq_nent >= *nget) && 12877c478bd9Sstevel@tonic-gate (!((pgt)->pgt_flags & PORTGET_WAIT_EVENTS) || 12887c478bd9Sstevel@tonic-gate !((portq)->portq_flags & PORTQ_WAIT_EVENTS))) 12897c478bd9Sstevel@tonic-gate break; 12907c478bd9Sstevel@tonic-gate 12917c478bd9Sstevel@tonic-gate if (portq->portq_flags & PORTQ_CLOSE) { 12927c478bd9Sstevel@tonic-gate error = EBADFD; 12937c478bd9Sstevel@tonic-gate break; 12947c478bd9Sstevel@tonic-gate } 12957c478bd9Sstevel@tonic-gate 12967c478bd9Sstevel@tonic-gate rval = cv_waituntil_sig(&pgetp->portget_cv, &portq->portq_mutex, 1297*3348528fSdm rqtp, timecheck); 12987c478bd9Sstevel@tonic-gate 12997c478bd9Sstevel@tonic-gate if (rval <= 0) { 13007c478bd9Sstevel@tonic-gate error = (rval == 0) ? EINTR : ETIME; 13017c478bd9Sstevel@tonic-gate break; 13027c478bd9Sstevel@tonic-gate } 13037c478bd9Sstevel@tonic-gate } 13047c478bd9Sstevel@tonic-gate 13057c478bd9Sstevel@tonic-gate /* take thread out of the wait queue */ 13067c478bd9Sstevel@tonic-gate port_dequeue_thread(portq, pgetp); 13077c478bd9Sstevel@tonic-gate 13087c478bd9Sstevel@tonic-gate if (error != 0 && (error == EINTR || error == EBADFD || 13097c478bd9Sstevel@tonic-gate (error == ETIME && flag))) { 13107c478bd9Sstevel@tonic-gate /* return without events */ 13117c478bd9Sstevel@tonic-gate port_check_return_cond(portq); 13127c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 13137c478bd9Sstevel@tonic-gate return (error); 13147c478bd9Sstevel@tonic-gate } 13157c478bd9Sstevel@tonic-gate 13167c478bd9Sstevel@tonic-gate portnowait: 13177c478bd9Sstevel@tonic-gate /* 13187c478bd9Sstevel@tonic-gate * Move port event queue to a temporary event queue . 13197c478bd9Sstevel@tonic-gate * New incoming events will be continue be posted to the event queue 13207c478bd9Sstevel@tonic-gate * and they will not be considered by the current thread. 13217c478bd9Sstevel@tonic-gate * The idea is to avoid lock contentions or an often locking/unlocking 13227c478bd9Sstevel@tonic-gate * of the port queue mutex. The contention and performance degradation 13237c478bd9Sstevel@tonic-gate * could happen because: 13247c478bd9Sstevel@tonic-gate * a) incoming events use the port queue mutex to enqueue new events and 13257c478bd9Sstevel@tonic-gate * b) before the event can be delivered to the application it is 13267c478bd9Sstevel@tonic-gate * necessary to notify the event sources about the event delivery. 13277c478bd9Sstevel@tonic-gate * Sometimes the event sources can require a long time to return and 13287c478bd9Sstevel@tonic-gate * the queue mutex would block incoming events. 13297c478bd9Sstevel@tonic-gate * During this time incoming events (port_send_event()) do not need 13307c478bd9Sstevel@tonic-gate * to awake threads waiting for events. Before the current thread 13317c478bd9Sstevel@tonic-gate * returns it will check the conditions to awake other waiting threads. 13327c478bd9Sstevel@tonic-gate */ 13337c478bd9Sstevel@tonic-gate portq->portq_getn++; /* number of threads retrieving events */ 133434709573Sraf port_block(portq); /* block other threads here */ 133534709573Sraf nmax = max < portq->portq_nent ? max : portq->portq_nent; 133634709573Sraf 13377c478bd9Sstevel@tonic-gate if (portq->portq_tnent) { 13387c478bd9Sstevel@tonic-gate /* 13397c478bd9Sstevel@tonic-gate * Move remaining events from previous thread back to the 13407c478bd9Sstevel@tonic-gate * port event queue. 13417c478bd9Sstevel@tonic-gate */ 13427c478bd9Sstevel@tonic-gate port_push_eventq(portq); 13437c478bd9Sstevel@tonic-gate } 13447c478bd9Sstevel@tonic-gate /* move port event queue to a temporary queue */ 13457c478bd9Sstevel@tonic-gate list_move_tail(&portq->portq_get_list, &portq->portq_list); 13467c478bd9Sstevel@tonic-gate glist = &portq->portq_get_list; /* use temporary event queue */ 13477c478bd9Sstevel@tonic-gate tnent = portq->portq_nent; /* get current number of events */ 13487c478bd9Sstevel@tonic-gate portq->portq_nent = 0; /* no events in the port event queue */ 13497c478bd9Sstevel@tonic-gate portq->portq_flags |= PORTQ_WAIT_EVENTS; /* detect incoming events */ 13507c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); /* event queue can be reused now */ 13517c478bd9Sstevel@tonic-gate 13527c478bd9Sstevel@tonic-gate if (model == DATAMODEL_NATIVE) { 13537c478bd9Sstevel@tonic-gate eventsz = sizeof (port_event_t); 13547c478bd9Sstevel@tonic-gate kevp = kmem_alloc(eventsz * nmax, KM_NOSLEEP); 13557c478bd9Sstevel@tonic-gate if (kevp == NULL) { 13567c478bd9Sstevel@tonic-gate if (nmax > pp->port_max_list) 13577c478bd9Sstevel@tonic-gate nmax = pp->port_max_list; 13587c478bd9Sstevel@tonic-gate kevp = kmem_alloc(eventsz * nmax, KM_SLEEP); 13597c478bd9Sstevel@tonic-gate } 13607c478bd9Sstevel@tonic-gate results = kevp; 13617c478bd9Sstevel@tonic-gate lev = NULL; /* start with first event in the queue */ 13627c478bd9Sstevel@tonic-gate for (nevents = 0; nevents < nmax; ) { 13637c478bd9Sstevel@tonic-gate pev = port_get_kevent(glist, lev); 13647c478bd9Sstevel@tonic-gate if (pev == NULL) /* no more events available */ 13657c478bd9Sstevel@tonic-gate break; 13667c478bd9Sstevel@tonic-gate if (pev->portkev_flags & PORT_KEV_FREE) { 13677c478bd9Sstevel@tonic-gate /* Just discard event */ 13687c478bd9Sstevel@tonic-gate list_remove(glist, pev); 13697c478bd9Sstevel@tonic-gate pev->portkev_flags &= ~(PORT_CLEANUP_DONE); 13707c478bd9Sstevel@tonic-gate if (PORT_FREE_EVENT(pev)) 13717c478bd9Sstevel@tonic-gate port_free_event_local(pev, 0); 13727c478bd9Sstevel@tonic-gate tnent--; 13737c478bd9Sstevel@tonic-gate continue; 13747c478bd9Sstevel@tonic-gate } 13757c478bd9Sstevel@tonic-gate 13767c478bd9Sstevel@tonic-gate /* move event data to copyout list */ 13777c478bd9Sstevel@tonic-gate if (port_copy_event(&kevp[nevents], pev, glist)) { 13787c478bd9Sstevel@tonic-gate /* 13797c478bd9Sstevel@tonic-gate * Event can not be delivered to the 13807c478bd9Sstevel@tonic-gate * current process. 13817c478bd9Sstevel@tonic-gate */ 13827c478bd9Sstevel@tonic-gate if (lev != NULL) 13837c478bd9Sstevel@tonic-gate list_insert_after(glist, lev, pev); 13847c478bd9Sstevel@tonic-gate else 13857c478bd9Sstevel@tonic-gate list_insert_head(glist, pev); 13867c478bd9Sstevel@tonic-gate lev = pev; /* last checked event */ 13877c478bd9Sstevel@tonic-gate } else { 13887c478bd9Sstevel@tonic-gate nevents++; /* # of events ready */ 13897c478bd9Sstevel@tonic-gate } 13907c478bd9Sstevel@tonic-gate } 13917c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 13927c478bd9Sstevel@tonic-gate } else { 13937c478bd9Sstevel@tonic-gate port_event32_t *kevp32; 13947c478bd9Sstevel@tonic-gate 13957c478bd9Sstevel@tonic-gate eventsz = sizeof (port_event32_t); 13967c478bd9Sstevel@tonic-gate kevp32 = kmem_alloc(eventsz * nmax, KM_NOSLEEP); 13977c478bd9Sstevel@tonic-gate if (kevp32 == NULL) { 13987c478bd9Sstevel@tonic-gate if (nmax > pp->port_max_list) 13997c478bd9Sstevel@tonic-gate nmax = pp->port_max_list; 14007c478bd9Sstevel@tonic-gate kevp32 = kmem_alloc(eventsz * nmax, KM_SLEEP); 14017c478bd9Sstevel@tonic-gate } 14027c478bd9Sstevel@tonic-gate results = kevp32; 14037c478bd9Sstevel@tonic-gate lev = NULL; /* start with first event in the queue */ 14047c478bd9Sstevel@tonic-gate for (nevents = 0; nevents < nmax; ) { 14057c478bd9Sstevel@tonic-gate pev = port_get_kevent(glist, lev); 14067c478bd9Sstevel@tonic-gate if (pev == NULL) /* no more events available */ 14077c478bd9Sstevel@tonic-gate break; 14087c478bd9Sstevel@tonic-gate if (pev->portkev_flags & PORT_KEV_FREE) { 14097c478bd9Sstevel@tonic-gate /* Just discard event */ 14107c478bd9Sstevel@tonic-gate list_remove(glist, pev); 14117c478bd9Sstevel@tonic-gate pev->portkev_flags &= ~(PORT_CLEANUP_DONE); 14127c478bd9Sstevel@tonic-gate if (PORT_FREE_EVENT(pev)) 14137c478bd9Sstevel@tonic-gate port_free_event_local(pev, 0); 14147c478bd9Sstevel@tonic-gate tnent--; 14157c478bd9Sstevel@tonic-gate continue; 14167c478bd9Sstevel@tonic-gate } 14177c478bd9Sstevel@tonic-gate 14187c478bd9Sstevel@tonic-gate /* move event data to copyout list */ 14197c478bd9Sstevel@tonic-gate if (port_copy_event32(&kevp32[nevents], pev, glist)) { 14207c478bd9Sstevel@tonic-gate /* 14217c478bd9Sstevel@tonic-gate * Event can not be delivered to the 14227c478bd9Sstevel@tonic-gate * current process. 14237c478bd9Sstevel@tonic-gate */ 14247c478bd9Sstevel@tonic-gate if (lev != NULL) 14257c478bd9Sstevel@tonic-gate list_insert_after(glist, lev, pev); 14267c478bd9Sstevel@tonic-gate else 14277c478bd9Sstevel@tonic-gate list_insert_head(glist, pev); 14287c478bd9Sstevel@tonic-gate lev = pev; /* last checked event */ 14297c478bd9Sstevel@tonic-gate } else { 14307c478bd9Sstevel@tonic-gate nevents++; /* # of events ready */ 14317c478bd9Sstevel@tonic-gate } 14327c478bd9Sstevel@tonic-gate } 14337c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */ 14347c478bd9Sstevel@tonic-gate } 14357c478bd9Sstevel@tonic-gate 14367c478bd9Sstevel@tonic-gate /* 14377c478bd9Sstevel@tonic-gate * Remember number of remaining events in the temporary event queue. 14387c478bd9Sstevel@tonic-gate */ 14397c478bd9Sstevel@tonic-gate portq->portq_tnent = tnent - nevents; 14407c478bd9Sstevel@tonic-gate 14417c478bd9Sstevel@tonic-gate /* 14427c478bd9Sstevel@tonic-gate * Work to do before return : 14437c478bd9Sstevel@tonic-gate * - push list of remaining events back to the top of the standard 14447c478bd9Sstevel@tonic-gate * port queue. 14457c478bd9Sstevel@tonic-gate * - if this is the last thread calling port_get(n) then wakeup the 14467c478bd9Sstevel@tonic-gate * thread waiting on close(2). 14477c478bd9Sstevel@tonic-gate * - check for a deferred cv_signal from port_send_event() and wakeup 14487c478bd9Sstevel@tonic-gate * the sleeping thread. 14497c478bd9Sstevel@tonic-gate */ 14507c478bd9Sstevel@tonic-gate 14517c478bd9Sstevel@tonic-gate mutex_enter(&portq->portq_mutex); 145234709573Sraf port_unblock(portq); 14537c478bd9Sstevel@tonic-gate if (portq->portq_tnent) { 14547c478bd9Sstevel@tonic-gate /* 14557c478bd9Sstevel@tonic-gate * move remaining events in the temporary event queue back 14567c478bd9Sstevel@tonic-gate * to the port event queue 14577c478bd9Sstevel@tonic-gate */ 14587c478bd9Sstevel@tonic-gate port_push_eventq(portq); 14597c478bd9Sstevel@tonic-gate } 14607c478bd9Sstevel@tonic-gate portq->portq_getn--; /* update # of threads retrieving events */ 14617c478bd9Sstevel@tonic-gate if (--portq->portq_thrcnt == 0) { /* # of threads waiting ... */ 14627c478bd9Sstevel@tonic-gate /* Last thread => check close(2) conditions ... */ 14637c478bd9Sstevel@tonic-gate if (portq->portq_flags & PORTQ_CLOSE) { 14647c478bd9Sstevel@tonic-gate cv_signal(&portq->portq_closecv); 14657c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 14667c478bd9Sstevel@tonic-gate kmem_free(results, eventsz * nmax); 14677c478bd9Sstevel@tonic-gate /* do not copyout events */ 14687c478bd9Sstevel@tonic-gate *nget = 0; 14697c478bd9Sstevel@tonic-gate return (EBADFD); 14707c478bd9Sstevel@tonic-gate } 14717c478bd9Sstevel@tonic-gate } else if (portq->portq_getn == 0) { 14727c478bd9Sstevel@tonic-gate /* 14737c478bd9Sstevel@tonic-gate * no other threads retrieving events ... 14747c478bd9Sstevel@tonic-gate * check wakeup conditions of sleeping threads 14757c478bd9Sstevel@tonic-gate */ 14767c478bd9Sstevel@tonic-gate if ((portq->portq_thread != NULL) && 14777c478bd9Sstevel@tonic-gate (portq->portq_nent >= portq->portq_nget)) 14787c478bd9Sstevel@tonic-gate cv_signal(&portq->portq_thread->portget_cv); 14797c478bd9Sstevel@tonic-gate } 14807c478bd9Sstevel@tonic-gate 14817c478bd9Sstevel@tonic-gate /* 14827c478bd9Sstevel@tonic-gate * Check PORTQ_POLLIN here because the current thread set temporarily 14837c478bd9Sstevel@tonic-gate * the number of events in the queue to zero. 14847c478bd9Sstevel@tonic-gate */ 14857c478bd9Sstevel@tonic-gate if (portq->portq_flags & PORTQ_POLLIN) { 14867c478bd9Sstevel@tonic-gate portq->portq_flags &= ~PORTQ_POLLIN; 14877c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 14887c478bd9Sstevel@tonic-gate pollwakeup(&pp->port_pollhd, POLLIN); 14897c478bd9Sstevel@tonic-gate } else { 14907c478bd9Sstevel@tonic-gate mutex_exit(&portq->portq_mutex); 14917c478bd9Sstevel@tonic-gate } 14927c478bd9Sstevel@tonic-gate 14937c478bd9Sstevel@tonic-gate /* now copyout list of user event structures to user space */ 14947c478bd9Sstevel@tonic-gate if (nevents) { 14957c478bd9Sstevel@tonic-gate if (copyout(results, uevp, nevents * eventsz)) 14967c478bd9Sstevel@tonic-gate error = EFAULT; 14977c478bd9Sstevel@tonic-gate } 14987c478bd9Sstevel@tonic-gate kmem_free(results, eventsz * nmax); 14997c478bd9Sstevel@tonic-gate 15007c478bd9Sstevel@tonic-gate if (nevents == 0 && error == 0 && pgt->pgt_loop == 0 && blocking != 0) { 15017c478bd9Sstevel@tonic-gate /* no events retrieved: check loop conditions */ 15027c478bd9Sstevel@tonic-gate if (blocking == -1) { 15037c478bd9Sstevel@tonic-gate /* no timeout checked */ 15047c478bd9Sstevel@tonic-gate error = port_get_timeout(pgt->pgt_timeout, 15057c478bd9Sstevel@tonic-gate &pgt->pgt_rqtime, &rqtp, &blocking, flag); 15067c478bd9Sstevel@tonic-gate if (error) { 15077c478bd9Sstevel@tonic-gate *nget = nevents; 15087c478bd9Sstevel@tonic-gate return (error); 15097c478bd9Sstevel@tonic-gate } 15107c478bd9Sstevel@tonic-gate if (rqtp != NULL) { 15117c478bd9Sstevel@tonic-gate timespec_t now; 1512*3348528fSdm pgt->pgt_timecheck = timechanged; 15137c478bd9Sstevel@tonic-gate gethrestime(&now); 15147c478bd9Sstevel@tonic-gate timespecadd(&pgt->pgt_rqtime, &now); 15157c478bd9Sstevel@tonic-gate } 15167c478bd9Sstevel@tonic-gate pgt->pgt_rqtp = rqtp; 15177c478bd9Sstevel@tonic-gate } else { 15187c478bd9Sstevel@tonic-gate /* timeout already checked -> remember values */ 15197c478bd9Sstevel@tonic-gate pgt->pgt_rqtp = rqtp; 1520f7ccf9b3Spraks if (rqtp != NULL) { 1521*3348528fSdm pgt->pgt_timecheck = timecheck; 1522f7ccf9b3Spraks pgt->pgt_rqtime = *rqtp; 1523f7ccf9b3Spraks } 15247c478bd9Sstevel@tonic-gate } 15257c478bd9Sstevel@tonic-gate if (blocking) 15267c478bd9Sstevel@tonic-gate /* timeout remaining */ 15277c478bd9Sstevel@tonic-gate pgt->pgt_loop = 1; 15287c478bd9Sstevel@tonic-gate } 15297c478bd9Sstevel@tonic-gate 15307c478bd9Sstevel@tonic-gate /* set number of user event structures completed */ 15317c478bd9Sstevel@tonic-gate *nget = nevents; 15327c478bd9Sstevel@tonic-gate return (error); 15337c478bd9Sstevel@tonic-gate } 15347c478bd9Sstevel@tonic-gate 15357c478bd9Sstevel@tonic-gate /* 15367c478bd9Sstevel@tonic-gate * 1. copy kernel event structure to user event structure. 15377c478bd9Sstevel@tonic-gate * 2. PORT_KEV_WIRED event structures will be reused by the "source" 15387c478bd9Sstevel@tonic-gate * 3. Remove PORT_KEV_DONEQ flag (event removed from the event queue) 15397c478bd9Sstevel@tonic-gate * 4. Other types of event structures can be delivered back to the port cache 15407c478bd9Sstevel@tonic-gate * (port_free_event_local()). 15417c478bd9Sstevel@tonic-gate * 5. The event source callback function is the last opportunity for the 15427c478bd9Sstevel@tonic-gate * event source to update events, to free local resources associated with 15437c478bd9Sstevel@tonic-gate * the event or to deny the delivery of the event. 15447c478bd9Sstevel@tonic-gate */ 15457c478bd9Sstevel@tonic-gate static int 15467c478bd9Sstevel@tonic-gate port_copy_event(port_event_t *puevp, port_kevent_t *pkevp, list_t *list) 15477c478bd9Sstevel@tonic-gate { 15487c478bd9Sstevel@tonic-gate int free_event = 0; 15497c478bd9Sstevel@tonic-gate int flags; 15507c478bd9Sstevel@tonic-gate int error; 15517c478bd9Sstevel@tonic-gate 15527c478bd9Sstevel@tonic-gate puevp->portev_source = pkevp->portkev_source; 15537c478bd9Sstevel@tonic-gate puevp->portev_object = pkevp->portkev_object; 15547c478bd9Sstevel@tonic-gate puevp->portev_user = pkevp->portkev_user; 15557c478bd9Sstevel@tonic-gate puevp->portev_events = pkevp->portkev_events; 15567c478bd9Sstevel@tonic-gate 15577c478bd9Sstevel@tonic-gate /* remove event from the queue */ 15587c478bd9Sstevel@tonic-gate list_remove(list, pkevp); 15597c478bd9Sstevel@tonic-gate 15607c478bd9Sstevel@tonic-gate /* 15617c478bd9Sstevel@tonic-gate * Events of type PORT_KEV_WIRED remain allocated by the 15627c478bd9Sstevel@tonic-gate * event source. 15637c478bd9Sstevel@tonic-gate */ 15647c478bd9Sstevel@tonic-gate flags = pkevp->portkev_flags; 15657c478bd9Sstevel@tonic-gate if (pkevp->portkev_flags & PORT_KEV_WIRED) 15667c478bd9Sstevel@tonic-gate pkevp->portkev_flags &= ~PORT_KEV_DONEQ; 15677c478bd9Sstevel@tonic-gate else 15687c478bd9Sstevel@tonic-gate free_event = 1; 15697c478bd9Sstevel@tonic-gate 15707c478bd9Sstevel@tonic-gate if (pkevp->portkev_callback) { 15717c478bd9Sstevel@tonic-gate error = (*pkevp->portkev_callback)(pkevp->portkev_arg, 15727c478bd9Sstevel@tonic-gate &puevp->portev_events, pkevp->portkev_pid, 15737c478bd9Sstevel@tonic-gate PORT_CALLBACK_DEFAULT, pkevp); 15747c478bd9Sstevel@tonic-gate 15757c478bd9Sstevel@tonic-gate if (error) { 15767c478bd9Sstevel@tonic-gate /* 15777c478bd9Sstevel@tonic-gate * Event can not be delivered. 15787c478bd9Sstevel@tonic-gate * Caller must reinsert the event into the queue. 15797c478bd9Sstevel@tonic-gate */ 15807c478bd9Sstevel@tonic-gate pkevp->portkev_flags = flags; 15817c478bd9Sstevel@tonic-gate return (error); 15827c478bd9Sstevel@tonic-gate } 15837c478bd9Sstevel@tonic-gate } 15847c478bd9Sstevel@tonic-gate if (free_event) 15857c478bd9Sstevel@tonic-gate port_free_event_local(pkevp, 0); 15867c478bd9Sstevel@tonic-gate return (0); 15877c478bd9Sstevel@tonic-gate } 15887c478bd9Sstevel@tonic-gate 15897c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 15907c478bd9Sstevel@tonic-gate /* 15917c478bd9Sstevel@tonic-gate * 1. copy kernel event structure to user event structure. 15927c478bd9Sstevel@tonic-gate * 2. PORT_KEV_WIRED event structures will be reused by the "source" 15937c478bd9Sstevel@tonic-gate * 3. Remove PORT_KEV_DONEQ flag (event removed from the event queue) 15947c478bd9Sstevel@tonic-gate * 4. Other types of event structures can be delivered back to the port cache 15957c478bd9Sstevel@tonic-gate * (port_free_event_local()). 15967c478bd9Sstevel@tonic-gate * 5. The event source callback function is the last opportunity for the 15977c478bd9Sstevel@tonic-gate * event source to update events, to free local resources associated with 15987c478bd9Sstevel@tonic-gate * the event or to deny the delivery of the event. 15997c478bd9Sstevel@tonic-gate */ 16007c478bd9Sstevel@tonic-gate static int 16017c478bd9Sstevel@tonic-gate port_copy_event32(port_event32_t *puevp, port_kevent_t *pkevp, list_t *list) 16027c478bd9Sstevel@tonic-gate { 16037c478bd9Sstevel@tonic-gate int free_event = 0; 16047c478bd9Sstevel@tonic-gate int error; 16057c478bd9Sstevel@tonic-gate int flags; 16067c478bd9Sstevel@tonic-gate 16077c478bd9Sstevel@tonic-gate puevp->portev_source = pkevp->portkev_source; 16087c478bd9Sstevel@tonic-gate puevp->portev_object = (daddr32_t)pkevp->portkev_object; 16097c478bd9Sstevel@tonic-gate puevp->portev_user = (caddr32_t)(uintptr_t)pkevp->portkev_user; 16107c478bd9Sstevel@tonic-gate puevp->portev_events = pkevp->portkev_events; 16117c478bd9Sstevel@tonic-gate 16127c478bd9Sstevel@tonic-gate /* remove event from the queue */ 16137c478bd9Sstevel@tonic-gate list_remove(list, pkevp); 16147c478bd9Sstevel@tonic-gate 16157c478bd9Sstevel@tonic-gate /* 16167c478bd9Sstevel@tonic-gate * Events if type PORT_KEV_WIRED remain allocated by the 16177c478bd9Sstevel@tonic-gate * sub-system (source). 16187c478bd9Sstevel@tonic-gate */ 16197c478bd9Sstevel@tonic-gate 16207c478bd9Sstevel@tonic-gate flags = pkevp->portkev_flags; 16217c478bd9Sstevel@tonic-gate if (pkevp->portkev_flags & PORT_KEV_WIRED) 16227c478bd9Sstevel@tonic-gate pkevp->portkev_flags &= ~PORT_KEV_DONEQ; 16237c478bd9Sstevel@tonic-gate else 16247c478bd9Sstevel@tonic-gate free_event = 1; 16257c478bd9Sstevel@tonic-gate 16267c478bd9Sstevel@tonic-gate if (pkevp->portkev_callback != NULL) { 16277c478bd9Sstevel@tonic-gate error = (*pkevp->portkev_callback)(pkevp->portkev_arg, 16287c478bd9Sstevel@tonic-gate &puevp->portev_events, pkevp->portkev_pid, 16297c478bd9Sstevel@tonic-gate PORT_CALLBACK_DEFAULT, pkevp); 16307c478bd9Sstevel@tonic-gate if (error) { 16317c478bd9Sstevel@tonic-gate /* 16327c478bd9Sstevel@tonic-gate * Event can not be delivered. 16337c478bd9Sstevel@tonic-gate * Caller must reinsert the event into the queue. 16347c478bd9Sstevel@tonic-gate */ 16357c478bd9Sstevel@tonic-gate pkevp->portkev_flags = flags; 16367c478bd9Sstevel@tonic-gate return (error); 16377c478bd9Sstevel@tonic-gate } 16387c478bd9Sstevel@tonic-gate } 16397c478bd9Sstevel@tonic-gate if (free_event) 16407c478bd9Sstevel@tonic-gate port_free_event_local(pkevp, 0); 16417c478bd9Sstevel@tonic-gate return (0); 16427c478bd9Sstevel@tonic-gate } 16437c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */ 16447c478bd9Sstevel@tonic-gate 16457c478bd9Sstevel@tonic-gate /* 16467c478bd9Sstevel@tonic-gate * copyout alert event. 16477c478bd9Sstevel@tonic-gate */ 16487c478bd9Sstevel@tonic-gate static int 16497c478bd9Sstevel@tonic-gate port_get_alert(port_alert_t *pa, port_event_t *uevp) 16507c478bd9Sstevel@tonic-gate { 16517c478bd9Sstevel@tonic-gate model_t model = get_udatamodel(); 16527c478bd9Sstevel@tonic-gate 16537c478bd9Sstevel@tonic-gate /* copyout alert event structures to user space */ 16547c478bd9Sstevel@tonic-gate if (model == DATAMODEL_NATIVE) { 16557c478bd9Sstevel@tonic-gate port_event_t uev; 16567c478bd9Sstevel@tonic-gate uev.portev_source = PORT_SOURCE_ALERT; 16577c478bd9Sstevel@tonic-gate uev.portev_object = pa->portal_object; 16587c478bd9Sstevel@tonic-gate uev.portev_events = pa->portal_events; 16597c478bd9Sstevel@tonic-gate uev.portev_user = pa->portal_user; 16607c478bd9Sstevel@tonic-gate if (copyout(&uev, uevp, sizeof (port_event_t))) 16617c478bd9Sstevel@tonic-gate return (EFAULT); 16627c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 16637c478bd9Sstevel@tonic-gate } else { 16647c478bd9Sstevel@tonic-gate port_event32_t uev32; 16657c478bd9Sstevel@tonic-gate uev32.portev_source = PORT_SOURCE_ALERT; 16667c478bd9Sstevel@tonic-gate uev32.portev_object = (daddr32_t)pa->portal_object; 16677c478bd9Sstevel@tonic-gate uev32.portev_events = pa->portal_events; 16687c478bd9Sstevel@tonic-gate uev32.portev_user = (daddr32_t)(uintptr_t)pa->portal_user; 16697c478bd9Sstevel@tonic-gate if (copyout(&uev32, uevp, sizeof (port_event32_t))) 16707c478bd9Sstevel@tonic-gate return (EFAULT); 16717c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */ 16727c478bd9Sstevel@tonic-gate } 16737c478bd9Sstevel@tonic-gate return (0); 16747c478bd9Sstevel@tonic-gate } 16757c478bd9Sstevel@tonic-gate 16767c478bd9Sstevel@tonic-gate /* 16777c478bd9Sstevel@tonic-gate * Check return conditions : 16787c478bd9Sstevel@tonic-gate * - pending port close(2) 16797c478bd9Sstevel@tonic-gate * - threads waiting for events 16807c478bd9Sstevel@tonic-gate */ 16817c478bd9Sstevel@tonic-gate static void 16827c478bd9Sstevel@tonic-gate port_check_return_cond(port_queue_t *portq) 16837c478bd9Sstevel@tonic-gate { 16847c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&portq->portq_mutex)); 16857c478bd9Sstevel@tonic-gate portq->portq_thrcnt--; 16867c478bd9Sstevel@tonic-gate if (portq->portq_flags & PORTQ_CLOSE) { 16877c478bd9Sstevel@tonic-gate if (portq->portq_thrcnt == 0) 16887c478bd9Sstevel@tonic-gate cv_signal(&portq->portq_closecv); 16897c478bd9Sstevel@tonic-gate else 16907c478bd9Sstevel@tonic-gate cv_signal(&portq->portq_thread->portget_cv); 16917c478bd9Sstevel@tonic-gate } 16927c478bd9Sstevel@tonic-gate } 16937c478bd9Sstevel@tonic-gate 16947c478bd9Sstevel@tonic-gate /* 16957c478bd9Sstevel@tonic-gate * The port_get_kevent() function returns 16967c478bd9Sstevel@tonic-gate * - the event located at the head of the queue if 'last' pointer is NULL 16977c478bd9Sstevel@tonic-gate * - the next event after the event pointed by 'last' 16987c478bd9Sstevel@tonic-gate * The caller of this function is responsible for the integrity of the queue 16997c478bd9Sstevel@tonic-gate * in use: 170034709573Sraf * - port_getn() is using a temporary queue protected with port_block(). 170134709573Sraf * - port_close_events() is working on the global event queue and protects 170234709573Sraf * the queue with portq->portq_mutex. 17037c478bd9Sstevel@tonic-gate */ 17047c478bd9Sstevel@tonic-gate port_kevent_t * 17057c478bd9Sstevel@tonic-gate port_get_kevent(list_t *list, port_kevent_t *last) 17067c478bd9Sstevel@tonic-gate { 17077c478bd9Sstevel@tonic-gate if (last == NULL) 17087c478bd9Sstevel@tonic-gate return (list_head(list)); 17097c478bd9Sstevel@tonic-gate else 17107c478bd9Sstevel@tonic-gate return (list_next(list, last)); 17117c478bd9Sstevel@tonic-gate } 17127c478bd9Sstevel@tonic-gate 17137c478bd9Sstevel@tonic-gate /* 17147c478bd9Sstevel@tonic-gate * The port_get_timeout() function gets the timeout data from user space 17157c478bd9Sstevel@tonic-gate * and converts that info into a corresponding internal representation. 17167c478bd9Sstevel@tonic-gate * The kerneldata flag means that the timeout data is already loaded. 17177c478bd9Sstevel@tonic-gate */ 17187c478bd9Sstevel@tonic-gate static int 17197c478bd9Sstevel@tonic-gate port_get_timeout(timespec_t *timeout, timespec_t *rqtime, timespec_t **rqtp, 17207c478bd9Sstevel@tonic-gate int *blocking, int kerneldata) 17217c478bd9Sstevel@tonic-gate { 17227c478bd9Sstevel@tonic-gate model_t model = get_udatamodel(); 17237c478bd9Sstevel@tonic-gate 17247c478bd9Sstevel@tonic-gate *rqtp = NULL; 17257c478bd9Sstevel@tonic-gate if (timeout == NULL) { 17267c478bd9Sstevel@tonic-gate *blocking = 1; 17277c478bd9Sstevel@tonic-gate return (0); 17287c478bd9Sstevel@tonic-gate } 17297c478bd9Sstevel@tonic-gate 17307c478bd9Sstevel@tonic-gate if (kerneldata) { 17317c478bd9Sstevel@tonic-gate *rqtime = *timeout; 17327c478bd9Sstevel@tonic-gate } else { 17337c478bd9Sstevel@tonic-gate if (model == DATAMODEL_NATIVE) { 17347c478bd9Sstevel@tonic-gate if (copyin(timeout, rqtime, sizeof (*rqtime))) 17357c478bd9Sstevel@tonic-gate return (EFAULT); 17367c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 17377c478bd9Sstevel@tonic-gate } else { 17387c478bd9Sstevel@tonic-gate timespec32_t wait_time_32; 17397c478bd9Sstevel@tonic-gate if (copyin(timeout, &wait_time_32, 17407c478bd9Sstevel@tonic-gate sizeof (wait_time_32))) 17417c478bd9Sstevel@tonic-gate return (EFAULT); 17427c478bd9Sstevel@tonic-gate TIMESPEC32_TO_TIMESPEC(rqtime, &wait_time_32); 17437c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */ 17447c478bd9Sstevel@tonic-gate } 17457c478bd9Sstevel@tonic-gate } 17467c478bd9Sstevel@tonic-gate 17477c478bd9Sstevel@tonic-gate if (rqtime->tv_sec == 0 && rqtime->tv_nsec == 0) { 17487c478bd9Sstevel@tonic-gate *blocking = 0; 17497c478bd9Sstevel@tonic-gate return (0); 17507c478bd9Sstevel@tonic-gate } 17517c478bd9Sstevel@tonic-gate 17527c478bd9Sstevel@tonic-gate if (rqtime->tv_sec < 0 || 17537c478bd9Sstevel@tonic-gate rqtime->tv_nsec < 0 || rqtime->tv_nsec >= NANOSEC) 17547c478bd9Sstevel@tonic-gate return (EINVAL); 17557c478bd9Sstevel@tonic-gate 17567c478bd9Sstevel@tonic-gate *rqtp = rqtime; 17577c478bd9Sstevel@tonic-gate *blocking = 1; 17587c478bd9Sstevel@tonic-gate return (0); 17597c478bd9Sstevel@tonic-gate } 17607c478bd9Sstevel@tonic-gate 17617c478bd9Sstevel@tonic-gate /* 17627c478bd9Sstevel@tonic-gate * port_queue_thread() 17637c478bd9Sstevel@tonic-gate * Threads requiring more events than available will be put in a wait queue. 17647c478bd9Sstevel@tonic-gate * There is a "thread wait queue" per port. 17657c478bd9Sstevel@tonic-gate * Threads requiring less events get a higher priority than others and they 17667c478bd9Sstevel@tonic-gate * will be awoken first. 17677c478bd9Sstevel@tonic-gate */ 17687c478bd9Sstevel@tonic-gate static portget_t * 17697c478bd9Sstevel@tonic-gate port_queue_thread(port_queue_t *portq, uint_t nget) 17707c478bd9Sstevel@tonic-gate { 17717c478bd9Sstevel@tonic-gate portget_t *pgetp; 17727c478bd9Sstevel@tonic-gate portget_t *ttp; 17737c478bd9Sstevel@tonic-gate portget_t *htp; 17747c478bd9Sstevel@tonic-gate 17757c478bd9Sstevel@tonic-gate pgetp = kmem_zalloc(sizeof (portget_t), KM_SLEEP); 17767c478bd9Sstevel@tonic-gate pgetp->portget_nget = nget; 17777c478bd9Sstevel@tonic-gate pgetp->portget_pid = curproc->p_pid; 17787c478bd9Sstevel@tonic-gate if (portq->portq_thread == NULL) { 17797c478bd9Sstevel@tonic-gate /* first waiting thread */ 17807c478bd9Sstevel@tonic-gate portq->portq_thread = pgetp; 17817c478bd9Sstevel@tonic-gate portq->portq_nget = nget; 17827c478bd9Sstevel@tonic-gate pgetp->portget_prev = pgetp; 17837c478bd9Sstevel@tonic-gate pgetp->portget_next = pgetp; 17847c478bd9Sstevel@tonic-gate return (pgetp); 17857c478bd9Sstevel@tonic-gate } 17867c478bd9Sstevel@tonic-gate 17877c478bd9Sstevel@tonic-gate /* 17887c478bd9Sstevel@tonic-gate * thread waiting for less events will be set on top of the queue. 17897c478bd9Sstevel@tonic-gate */ 17907c478bd9Sstevel@tonic-gate ttp = portq->portq_thread; 17917c478bd9Sstevel@tonic-gate htp = ttp; 17927c478bd9Sstevel@tonic-gate for (;;) { 17937c478bd9Sstevel@tonic-gate if (nget <= ttp->portget_nget) 17947c478bd9Sstevel@tonic-gate break; 17957c478bd9Sstevel@tonic-gate if (htp == ttp->portget_next) 17967c478bd9Sstevel@tonic-gate break; /* last event */ 17977c478bd9Sstevel@tonic-gate ttp = ttp->portget_next; 17987c478bd9Sstevel@tonic-gate } 17997c478bd9Sstevel@tonic-gate 18007c478bd9Sstevel@tonic-gate /* add thread to the queue */ 18017c478bd9Sstevel@tonic-gate pgetp->portget_next = ttp; 18027c478bd9Sstevel@tonic-gate pgetp->portget_prev = ttp->portget_prev; 18037c478bd9Sstevel@tonic-gate ttp->portget_prev->portget_next = pgetp; 18047c478bd9Sstevel@tonic-gate ttp->portget_prev = pgetp; 18057c478bd9Sstevel@tonic-gate if (portq->portq_thread == ttp) 18067c478bd9Sstevel@tonic-gate portq->portq_thread = pgetp; 18077c478bd9Sstevel@tonic-gate portq->portq_nget = portq->portq_thread->portget_nget; 18087c478bd9Sstevel@tonic-gate return (pgetp); 18097c478bd9Sstevel@tonic-gate } 18107c478bd9Sstevel@tonic-gate 18117c478bd9Sstevel@tonic-gate /* 18127c478bd9Sstevel@tonic-gate * Take thread out of the queue. 18137c478bd9Sstevel@tonic-gate */ 18147c478bd9Sstevel@tonic-gate static void 18157c478bd9Sstevel@tonic-gate port_dequeue_thread(port_queue_t *portq, portget_t *pgetp) 18167c478bd9Sstevel@tonic-gate { 18177c478bd9Sstevel@tonic-gate if (pgetp->portget_next == pgetp) { 18187c478bd9Sstevel@tonic-gate /* last (single) waiting thread */ 18197c478bd9Sstevel@tonic-gate portq->portq_thread = NULL; 182034709573Sraf portq->portq_nget = 0; 18217c478bd9Sstevel@tonic-gate } else { 18227c478bd9Sstevel@tonic-gate pgetp->portget_prev->portget_next = pgetp->portget_next; 18237c478bd9Sstevel@tonic-gate pgetp->portget_next->portget_prev = pgetp->portget_prev; 18247c478bd9Sstevel@tonic-gate if (portq->portq_thread == pgetp) 18257c478bd9Sstevel@tonic-gate portq->portq_thread = pgetp->portget_next; 18267c478bd9Sstevel@tonic-gate portq->portq_nget = portq->portq_thread->portget_nget; 18277c478bd9Sstevel@tonic-gate } 18287c478bd9Sstevel@tonic-gate kmem_free(pgetp, sizeof (portget_t)); 18297c478bd9Sstevel@tonic-gate } 18307c478bd9Sstevel@tonic-gate 18317c478bd9Sstevel@tonic-gate /* 18327c478bd9Sstevel@tonic-gate * Set up event port kstats. 18337c478bd9Sstevel@tonic-gate */ 18347c478bd9Sstevel@tonic-gate static void 18357c478bd9Sstevel@tonic-gate port_kstat_init() 18367c478bd9Sstevel@tonic-gate { 18377c478bd9Sstevel@tonic-gate kstat_t *ksp; 18387c478bd9Sstevel@tonic-gate uint_t ndata; 18397c478bd9Sstevel@tonic-gate 18407c478bd9Sstevel@tonic-gate ndata = sizeof (port_kstat) / sizeof (kstat_named_t); 18417c478bd9Sstevel@tonic-gate ksp = kstat_create("portfs", 0, "Event Ports", "misc", 18427c478bd9Sstevel@tonic-gate KSTAT_TYPE_NAMED, ndata, KSTAT_FLAG_VIRTUAL); 18437c478bd9Sstevel@tonic-gate if (ksp) { 18447c478bd9Sstevel@tonic-gate ksp->ks_data = &port_kstat; 18457c478bd9Sstevel@tonic-gate kstat_install(ksp); 18467c478bd9Sstevel@tonic-gate } 18477c478bd9Sstevel@tonic-gate } 1848