10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 53490Seschrock * Common Development and Distribution License (the "License"). 63490Seschrock * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*6997Sjwadams * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * This file contains the source of the general purpose event channel extension 300Sstevel@tonic-gate * to the sysevent framework. This implementation is made up mainly of four 310Sstevel@tonic-gate * layers of functionality: the event queues (evch_evq_*()), the handling of 320Sstevel@tonic-gate * channels (evch_ch*()), the kernel interface (sysevent_evc_*()) and the 330Sstevel@tonic-gate * interface for the sysevent pseudo driver (evch_usr*()). 340Sstevel@tonic-gate * Libsysevent.so uses the pseudo driver sysevent's ioctl to access the event 350Sstevel@tonic-gate * channel extensions. The driver in turn uses the evch_usr*() functions below. 360Sstevel@tonic-gate * 370Sstevel@tonic-gate * The interfaces for user land and kernel are declared in sys/sysevent.h 380Sstevel@tonic-gate * Internal data structures for event channels are defined in 390Sstevel@tonic-gate * sys/sysevent_impl.h. 400Sstevel@tonic-gate * 410Sstevel@tonic-gate * The basic data structure for an event channel is of type evch_chan_t. 420Sstevel@tonic-gate * All channels are maintained by a list named evch_list. The list head 430Sstevel@tonic-gate * is of type evch_dlist_t. 440Sstevel@tonic-gate */ 450Sstevel@tonic-gate 460Sstevel@tonic-gate #include <sys/types.h> 470Sstevel@tonic-gate #include <sys/errno.h> 480Sstevel@tonic-gate #include <sys/stropts.h> 490Sstevel@tonic-gate #include <sys/debug.h> 500Sstevel@tonic-gate #include <sys/ddi.h> 510Sstevel@tonic-gate #include <sys/vmem.h> 520Sstevel@tonic-gate #include <sys/cmn_err.h> 530Sstevel@tonic-gate #include <sys/callb.h> 540Sstevel@tonic-gate #include <sys/sysevent.h> 550Sstevel@tonic-gate #include <sys/sysevent_impl.h> 560Sstevel@tonic-gate #include <sys/sysmacros.h> 570Sstevel@tonic-gate #include <sys/disp.h> 580Sstevel@tonic-gate #include <sys/atomic.h> 590Sstevel@tonic-gate #include <sys/door.h> 600Sstevel@tonic-gate #include <sys/zone.h> 613490Seschrock #include <sys/sdt.h> 620Sstevel@tonic-gate 630Sstevel@tonic-gate /* Back-off delay for door_ki_upcall */ 640Sstevel@tonic-gate #define EVCH_MIN_PAUSE 8 650Sstevel@tonic-gate #define EVCH_MAX_PAUSE 128 660Sstevel@tonic-gate 670Sstevel@tonic-gate #define GEVENT(ev) ((evch_gevent_t *)((char *)ev - \ 680Sstevel@tonic-gate offsetof(evch_gevent_t, ge_payload))) 690Sstevel@tonic-gate 700Sstevel@tonic-gate #define EVCH_EVQ_EVCOUNT(x) ((&(x)->eq_eventq)->sq_count) 710Sstevel@tonic-gate #define EVCH_EVQ_HIGHWM(x) ((&(x)->eq_eventq)->sq_highwm) 720Sstevel@tonic-gate 730Sstevel@tonic-gate struct evch_globals { 740Sstevel@tonic-gate evch_dlist_t evch_list; 750Sstevel@tonic-gate kmutex_t evch_list_lock; 760Sstevel@tonic-gate }; 770Sstevel@tonic-gate 780Sstevel@tonic-gate /* Variables used by event channel routines */ 790Sstevel@tonic-gate static int evq_initcomplete = 0; 800Sstevel@tonic-gate static zone_key_t evch_zone_key; 810Sstevel@tonic-gate static uint32_t evch_channels_max; 820Sstevel@tonic-gate static uint32_t evch_bindings_max = EVCH_MAX_BINDS_PER_CHANNEL; 830Sstevel@tonic-gate static uint32_t evch_events_max; 840Sstevel@tonic-gate 850Sstevel@tonic-gate static void evch_evq_unsub(evch_eventq_t *, evch_evqsub_t *); 860Sstevel@tonic-gate static void evch_evq_destroy(evch_eventq_t *); 870Sstevel@tonic-gate 880Sstevel@tonic-gate /* 890Sstevel@tonic-gate * List handling. These functions handle a doubly linked list. The list has 900Sstevel@tonic-gate * to be protected by the calling functions. evch_dlist_t is the list head. 910Sstevel@tonic-gate * Every node of the list has to put a evch_dlelem_t data type in its data 920Sstevel@tonic-gate * structure as its first element. 930Sstevel@tonic-gate * 940Sstevel@tonic-gate * evch_dl_init - Initialize list head 950Sstevel@tonic-gate * evch_dl_fini - Terminate list handling 960Sstevel@tonic-gate * evch_dl_is_init - Returns one if list is initialized 970Sstevel@tonic-gate * evch_dl_add - Add element to end of list 980Sstevel@tonic-gate * evch_dl_del - Remove given element from list 990Sstevel@tonic-gate * evch_dl_search - Lookup element in list 1000Sstevel@tonic-gate * evch_dl_getnum - Get number of elements in list 1010Sstevel@tonic-gate * evch_dl_next - Get next elements of list 1020Sstevel@tonic-gate */ 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate static void 1050Sstevel@tonic-gate evch_dl_init(evch_dlist_t *hp) 1060Sstevel@tonic-gate { 1070Sstevel@tonic-gate hp->dh_head.dl_prev = hp->dh_head.dl_next = &hp->dh_head; 1080Sstevel@tonic-gate hp->dh_count = 0; 1090Sstevel@tonic-gate } 1100Sstevel@tonic-gate 1110Sstevel@tonic-gate /* 1120Sstevel@tonic-gate * Assumes that list is empty. 1130Sstevel@tonic-gate */ 1140Sstevel@tonic-gate static void 1150Sstevel@tonic-gate evch_dl_fini(evch_dlist_t *hp) 1160Sstevel@tonic-gate { 1170Sstevel@tonic-gate hp->dh_head.dl_prev = hp->dh_head.dl_next = NULL; 1180Sstevel@tonic-gate } 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate static int 1210Sstevel@tonic-gate evch_dl_is_init(evch_dlist_t *hp) 1220Sstevel@tonic-gate { 1230Sstevel@tonic-gate return (hp->dh_head.dl_next != NULL ? 1 : 0); 1240Sstevel@tonic-gate } 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate /* 1270Sstevel@tonic-gate * Add an element at the end of the list. 1280Sstevel@tonic-gate */ 1290Sstevel@tonic-gate static void 1300Sstevel@tonic-gate evch_dl_add(evch_dlist_t *hp, evch_dlelem_t *el) 1310Sstevel@tonic-gate { 1320Sstevel@tonic-gate evch_dlelem_t *x = hp->dh_head.dl_prev; 1330Sstevel@tonic-gate evch_dlelem_t *y = &hp->dh_head; 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate x->dl_next = el; 1360Sstevel@tonic-gate y->dl_prev = el; 1370Sstevel@tonic-gate el->dl_next = y; 1380Sstevel@tonic-gate el->dl_prev = x; 1390Sstevel@tonic-gate hp->dh_count++; 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate /* 1430Sstevel@tonic-gate * Remove arbitrary element out of dlist. 1440Sstevel@tonic-gate */ 1450Sstevel@tonic-gate static void 1460Sstevel@tonic-gate evch_dl_del(evch_dlist_t *hp, evch_dlelem_t *p) 1470Sstevel@tonic-gate { 1480Sstevel@tonic-gate ASSERT(hp->dh_count > 0 && p != &hp->dh_head); 1490Sstevel@tonic-gate p->dl_prev->dl_next = p->dl_next; 1500Sstevel@tonic-gate p->dl_next->dl_prev = p->dl_prev; 1510Sstevel@tonic-gate p->dl_prev = NULL; 1520Sstevel@tonic-gate p->dl_next = NULL; 1530Sstevel@tonic-gate hp->dh_count--; 1540Sstevel@tonic-gate } 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate /* 1570Sstevel@tonic-gate * Search an element in a list. Caller provides comparison callback function. 1580Sstevel@tonic-gate */ 1590Sstevel@tonic-gate static evch_dlelem_t * 1600Sstevel@tonic-gate evch_dl_search(evch_dlist_t *hp, int (*cmp)(evch_dlelem_t *, char *), char *s) 1610Sstevel@tonic-gate { 1620Sstevel@tonic-gate evch_dlelem_t *p; 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate for (p = hp->dh_head.dl_next; p != &hp->dh_head; p = p->dl_next) { 1650Sstevel@tonic-gate if (cmp(p, s) == 0) { 1660Sstevel@tonic-gate return (p); 1670Sstevel@tonic-gate } 1680Sstevel@tonic-gate } 1690Sstevel@tonic-gate return (NULL); 1700Sstevel@tonic-gate } 1710Sstevel@tonic-gate 1720Sstevel@tonic-gate /* 1730Sstevel@tonic-gate * Return number of elements in the list. 1740Sstevel@tonic-gate */ 1750Sstevel@tonic-gate static int 1760Sstevel@tonic-gate evch_dl_getnum(evch_dlist_t *hp) 1770Sstevel@tonic-gate { 1780Sstevel@tonic-gate return (hp->dh_count); 1790Sstevel@tonic-gate } 1800Sstevel@tonic-gate 1810Sstevel@tonic-gate /* 1820Sstevel@tonic-gate * Find next element of a evch_dlist_t list. Find first element if el == NULL. 1830Sstevel@tonic-gate * Returns NULL if end of list is reached. 1840Sstevel@tonic-gate */ 1850Sstevel@tonic-gate static void * 1860Sstevel@tonic-gate evch_dl_next(evch_dlist_t *hp, void *el) 1870Sstevel@tonic-gate { 1880Sstevel@tonic-gate evch_dlelem_t *ep = (evch_dlelem_t *)el; 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate if (hp->dh_count == 0) { 1910Sstevel@tonic-gate return (NULL); 1920Sstevel@tonic-gate } 1930Sstevel@tonic-gate if (ep == NULL) { 1940Sstevel@tonic-gate return (hp->dh_head.dl_next); 1950Sstevel@tonic-gate } 1960Sstevel@tonic-gate if ((ep = ep->dl_next) == (evch_dlelem_t *)hp) { 1970Sstevel@tonic-gate return (NULL); 1980Sstevel@tonic-gate } 1990Sstevel@tonic-gate return ((void *)ep); 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate /* 2030Sstevel@tonic-gate * Queue handling routines. Mutexes have to be entered previously. 2040Sstevel@tonic-gate * 2050Sstevel@tonic-gate * evch_q_init - Initialize queue head 2060Sstevel@tonic-gate * evch_q_in - Put element into queue 2070Sstevel@tonic-gate * evch_q_out - Get element out of queue 2080Sstevel@tonic-gate * evch_q_next - Iterate over the elements of a queue 2090Sstevel@tonic-gate */ 2100Sstevel@tonic-gate static void 2110Sstevel@tonic-gate evch_q_init(evch_squeue_t *q) 2120Sstevel@tonic-gate { 2130Sstevel@tonic-gate q->sq_head = NULL; 2140Sstevel@tonic-gate q->sq_tail = (evch_qelem_t *)q; 2150Sstevel@tonic-gate q->sq_count = 0; 2160Sstevel@tonic-gate q->sq_highwm = 0; 2170Sstevel@tonic-gate } 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate /* 2200Sstevel@tonic-gate * Put element into the queue q 2210Sstevel@tonic-gate */ 2220Sstevel@tonic-gate static void 2230Sstevel@tonic-gate evch_q_in(evch_squeue_t *q, evch_qelem_t *el) 2240Sstevel@tonic-gate { 2250Sstevel@tonic-gate q->sq_tail->q_next = el; 2260Sstevel@tonic-gate el->q_next = NULL; 2270Sstevel@tonic-gate q->sq_tail = el; 2280Sstevel@tonic-gate q->sq_count++; 2290Sstevel@tonic-gate if (q->sq_count > q->sq_highwm) { 2300Sstevel@tonic-gate q->sq_highwm = q->sq_count; 2310Sstevel@tonic-gate } 2320Sstevel@tonic-gate } 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate /* 2350Sstevel@tonic-gate * Returns NULL if queue is empty. 2360Sstevel@tonic-gate */ 2370Sstevel@tonic-gate static evch_qelem_t * 2380Sstevel@tonic-gate evch_q_out(evch_squeue_t *q) 2390Sstevel@tonic-gate { 2400Sstevel@tonic-gate evch_qelem_t *el; 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate if ((el = q->sq_head) != NULL) { 2430Sstevel@tonic-gate q->sq_head = el->q_next; 2440Sstevel@tonic-gate q->sq_count--; 2450Sstevel@tonic-gate if (q->sq_head == NULL) { 2460Sstevel@tonic-gate q->sq_tail = (evch_qelem_t *)q; 2470Sstevel@tonic-gate } 2480Sstevel@tonic-gate } 2490Sstevel@tonic-gate return (el); 2500Sstevel@tonic-gate } 2510Sstevel@tonic-gate 2520Sstevel@tonic-gate /* 2530Sstevel@tonic-gate * Returns element after *el or first if el == NULL. NULL is returned 2540Sstevel@tonic-gate * if queue is empty or *el points to the last element in the queue. 2550Sstevel@tonic-gate */ 2560Sstevel@tonic-gate static evch_qelem_t * 2570Sstevel@tonic-gate evch_q_next(evch_squeue_t *q, evch_qelem_t *el) 2580Sstevel@tonic-gate { 2590Sstevel@tonic-gate if (el == NULL) 2600Sstevel@tonic-gate return (q->sq_head); 2610Sstevel@tonic-gate return (el->q_next); 2620Sstevel@tonic-gate } 2630Sstevel@tonic-gate 2640Sstevel@tonic-gate /* 2650Sstevel@tonic-gate * Event queue handling functions. An event queue is the basic building block 2660Sstevel@tonic-gate * of an event channel. One event queue makes up the publisher-side event queue. 2670Sstevel@tonic-gate * Further event queues build the per-subscriber queues of an event channel. 2680Sstevel@tonic-gate * Each queue is associated an event delivery thread. 2690Sstevel@tonic-gate * These functions support a two-step initialization. First step, when kernel 2700Sstevel@tonic-gate * memory is ready and second when threads are ready. 2710Sstevel@tonic-gate * Events consist of an administrating evch_gevent_t structure with the event 2720Sstevel@tonic-gate * data appended as variable length payload. 2730Sstevel@tonic-gate * The internal interface functions for the event queue handling are: 2740Sstevel@tonic-gate * 2750Sstevel@tonic-gate * evch_evq_create - create an event queue 2760Sstevel@tonic-gate * evch_evq_thrcreate - create thread for an event queue. 2770Sstevel@tonic-gate * evch_evq_destroy - delete an event queue 2780Sstevel@tonic-gate * evch_evq_sub - Subscribe to event delivery from an event queue 2790Sstevel@tonic-gate * evch_evq_unsub - Unsubscribe 2800Sstevel@tonic-gate * evch_evq_pub - Post an event into an event queue 2810Sstevel@tonic-gate * evch_evq_stop - Put delivery thread on hold 2820Sstevel@tonic-gate * evch_evq_continue - Resume event delivery thread 2830Sstevel@tonic-gate * evch_evq_status - Return status of delivery thread, running or on hold 2840Sstevel@tonic-gate * evch_evq_evzalloc - Allocate an event structure 2850Sstevel@tonic-gate * evch_evq_evfree - Free an event structure 2860Sstevel@tonic-gate * evch_evq_evadd_dest - Add a destructor function to an event structure 2870Sstevel@tonic-gate * evch_evq_evnext - Iterate over events non-destructive 2880Sstevel@tonic-gate */ 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate /*ARGSUSED*/ 2910Sstevel@tonic-gate static void * 2920Sstevel@tonic-gate evch_zoneinit(zoneid_t zoneid) 2930Sstevel@tonic-gate { 2940Sstevel@tonic-gate struct evch_globals *eg; 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate eg = kmem_zalloc(sizeof (*eg), KM_SLEEP); 2970Sstevel@tonic-gate evch_dl_init(&eg->evch_list); 2980Sstevel@tonic-gate return (eg); 2990Sstevel@tonic-gate } 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate /*ARGSUSED*/ 3020Sstevel@tonic-gate static void 3030Sstevel@tonic-gate evch_zonefree(zoneid_t zoneid, void *arg) 3040Sstevel@tonic-gate { 3050Sstevel@tonic-gate struct evch_globals *eg = arg; 3060Sstevel@tonic-gate evch_chan_t *chp; 3070Sstevel@tonic-gate evch_subd_t *sdp; 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate mutex_enter(&eg->evch_list_lock); 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate /* 3120Sstevel@tonic-gate * Keep picking the head element off the list until there are no 3130Sstevel@tonic-gate * more. 3140Sstevel@tonic-gate */ 3150Sstevel@tonic-gate while ((chp = evch_dl_next(&eg->evch_list, NULL)) != NULL) { 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate /* 3180Sstevel@tonic-gate * Since all processes are gone, all bindings should be gone, 3190Sstevel@tonic-gate * and only channels with SUB_KEEP subscribers should remain. 3200Sstevel@tonic-gate */ 3210Sstevel@tonic-gate mutex_enter(&chp->ch_mutex); 3220Sstevel@tonic-gate ASSERT(chp->ch_bindings == 0); 3230Sstevel@tonic-gate ASSERT(evch_dl_getnum(&chp->ch_subscr) != 0); 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate /* Forcibly unsubscribe each remaining subscription */ 3260Sstevel@tonic-gate while ((sdp = evch_dl_next(&chp->ch_subscr, NULL)) != NULL) { 3270Sstevel@tonic-gate /* 3280Sstevel@tonic-gate * We should only be tearing down persistent 3290Sstevel@tonic-gate * subscribers at this point, since all processes 3300Sstevel@tonic-gate * from this zone are gone. 3310Sstevel@tonic-gate */ 3320Sstevel@tonic-gate ASSERT(sdp->sd_active == 0); 3330Sstevel@tonic-gate ASSERT((sdp->sd_persist & EVCH_SUB_KEEP) != 0); 3340Sstevel@tonic-gate /* 3350Sstevel@tonic-gate * Disconnect subscriber queue from main event queue. 3360Sstevel@tonic-gate */ 3370Sstevel@tonic-gate evch_evq_unsub(chp->ch_queue, sdp->sd_msub); 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate /* Destruct per subscriber queue */ 3400Sstevel@tonic-gate evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub); 3410Sstevel@tonic-gate evch_evq_destroy(sdp->sd_queue); 3420Sstevel@tonic-gate /* 3430Sstevel@tonic-gate * Eliminate the subscriber data from channel list. 3440Sstevel@tonic-gate */ 3450Sstevel@tonic-gate evch_dl_del(&chp->ch_subscr, &sdp->sd_link); 3460Sstevel@tonic-gate kmem_free(sdp->sd_classname, sdp->sd_clnsize); 3470Sstevel@tonic-gate kmem_free(sdp->sd_ident, strlen(sdp->sd_ident) + 1); 3480Sstevel@tonic-gate kmem_free(sdp, sizeof (evch_subd_t)); 3490Sstevel@tonic-gate } 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate /* Channel must now have no subscribers */ 3520Sstevel@tonic-gate ASSERT(evch_dl_getnum(&chp->ch_subscr) == 0); 3530Sstevel@tonic-gate 3540Sstevel@tonic-gate /* Just like unbind */ 3550Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 3560Sstevel@tonic-gate evch_dl_del(&eg->evch_list, &chp->ch_link); 3570Sstevel@tonic-gate evch_evq_destroy(chp->ch_queue); 3580Sstevel@tonic-gate mutex_destroy(&chp->ch_mutex); 3590Sstevel@tonic-gate mutex_destroy(&chp->ch_pubmx); 3600Sstevel@tonic-gate cv_destroy(&chp->ch_pubcv); 3610Sstevel@tonic-gate kmem_free(chp->ch_name, chp->ch_namelen); 3620Sstevel@tonic-gate kmem_free(chp, sizeof (evch_chan_t)); 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 3660Sstevel@tonic-gate /* all channels should now be gone */ 3670Sstevel@tonic-gate ASSERT(evch_dl_getnum(&eg->evch_list) == 0); 3680Sstevel@tonic-gate kmem_free(eg, sizeof (*eg)); 3690Sstevel@tonic-gate } 3700Sstevel@tonic-gate 3710Sstevel@tonic-gate /* 3720Sstevel@tonic-gate * Frees evch_gevent_t structure including the payload, if the reference count 3730Sstevel@tonic-gate * drops to or below zero. Below zero happens when the event is freed 3740Sstevel@tonic-gate * without beeing queued into a queue. 3750Sstevel@tonic-gate */ 3760Sstevel@tonic-gate static void 3770Sstevel@tonic-gate evch_gevent_free(evch_gevent_t *evp) 3780Sstevel@tonic-gate { 3790Sstevel@tonic-gate int32_t refcnt; 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate refcnt = (int32_t)atomic_add_32_nv(&evp->ge_refcount, -1); 3820Sstevel@tonic-gate if (refcnt <= 0) { 3830Sstevel@tonic-gate if (evp->ge_destruct != NULL) { 3840Sstevel@tonic-gate evp->ge_destruct((void *)&(evp->ge_payload), 3850Sstevel@tonic-gate evp->ge_dstcookie); 3860Sstevel@tonic-gate } 3870Sstevel@tonic-gate kmem_free(evp, evp->ge_size); 3880Sstevel@tonic-gate } 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate /* 3920Sstevel@tonic-gate * Deliver is called for every subscription to the current event 3930Sstevel@tonic-gate * It calls the registered filter function and then the registered delivery 3940Sstevel@tonic-gate * callback routine. Returns 0 on success. The callback routine returns 3950Sstevel@tonic-gate * EVQ_AGAIN or EVQ_SLEEP in case the event could not be delivered. 3960Sstevel@tonic-gate */ 3970Sstevel@tonic-gate static int 3980Sstevel@tonic-gate evch_deliver(evch_evqsub_t *sp, evch_gevent_t *ep) 3990Sstevel@tonic-gate { 4000Sstevel@tonic-gate void *uep = &ep->ge_payload; 4010Sstevel@tonic-gate int res = EVQ_DELIVER; 4020Sstevel@tonic-gate 4030Sstevel@tonic-gate if (sp->su_filter != NULL) { 4040Sstevel@tonic-gate res = sp->su_filter(uep, sp->su_fcookie); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate if (res == EVQ_DELIVER) { 4070Sstevel@tonic-gate return (sp->su_callb(uep, sp->su_cbcookie)); 4080Sstevel@tonic-gate } 4090Sstevel@tonic-gate return (0); 4100Sstevel@tonic-gate } 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate /* 4130Sstevel@tonic-gate * Holds event delivery in case of eq_holdmode set or in case the 4140Sstevel@tonic-gate * event queue is empty. Mutex must be held when called. 4150Sstevel@tonic-gate * Wakes up a thread waiting for the delivery thread reaching the hold mode. 4160Sstevel@tonic-gate */ 4170Sstevel@tonic-gate static void 4180Sstevel@tonic-gate evch_delivery_hold(evch_eventq_t *eqp, callb_cpr_t *cpip) 4190Sstevel@tonic-gate { 4200Sstevel@tonic-gate if (eqp->eq_tabortflag == 0) { 4210Sstevel@tonic-gate do { 4220Sstevel@tonic-gate if (eqp->eq_holdmode) { 4230Sstevel@tonic-gate cv_signal(&eqp->eq_onholdcv); 4240Sstevel@tonic-gate } 4250Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(cpip); 4260Sstevel@tonic-gate cv_wait(&eqp->eq_thrsleepcv, &eqp->eq_queuemx); 4270Sstevel@tonic-gate CALLB_CPR_SAFE_END(cpip, &eqp->eq_queuemx); 4280Sstevel@tonic-gate } while (eqp->eq_holdmode); 4290Sstevel@tonic-gate } 4300Sstevel@tonic-gate } 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate /* 4330Sstevel@tonic-gate * Event delivery thread. Enumerates all subscribers and calls evch_deliver() 4340Sstevel@tonic-gate * for each one. 4350Sstevel@tonic-gate */ 4360Sstevel@tonic-gate static void 4370Sstevel@tonic-gate evch_delivery_thr(evch_eventq_t *eqp) 4380Sstevel@tonic-gate { 4390Sstevel@tonic-gate evch_qelem_t *qep; 4400Sstevel@tonic-gate callb_cpr_t cprinfo; 4410Sstevel@tonic-gate int res; 4420Sstevel@tonic-gate evch_evqsub_t *sub; 4430Sstevel@tonic-gate int deltime; 4440Sstevel@tonic-gate int repeatcount; 4450Sstevel@tonic-gate char thnam[32]; 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate (void) snprintf(thnam, sizeof (thnam), "sysevent_chan-%d", 4480Sstevel@tonic-gate (int)eqp->eq_thrid); 4490Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &eqp->eq_queuemx, callb_generic_cpr, thnam); 4500Sstevel@tonic-gate mutex_enter(&eqp->eq_queuemx); 4510Sstevel@tonic-gate while (eqp->eq_tabortflag == 0) { 4520Sstevel@tonic-gate while (eqp->eq_holdmode == 0 && eqp->eq_tabortflag == 0 && 4530Sstevel@tonic-gate (qep = evch_q_out(&eqp->eq_eventq)) != NULL) { 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate /* Filter and deliver event to all subscribers */ 4560Sstevel@tonic-gate deltime = EVCH_MIN_PAUSE; 4570Sstevel@tonic-gate repeatcount = EVCH_MAX_TRY_DELIVERY; 4580Sstevel@tonic-gate eqp->eq_curevent = qep->q_objref; 4590Sstevel@tonic-gate sub = evch_dl_next(&eqp->eq_subscr, NULL); 4600Sstevel@tonic-gate while (sub != NULL) { 4610Sstevel@tonic-gate eqp->eq_dactive = 1; 4620Sstevel@tonic-gate mutex_exit(&eqp->eq_queuemx); 4630Sstevel@tonic-gate res = evch_deliver(sub, qep->q_objref); 4640Sstevel@tonic-gate mutex_enter(&eqp->eq_queuemx); 4650Sstevel@tonic-gate eqp->eq_dactive = 0; 4660Sstevel@tonic-gate cv_signal(&eqp->eq_dactivecv); 4670Sstevel@tonic-gate switch (res) { 4680Sstevel@tonic-gate case EVQ_SLEEP: 4690Sstevel@tonic-gate /* 4700Sstevel@tonic-gate * Wait for subscriber to return. 4710Sstevel@tonic-gate */ 4720Sstevel@tonic-gate eqp->eq_holdmode = 1; 4730Sstevel@tonic-gate evch_delivery_hold(eqp, &cprinfo); 4740Sstevel@tonic-gate if (eqp->eq_tabortflag) { 4750Sstevel@tonic-gate break; 4760Sstevel@tonic-gate } 4770Sstevel@tonic-gate continue; 4780Sstevel@tonic-gate case EVQ_AGAIN: 4790Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 4800Sstevel@tonic-gate mutex_exit(&eqp->eq_queuemx); 4810Sstevel@tonic-gate delay(deltime); 4820Sstevel@tonic-gate deltime = 4830Sstevel@tonic-gate deltime > EVCH_MAX_PAUSE ? 4840Sstevel@tonic-gate deltime : deltime << 1; 4850Sstevel@tonic-gate mutex_enter(&eqp->eq_queuemx); 4860Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, 4870Sstevel@tonic-gate &eqp->eq_queuemx); 4880Sstevel@tonic-gate if (repeatcount-- > 0) { 4890Sstevel@tonic-gate continue; 4900Sstevel@tonic-gate } 4910Sstevel@tonic-gate break; 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate if (eqp->eq_tabortflag) { 4940Sstevel@tonic-gate break; 4950Sstevel@tonic-gate } 4960Sstevel@tonic-gate sub = evch_dl_next(&eqp->eq_subscr, sub); 4970Sstevel@tonic-gate repeatcount = EVCH_MAX_TRY_DELIVERY; 4980Sstevel@tonic-gate } 4990Sstevel@tonic-gate eqp->eq_curevent = NULL; 5000Sstevel@tonic-gate 5010Sstevel@tonic-gate /* Free event data and queue element */ 5020Sstevel@tonic-gate evch_gevent_free((evch_gevent_t *)qep->q_objref); 5030Sstevel@tonic-gate kmem_free(qep, qep->q_objsize); 5040Sstevel@tonic-gate } 5050Sstevel@tonic-gate 5060Sstevel@tonic-gate /* Wait for next event or end of hold mode if set */ 5070Sstevel@tonic-gate evch_delivery_hold(eqp, &cprinfo); 5080Sstevel@tonic-gate } 5090Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo); /* Does mutex_exit of eqp->eq_queuemx */ 5100Sstevel@tonic-gate thread_exit(); 5110Sstevel@tonic-gate } 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate /* 5140Sstevel@tonic-gate * Create the event delivery thread for an existing event queue. 5150Sstevel@tonic-gate */ 5160Sstevel@tonic-gate static void 5170Sstevel@tonic-gate evch_evq_thrcreate(evch_eventq_t *eqp) 5180Sstevel@tonic-gate { 5190Sstevel@tonic-gate kthread_t *thp; 5200Sstevel@tonic-gate 5210Sstevel@tonic-gate thp = thread_create(NULL, 0, evch_delivery_thr, (char *)eqp, 0, &p0, 5220Sstevel@tonic-gate TS_RUN, minclsyspri); 5230Sstevel@tonic-gate eqp->eq_thrid = thp->t_did; 5240Sstevel@tonic-gate } 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate /* 5270Sstevel@tonic-gate * Create event queue. 5280Sstevel@tonic-gate */ 5290Sstevel@tonic-gate static evch_eventq_t * 5300Sstevel@tonic-gate evch_evq_create() 5310Sstevel@tonic-gate { 5320Sstevel@tonic-gate evch_eventq_t *p; 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate /* Allocate and initialize event queue descriptor */ 5350Sstevel@tonic-gate p = kmem_zalloc(sizeof (evch_eventq_t), KM_SLEEP); 5360Sstevel@tonic-gate mutex_init(&p->eq_queuemx, NULL, MUTEX_DEFAULT, NULL); 5370Sstevel@tonic-gate cv_init(&p->eq_thrsleepcv, NULL, CV_DEFAULT, NULL); 5380Sstevel@tonic-gate evch_q_init(&p->eq_eventq); 5390Sstevel@tonic-gate evch_dl_init(&p->eq_subscr); 5400Sstevel@tonic-gate cv_init(&p->eq_dactivecv, NULL, CV_DEFAULT, NULL); 5410Sstevel@tonic-gate cv_init(&p->eq_onholdcv, NULL, CV_DEFAULT, NULL); 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate /* Create delivery thread */ 5440Sstevel@tonic-gate if (evq_initcomplete) { 5450Sstevel@tonic-gate evch_evq_thrcreate(p); 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate return (p); 5480Sstevel@tonic-gate } 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate /* 5510Sstevel@tonic-gate * Destroy an event queue. All subscribers have to be unsubscribed prior to 5520Sstevel@tonic-gate * this call. 5530Sstevel@tonic-gate */ 5540Sstevel@tonic-gate static void 5550Sstevel@tonic-gate evch_evq_destroy(evch_eventq_t *eqp) 5560Sstevel@tonic-gate { 5570Sstevel@tonic-gate evch_qelem_t *qep; 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate ASSERT(evch_dl_getnum(&eqp->eq_subscr) == 0); 5600Sstevel@tonic-gate /* Kill delivery thread */ 5610Sstevel@tonic-gate if (eqp->eq_thrid != NULL) { 5620Sstevel@tonic-gate mutex_enter(&eqp->eq_queuemx); 5630Sstevel@tonic-gate eqp->eq_tabortflag = 1; 5640Sstevel@tonic-gate eqp->eq_holdmode = 0; 5650Sstevel@tonic-gate cv_signal(&eqp->eq_thrsleepcv); 5660Sstevel@tonic-gate mutex_exit(&eqp->eq_queuemx); 5670Sstevel@tonic-gate thread_join(eqp->eq_thrid); 5680Sstevel@tonic-gate } 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate /* Get rid of stale events in the event queue */ 5710Sstevel@tonic-gate while ((qep = (evch_qelem_t *)evch_q_out(&eqp->eq_eventq)) != NULL) { 5720Sstevel@tonic-gate evch_gevent_free((evch_gevent_t *)qep->q_objref); 5730Sstevel@tonic-gate kmem_free(qep, qep->q_objsize); 5740Sstevel@tonic-gate } 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate /* Wrap up event queue structure */ 5770Sstevel@tonic-gate cv_destroy(&eqp->eq_onholdcv); 5780Sstevel@tonic-gate cv_destroy(&eqp->eq_dactivecv); 5790Sstevel@tonic-gate cv_destroy(&eqp->eq_thrsleepcv); 5800Sstevel@tonic-gate evch_dl_fini(&eqp->eq_subscr); 5810Sstevel@tonic-gate mutex_destroy(&eqp->eq_queuemx); 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate /* Free descriptor structure */ 5840Sstevel@tonic-gate kmem_free(eqp, sizeof (evch_eventq_t)); 5850Sstevel@tonic-gate } 5860Sstevel@tonic-gate 5870Sstevel@tonic-gate /* 5880Sstevel@tonic-gate * Subscribe to an event queue. Every subscriber provides a filter callback 5890Sstevel@tonic-gate * routine and an event delivery callback routine. 5900Sstevel@tonic-gate */ 5910Sstevel@tonic-gate static evch_evqsub_t * 5920Sstevel@tonic-gate evch_evq_sub(evch_eventq_t *eqp, filter_f filter, void *fcookie, 5930Sstevel@tonic-gate deliver_f callb, void *cbcookie) 5940Sstevel@tonic-gate { 5950Sstevel@tonic-gate evch_evqsub_t *sp = kmem_zalloc(sizeof (evch_evqsub_t), KM_SLEEP); 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate /* Initialize subscriber structure */ 5980Sstevel@tonic-gate sp->su_filter = filter; 5990Sstevel@tonic-gate sp->su_fcookie = fcookie; 6000Sstevel@tonic-gate sp->su_callb = callb; 6010Sstevel@tonic-gate sp->su_cbcookie = cbcookie; 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate /* Add subscription to queue */ 6040Sstevel@tonic-gate mutex_enter(&eqp->eq_queuemx); 6050Sstevel@tonic-gate evch_dl_add(&eqp->eq_subscr, &sp->su_link); 6060Sstevel@tonic-gate mutex_exit(&eqp->eq_queuemx); 6070Sstevel@tonic-gate return (sp); 6080Sstevel@tonic-gate } 6090Sstevel@tonic-gate 6100Sstevel@tonic-gate /* 6110Sstevel@tonic-gate * Unsubscribe from an event queue. 6120Sstevel@tonic-gate */ 6130Sstevel@tonic-gate static void 6140Sstevel@tonic-gate evch_evq_unsub(evch_eventq_t *eqp, evch_evqsub_t *sp) 6150Sstevel@tonic-gate { 6160Sstevel@tonic-gate mutex_enter(&eqp->eq_queuemx); 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate /* Wait if delivery is just in progress */ 6190Sstevel@tonic-gate if (eqp->eq_dactive) { 6200Sstevel@tonic-gate cv_wait(&eqp->eq_dactivecv, &eqp->eq_queuemx); 6210Sstevel@tonic-gate } 6220Sstevel@tonic-gate evch_dl_del(&eqp->eq_subscr, &sp->su_link); 6230Sstevel@tonic-gate mutex_exit(&eqp->eq_queuemx); 6240Sstevel@tonic-gate kmem_free(sp, sizeof (evch_evqsub_t)); 6250Sstevel@tonic-gate } 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate /* 6280Sstevel@tonic-gate * Publish an event. Returns 0 on success and -1 if memory alloc failed. 6290Sstevel@tonic-gate */ 6300Sstevel@tonic-gate static int 6310Sstevel@tonic-gate evch_evq_pub(evch_eventq_t *eqp, void *ev, int flags) 6320Sstevel@tonic-gate { 6330Sstevel@tonic-gate size_t size; 6340Sstevel@tonic-gate evch_qelem_t *qep; 6350Sstevel@tonic-gate evch_gevent_t *evp = GEVENT(ev); 6360Sstevel@tonic-gate 6370Sstevel@tonic-gate size = sizeof (evch_qelem_t); 6380Sstevel@tonic-gate if (flags & EVCH_TRYHARD) { 6390Sstevel@tonic-gate qep = kmem_alloc_tryhard(size, &size, KM_NOSLEEP); 6400Sstevel@tonic-gate } else { 6410Sstevel@tonic-gate qep = kmem_alloc(size, flags & EVCH_NOSLEEP ? 6420Sstevel@tonic-gate KM_NOSLEEP : KM_SLEEP); 6430Sstevel@tonic-gate } 6440Sstevel@tonic-gate if (qep == NULL) { 6450Sstevel@tonic-gate return (-1); 6460Sstevel@tonic-gate } 6470Sstevel@tonic-gate qep->q_objref = (void *)evp; 6480Sstevel@tonic-gate qep->q_objsize = size; 6490Sstevel@tonic-gate atomic_add_32(&evp->ge_refcount, 1); 6500Sstevel@tonic-gate mutex_enter(&eqp->eq_queuemx); 6510Sstevel@tonic-gate evch_q_in(&eqp->eq_eventq, qep); 6520Sstevel@tonic-gate 6530Sstevel@tonic-gate /* Wakeup delivery thread */ 6540Sstevel@tonic-gate cv_signal(&eqp->eq_thrsleepcv); 6550Sstevel@tonic-gate mutex_exit(&eqp->eq_queuemx); 6560Sstevel@tonic-gate return (0); 6570Sstevel@tonic-gate } 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate /* 6600Sstevel@tonic-gate * Enter hold mode of an event queue. Event delivery thread stops event 6610Sstevel@tonic-gate * handling after delivery of current event (if any). 6620Sstevel@tonic-gate */ 6630Sstevel@tonic-gate static void 6640Sstevel@tonic-gate evch_evq_stop(evch_eventq_t *eqp) 6650Sstevel@tonic-gate { 6660Sstevel@tonic-gate mutex_enter(&eqp->eq_queuemx); 6670Sstevel@tonic-gate eqp->eq_holdmode = 1; 6680Sstevel@tonic-gate if (evq_initcomplete) { 6690Sstevel@tonic-gate cv_signal(&eqp->eq_thrsleepcv); 6700Sstevel@tonic-gate cv_wait(&eqp->eq_onholdcv, &eqp->eq_queuemx); 6710Sstevel@tonic-gate } 6720Sstevel@tonic-gate mutex_exit(&eqp->eq_queuemx); 6730Sstevel@tonic-gate } 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate /* 6760Sstevel@tonic-gate * Continue event delivery. 6770Sstevel@tonic-gate */ 6780Sstevel@tonic-gate static void 6790Sstevel@tonic-gate evch_evq_continue(evch_eventq_t *eqp) 6800Sstevel@tonic-gate { 6810Sstevel@tonic-gate mutex_enter(&eqp->eq_queuemx); 6820Sstevel@tonic-gate eqp->eq_holdmode = 0; 6830Sstevel@tonic-gate cv_signal(&eqp->eq_thrsleepcv); 6840Sstevel@tonic-gate mutex_exit(&eqp->eq_queuemx); 6850Sstevel@tonic-gate } 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate /* 6880Sstevel@tonic-gate * Returns status of delivery thread. 0 if running and 1 if on hold. 6890Sstevel@tonic-gate */ 6900Sstevel@tonic-gate static int 6910Sstevel@tonic-gate evch_evq_status(evch_eventq_t *eqp) 6920Sstevel@tonic-gate { 6930Sstevel@tonic-gate return (eqp->eq_holdmode); 6940Sstevel@tonic-gate } 6950Sstevel@tonic-gate 6960Sstevel@tonic-gate /* 6970Sstevel@tonic-gate * Add a destructor function to an event structure. 6980Sstevel@tonic-gate */ 6990Sstevel@tonic-gate static void 7000Sstevel@tonic-gate evch_evq_evadd_dest(void *ev, destr_f destructor, void *cookie) 7010Sstevel@tonic-gate { 7020Sstevel@tonic-gate evch_gevent_t *evp = GEVENT(ev); 7030Sstevel@tonic-gate 7040Sstevel@tonic-gate evp->ge_destruct = destructor; 7050Sstevel@tonic-gate evp->ge_dstcookie = cookie; 7060Sstevel@tonic-gate } 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate /* 7090Sstevel@tonic-gate * Allocate evch_gevent_t structure. Return address of payload offset of 7100Sstevel@tonic-gate * evch_gevent_t. If EVCH_TRYHARD allocation is requested, we use 7110Sstevel@tonic-gate * kmem_alloc_tryhard to alloc memory of at least paylsize bytes. 7120Sstevel@tonic-gate * 7130Sstevel@tonic-gate * If either memory allocation is unsuccessful, we return NULL. 7140Sstevel@tonic-gate */ 7150Sstevel@tonic-gate static void * 7160Sstevel@tonic-gate evch_evq_evzalloc(size_t paylsize, int flag) 7170Sstevel@tonic-gate { 7180Sstevel@tonic-gate evch_gevent_t *evp; 7195516Sgavinm size_t rsize, evsize, ge_size; 7200Sstevel@tonic-gate 7210Sstevel@tonic-gate rsize = offsetof(evch_gevent_t, ge_payload) + paylsize; 7220Sstevel@tonic-gate if (flag & EVCH_TRYHARD) { 7230Sstevel@tonic-gate evp = kmem_alloc_tryhard(rsize, &evsize, KM_NOSLEEP); 7245516Sgavinm ge_size = evsize; 7250Sstevel@tonic-gate } else { 7260Sstevel@tonic-gate evp = kmem_alloc(rsize, flag & EVCH_NOSLEEP ? KM_NOSLEEP : 7270Sstevel@tonic-gate KM_SLEEP); 7285516Sgavinm ge_size = rsize; 7290Sstevel@tonic-gate } 7300Sstevel@tonic-gate 7310Sstevel@tonic-gate if (evp) { 7325516Sgavinm bzero(evp, rsize); 7335516Sgavinm evp->ge_size = ge_size; 7340Sstevel@tonic-gate return (&evp->ge_payload); 7350Sstevel@tonic-gate } 7360Sstevel@tonic-gate return (evp); 7370Sstevel@tonic-gate } 7380Sstevel@tonic-gate 7390Sstevel@tonic-gate /* 7400Sstevel@tonic-gate * Free event structure. Argument ev is address of payload offset. 7410Sstevel@tonic-gate */ 7420Sstevel@tonic-gate static void 7430Sstevel@tonic-gate evch_evq_evfree(void *ev) 7440Sstevel@tonic-gate { 7450Sstevel@tonic-gate evch_gevent_free(GEVENT(ev)); 7460Sstevel@tonic-gate } 7470Sstevel@tonic-gate 7480Sstevel@tonic-gate /* 7490Sstevel@tonic-gate * Iterate over all events in the event queue. Begin with an event 7500Sstevel@tonic-gate * which is currently being delivered. No mutexes are grabbed and no 7510Sstevel@tonic-gate * resources allocated so that this function can be called in panic 7520Sstevel@tonic-gate * context too. This function has to be called with ev == NULL initially. 7530Sstevel@tonic-gate * Actually argument ev is only a flag. Internally the member eq_nextev 7540Sstevel@tonic-gate * is used to determine the next event. But ev allows for the convenient 7550Sstevel@tonic-gate * use like 7560Sstevel@tonic-gate * ev = NULL; 7570Sstevel@tonic-gate * while ((ev = evch_evq_evnext(evp, ev)) != NULL) ... 7580Sstevel@tonic-gate */ 7590Sstevel@tonic-gate static void * 7600Sstevel@tonic-gate evch_evq_evnext(evch_eventq_t *evq, void *ev) 7610Sstevel@tonic-gate { 7620Sstevel@tonic-gate if (ev == NULL) { 7630Sstevel@tonic-gate evq->eq_nextev = NULL; 7640Sstevel@tonic-gate if (evq->eq_curevent != NULL) 7650Sstevel@tonic-gate return (&evq->eq_curevent->ge_payload); 7660Sstevel@tonic-gate } 7670Sstevel@tonic-gate evq->eq_nextev = evch_q_next(&evq->eq_eventq, evq->eq_nextev); 7680Sstevel@tonic-gate if (evq->eq_nextev == NULL) 7690Sstevel@tonic-gate return (NULL); 7700Sstevel@tonic-gate return (&((evch_gevent_t *)evq->eq_nextev->q_objref)->ge_payload); 7710Sstevel@tonic-gate } 7720Sstevel@tonic-gate 7730Sstevel@tonic-gate /* 7740Sstevel@tonic-gate * Channel handling functions. First some support functions. Functions belonging 7750Sstevel@tonic-gate * to the channel handling interface start with evch_ch. The following functions 7760Sstevel@tonic-gate * make up the channel handling internal interfaces: 7770Sstevel@tonic-gate * 7780Sstevel@tonic-gate * evch_chinit - Initialize channel handling 7790Sstevel@tonic-gate * evch_chinitthr - Second step init: initialize threads 7800Sstevel@tonic-gate * evch_chbind - Bind to a channel 7810Sstevel@tonic-gate * evch_chunbind - Unbind from a channel 7820Sstevel@tonic-gate * evch_chsubscribe - Subscribe to a sysevent class 7830Sstevel@tonic-gate * evch_chunsubscribe - Unsubscribe 7840Sstevel@tonic-gate * evch_chpublish - Publish an event 7850Sstevel@tonic-gate * evch_chgetnames - Get names of all channels 7860Sstevel@tonic-gate * evch_chgetchdata - Get data of a channel 7870Sstevel@tonic-gate * evch_chrdevent_init - Init event q traversal 7880Sstevel@tonic-gate * evch_chgetnextev - Read out events queued for a subscriber 7890Sstevel@tonic-gate * evch_chrdevent_fini - Finish event q traversal 7900Sstevel@tonic-gate */ 7910Sstevel@tonic-gate 7920Sstevel@tonic-gate /* 7930Sstevel@tonic-gate * Compare channel name. Used for evch_dl_search to find a channel with the 7940Sstevel@tonic-gate * name s. 7950Sstevel@tonic-gate */ 7960Sstevel@tonic-gate static int 7970Sstevel@tonic-gate evch_namecmp(evch_dlelem_t *ep, char *s) 7980Sstevel@tonic-gate { 7990Sstevel@tonic-gate return (strcmp(((evch_chan_t *)ep)->ch_name, s)); 8000Sstevel@tonic-gate } 8010Sstevel@tonic-gate 8020Sstevel@tonic-gate /* 8030Sstevel@tonic-gate * Sysevent filter callback routine. Enables event delivery only if it matches 8040Sstevel@tonic-gate * the event class string given by parameter cookie. 8050Sstevel@tonic-gate */ 8060Sstevel@tonic-gate static int 8070Sstevel@tonic-gate evch_class_filter(void *ev, void *cookie) 8080Sstevel@tonic-gate { 8090Sstevel@tonic-gate char *class = (char *)cookie; 8100Sstevel@tonic-gate 8110Sstevel@tonic-gate if (class == NULL || strcmp(SE_CLASS_NAME(ev), class) == 0) { 8120Sstevel@tonic-gate return (EVQ_DELIVER); 8130Sstevel@tonic-gate } 8140Sstevel@tonic-gate return (EVQ_IGNORE); 8150Sstevel@tonic-gate } 8160Sstevel@tonic-gate 8170Sstevel@tonic-gate /* 8180Sstevel@tonic-gate * Callback routine to propagate the event into a per subscriber queue. 8190Sstevel@tonic-gate */ 8200Sstevel@tonic-gate static int 8210Sstevel@tonic-gate evch_subq_deliver(void *evp, void *cookie) 8220Sstevel@tonic-gate { 8230Sstevel@tonic-gate evch_subd_t *p = (evch_subd_t *)cookie; 8240Sstevel@tonic-gate 8250Sstevel@tonic-gate (void) evch_evq_pub(p->sd_queue, evp, EVCH_SLEEP); 8260Sstevel@tonic-gate return (EVQ_CONT); 8270Sstevel@tonic-gate } 8280Sstevel@tonic-gate 8290Sstevel@tonic-gate /* 8300Sstevel@tonic-gate * Call kernel callback routine for sysevent kernel delivery. 8310Sstevel@tonic-gate */ 8320Sstevel@tonic-gate static int 8330Sstevel@tonic-gate evch_kern_deliver(void *evp, void *cookie) 8340Sstevel@tonic-gate { 8350Sstevel@tonic-gate sysevent_impl_t *ev = (sysevent_impl_t *)evp; 8360Sstevel@tonic-gate evch_subd_t *sdp = (evch_subd_t *)cookie; 8370Sstevel@tonic-gate 8380Sstevel@tonic-gate return (sdp->sd_callback(ev, sdp->sd_cbcookie)); 8390Sstevel@tonic-gate } 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate /* 8420Sstevel@tonic-gate * Door upcall for user land sysevent delivery. 8430Sstevel@tonic-gate */ 8440Sstevel@tonic-gate static int 8450Sstevel@tonic-gate evch_door_deliver(void *evp, void *cookie) 8460Sstevel@tonic-gate { 8470Sstevel@tonic-gate int error; 8480Sstevel@tonic-gate size_t size; 8490Sstevel@tonic-gate sysevent_impl_t *ev = (sysevent_impl_t *)evp; 8500Sstevel@tonic-gate door_arg_t darg; 8510Sstevel@tonic-gate evch_subd_t *sdp = (evch_subd_t *)cookie; 8520Sstevel@tonic-gate int nticks = EVCH_MIN_PAUSE; 8530Sstevel@tonic-gate uint32_t retval; 8540Sstevel@tonic-gate int retry = 20; 8550Sstevel@tonic-gate 8560Sstevel@tonic-gate /* Initialize door args */ 8570Sstevel@tonic-gate size = sizeof (sysevent_impl_t) + SE_PAYLOAD_SZ(ev); 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate darg.rbuf = (char *)&retval; 8600Sstevel@tonic-gate darg.rsize = sizeof (retval); 8610Sstevel@tonic-gate darg.data_ptr = (char *)ev; 8620Sstevel@tonic-gate darg.data_size = size; 8630Sstevel@tonic-gate darg.desc_ptr = NULL; 8640Sstevel@tonic-gate darg.desc_num = 0; 8650Sstevel@tonic-gate 8660Sstevel@tonic-gate for (;;) { 867*6997Sjwadams if ((error = door_ki_upcall_limited(sdp->sd_door, &darg, 868*6997Sjwadams NULL, SIZE_MAX, 0)) == 0) { 8690Sstevel@tonic-gate break; 8700Sstevel@tonic-gate } 8710Sstevel@tonic-gate switch (error) { 8720Sstevel@tonic-gate case EAGAIN: 8730Sstevel@tonic-gate /* Cannot deliver event - process may be forking */ 8740Sstevel@tonic-gate delay(nticks); 8750Sstevel@tonic-gate nticks <<= 1; 8760Sstevel@tonic-gate if (nticks > EVCH_MAX_PAUSE) { 8770Sstevel@tonic-gate nticks = EVCH_MAX_PAUSE; 8780Sstevel@tonic-gate } 8790Sstevel@tonic-gate if (retry-- <= 0) { 8800Sstevel@tonic-gate cmn_err(CE_CONT, "event delivery thread: " 8810Sstevel@tonic-gate "door_ki_upcall error EAGAIN\n"); 8820Sstevel@tonic-gate return (EVQ_CONT); 8830Sstevel@tonic-gate } 8840Sstevel@tonic-gate break; 8850Sstevel@tonic-gate case EINTR: 8860Sstevel@tonic-gate case EBADF: 8870Sstevel@tonic-gate /* Process died */ 8880Sstevel@tonic-gate return (EVQ_SLEEP); 8890Sstevel@tonic-gate default: 8900Sstevel@tonic-gate cmn_err(CE_CONT, 8910Sstevel@tonic-gate "event delivery thread: door_ki_upcall error %d\n", 8920Sstevel@tonic-gate error); 8930Sstevel@tonic-gate return (EVQ_CONT); 8940Sstevel@tonic-gate } 8950Sstevel@tonic-gate } 8960Sstevel@tonic-gate if (retval == EAGAIN) { 8970Sstevel@tonic-gate return (EVQ_AGAIN); 8980Sstevel@tonic-gate } 8990Sstevel@tonic-gate return (EVQ_CONT); 9000Sstevel@tonic-gate } 9010Sstevel@tonic-gate 9020Sstevel@tonic-gate /* 9030Sstevel@tonic-gate * Callback routine for evch_dl_search() to compare subscriber id's. Used by 9040Sstevel@tonic-gate * evch_subscribe() and evch_chrdevent_init(). 9050Sstevel@tonic-gate */ 9060Sstevel@tonic-gate static int 9070Sstevel@tonic-gate evch_subidcmp(evch_dlelem_t *ep, char *s) 9080Sstevel@tonic-gate { 9090Sstevel@tonic-gate return (strcmp(((evch_subd_t *)ep)->sd_ident, s)); 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate 9120Sstevel@tonic-gate /* 9130Sstevel@tonic-gate * Callback routine for evch_dl_search() to find a subscriber with EVCH_SUB_DUMP 9140Sstevel@tonic-gate * set (indicated by sub->sd_dump != 0). Used by evch_chrdevent_init() and 9150Sstevel@tonic-gate * evch_subscribe(). Needs to returns 0 if subscriber with sd_dump set is 9160Sstevel@tonic-gate * found. 9170Sstevel@tonic-gate */ 9180Sstevel@tonic-gate /*ARGSUSED1*/ 9190Sstevel@tonic-gate static int 9200Sstevel@tonic-gate evch_dumpflgcmp(evch_dlelem_t *ep, char *s) 9210Sstevel@tonic-gate { 9220Sstevel@tonic-gate return (((evch_subd_t *)ep)->sd_dump ? 0 : 1); 9230Sstevel@tonic-gate } 9240Sstevel@tonic-gate 9250Sstevel@tonic-gate /* 9260Sstevel@tonic-gate * Event destructor function. Used to maintain the number of events per channel. 9270Sstevel@tonic-gate */ 9280Sstevel@tonic-gate /*ARGSUSED*/ 9290Sstevel@tonic-gate static void 9300Sstevel@tonic-gate evch_destr_event(void *ev, void *ch) 9310Sstevel@tonic-gate { 9320Sstevel@tonic-gate evch_chan_t *chp = (evch_chan_t *)ch; 9330Sstevel@tonic-gate 9340Sstevel@tonic-gate mutex_enter(&chp->ch_pubmx); 9350Sstevel@tonic-gate chp->ch_nevents--; 9360Sstevel@tonic-gate cv_signal(&chp->ch_pubcv); 9370Sstevel@tonic-gate mutex_exit(&chp->ch_pubmx); 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate /* 9410Sstevel@tonic-gate * Integer square root according to Newton's iteration. 9420Sstevel@tonic-gate */ 9430Sstevel@tonic-gate static uint32_t 9440Sstevel@tonic-gate evch_isqrt(uint64_t n) 9450Sstevel@tonic-gate { 9460Sstevel@tonic-gate uint64_t x = n >> 1; 9470Sstevel@tonic-gate uint64_t xn = x - 1; 9480Sstevel@tonic-gate static uint32_t lowval[] = { 0, 1, 1, 2 }; 9490Sstevel@tonic-gate 9500Sstevel@tonic-gate if (n < 4) { 9510Sstevel@tonic-gate return (lowval[n]); 9520Sstevel@tonic-gate } 9530Sstevel@tonic-gate while (xn < x) { 9540Sstevel@tonic-gate x = xn; 9550Sstevel@tonic-gate xn = (x + n / x) / 2; 9560Sstevel@tonic-gate } 9570Sstevel@tonic-gate return ((uint32_t)xn); 9580Sstevel@tonic-gate } 9590Sstevel@tonic-gate 9600Sstevel@tonic-gate /* 9610Sstevel@tonic-gate * First step sysevent channel initialization. Called when kernel memory 9620Sstevel@tonic-gate * allocator is initialized. 9630Sstevel@tonic-gate */ 9640Sstevel@tonic-gate static void 9650Sstevel@tonic-gate evch_chinit() 9660Sstevel@tonic-gate { 9670Sstevel@tonic-gate size_t k; 9680Sstevel@tonic-gate 9690Sstevel@tonic-gate /* 9700Sstevel@tonic-gate * Calculate limits: max no of channels and max no of events per 9710Sstevel@tonic-gate * channel. The smallest machine with 128 MByte will allow for 9720Sstevel@tonic-gate * >= 8 channels and an upper limit of 2048 events per channel. 9730Sstevel@tonic-gate * The event limit is the number of channels times 256 (hence 9740Sstevel@tonic-gate * the shift factor of 8). These number where selected arbitrarily. 9750Sstevel@tonic-gate */ 9760Sstevel@tonic-gate k = kmem_maxavail() >> 20; 9770Sstevel@tonic-gate evch_channels_max = min(evch_isqrt(k), EVCH_MAX_CHANNELS); 9780Sstevel@tonic-gate evch_events_max = evch_channels_max << 8; 9790Sstevel@tonic-gate 9800Sstevel@tonic-gate /* 9810Sstevel@tonic-gate * Will trigger creation of the global zone's evch state. 9820Sstevel@tonic-gate */ 9830Sstevel@tonic-gate zone_key_create(&evch_zone_key, evch_zoneinit, NULL, evch_zonefree); 9840Sstevel@tonic-gate } 9850Sstevel@tonic-gate 9860Sstevel@tonic-gate /* 9870Sstevel@tonic-gate * Second step sysevent channel initialization. Called when threads are ready. 9880Sstevel@tonic-gate */ 9890Sstevel@tonic-gate static void 9900Sstevel@tonic-gate evch_chinitthr() 9910Sstevel@tonic-gate { 9920Sstevel@tonic-gate struct evch_globals *eg; 9930Sstevel@tonic-gate evch_chan_t *chp; 9940Sstevel@tonic-gate evch_subd_t *sdp; 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate /* 9970Sstevel@tonic-gate * We're early enough in boot that we know that only the global 9980Sstevel@tonic-gate * zone exists; we only need to initialize its threads. 9990Sstevel@tonic-gate */ 10000Sstevel@tonic-gate eg = zone_getspecific(evch_zone_key, global_zone); 10010Sstevel@tonic-gate ASSERT(eg != NULL); 10020Sstevel@tonic-gate 10030Sstevel@tonic-gate for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL; 10040Sstevel@tonic-gate chp = evch_dl_next(&eg->evch_list, chp)) { 10050Sstevel@tonic-gate for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp; 10060Sstevel@tonic-gate sdp = evch_dl_next(&chp->ch_subscr, sdp)) { 10070Sstevel@tonic-gate evch_evq_thrcreate(sdp->sd_queue); 10080Sstevel@tonic-gate } 10090Sstevel@tonic-gate evch_evq_thrcreate(chp->ch_queue); 10100Sstevel@tonic-gate } 10110Sstevel@tonic-gate evq_initcomplete = 1; 10120Sstevel@tonic-gate } 10130Sstevel@tonic-gate 10140Sstevel@tonic-gate /* 10150Sstevel@tonic-gate * Sysevent channel bind. Create channel and allocate binding structure. 10160Sstevel@tonic-gate */ 10170Sstevel@tonic-gate static int 10180Sstevel@tonic-gate evch_chbind(const char *chnam, evch_bind_t **scpp, uint32_t flags) 10190Sstevel@tonic-gate { 10200Sstevel@tonic-gate struct evch_globals *eg; 10210Sstevel@tonic-gate evch_bind_t *bp; 10220Sstevel@tonic-gate evch_chan_t *p; 10230Sstevel@tonic-gate char *chn; 10240Sstevel@tonic-gate size_t namlen; 10250Sstevel@tonic-gate int rv; 10260Sstevel@tonic-gate 10270Sstevel@tonic-gate eg = zone_getspecific(evch_zone_key, curproc->p_zone); 10280Sstevel@tonic-gate ASSERT(eg != NULL); 10290Sstevel@tonic-gate 10300Sstevel@tonic-gate /* Create channel if it does not exist */ 10310Sstevel@tonic-gate ASSERT(evch_dl_is_init(&eg->evch_list)); 10320Sstevel@tonic-gate if ((namlen = strlen(chnam) + 1) > MAX_CHNAME_LEN) { 10330Sstevel@tonic-gate return (EINVAL); 10340Sstevel@tonic-gate } 10350Sstevel@tonic-gate mutex_enter(&eg->evch_list_lock); 10360Sstevel@tonic-gate if ((p = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp, 10370Sstevel@tonic-gate (char *)chnam)) == NULL) { 10380Sstevel@tonic-gate if (flags & EVCH_CREAT) { 10390Sstevel@tonic-gate if (evch_dl_getnum(&eg->evch_list) >= 10400Sstevel@tonic-gate evch_channels_max) { 10410Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 10420Sstevel@tonic-gate return (ENOMEM); 10430Sstevel@tonic-gate } 10440Sstevel@tonic-gate chn = kmem_alloc(namlen, KM_SLEEP); 10450Sstevel@tonic-gate bcopy(chnam, chn, namlen); 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate /* Allocate and initialize channel descriptor */ 10480Sstevel@tonic-gate p = kmem_zalloc(sizeof (evch_chan_t), KM_SLEEP); 10490Sstevel@tonic-gate p->ch_name = chn; 10500Sstevel@tonic-gate p->ch_namelen = namlen; 10510Sstevel@tonic-gate mutex_init(&p->ch_mutex, NULL, MUTEX_DEFAULT, NULL); 10520Sstevel@tonic-gate p->ch_queue = evch_evq_create(); 10530Sstevel@tonic-gate evch_dl_init(&p->ch_subscr); 10540Sstevel@tonic-gate if (evq_initcomplete) { 10550Sstevel@tonic-gate p->ch_uid = crgetuid(curthread->t_cred); 10560Sstevel@tonic-gate p->ch_gid = crgetgid(curthread->t_cred); 10570Sstevel@tonic-gate } 10580Sstevel@tonic-gate cv_init(&p->ch_pubcv, NULL, CV_DEFAULT, NULL); 10590Sstevel@tonic-gate mutex_init(&p->ch_pubmx, NULL, MUTEX_DEFAULT, NULL); 10600Sstevel@tonic-gate p->ch_maxev = min(EVCH_DEFAULT_EVENTS, evch_events_max); 10610Sstevel@tonic-gate p->ch_maxsubscr = EVCH_MAX_SUBSCRIPTIONS; 10620Sstevel@tonic-gate p->ch_maxbinds = evch_bindings_max; 10630Sstevel@tonic-gate p->ch_ctime = gethrestime_sec(); 10640Sstevel@tonic-gate if (flags & EVCH_HOLD_PEND) { 10650Sstevel@tonic-gate p->ch_holdpend = 1; 10660Sstevel@tonic-gate evch_evq_stop(p->ch_queue); 10670Sstevel@tonic-gate } 10680Sstevel@tonic-gate 10690Sstevel@tonic-gate /* Put new descriptor into channel list */ 10700Sstevel@tonic-gate evch_dl_add(&eg->evch_list, (evch_dlelem_t *)p); 10710Sstevel@tonic-gate } else { 10720Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 10730Sstevel@tonic-gate return (ENOENT); 10740Sstevel@tonic-gate } 10750Sstevel@tonic-gate } 10760Sstevel@tonic-gate 10770Sstevel@tonic-gate /* Check for max binds and create binding */ 10780Sstevel@tonic-gate mutex_enter(&p->ch_mutex); 10790Sstevel@tonic-gate if (p->ch_bindings >= p->ch_maxbinds) { 10800Sstevel@tonic-gate rv = ENOMEM; 10810Sstevel@tonic-gate /* 10820Sstevel@tonic-gate * No need to destroy the channel because this call did not 10830Sstevel@tonic-gate * create it. Other bindings will be present if ch_maxbinds 10840Sstevel@tonic-gate * is exceeded. 10850Sstevel@tonic-gate */ 10860Sstevel@tonic-gate goto errorexit; 10870Sstevel@tonic-gate } 10880Sstevel@tonic-gate bp = kmem_alloc(sizeof (evch_bind_t), KM_SLEEP); 10890Sstevel@tonic-gate bp->bd_channel = p; 10900Sstevel@tonic-gate bp->bd_sublst = NULL; 10910Sstevel@tonic-gate p->ch_bindings++; 10920Sstevel@tonic-gate rv = 0; 10930Sstevel@tonic-gate *scpp = bp; 10940Sstevel@tonic-gate errorexit: 10950Sstevel@tonic-gate mutex_exit(&p->ch_mutex); 10960Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 10970Sstevel@tonic-gate return (rv); 10980Sstevel@tonic-gate } 10990Sstevel@tonic-gate 11000Sstevel@tonic-gate /* 11010Sstevel@tonic-gate * Unbind: Free bind structure. Remove channel if last binding was freed. 11020Sstevel@tonic-gate */ 11030Sstevel@tonic-gate static void 11040Sstevel@tonic-gate evch_chunbind(evch_bind_t *bp) 11050Sstevel@tonic-gate { 11060Sstevel@tonic-gate struct evch_globals *eg; 11070Sstevel@tonic-gate evch_chan_t *chp = bp->bd_channel; 11080Sstevel@tonic-gate 11090Sstevel@tonic-gate eg = zone_getspecific(evch_zone_key, curproc->p_zone); 11100Sstevel@tonic-gate ASSERT(eg != NULL); 11110Sstevel@tonic-gate 11120Sstevel@tonic-gate mutex_enter(&eg->evch_list_lock); 11130Sstevel@tonic-gate mutex_enter(&chp->ch_mutex); 11140Sstevel@tonic-gate ASSERT(chp->ch_bindings > 0); 11150Sstevel@tonic-gate chp->ch_bindings--; 11160Sstevel@tonic-gate kmem_free(bp, sizeof (evch_bind_t)); 11170Sstevel@tonic-gate if (chp->ch_bindings == 0 && evch_dl_getnum(&chp->ch_subscr) == 0) { 11180Sstevel@tonic-gate /* 11190Sstevel@tonic-gate * No more bindings or persistent subscriber, destroy channel. 11200Sstevel@tonic-gate */ 11210Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 11220Sstevel@tonic-gate evch_dl_del(&eg->evch_list, &chp->ch_link); 11230Sstevel@tonic-gate evch_evq_destroy(chp->ch_queue); 11240Sstevel@tonic-gate mutex_destroy(&chp->ch_mutex); 11250Sstevel@tonic-gate mutex_destroy(&chp->ch_pubmx); 11260Sstevel@tonic-gate cv_destroy(&chp->ch_pubcv); 11270Sstevel@tonic-gate kmem_free(chp->ch_name, chp->ch_namelen); 11280Sstevel@tonic-gate kmem_free(chp, sizeof (evch_chan_t)); 11290Sstevel@tonic-gate } else 11300Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 11310Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 11320Sstevel@tonic-gate } 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate /* 11350Sstevel@tonic-gate * Subscribe to a channel. dtype is either EVCH_DELKERN for kernel callbacks 11360Sstevel@tonic-gate * or EVCH_DELDOOR for door upcall delivery to user land. Depending on dtype 11370Sstevel@tonic-gate * dinfo gives the call back routine address or the door handle. 11380Sstevel@tonic-gate */ 11390Sstevel@tonic-gate static int 11400Sstevel@tonic-gate evch_chsubscribe(evch_bind_t *bp, int dtype, const char *sid, const char *class, 11410Sstevel@tonic-gate void *dinfo, void *cookie, int flags, pid_t pid) 11420Sstevel@tonic-gate { 11430Sstevel@tonic-gate evch_chan_t *chp = bp->bd_channel; 11440Sstevel@tonic-gate evch_eventq_t *eqp = chp->ch_queue; 11450Sstevel@tonic-gate evch_subd_t *sdp; 11460Sstevel@tonic-gate evch_subd_t *esp; 11470Sstevel@tonic-gate int (*delivfkt)(); 11480Sstevel@tonic-gate char *clb = NULL; 11490Sstevel@tonic-gate int clblen = 0; 11500Sstevel@tonic-gate char *subid; 11510Sstevel@tonic-gate int subidblen; 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate /* 11540Sstevel@tonic-gate * Check if only known flags are set. 11550Sstevel@tonic-gate */ 11560Sstevel@tonic-gate if (flags & ~(EVCH_SUB_KEEP | EVCH_SUB_DUMP)) 11570Sstevel@tonic-gate return (EINVAL); 11580Sstevel@tonic-gate /* 11590Sstevel@tonic-gate * Check if we have already a subscription with that name and if we 11600Sstevel@tonic-gate * have to reconnect the subscriber to a persistent subscription. 11610Sstevel@tonic-gate */ 11620Sstevel@tonic-gate mutex_enter(&chp->ch_mutex); 11630Sstevel@tonic-gate if ((esp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr, 11640Sstevel@tonic-gate evch_subidcmp, (char *)sid)) != NULL) { 11650Sstevel@tonic-gate int error = 0; 11660Sstevel@tonic-gate if ((flags & EVCH_SUB_KEEP) && (esp->sd_active == 0)) { 11670Sstevel@tonic-gate /* 11680Sstevel@tonic-gate * Subscription with the name on hold, reconnect to 11690Sstevel@tonic-gate * existing queue. 11700Sstevel@tonic-gate */ 11710Sstevel@tonic-gate ASSERT(dtype == EVCH_DELDOOR); 11720Sstevel@tonic-gate esp->sd_subnxt = bp->bd_sublst; 11730Sstevel@tonic-gate bp->bd_sublst = esp; 11740Sstevel@tonic-gate esp->sd_pid = pid; 11750Sstevel@tonic-gate esp->sd_door = (door_handle_t)dinfo; 11760Sstevel@tonic-gate esp->sd_active++; 11770Sstevel@tonic-gate evch_evq_continue(esp->sd_queue); 11780Sstevel@tonic-gate } else { 11790Sstevel@tonic-gate /* Subscriber with given name already exists */ 11800Sstevel@tonic-gate error = EEXIST; 11810Sstevel@tonic-gate } 11820Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 11830Sstevel@tonic-gate return (error); 11840Sstevel@tonic-gate } 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate if (evch_dl_getnum(&chp->ch_subscr) >= chp->ch_maxsubscr) { 11870Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 11880Sstevel@tonic-gate return (ENOMEM); 11890Sstevel@tonic-gate } 11900Sstevel@tonic-gate 11910Sstevel@tonic-gate if (flags & EVCH_SUB_DUMP && evch_dl_search(&chp->ch_subscr, 11920Sstevel@tonic-gate evch_dumpflgcmp, NULL) != NULL) { 11930Sstevel@tonic-gate /* 11940Sstevel@tonic-gate * Subscription with EVCH_SUB_DUMP flagged already exists. 11950Sstevel@tonic-gate * Only one subscription with EVCH_SUB_DUMP possible. Return 11960Sstevel@tonic-gate * error. 11970Sstevel@tonic-gate */ 11980Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 11990Sstevel@tonic-gate return (EINVAL); 12000Sstevel@tonic-gate } 12010Sstevel@tonic-gate 12020Sstevel@tonic-gate if (class != NULL) { 12030Sstevel@tonic-gate clblen = strlen(class) + 1; 12040Sstevel@tonic-gate clb = kmem_alloc(clblen, KM_SLEEP); 12050Sstevel@tonic-gate bcopy(class, clb, clblen); 12060Sstevel@tonic-gate } 12070Sstevel@tonic-gate 12080Sstevel@tonic-gate subidblen = strlen(sid) + 1; 12090Sstevel@tonic-gate subid = kmem_alloc(subidblen, KM_SLEEP); 12100Sstevel@tonic-gate bcopy(sid, subid, subidblen); 12110Sstevel@tonic-gate 12120Sstevel@tonic-gate /* Create per subscriber queue */ 12130Sstevel@tonic-gate sdp = kmem_zalloc(sizeof (evch_subd_t), KM_SLEEP); 12140Sstevel@tonic-gate sdp->sd_queue = evch_evq_create(); 12150Sstevel@tonic-gate 12160Sstevel@tonic-gate /* Subscribe to subscriber queue */ 12170Sstevel@tonic-gate sdp->sd_persist = flags & EVCH_SUB_KEEP ? 1 : 0; 12180Sstevel@tonic-gate sdp->sd_dump = flags & EVCH_SUB_DUMP ? 1 : 0; 12190Sstevel@tonic-gate sdp->sd_type = dtype; 12200Sstevel@tonic-gate sdp->sd_cbcookie = cookie; 12210Sstevel@tonic-gate sdp->sd_ident = subid; 12220Sstevel@tonic-gate if (dtype == EVCH_DELKERN) { 12230Sstevel@tonic-gate sdp->sd_callback = (kerndlv_f)dinfo; 12240Sstevel@tonic-gate delivfkt = evch_kern_deliver; 12250Sstevel@tonic-gate } else { 12260Sstevel@tonic-gate sdp->sd_door = (door_handle_t)dinfo; 12270Sstevel@tonic-gate delivfkt = evch_door_deliver; 12280Sstevel@tonic-gate } 12290Sstevel@tonic-gate sdp->sd_ssub = 12300Sstevel@tonic-gate evch_evq_sub(sdp->sd_queue, NULL, NULL, delivfkt, (void *)sdp); 12310Sstevel@tonic-gate 12320Sstevel@tonic-gate /* Connect per subscriber queue to main event queue */ 12330Sstevel@tonic-gate sdp->sd_msub = evch_evq_sub(eqp, evch_class_filter, clb, 12340Sstevel@tonic-gate evch_subq_deliver, (void *)sdp); 12350Sstevel@tonic-gate sdp->sd_classname = clb; 12360Sstevel@tonic-gate sdp->sd_clnsize = clblen; 12370Sstevel@tonic-gate sdp->sd_pid = pid; 12380Sstevel@tonic-gate sdp->sd_active++; 12390Sstevel@tonic-gate 12400Sstevel@tonic-gate /* Add subscription to binding */ 12410Sstevel@tonic-gate sdp->sd_subnxt = bp->bd_sublst; 12420Sstevel@tonic-gate bp->bd_sublst = sdp; 12430Sstevel@tonic-gate 12440Sstevel@tonic-gate /* Add subscription to channel */ 12450Sstevel@tonic-gate evch_dl_add(&chp->ch_subscr, &sdp->sd_link); 12460Sstevel@tonic-gate if (chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 1) { 12470Sstevel@tonic-gate 12480Sstevel@tonic-gate /* Let main event queue run in case of HOLDPEND */ 12490Sstevel@tonic-gate evch_evq_continue(eqp); 12500Sstevel@tonic-gate } 12510Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 12520Sstevel@tonic-gate 12530Sstevel@tonic-gate return (0); 12540Sstevel@tonic-gate } 12550Sstevel@tonic-gate 12560Sstevel@tonic-gate /* 12570Sstevel@tonic-gate * If flag == EVCH_SUB_KEEP only non-persistent subscriptions are deleted. 12580Sstevel@tonic-gate * When sid == NULL all subscriptions except the ones with EVCH_SUB_KEEP set 12590Sstevel@tonic-gate * are removed. 12600Sstevel@tonic-gate */ 12610Sstevel@tonic-gate static void 12620Sstevel@tonic-gate evch_chunsubscribe(evch_bind_t *bp, const char *sid, uint32_t flags) 12630Sstevel@tonic-gate { 12640Sstevel@tonic-gate evch_subd_t *sdp; 12650Sstevel@tonic-gate evch_subd_t *next; 12660Sstevel@tonic-gate evch_subd_t *prev; 12670Sstevel@tonic-gate evch_chan_t *chp = bp->bd_channel; 12680Sstevel@tonic-gate 12690Sstevel@tonic-gate mutex_enter(&chp->ch_mutex); 12700Sstevel@tonic-gate if (chp->ch_holdpend) { 12710Sstevel@tonic-gate evch_evq_stop(chp->ch_queue); /* Hold main event queue */ 12720Sstevel@tonic-gate } 12730Sstevel@tonic-gate prev = NULL; 12740Sstevel@tonic-gate for (sdp = bp->bd_sublst; sdp; sdp = next) { 12750Sstevel@tonic-gate if (sid == NULL || strcmp(sid, sdp->sd_ident) == 0) { 12760Sstevel@tonic-gate if (flags == 0 || sdp->sd_persist == 0) { 12770Sstevel@tonic-gate /* 12780Sstevel@tonic-gate * Disconnect subscriber queue from main event 12790Sstevel@tonic-gate * queue. 12800Sstevel@tonic-gate */ 12810Sstevel@tonic-gate evch_evq_unsub(chp->ch_queue, sdp->sd_msub); 12820Sstevel@tonic-gate 12830Sstevel@tonic-gate /* Destruct per subscriber queue */ 12840Sstevel@tonic-gate evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub); 12850Sstevel@tonic-gate evch_evq_destroy(sdp->sd_queue); 12860Sstevel@tonic-gate /* 12870Sstevel@tonic-gate * Eliminate the subscriber data from channel 12880Sstevel@tonic-gate * list. 12890Sstevel@tonic-gate */ 12900Sstevel@tonic-gate evch_dl_del(&chp->ch_subscr, &sdp->sd_link); 12910Sstevel@tonic-gate kmem_free(sdp->sd_classname, sdp->sd_clnsize); 12920Sstevel@tonic-gate if (sdp->sd_type == EVCH_DELDOOR) { 12930Sstevel@tonic-gate door_ki_rele(sdp->sd_door); 12940Sstevel@tonic-gate } 12950Sstevel@tonic-gate next = sdp->sd_subnxt; 12960Sstevel@tonic-gate if (prev) { 12970Sstevel@tonic-gate prev->sd_subnxt = next; 12980Sstevel@tonic-gate } else { 12990Sstevel@tonic-gate bp->bd_sublst = next; 13000Sstevel@tonic-gate } 13010Sstevel@tonic-gate kmem_free(sdp->sd_ident, 13020Sstevel@tonic-gate strlen(sdp->sd_ident) + 1); 13030Sstevel@tonic-gate kmem_free(sdp, sizeof (evch_subd_t)); 13040Sstevel@tonic-gate } else { 13050Sstevel@tonic-gate /* 13060Sstevel@tonic-gate * EVCH_SUB_KEEP case 13070Sstevel@tonic-gate */ 13080Sstevel@tonic-gate evch_evq_stop(sdp->sd_queue); 13090Sstevel@tonic-gate if (sdp->sd_type == EVCH_DELDOOR) { 13100Sstevel@tonic-gate door_ki_rele(sdp->sd_door); 13110Sstevel@tonic-gate } 13120Sstevel@tonic-gate sdp->sd_active--; 13130Sstevel@tonic-gate ASSERT(sdp->sd_active == 0); 13140Sstevel@tonic-gate next = sdp->sd_subnxt; 13150Sstevel@tonic-gate prev = sdp; 13160Sstevel@tonic-gate } 13170Sstevel@tonic-gate if (sid != NULL) { 13180Sstevel@tonic-gate break; 13190Sstevel@tonic-gate } 13200Sstevel@tonic-gate } else { 13210Sstevel@tonic-gate next = sdp->sd_subnxt; 13220Sstevel@tonic-gate prev = sdp; 13230Sstevel@tonic-gate } 13240Sstevel@tonic-gate } 13250Sstevel@tonic-gate if (!(chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 0)) { 13260Sstevel@tonic-gate /* 13270Sstevel@tonic-gate * Continue dispatch thread except if no subscribers are present 13280Sstevel@tonic-gate * in HOLDPEND mode. 13290Sstevel@tonic-gate */ 13300Sstevel@tonic-gate evch_evq_continue(chp->ch_queue); 13310Sstevel@tonic-gate } 13320Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 13330Sstevel@tonic-gate } 13340Sstevel@tonic-gate 13350Sstevel@tonic-gate /* 13360Sstevel@tonic-gate * Publish an event. Returns zero on success and an error code else. 13370Sstevel@tonic-gate */ 13380Sstevel@tonic-gate static int 13390Sstevel@tonic-gate evch_chpublish(evch_bind_t *bp, sysevent_impl_t *ev, int flags) 13400Sstevel@tonic-gate { 13410Sstevel@tonic-gate evch_chan_t *chp = bp->bd_channel; 13420Sstevel@tonic-gate 13433490Seschrock DTRACE_SYSEVENT2(post, evch_bind_t *, bp, sysevent_impl_t *, ev); 13443490Seschrock 13450Sstevel@tonic-gate mutex_enter(&chp->ch_pubmx); 13460Sstevel@tonic-gate if (chp->ch_nevents >= chp->ch_maxev) { 13470Sstevel@tonic-gate if (!(flags & EVCH_QWAIT)) { 13480Sstevel@tonic-gate evch_evq_evfree(ev); 13490Sstevel@tonic-gate mutex_exit(&chp->ch_pubmx); 13500Sstevel@tonic-gate return (EAGAIN); 13510Sstevel@tonic-gate } else { 13520Sstevel@tonic-gate while (chp->ch_nevents >= chp->ch_maxev) { 13530Sstevel@tonic-gate if (cv_wait_sig(&chp->ch_pubcv, 13540Sstevel@tonic-gate &chp->ch_pubmx) == 0) { 13550Sstevel@tonic-gate 13560Sstevel@tonic-gate /* Got Signal, return EINTR */ 13570Sstevel@tonic-gate evch_evq_evfree(ev); 13580Sstevel@tonic-gate mutex_exit(&chp->ch_pubmx); 13590Sstevel@tonic-gate return (EINTR); 13600Sstevel@tonic-gate } 13610Sstevel@tonic-gate } 13620Sstevel@tonic-gate } 13630Sstevel@tonic-gate } 13640Sstevel@tonic-gate chp->ch_nevents++; 13650Sstevel@tonic-gate mutex_exit(&chp->ch_pubmx); 13660Sstevel@tonic-gate SE_TIME(ev) = gethrtime(); 13670Sstevel@tonic-gate SE_SEQ(ev) = log_sysevent_new_id(); 13680Sstevel@tonic-gate /* 13690Sstevel@tonic-gate * Add the destructor function to the event structure, now that the 13700Sstevel@tonic-gate * event is accounted for. The only task of the descructor is to 13710Sstevel@tonic-gate * decrement the channel event count. The evq_*() routines (including 13720Sstevel@tonic-gate * the event delivery thread) do not have knowledge of the channel 13730Sstevel@tonic-gate * data. So the anonymous destructor handles the channel data for it. 13740Sstevel@tonic-gate */ 13750Sstevel@tonic-gate evch_evq_evadd_dest(ev, evch_destr_event, (void *)chp); 13760Sstevel@tonic-gate return (evch_evq_pub(chp->ch_queue, ev, flags) == 0 ? 0 : EAGAIN); 13770Sstevel@tonic-gate } 13780Sstevel@tonic-gate 13790Sstevel@tonic-gate /* 13800Sstevel@tonic-gate * Fills a buffer consecutive with the names of all available channels. 13810Sstevel@tonic-gate * Returns the length of all name strings or -1 if buffer size was unsufficient. 13820Sstevel@tonic-gate */ 13830Sstevel@tonic-gate static int 13840Sstevel@tonic-gate evch_chgetnames(char *buf, size_t size) 13850Sstevel@tonic-gate { 13860Sstevel@tonic-gate struct evch_globals *eg; 13870Sstevel@tonic-gate int len = 0; 13880Sstevel@tonic-gate char *addr = buf; 13890Sstevel@tonic-gate int max = size; 13900Sstevel@tonic-gate evch_chan_t *chp; 13910Sstevel@tonic-gate 13920Sstevel@tonic-gate eg = zone_getspecific(evch_zone_key, curproc->p_zone); 13930Sstevel@tonic-gate ASSERT(eg != NULL); 13940Sstevel@tonic-gate 13950Sstevel@tonic-gate mutex_enter(&eg->evch_list_lock); 13960Sstevel@tonic-gate for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL; 13970Sstevel@tonic-gate chp = evch_dl_next(&eg->evch_list, chp)) { 13980Sstevel@tonic-gate len += chp->ch_namelen; 13990Sstevel@tonic-gate if (len >= max) { 14000Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 14010Sstevel@tonic-gate return (-1); 14020Sstevel@tonic-gate } 14030Sstevel@tonic-gate bcopy(chp->ch_name, addr, chp->ch_namelen); 14040Sstevel@tonic-gate addr += chp->ch_namelen; 14050Sstevel@tonic-gate } 14060Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 14070Sstevel@tonic-gate addr[0] = 0; 14080Sstevel@tonic-gate return (len + 1); 14090Sstevel@tonic-gate } 14100Sstevel@tonic-gate 14110Sstevel@tonic-gate /* 14120Sstevel@tonic-gate * Fills the data of one channel and all subscribers of that channel into 14130Sstevel@tonic-gate * a buffer. Returns -1 if the channel name is invalid and 0 on buffer overflow. 14140Sstevel@tonic-gate */ 14150Sstevel@tonic-gate static int 14160Sstevel@tonic-gate evch_chgetchdata(char *chname, void *buf, size_t size) 14170Sstevel@tonic-gate { 14180Sstevel@tonic-gate struct evch_globals *eg; 14190Sstevel@tonic-gate char *cpaddr; 14200Sstevel@tonic-gate int bufmax; 14210Sstevel@tonic-gate int buflen; 14220Sstevel@tonic-gate evch_chan_t *chp; 14230Sstevel@tonic-gate sev_chinfo_t *p = (sev_chinfo_t *)buf; 14240Sstevel@tonic-gate int chdlen; 14250Sstevel@tonic-gate evch_subd_t *sdp; 14260Sstevel@tonic-gate sev_subinfo_t *subp; 14270Sstevel@tonic-gate int idlen; 14280Sstevel@tonic-gate int len; 14290Sstevel@tonic-gate 14300Sstevel@tonic-gate eg = zone_getspecific(evch_zone_key, curproc->p_zone); 14310Sstevel@tonic-gate ASSERT(eg != NULL); 14320Sstevel@tonic-gate 14330Sstevel@tonic-gate mutex_enter(&eg->evch_list_lock); 14340Sstevel@tonic-gate chp = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp, 14350Sstevel@tonic-gate chname); 14360Sstevel@tonic-gate if (chp == NULL) { 14370Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 14380Sstevel@tonic-gate return (-1); 14390Sstevel@tonic-gate } 14400Sstevel@tonic-gate chdlen = offsetof(sev_chinfo_t, cd_subinfo); 14410Sstevel@tonic-gate if (size < chdlen) { 14420Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 14430Sstevel@tonic-gate return (0); 14440Sstevel@tonic-gate } 14450Sstevel@tonic-gate p->cd_version = 0; 14460Sstevel@tonic-gate p->cd_suboffs = chdlen; 14470Sstevel@tonic-gate p->cd_uid = chp->ch_uid; 14480Sstevel@tonic-gate p->cd_gid = chp->ch_gid; 14490Sstevel@tonic-gate p->cd_perms = 0; 14500Sstevel@tonic-gate p->cd_ctime = chp->ch_ctime; 14510Sstevel@tonic-gate p->cd_maxev = chp->ch_maxev; 14520Sstevel@tonic-gate p->cd_evhwm = EVCH_EVQ_HIGHWM(chp->ch_queue); 14530Sstevel@tonic-gate p->cd_nevents = EVCH_EVQ_EVCOUNT(chp->ch_queue); 14540Sstevel@tonic-gate p->cd_maxsub = chp->ch_maxsubscr; 14550Sstevel@tonic-gate p->cd_nsub = evch_dl_getnum(&chp->ch_subscr); 14560Sstevel@tonic-gate p->cd_maxbinds = chp->ch_maxbinds; 14570Sstevel@tonic-gate p->cd_nbinds = chp->ch_bindings; 14580Sstevel@tonic-gate p->cd_holdpend = chp->ch_holdpend; 14590Sstevel@tonic-gate p->cd_limev = evch_events_max; 14600Sstevel@tonic-gate cpaddr = (char *)p + chdlen; 14610Sstevel@tonic-gate bufmax = size - chdlen; 14620Sstevel@tonic-gate buflen = 0; 14630Sstevel@tonic-gate 14640Sstevel@tonic-gate for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp != NULL; 14650Sstevel@tonic-gate sdp = evch_dl_next(&chp->ch_subscr, sdp)) { 14660Sstevel@tonic-gate idlen = strlen(sdp->sd_ident) + 1; 14670Sstevel@tonic-gate len = SE_ALIGN(offsetof(sev_subinfo_t, sb_strings) + idlen + 14680Sstevel@tonic-gate sdp->sd_clnsize); 14690Sstevel@tonic-gate buflen += len; 14700Sstevel@tonic-gate if (buflen >= bufmax) { 14710Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 14720Sstevel@tonic-gate return (0); 14730Sstevel@tonic-gate } 14740Sstevel@tonic-gate subp = (sev_subinfo_t *)cpaddr; 14750Sstevel@tonic-gate subp->sb_nextoff = len; 14760Sstevel@tonic-gate subp->sb_stroff = offsetof(sev_subinfo_t, sb_strings); 14770Sstevel@tonic-gate if (sdp->sd_classname) { 14780Sstevel@tonic-gate bcopy(sdp->sd_classname, subp->sb_strings + idlen, 14790Sstevel@tonic-gate sdp->sd_clnsize); 14800Sstevel@tonic-gate subp->sb_clnamoff = idlen; 14810Sstevel@tonic-gate } else { 14820Sstevel@tonic-gate subp->sb_clnamoff = idlen - 1; 14830Sstevel@tonic-gate } 14840Sstevel@tonic-gate subp->sb_pid = sdp->sd_pid; 14850Sstevel@tonic-gate subp->sb_nevents = EVCH_EVQ_EVCOUNT(sdp->sd_queue); 14860Sstevel@tonic-gate subp->sb_evhwm = EVCH_EVQ_HIGHWM(sdp->sd_queue); 14870Sstevel@tonic-gate subp->sb_persist = sdp->sd_persist; 14880Sstevel@tonic-gate subp->sb_status = evch_evq_status(sdp->sd_queue); 14890Sstevel@tonic-gate subp->sb_active = sdp->sd_active; 14900Sstevel@tonic-gate subp->sb_dump = sdp->sd_dump; 14910Sstevel@tonic-gate bcopy(sdp->sd_ident, subp->sb_strings, idlen); 14920Sstevel@tonic-gate cpaddr += len; 14930Sstevel@tonic-gate } 14940Sstevel@tonic-gate mutex_exit(&eg->evch_list_lock); 14950Sstevel@tonic-gate return (chdlen + buflen); 14960Sstevel@tonic-gate } 14970Sstevel@tonic-gate 14980Sstevel@tonic-gate /* 14990Sstevel@tonic-gate * Init iteration of all events of a channel. This function creates a new 15000Sstevel@tonic-gate * event queue and puts all events from the channel into that queue. 15010Sstevel@tonic-gate * Subsequent calls to evch_chgetnextev will deliver the events from that 15020Sstevel@tonic-gate * queue. Only one thread per channel is allowed to read through the events. 15030Sstevel@tonic-gate * Returns 0 on success and 1 if there is already someone reading the 15040Sstevel@tonic-gate * events. 15050Sstevel@tonic-gate * If argument subid == NULL, we look for a subscriber which has 15060Sstevel@tonic-gate * flag EVCH_SUB_DUMP set. 15070Sstevel@tonic-gate */ 15080Sstevel@tonic-gate /* 15090Sstevel@tonic-gate * Static variables that are used to traverse events of a channel in panic case. 15100Sstevel@tonic-gate */ 15110Sstevel@tonic-gate static evch_chan_t *evch_chan; 15120Sstevel@tonic-gate static evch_eventq_t *evch_subq; 15130Sstevel@tonic-gate static sysevent_impl_t *evch_curev; 15140Sstevel@tonic-gate 15150Sstevel@tonic-gate static evchanq_t * 15160Sstevel@tonic-gate evch_chrdevent_init(evch_chan_t *chp, char *subid) 15170Sstevel@tonic-gate { 15180Sstevel@tonic-gate evch_subd_t *sdp; 15190Sstevel@tonic-gate void *ev; 15200Sstevel@tonic-gate int pmqstat; /* Prev status of main queue */ 15210Sstevel@tonic-gate int psqstat; /* Prev status of subscriber queue */ 15220Sstevel@tonic-gate evchanq_t *snp; /* Pointer to q with snapshot of ev */ 15230Sstevel@tonic-gate compare_f compfunc; 15240Sstevel@tonic-gate 15250Sstevel@tonic-gate compfunc = subid == NULL ? evch_dumpflgcmp : evch_subidcmp; 15260Sstevel@tonic-gate if (panicstr != NULL) { 15270Sstevel@tonic-gate evch_chan = chp; 15280Sstevel@tonic-gate evch_subq = NULL; 15290Sstevel@tonic-gate evch_curev = NULL; 15300Sstevel@tonic-gate if ((sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr, 15310Sstevel@tonic-gate compfunc, subid)) != NULL) { 15320Sstevel@tonic-gate evch_subq = sdp->sd_queue; 15330Sstevel@tonic-gate } 15340Sstevel@tonic-gate return (NULL); 15350Sstevel@tonic-gate } 15360Sstevel@tonic-gate mutex_enter(&chp->ch_mutex); 15370Sstevel@tonic-gate sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr, compfunc, subid); 15380Sstevel@tonic-gate /* 15390Sstevel@tonic-gate * Stop main event queue and subscriber queue if not already 15400Sstevel@tonic-gate * in stop mode. 15410Sstevel@tonic-gate */ 15420Sstevel@tonic-gate pmqstat = evch_evq_status(chp->ch_queue); 15430Sstevel@tonic-gate if (pmqstat == 0) 15440Sstevel@tonic-gate evch_evq_stop(chp->ch_queue); 15450Sstevel@tonic-gate if (sdp != NULL) { 15460Sstevel@tonic-gate psqstat = evch_evq_status(sdp->sd_queue); 15470Sstevel@tonic-gate if (psqstat == 0) 15480Sstevel@tonic-gate evch_evq_stop(sdp->sd_queue); 15490Sstevel@tonic-gate } 15500Sstevel@tonic-gate /* 15510Sstevel@tonic-gate * Create event queue to make a snapshot of all events in the 15520Sstevel@tonic-gate * channel. 15530Sstevel@tonic-gate */ 15540Sstevel@tonic-gate snp = kmem_alloc(sizeof (evchanq_t), KM_SLEEP); 15550Sstevel@tonic-gate snp->sn_queue = evch_evq_create(); 15560Sstevel@tonic-gate evch_evq_stop(snp->sn_queue); 15570Sstevel@tonic-gate /* 15580Sstevel@tonic-gate * Make a snapshot of the subscriber queue and the main event queue. 15590Sstevel@tonic-gate */ 15600Sstevel@tonic-gate if (sdp != NULL) { 15610Sstevel@tonic-gate ev = NULL; 15620Sstevel@tonic-gate while ((ev = evch_evq_evnext(sdp->sd_queue, ev)) != NULL) { 15630Sstevel@tonic-gate (void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP); 15640Sstevel@tonic-gate } 15650Sstevel@tonic-gate } 15660Sstevel@tonic-gate ev = NULL; 15670Sstevel@tonic-gate while ((ev = evch_evq_evnext(chp->ch_queue, ev)) != NULL) { 15680Sstevel@tonic-gate (void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP); 15690Sstevel@tonic-gate } 15700Sstevel@tonic-gate snp->sn_nxtev = NULL; 15710Sstevel@tonic-gate /* 15720Sstevel@tonic-gate * Restart main and subscriber queue if previously stopped 15730Sstevel@tonic-gate */ 15740Sstevel@tonic-gate if (sdp != NULL && psqstat == 0) 15750Sstevel@tonic-gate evch_evq_continue(sdp->sd_queue); 15760Sstevel@tonic-gate if (pmqstat == 0) 15770Sstevel@tonic-gate evch_evq_continue(chp->ch_queue); 15780Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 15790Sstevel@tonic-gate return (snp); 15800Sstevel@tonic-gate } 15810Sstevel@tonic-gate 15820Sstevel@tonic-gate /* 15830Sstevel@tonic-gate * Free all resources of the event queue snapshot. In case of panic 15840Sstevel@tonic-gate * context snp must be NULL and no resources need to be free'ed. 15850Sstevel@tonic-gate */ 15860Sstevel@tonic-gate static void 15870Sstevel@tonic-gate evch_chrdevent_fini(evchanq_t *snp) 15880Sstevel@tonic-gate { 15890Sstevel@tonic-gate if (snp != NULL) { 15900Sstevel@tonic-gate evch_evq_destroy(snp->sn_queue); 15910Sstevel@tonic-gate kmem_free(snp, sizeof (evchanq_t)); 15920Sstevel@tonic-gate } 15930Sstevel@tonic-gate } 15940Sstevel@tonic-gate 15950Sstevel@tonic-gate /* 15960Sstevel@tonic-gate * Get address of next event from an event channel. 15970Sstevel@tonic-gate * This function might be called in a panic context. In that case 15980Sstevel@tonic-gate * no resources will be allocated and no locks grabbed. 15990Sstevel@tonic-gate * In normal operation context a snapshot of the event queues of the 16000Sstevel@tonic-gate * specified event channel will be taken. 16010Sstevel@tonic-gate */ 16020Sstevel@tonic-gate static sysevent_impl_t * 16030Sstevel@tonic-gate evch_chgetnextev(evchanq_t *snp) 16040Sstevel@tonic-gate { 16050Sstevel@tonic-gate if (panicstr != NULL) { 16060Sstevel@tonic-gate if (evch_chan == NULL) 16070Sstevel@tonic-gate return (NULL); 16080Sstevel@tonic-gate if (evch_subq != NULL) { 16090Sstevel@tonic-gate /* 16100Sstevel@tonic-gate * We have a subscriber queue. Traverse this queue 16110Sstevel@tonic-gate * first. 16120Sstevel@tonic-gate */ 16130Sstevel@tonic-gate if ((evch_curev = (sysevent_impl_t *) 16140Sstevel@tonic-gate evch_evq_evnext(evch_subq, evch_curev)) != NULL) { 16150Sstevel@tonic-gate return (evch_curev); 16160Sstevel@tonic-gate } else { 16170Sstevel@tonic-gate /* 16180Sstevel@tonic-gate * All subscriber events traversed. evch_subq 16190Sstevel@tonic-gate * == NULL indicates to take the main event 16200Sstevel@tonic-gate * queue now. 16210Sstevel@tonic-gate */ 16220Sstevel@tonic-gate evch_subq = NULL; 16230Sstevel@tonic-gate } 16240Sstevel@tonic-gate } 16250Sstevel@tonic-gate /* 16260Sstevel@tonic-gate * Traverse the main event queue. 16270Sstevel@tonic-gate */ 16280Sstevel@tonic-gate if ((evch_curev = (sysevent_impl_t *) 16290Sstevel@tonic-gate evch_evq_evnext(evch_chan->ch_queue, evch_curev)) == 16300Sstevel@tonic-gate NULL) { 16310Sstevel@tonic-gate evch_chan = NULL; 16320Sstevel@tonic-gate } 16330Sstevel@tonic-gate return (evch_curev); 16340Sstevel@tonic-gate } 16350Sstevel@tonic-gate ASSERT(snp != NULL); 16360Sstevel@tonic-gate snp->sn_nxtev = (sysevent_impl_t *)evch_evq_evnext(snp->sn_queue, 16370Sstevel@tonic-gate snp->sn_nxtev); 16380Sstevel@tonic-gate return (snp->sn_nxtev); 16390Sstevel@tonic-gate } 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate /* 16420Sstevel@tonic-gate * The functions below build up the interface for the kernel to bind/unbind, 16430Sstevel@tonic-gate * subscribe/unsubscribe and publish to event channels. It consists of the 16440Sstevel@tonic-gate * following functions: 16450Sstevel@tonic-gate * 16460Sstevel@tonic-gate * sysevent_evc_bind - Bind to a channel. Create a channel if required 16470Sstevel@tonic-gate * sysevent_evc_unbind - Unbind from a channel. Destroy ch. if last unbind 16480Sstevel@tonic-gate * sysevent_evc_subscribe - Subscribe to events from a channel 16490Sstevel@tonic-gate * sysevent_evc_unsubscribe - Unsubscribe from an event class 16500Sstevel@tonic-gate * sysevent_evc_publish - Publish an event to an event channel 16510Sstevel@tonic-gate * sysevent_evc_control - Various control operation on event channel 16520Sstevel@tonic-gate * 16530Sstevel@tonic-gate * The function below are for evaluating a sysevent: 16540Sstevel@tonic-gate * 16550Sstevel@tonic-gate * sysevent_get_class_name - Get pointer to event class string 16560Sstevel@tonic-gate * sysevent_get_subclass_name - Get pointer to event subclass string 16570Sstevel@tonic-gate * sysevent_get_seq - Get unique event sequence number 16580Sstevel@tonic-gate * sysevent_get_time - Get hrestime of event publish 16590Sstevel@tonic-gate * sysevent_get_size - Get size of event structure 16600Sstevel@tonic-gate * sysevent_get_pub - Get publisher string 16610Sstevel@tonic-gate * sysevent_get_attr_list - Get copy of attribute list 16620Sstevel@tonic-gate * 16630Sstevel@tonic-gate * The following interfaces represent stability level project privat 16640Sstevel@tonic-gate * and allow to save the events of an event channel even in a panic case. 16650Sstevel@tonic-gate * 16660Sstevel@tonic-gate * sysevent_evc_walk_init - Take a snapshot of the events in a channel 16670Sstevel@tonic-gate * sysevent_evc_walk_step - Read next event from snapshot 16680Sstevel@tonic-gate * sysevent_evc_walk_fini - Free resources from event channel snapshot 16690Sstevel@tonic-gate * sysevent_evc_event_attr - Get event payload address and size 16700Sstevel@tonic-gate */ 16710Sstevel@tonic-gate /* 16720Sstevel@tonic-gate * allocate sysevent structure with optional space for attributes 16730Sstevel@tonic-gate */ 16740Sstevel@tonic-gate static sysevent_impl_t * 16750Sstevel@tonic-gate sysevent_evc_alloc(const char *class, const char *subclass, const char *pub, 16760Sstevel@tonic-gate size_t pub_sz, size_t atsz, uint32_t flag) 16770Sstevel@tonic-gate { 16780Sstevel@tonic-gate int payload_sz; 16790Sstevel@tonic-gate int class_sz, subclass_sz; 16800Sstevel@tonic-gate int aligned_class_sz, aligned_subclass_sz, aligned_pub_sz; 16810Sstevel@tonic-gate sysevent_impl_t *ev; 16820Sstevel@tonic-gate 16830Sstevel@tonic-gate /* 16840Sstevel@tonic-gate * Calculate and reserve space for the class, subclass and 16850Sstevel@tonic-gate * publisher strings in the event buffer 16860Sstevel@tonic-gate */ 16870Sstevel@tonic-gate class_sz = strlen(class) + 1; 16880Sstevel@tonic-gate subclass_sz = strlen(subclass) + 1; 16890Sstevel@tonic-gate 16900Sstevel@tonic-gate ASSERT((class_sz <= MAX_CLASS_LEN) && (subclass_sz <= 16910Sstevel@tonic-gate MAX_SUBCLASS_LEN) && (pub_sz <= MAX_PUB_LEN)); 16920Sstevel@tonic-gate 16930Sstevel@tonic-gate /* String sizes must be 64-bit aligned in the event buffer */ 16940Sstevel@tonic-gate aligned_class_sz = SE_ALIGN(class_sz); 16950Sstevel@tonic-gate aligned_subclass_sz = SE_ALIGN(subclass_sz); 16960Sstevel@tonic-gate aligned_pub_sz = SE_ALIGN(pub_sz); 16970Sstevel@tonic-gate 16980Sstevel@tonic-gate /* 16990Sstevel@tonic-gate * Calculate payload size. Consider the space needed for alignment 17000Sstevel@tonic-gate * and subtract the size of the uint64_t placeholder variables of 17010Sstevel@tonic-gate * sysevent_impl_t. 17020Sstevel@tonic-gate */ 17030Sstevel@tonic-gate payload_sz = (aligned_class_sz - sizeof (uint64_t)) + 17040Sstevel@tonic-gate (aligned_subclass_sz - sizeof (uint64_t)) + 17050Sstevel@tonic-gate (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) + 17060Sstevel@tonic-gate atsz; 17070Sstevel@tonic-gate 17080Sstevel@tonic-gate /* 17090Sstevel@tonic-gate * Allocate event buffer plus additional payload overhead 17100Sstevel@tonic-gate */ 17110Sstevel@tonic-gate if ((ev = evch_evq_evzalloc(sizeof (sysevent_impl_t) + 17120Sstevel@tonic-gate payload_sz, flag)) == NULL) { 17130Sstevel@tonic-gate return (NULL); 17140Sstevel@tonic-gate } 17150Sstevel@tonic-gate 17160Sstevel@tonic-gate /* Initialize the event buffer data */ 17170Sstevel@tonic-gate SE_VERSION(ev) = SYS_EVENT_VERSION; 17180Sstevel@tonic-gate bcopy(class, SE_CLASS_NAME(ev), class_sz); 17190Sstevel@tonic-gate 17200Sstevel@tonic-gate SE_SUBCLASS_OFF(ev) = SE_ALIGN(offsetof(sysevent_impl_t, 17210Sstevel@tonic-gate se_class_name)) + aligned_class_sz; 17220Sstevel@tonic-gate bcopy(subclass, SE_SUBCLASS_NAME(ev), subclass_sz); 17230Sstevel@tonic-gate 17240Sstevel@tonic-gate SE_PUB_OFF(ev) = SE_SUBCLASS_OFF(ev) + aligned_subclass_sz; 17250Sstevel@tonic-gate bcopy(pub, SE_PUB_NAME(ev), pub_sz); 17260Sstevel@tonic-gate 17270Sstevel@tonic-gate SE_ATTR_PTR(ev) = (uint64_t)0; 17280Sstevel@tonic-gate SE_PAYLOAD_SZ(ev) = payload_sz; 17290Sstevel@tonic-gate 17300Sstevel@tonic-gate return (ev); 17310Sstevel@tonic-gate } 17320Sstevel@tonic-gate 17330Sstevel@tonic-gate /* 17340Sstevel@tonic-gate * Initialize event channel handling queues. 17350Sstevel@tonic-gate */ 17360Sstevel@tonic-gate void 17370Sstevel@tonic-gate sysevent_evc_init() 17380Sstevel@tonic-gate { 17390Sstevel@tonic-gate evch_chinit(); 17400Sstevel@tonic-gate } 17410Sstevel@tonic-gate 17420Sstevel@tonic-gate /* 17430Sstevel@tonic-gate * Second initialization step: create threads, if event channels are already 17440Sstevel@tonic-gate * created 17450Sstevel@tonic-gate */ 17460Sstevel@tonic-gate void 17470Sstevel@tonic-gate sysevent_evc_thrinit() 17480Sstevel@tonic-gate { 17490Sstevel@tonic-gate evch_chinitthr(); 17500Sstevel@tonic-gate } 17510Sstevel@tonic-gate 17520Sstevel@tonic-gate int 17530Sstevel@tonic-gate sysevent_evc_bind(const char *ch_name, evchan_t **scpp, uint32_t flags) 17540Sstevel@tonic-gate { 17550Sstevel@tonic-gate ASSERT(ch_name != NULL && scpp != NULL); 17560Sstevel@tonic-gate ASSERT((flags & ~EVCH_B_FLAGS) == 0); 17570Sstevel@tonic-gate return (evch_chbind(ch_name, (evch_bind_t **)scpp, flags)); 17580Sstevel@tonic-gate } 17590Sstevel@tonic-gate 17600Sstevel@tonic-gate void 17610Sstevel@tonic-gate sysevent_evc_unbind(evchan_t *scp) 17620Sstevel@tonic-gate { 17630Sstevel@tonic-gate evch_bind_t *bp = (evch_bind_t *)scp; 17640Sstevel@tonic-gate 17650Sstevel@tonic-gate ASSERT(scp != NULL); 17660Sstevel@tonic-gate evch_chunsubscribe(bp, NULL, 0); 17670Sstevel@tonic-gate evch_chunbind(bp); 17680Sstevel@tonic-gate } 17690Sstevel@tonic-gate 17700Sstevel@tonic-gate int 17710Sstevel@tonic-gate sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class, 17720Sstevel@tonic-gate int (*callb)(sysevent_t *ev, void *cookie), 17730Sstevel@tonic-gate void *cookie, uint32_t flags) 17740Sstevel@tonic-gate { 17750Sstevel@tonic-gate ASSERT(scp != NULL && sid != NULL && class != NULL && callb != NULL); 17760Sstevel@tonic-gate ASSERT(flags == 0); 17770Sstevel@tonic-gate if (strlen(sid) > MAX_SUBID_LEN) { 17780Sstevel@tonic-gate return (EINVAL); 17790Sstevel@tonic-gate } 17800Sstevel@tonic-gate if (strcmp(class, EC_ALL) == 0) { 17810Sstevel@tonic-gate class = NULL; 17820Sstevel@tonic-gate } 17830Sstevel@tonic-gate return (evch_chsubscribe((evch_bind_t *)scp, EVCH_DELKERN, sid, class, 17840Sstevel@tonic-gate (void *)callb, cookie, 0, 0)); 17850Sstevel@tonic-gate } 17860Sstevel@tonic-gate 17870Sstevel@tonic-gate void 17880Sstevel@tonic-gate sysevent_evc_unsubscribe(evchan_t *scp, const char *sid) 17890Sstevel@tonic-gate { 17900Sstevel@tonic-gate ASSERT(scp != NULL && sid != NULL); 17910Sstevel@tonic-gate if (strcmp(sid, EVCH_ALLSUB) == 0) { 17920Sstevel@tonic-gate sid = NULL; 17930Sstevel@tonic-gate } 17940Sstevel@tonic-gate evch_chunsubscribe((evch_bind_t *)scp, sid, 0); 17950Sstevel@tonic-gate } 17960Sstevel@tonic-gate 17970Sstevel@tonic-gate /* 17980Sstevel@tonic-gate * Publish kernel event. Returns 0 on success, error code else. 17990Sstevel@tonic-gate * Optional attribute data is packed into the event structure. 18000Sstevel@tonic-gate */ 18010Sstevel@tonic-gate int 18020Sstevel@tonic-gate sysevent_evc_publish(evchan_t *scp, const char *class, const char *subclass, 18030Sstevel@tonic-gate const char *vendor, const char *pubs, nvlist_t *attr, uint32_t flags) 18040Sstevel@tonic-gate { 18050Sstevel@tonic-gate sysevent_impl_t *evp; 18060Sstevel@tonic-gate char pub[MAX_PUB_LEN]; 18070Sstevel@tonic-gate int pub_sz; /* includes terminating 0 */ 18080Sstevel@tonic-gate int km_flags; 18090Sstevel@tonic-gate size_t asz = 0; 18100Sstevel@tonic-gate uint64_t attr_offset; 18110Sstevel@tonic-gate caddr_t patt; 18120Sstevel@tonic-gate int err; 18130Sstevel@tonic-gate 18140Sstevel@tonic-gate ASSERT(scp != NULL && class != NULL && subclass != NULL && 18150Sstevel@tonic-gate vendor != NULL && pubs != NULL); 18160Sstevel@tonic-gate 18170Sstevel@tonic-gate ASSERT((flags & ~(EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD | 18180Sstevel@tonic-gate EVCH_QWAIT)) == 0); 18190Sstevel@tonic-gate 18200Sstevel@tonic-gate km_flags = flags & (EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD); 18210Sstevel@tonic-gate ASSERT(km_flags == EVCH_SLEEP || km_flags == EVCH_NOSLEEP || 18220Sstevel@tonic-gate km_flags == EVCH_TRYHARD); 18230Sstevel@tonic-gate 18240Sstevel@tonic-gate pub_sz = snprintf(pub, MAX_PUB_LEN, "%s:kern:%s", vendor, pubs) + 1; 18250Sstevel@tonic-gate if (pub_sz > MAX_PUB_LEN) 18260Sstevel@tonic-gate return (EINVAL); 18270Sstevel@tonic-gate 18280Sstevel@tonic-gate if (attr != NULL) { 18290Sstevel@tonic-gate if ((err = nvlist_size(attr, &asz, NV_ENCODE_NATIVE)) != 0) { 18300Sstevel@tonic-gate return (err); 18310Sstevel@tonic-gate } 18320Sstevel@tonic-gate } 18330Sstevel@tonic-gate evp = sysevent_evc_alloc(class, subclass, pub, pub_sz, asz, km_flags); 18340Sstevel@tonic-gate if (evp == NULL) { 18350Sstevel@tonic-gate return (ENOMEM); 18360Sstevel@tonic-gate } 18370Sstevel@tonic-gate if (attr != NULL) { 18380Sstevel@tonic-gate /* 18390Sstevel@tonic-gate * Pack attributes into event buffer. Event buffer already 18400Sstevel@tonic-gate * has enough room for the packed nvlist. 18410Sstevel@tonic-gate */ 18420Sstevel@tonic-gate attr_offset = SE_ATTR_OFF(evp); 18430Sstevel@tonic-gate patt = (caddr_t)evp + attr_offset; 18440Sstevel@tonic-gate 18450Sstevel@tonic-gate err = nvlist_pack(attr, &patt, &asz, NV_ENCODE_NATIVE, 18460Sstevel@tonic-gate km_flags & EVCH_SLEEP ? KM_SLEEP : KM_NOSLEEP); 18470Sstevel@tonic-gate 18480Sstevel@tonic-gate ASSERT(err != ENOMEM); 18490Sstevel@tonic-gate 18500Sstevel@tonic-gate if (err != 0) { 18510Sstevel@tonic-gate return (EINVAL); 18520Sstevel@tonic-gate } 18530Sstevel@tonic-gate 18540Sstevel@tonic-gate evp->seh_attr_off = attr_offset; 18550Sstevel@tonic-gate SE_FLAG(evp) = SE_PACKED_BUF; 18560Sstevel@tonic-gate } 18570Sstevel@tonic-gate return (evch_chpublish((evch_bind_t *)scp, evp, flags)); 18580Sstevel@tonic-gate } 18590Sstevel@tonic-gate 18600Sstevel@tonic-gate int 18610Sstevel@tonic-gate sysevent_evc_control(evchan_t *scp, int cmd, ...) 18620Sstevel@tonic-gate { 18630Sstevel@tonic-gate va_list ap; 18640Sstevel@tonic-gate evch_chan_t *chp = ((evch_bind_t *)scp)->bd_channel; 18650Sstevel@tonic-gate uint32_t *chlenp; 18660Sstevel@tonic-gate uint32_t chlen; 18670Sstevel@tonic-gate uint32_t ochlen; 18680Sstevel@tonic-gate int rc = 0; 18690Sstevel@tonic-gate 18700Sstevel@tonic-gate if (scp == NULL) { 18710Sstevel@tonic-gate return (EINVAL); 18720Sstevel@tonic-gate } 18730Sstevel@tonic-gate 18740Sstevel@tonic-gate va_start(ap, cmd); 18750Sstevel@tonic-gate mutex_enter(&chp->ch_mutex); 18760Sstevel@tonic-gate switch (cmd) { 18770Sstevel@tonic-gate case EVCH_GET_CHAN_LEN: 18780Sstevel@tonic-gate chlenp = va_arg(ap, uint32_t *); 18790Sstevel@tonic-gate *chlenp = chp->ch_maxev; 18800Sstevel@tonic-gate break; 18810Sstevel@tonic-gate case EVCH_SET_CHAN_LEN: 18820Sstevel@tonic-gate chlen = va_arg(ap, uint32_t); 18830Sstevel@tonic-gate ochlen = chp->ch_maxev; 18840Sstevel@tonic-gate chp->ch_maxev = min(chlen, evch_events_max); 18850Sstevel@tonic-gate if (ochlen < chp->ch_maxev) { 18860Sstevel@tonic-gate cv_signal(&chp->ch_pubcv); 18870Sstevel@tonic-gate } 18880Sstevel@tonic-gate break; 18890Sstevel@tonic-gate case EVCH_GET_CHAN_LEN_MAX: 18900Sstevel@tonic-gate *va_arg(ap, uint32_t *) = evch_events_max; 18910Sstevel@tonic-gate break; 18920Sstevel@tonic-gate default: 18930Sstevel@tonic-gate rc = EINVAL; 18940Sstevel@tonic-gate } 18950Sstevel@tonic-gate 18960Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 18970Sstevel@tonic-gate va_end(ap); 18980Sstevel@tonic-gate return (rc); 18990Sstevel@tonic-gate } 19000Sstevel@tonic-gate 19010Sstevel@tonic-gate /* 19020Sstevel@tonic-gate * Project private interface to take a snapshot of all events of the 19030Sstevel@tonic-gate * specified event channel. Argument subscr may be a subscriber id, the empty 19040Sstevel@tonic-gate * string "", or NULL. The empty string indicates that no subscriber is 19050Sstevel@tonic-gate * selected, for example if a previous subscriber died. sysevent_evc_walk_next() 19060Sstevel@tonic-gate * will deliver events from the main event queue in this case. If subscr is 19070Sstevel@tonic-gate * NULL, the subscriber with the EVCH_SUB_DUMP flag set (subd->sd_dump != 0) 19080Sstevel@tonic-gate * will be selected. 19090Sstevel@tonic-gate * 19100Sstevel@tonic-gate * In panic case this function returns NULL. This is legal. The NULL has 19110Sstevel@tonic-gate * to be delivered to sysevent_evc_walk_step() and sysevent_evc_walk_fini(). 19120Sstevel@tonic-gate */ 19130Sstevel@tonic-gate evchanq_t * 19140Sstevel@tonic-gate sysevent_evc_walk_init(evchan_t *scp, char *subscr) 19150Sstevel@tonic-gate { 19160Sstevel@tonic-gate if (panicstr != NULL && scp == NULL) 19170Sstevel@tonic-gate return (NULL); 19180Sstevel@tonic-gate ASSERT(scp != NULL); 19190Sstevel@tonic-gate return (evch_chrdevent_init(((evch_bind_t *)scp)->bd_channel, subscr)); 19200Sstevel@tonic-gate } 19210Sstevel@tonic-gate 19220Sstevel@tonic-gate /* 19230Sstevel@tonic-gate * Project private interface to read events from a previously taken 19240Sstevel@tonic-gate * snapshot (with sysevent_evc_walk_init). In case of panic events 19250Sstevel@tonic-gate * are retrieved directly from the channel data structures. No resources 19260Sstevel@tonic-gate * are allocated and no mutexes are grabbed in panic context. 19270Sstevel@tonic-gate */ 19280Sstevel@tonic-gate sysevent_t * 19290Sstevel@tonic-gate sysevent_evc_walk_step(evchanq_t *evcq) 19300Sstevel@tonic-gate { 19310Sstevel@tonic-gate return ((sysevent_t *)evch_chgetnextev(evcq)); 19320Sstevel@tonic-gate } 19330Sstevel@tonic-gate 19340Sstevel@tonic-gate /* 19350Sstevel@tonic-gate * Project private interface to free a previously taken snapshot. 19360Sstevel@tonic-gate */ 19370Sstevel@tonic-gate void 19380Sstevel@tonic-gate sysevent_evc_walk_fini(evchanq_t *evcq) 19390Sstevel@tonic-gate { 19400Sstevel@tonic-gate evch_chrdevent_fini(evcq); 19410Sstevel@tonic-gate } 19420Sstevel@tonic-gate 19430Sstevel@tonic-gate /* 19440Sstevel@tonic-gate * Get address and size of an event payload. Returns NULL when no 19450Sstevel@tonic-gate * payload present. 19460Sstevel@tonic-gate */ 19470Sstevel@tonic-gate char * 19480Sstevel@tonic-gate sysevent_evc_event_attr(sysevent_t *ev, size_t *plsize) 19490Sstevel@tonic-gate { 19500Sstevel@tonic-gate char *attrp; 19510Sstevel@tonic-gate size_t aoff; 19520Sstevel@tonic-gate size_t asz; 19530Sstevel@tonic-gate 19540Sstevel@tonic-gate aoff = SE_ATTR_OFF(ev); 19550Sstevel@tonic-gate attrp = (char *)ev + aoff; 19560Sstevel@tonic-gate asz = *plsize = SE_SIZE(ev) - aoff; 19570Sstevel@tonic-gate return (asz ? attrp : NULL); 19580Sstevel@tonic-gate } 19590Sstevel@tonic-gate 19600Sstevel@tonic-gate /* 19610Sstevel@tonic-gate * sysevent_get_class_name - Get class name string 19620Sstevel@tonic-gate */ 19630Sstevel@tonic-gate char * 19640Sstevel@tonic-gate sysevent_get_class_name(sysevent_t *ev) 19650Sstevel@tonic-gate { 19660Sstevel@tonic-gate return (SE_CLASS_NAME(ev)); 19670Sstevel@tonic-gate } 19680Sstevel@tonic-gate 19690Sstevel@tonic-gate /* 19700Sstevel@tonic-gate * sysevent_get_subclass_name - Get subclass name string 19710Sstevel@tonic-gate */ 19720Sstevel@tonic-gate char * 19730Sstevel@tonic-gate sysevent_get_subclass_name(sysevent_t *ev) 19740Sstevel@tonic-gate { 19750Sstevel@tonic-gate return (SE_SUBCLASS_NAME(ev)); 19760Sstevel@tonic-gate } 19770Sstevel@tonic-gate 19780Sstevel@tonic-gate /* 19790Sstevel@tonic-gate * sysevent_get_seq - Get event sequence id 19800Sstevel@tonic-gate */ 19810Sstevel@tonic-gate uint64_t 19820Sstevel@tonic-gate sysevent_get_seq(sysevent_t *ev) 19830Sstevel@tonic-gate { 19840Sstevel@tonic-gate return (SE_SEQ(ev)); 19850Sstevel@tonic-gate } 19860Sstevel@tonic-gate 19870Sstevel@tonic-gate /* 19880Sstevel@tonic-gate * sysevent_get_time - Get event timestamp 19890Sstevel@tonic-gate */ 19900Sstevel@tonic-gate void 19910Sstevel@tonic-gate sysevent_get_time(sysevent_t *ev, hrtime_t *etime) 19920Sstevel@tonic-gate { 19930Sstevel@tonic-gate *etime = SE_TIME(ev); 19940Sstevel@tonic-gate } 19950Sstevel@tonic-gate 19960Sstevel@tonic-gate /* 19970Sstevel@tonic-gate * sysevent_get_size - Get event buffer size 19980Sstevel@tonic-gate */ 19990Sstevel@tonic-gate size_t 20000Sstevel@tonic-gate sysevent_get_size(sysevent_t *ev) 20010Sstevel@tonic-gate { 20020Sstevel@tonic-gate return ((size_t)SE_SIZE(ev)); 20030Sstevel@tonic-gate } 20040Sstevel@tonic-gate 20050Sstevel@tonic-gate /* 20060Sstevel@tonic-gate * sysevent_get_pub - Get publisher name string 20070Sstevel@tonic-gate */ 20080Sstevel@tonic-gate char * 20090Sstevel@tonic-gate sysevent_get_pub(sysevent_t *ev) 20100Sstevel@tonic-gate { 20110Sstevel@tonic-gate return (SE_PUB_NAME(ev)); 20120Sstevel@tonic-gate } 20130Sstevel@tonic-gate 20140Sstevel@tonic-gate /* 20150Sstevel@tonic-gate * sysevent_get_attr_list - stores address of a copy of the attribute list 20160Sstevel@tonic-gate * associated with the given sysevent buffer. The list must be freed by the 20170Sstevel@tonic-gate * caller. 20180Sstevel@tonic-gate */ 20190Sstevel@tonic-gate int 20200Sstevel@tonic-gate sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist) 20210Sstevel@tonic-gate { 20220Sstevel@tonic-gate int error; 20230Sstevel@tonic-gate caddr_t attr; 20240Sstevel@tonic-gate size_t attr_len; 20250Sstevel@tonic-gate uint64_t attr_offset; 20260Sstevel@tonic-gate 20270Sstevel@tonic-gate *nvlist = NULL; 20280Sstevel@tonic-gate if (SE_FLAG(ev) != SE_PACKED_BUF) { 20290Sstevel@tonic-gate return (EINVAL); 20300Sstevel@tonic-gate } 20310Sstevel@tonic-gate attr_offset = SE_ATTR_OFF(ev); 20320Sstevel@tonic-gate if (SE_SIZE(ev) == attr_offset) { 20330Sstevel@tonic-gate return (EINVAL); 20340Sstevel@tonic-gate } 20350Sstevel@tonic-gate 20360Sstevel@tonic-gate /* unpack nvlist */ 20370Sstevel@tonic-gate attr = (caddr_t)ev + attr_offset; 20380Sstevel@tonic-gate attr_len = SE_SIZE(ev) - attr_offset; 20390Sstevel@tonic-gate if ((error = nvlist_unpack(attr, attr_len, nvlist, 0)) != 0) { 20400Sstevel@tonic-gate error = error != ENOMEM ? EINVAL : error; 20410Sstevel@tonic-gate return (error); 20420Sstevel@tonic-gate } 20430Sstevel@tonic-gate return (0); 20440Sstevel@tonic-gate } 20450Sstevel@tonic-gate 20460Sstevel@tonic-gate /* 20470Sstevel@tonic-gate * Functions called by the sysevent driver for general purpose event channels 20480Sstevel@tonic-gate * 20490Sstevel@tonic-gate * evch_usrchanopen - Create/Bind to an event channel 20500Sstevel@tonic-gate * evch_usrchanclose - Unbind/Destroy event channel 20510Sstevel@tonic-gate * evch_usrallocev - Allocate event data structure 20520Sstevel@tonic-gate * evch_usrfreeev - Free event data structure 20530Sstevel@tonic-gate * evch_usrpostevent - Publish event 20540Sstevel@tonic-gate * evch_usrsubscribe - Subscribe (register callback function) 20550Sstevel@tonic-gate * evch_usrunsubscribe - Unsubscribe 20560Sstevel@tonic-gate * evch_usrcontrol_set - Set channel properties 20570Sstevel@tonic-gate * evch_usrcontrol_get - Get channel properties 20580Sstevel@tonic-gate * evch_usrgetchnames - Get list of channel names 20590Sstevel@tonic-gate * evch_usrgetchdata - Get data of an event channel 20600Sstevel@tonic-gate */ 20610Sstevel@tonic-gate evchan_t * 20620Sstevel@tonic-gate evch_usrchanopen(const char *name, uint32_t flags, int *err) 20630Sstevel@tonic-gate { 20640Sstevel@tonic-gate evch_bind_t *bp = NULL; 20650Sstevel@tonic-gate 20660Sstevel@tonic-gate *err = evch_chbind(name, &bp, flags); 20670Sstevel@tonic-gate return ((evchan_t *)bp); 20680Sstevel@tonic-gate } 20690Sstevel@tonic-gate 20700Sstevel@tonic-gate /* 20710Sstevel@tonic-gate * Unbind from the channel. 20720Sstevel@tonic-gate */ 20730Sstevel@tonic-gate void 20740Sstevel@tonic-gate evch_usrchanclose(evchan_t *cbp) 20750Sstevel@tonic-gate { 20760Sstevel@tonic-gate evch_chunbind((evch_bind_t *)cbp); 20770Sstevel@tonic-gate } 20780Sstevel@tonic-gate 20790Sstevel@tonic-gate /* 20800Sstevel@tonic-gate * Allocates log_evch_eventq_t structure but returns the pointer of the embedded 20810Sstevel@tonic-gate * sysevent_impl_t structure as the opaque sysevent_t * data type 20820Sstevel@tonic-gate */ 20830Sstevel@tonic-gate sysevent_impl_t * 20840Sstevel@tonic-gate evch_usrallocev(size_t evsize, uint32_t flags) 20850Sstevel@tonic-gate { 20860Sstevel@tonic-gate return ((sysevent_impl_t *)evch_evq_evzalloc(evsize, flags)); 20870Sstevel@tonic-gate } 20880Sstevel@tonic-gate 20890Sstevel@tonic-gate /* 20900Sstevel@tonic-gate * Free evch_eventq_t structure 20910Sstevel@tonic-gate */ 20920Sstevel@tonic-gate void 20930Sstevel@tonic-gate evch_usrfreeev(sysevent_impl_t *ev) 20940Sstevel@tonic-gate { 20950Sstevel@tonic-gate evch_evq_evfree((void *)ev); 20960Sstevel@tonic-gate } 20970Sstevel@tonic-gate 20980Sstevel@tonic-gate /* 20990Sstevel@tonic-gate * Posts an event to the given channel. The event structure has to be 21000Sstevel@tonic-gate * allocated by evch_usrallocev(). Returns zero on success and an error 21010Sstevel@tonic-gate * code else. Attributes have to be packed and included in the event structure. 21020Sstevel@tonic-gate * 21030Sstevel@tonic-gate */ 21040Sstevel@tonic-gate int 21050Sstevel@tonic-gate evch_usrpostevent(evchan_t *bp, sysevent_impl_t *ev, uint32_t flags) 21060Sstevel@tonic-gate { 21070Sstevel@tonic-gate return (evch_chpublish((evch_bind_t *)bp, ev, flags)); 21080Sstevel@tonic-gate } 21090Sstevel@tonic-gate 21100Sstevel@tonic-gate /* 21110Sstevel@tonic-gate * Subscribe function for user land subscriptions 21120Sstevel@tonic-gate */ 21130Sstevel@tonic-gate int 21140Sstevel@tonic-gate evch_usrsubscribe(evchan_t *bp, const char *sid, const char *class, 21150Sstevel@tonic-gate int d, uint32_t flags) 21160Sstevel@tonic-gate { 21170Sstevel@tonic-gate door_handle_t dh = door_ki_lookup(d); 21180Sstevel@tonic-gate int rv; 21190Sstevel@tonic-gate 21200Sstevel@tonic-gate if (dh == NULL) { 21210Sstevel@tonic-gate return (EINVAL); 21220Sstevel@tonic-gate } 21230Sstevel@tonic-gate if ((rv = evch_chsubscribe((evch_bind_t *)bp, EVCH_DELDOOR, sid, class, 21240Sstevel@tonic-gate (void *)dh, NULL, flags, curproc->p_pid)) != 0) { 21250Sstevel@tonic-gate door_ki_rele(dh); 21260Sstevel@tonic-gate } 21270Sstevel@tonic-gate return (rv); 21280Sstevel@tonic-gate } 21290Sstevel@tonic-gate 21300Sstevel@tonic-gate /* 21310Sstevel@tonic-gate * Flag can be EVCH_SUB_KEEP or 0. EVCH_SUB_KEEP preserves persistent 21320Sstevel@tonic-gate * subscribers 21330Sstevel@tonic-gate */ 21340Sstevel@tonic-gate void 21350Sstevel@tonic-gate evch_usrunsubscribe(evchan_t *bp, const char *subid, uint32_t flags) 21360Sstevel@tonic-gate { 21370Sstevel@tonic-gate evch_chunsubscribe((evch_bind_t *)bp, subid, flags); 21380Sstevel@tonic-gate } 21390Sstevel@tonic-gate 21400Sstevel@tonic-gate /*ARGSUSED*/ 21410Sstevel@tonic-gate int 21420Sstevel@tonic-gate evch_usrcontrol_set(evchan_t *bp, int cmd, uint32_t value) 21430Sstevel@tonic-gate { 21440Sstevel@tonic-gate evch_chan_t *chp = ((evch_bind_t *)bp)->bd_channel; 21450Sstevel@tonic-gate uid_t uid = crgetuid(curthread->t_cred); 21460Sstevel@tonic-gate int rc = 0; 21470Sstevel@tonic-gate 21480Sstevel@tonic-gate mutex_enter(&chp->ch_mutex); 21490Sstevel@tonic-gate switch (cmd) { 21500Sstevel@tonic-gate case EVCH_SET_CHAN_LEN: 21510Sstevel@tonic-gate if (uid && uid != chp->ch_uid) { 21520Sstevel@tonic-gate rc = EACCES; 21530Sstevel@tonic-gate break; 21540Sstevel@tonic-gate } 21550Sstevel@tonic-gate chp->ch_maxev = min(value, evch_events_max); 21560Sstevel@tonic-gate break; 21570Sstevel@tonic-gate default: 21580Sstevel@tonic-gate rc = EINVAL; 21590Sstevel@tonic-gate } 21600Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 21610Sstevel@tonic-gate return (rc); 21620Sstevel@tonic-gate } 21630Sstevel@tonic-gate 21640Sstevel@tonic-gate /*ARGSUSED*/ 21650Sstevel@tonic-gate int 21660Sstevel@tonic-gate evch_usrcontrol_get(evchan_t *bp, int cmd, uint32_t *value) 21670Sstevel@tonic-gate { 21680Sstevel@tonic-gate evch_chan_t *chp = ((evch_bind_t *)bp)->bd_channel; 21690Sstevel@tonic-gate int rc = 0; 21700Sstevel@tonic-gate 21710Sstevel@tonic-gate mutex_enter(&chp->ch_mutex); 21720Sstevel@tonic-gate switch (cmd) { 21730Sstevel@tonic-gate case EVCH_GET_CHAN_LEN: 21740Sstevel@tonic-gate *value = chp->ch_maxev; 21750Sstevel@tonic-gate break; 21760Sstevel@tonic-gate case EVCH_GET_CHAN_LEN_MAX: 21770Sstevel@tonic-gate *value = evch_events_max; 21780Sstevel@tonic-gate break; 21790Sstevel@tonic-gate default: 21800Sstevel@tonic-gate rc = EINVAL; 21810Sstevel@tonic-gate } 21820Sstevel@tonic-gate mutex_exit(&chp->ch_mutex); 21830Sstevel@tonic-gate return (rc); 21840Sstevel@tonic-gate } 21850Sstevel@tonic-gate 21860Sstevel@tonic-gate int 21870Sstevel@tonic-gate evch_usrgetchnames(char *buf, size_t size) 21880Sstevel@tonic-gate { 21890Sstevel@tonic-gate return (evch_chgetnames(buf, size)); 21900Sstevel@tonic-gate } 21910Sstevel@tonic-gate 21920Sstevel@tonic-gate int 21930Sstevel@tonic-gate evch_usrgetchdata(char *chname, void *buf, size_t size) 21940Sstevel@tonic-gate { 21950Sstevel@tonic-gate return (evch_chgetchdata(chname, buf, size)); 21960Sstevel@tonic-gate } 2197