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