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