xref: /onnv-gate/usr/src/uts/common/os/evchannels.c (revision 12967:ab9ae749152f)
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*12967Sgavin.maltby@oracle.com  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate  * This file contains the source of the general purpose event channel extension
270Sstevel@tonic-gate  * to the sysevent framework. This implementation is made up mainly of four
280Sstevel@tonic-gate  * layers of functionality: the event queues (evch_evq_*()), the handling of
290Sstevel@tonic-gate  * channels (evch_ch*()), the kernel interface (sysevent_evc_*()) and the
300Sstevel@tonic-gate  * interface for the sysevent pseudo driver (evch_usr*()).
310Sstevel@tonic-gate  * Libsysevent.so uses the pseudo driver sysevent's ioctl to access the event
320Sstevel@tonic-gate  * channel extensions. The driver in turn uses the evch_usr*() functions below.
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * The interfaces for user land and kernel are declared in sys/sysevent.h
350Sstevel@tonic-gate  * Internal data structures for event channels are defined in
360Sstevel@tonic-gate  * sys/sysevent_impl.h.
370Sstevel@tonic-gate  *
380Sstevel@tonic-gate  * The basic data structure for an event channel is of type evch_chan_t.
390Sstevel@tonic-gate  * All channels are maintained by a list named evch_list. The list head
400Sstevel@tonic-gate  * is of type evch_dlist_t.
410Sstevel@tonic-gate  */
420Sstevel@tonic-gate 
430Sstevel@tonic-gate #include <sys/types.h>
440Sstevel@tonic-gate #include <sys/errno.h>
450Sstevel@tonic-gate #include <sys/stropts.h>
460Sstevel@tonic-gate #include <sys/debug.h>
470Sstevel@tonic-gate #include <sys/ddi.h>
480Sstevel@tonic-gate #include <sys/vmem.h>
490Sstevel@tonic-gate #include <sys/cmn_err.h>
500Sstevel@tonic-gate #include <sys/callb.h>
510Sstevel@tonic-gate #include <sys/sysevent.h>
520Sstevel@tonic-gate #include <sys/sysevent_impl.h>
530Sstevel@tonic-gate #include <sys/sysmacros.h>
540Sstevel@tonic-gate #include <sys/disp.h>
550Sstevel@tonic-gate #include <sys/atomic.h>
560Sstevel@tonic-gate #include <sys/door.h>
570Sstevel@tonic-gate #include <sys/zone.h>
583490Seschrock #include <sys/sdt.h>
590Sstevel@tonic-gate 
600Sstevel@tonic-gate /* Back-off delay for door_ki_upcall */
610Sstevel@tonic-gate #define	EVCH_MIN_PAUSE	8
620Sstevel@tonic-gate #define	EVCH_MAX_PAUSE	128
630Sstevel@tonic-gate 
640Sstevel@tonic-gate #define	GEVENT(ev)	((evch_gevent_t *)((char *)ev - \
650Sstevel@tonic-gate 			    offsetof(evch_gevent_t, ge_payload)))
660Sstevel@tonic-gate 
670Sstevel@tonic-gate #define	EVCH_EVQ_EVCOUNT(x)	((&(x)->eq_eventq)->sq_count)
680Sstevel@tonic-gate #define	EVCH_EVQ_HIGHWM(x)	((&(x)->eq_eventq)->sq_highwm)
690Sstevel@tonic-gate 
7011102SGavin.Maltby@Sun.COM #define	CH_HOLD_PEND		1
7111102SGavin.Maltby@Sun.COM #define	CH_HOLD_PEND_INDEF	2
7211102SGavin.Maltby@Sun.COM 
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
evch_dl_init(evch_dlist_t * hp)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
evch_dl_fini(evch_dlist_t * hp)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
evch_dl_is_init(evch_dlist_t * hp)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
evch_dl_add(evch_dlist_t * hp,evch_dlelem_t * el)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
evch_dl_del(evch_dlist_t * hp,evch_dlelem_t * p)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 *
evch_dl_search(evch_dlist_t * hp,int (* cmp)(evch_dlelem_t *,char *),char * s)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
evch_dl_getnum(evch_dlist_t * hp)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 *
evch_dl_next(evch_dlist_t * hp,void * el)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
evch_q_init(evch_squeue_t * q)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
evch_q_in(evch_squeue_t * q,evch_qelem_t * el)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 *
evch_q_out(evch_squeue_t * q)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 *
evch_q_next(evch_squeue_t * q,evch_qelem_t * el)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 *
evch_zoneinit(zoneid_t zoneid)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
evch_zonefree(zoneid_t zoneid,void * arg)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);
32311102SGavin.Maltby@Sun.COM 		ASSERT(evch_dl_getnum(&chp->ch_subscr) != 0 ||
32411102SGavin.Maltby@Sun.COM 		    chp->ch_holdpend == CH_HOLD_PEND_INDEF);
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 		/* Forcibly unsubscribe each remaining subscription */
3270Sstevel@tonic-gate 		while ((sdp = evch_dl_next(&chp->ch_subscr, NULL)) != NULL) {
3280Sstevel@tonic-gate 			/*
3290Sstevel@tonic-gate 			 * We should only be tearing down persistent
3300Sstevel@tonic-gate 			 * subscribers at this point, since all processes
3310Sstevel@tonic-gate 			 * from this zone are gone.
3320Sstevel@tonic-gate 			 */
3330Sstevel@tonic-gate 			ASSERT(sdp->sd_active == 0);
3340Sstevel@tonic-gate 			ASSERT((sdp->sd_persist & EVCH_SUB_KEEP) != 0);
3350Sstevel@tonic-gate 			/*
3360Sstevel@tonic-gate 			 * Disconnect subscriber queue from main event queue.
3370Sstevel@tonic-gate 			 */
3380Sstevel@tonic-gate 			evch_evq_unsub(chp->ch_queue, sdp->sd_msub);
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 			/* Destruct per subscriber queue */
3410Sstevel@tonic-gate 			evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub);
3420Sstevel@tonic-gate 			evch_evq_destroy(sdp->sd_queue);
3430Sstevel@tonic-gate 			/*
3440Sstevel@tonic-gate 			 * Eliminate the subscriber data from channel list.
3450Sstevel@tonic-gate 			 */
3460Sstevel@tonic-gate 			evch_dl_del(&chp->ch_subscr, &sdp->sd_link);
3470Sstevel@tonic-gate 			kmem_free(sdp->sd_classname, sdp->sd_clnsize);
3480Sstevel@tonic-gate 			kmem_free(sdp->sd_ident, strlen(sdp->sd_ident) + 1);
3490Sstevel@tonic-gate 			kmem_free(sdp, sizeof (evch_subd_t));
3500Sstevel@tonic-gate 		}
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 		/* Channel must now have no subscribers */
3530Sstevel@tonic-gate 		ASSERT(evch_dl_getnum(&chp->ch_subscr) == 0);
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 		/* Just like unbind */
3560Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
3570Sstevel@tonic-gate 		evch_dl_del(&eg->evch_list, &chp->ch_link);
3580Sstevel@tonic-gate 		evch_evq_destroy(chp->ch_queue);
3590Sstevel@tonic-gate 		mutex_destroy(&chp->ch_mutex);
3600Sstevel@tonic-gate 		mutex_destroy(&chp->ch_pubmx);
3610Sstevel@tonic-gate 		cv_destroy(&chp->ch_pubcv);
3620Sstevel@tonic-gate 		kmem_free(chp->ch_name, chp->ch_namelen);
3630Sstevel@tonic-gate 		kmem_free(chp, sizeof (evch_chan_t));
3640Sstevel@tonic-gate 	}
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
3670Sstevel@tonic-gate 	/* all channels should now be gone */
3680Sstevel@tonic-gate 	ASSERT(evch_dl_getnum(&eg->evch_list) == 0);
3690Sstevel@tonic-gate 	kmem_free(eg, sizeof (*eg));
3700Sstevel@tonic-gate }
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate /*
3730Sstevel@tonic-gate  * Frees evch_gevent_t structure including the payload, if the reference count
3740Sstevel@tonic-gate  * drops to or below zero. Below zero happens when the event is freed
3750Sstevel@tonic-gate  * without beeing queued into a queue.
3760Sstevel@tonic-gate  */
3770Sstevel@tonic-gate static void
evch_gevent_free(evch_gevent_t * evp)3780Sstevel@tonic-gate evch_gevent_free(evch_gevent_t *evp)
3790Sstevel@tonic-gate {
3800Sstevel@tonic-gate 	int32_t refcnt;
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	refcnt = (int32_t)atomic_add_32_nv(&evp->ge_refcount, -1);
3830Sstevel@tonic-gate 	if (refcnt <= 0) {
3840Sstevel@tonic-gate 		if (evp->ge_destruct != NULL) {
3850Sstevel@tonic-gate 			evp->ge_destruct((void *)&(evp->ge_payload),
3860Sstevel@tonic-gate 			    evp->ge_dstcookie);
3870Sstevel@tonic-gate 		}
3880Sstevel@tonic-gate 		kmem_free(evp, evp->ge_size);
3890Sstevel@tonic-gate 	}
3900Sstevel@tonic-gate }
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate /*
3930Sstevel@tonic-gate  * Deliver is called for every subscription to the current event
3940Sstevel@tonic-gate  * It calls the registered filter function and then the registered delivery
3950Sstevel@tonic-gate  * callback routine. Returns 0 on success. The callback routine returns
3960Sstevel@tonic-gate  * EVQ_AGAIN or EVQ_SLEEP in case the event could not be delivered.
3970Sstevel@tonic-gate  */
3980Sstevel@tonic-gate static int
evch_deliver(evch_evqsub_t * sp,evch_gevent_t * ep)3990Sstevel@tonic-gate evch_deliver(evch_evqsub_t *sp, evch_gevent_t *ep)
4000Sstevel@tonic-gate {
4010Sstevel@tonic-gate 	void		*uep = &ep->ge_payload;
4020Sstevel@tonic-gate 	int		res = EVQ_DELIVER;
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 	if (sp->su_filter != NULL) {
4050Sstevel@tonic-gate 		res = sp->su_filter(uep, sp->su_fcookie);
4060Sstevel@tonic-gate 	}
4070Sstevel@tonic-gate 	if (res == EVQ_DELIVER) {
4080Sstevel@tonic-gate 		return (sp->su_callb(uep, sp->su_cbcookie));
4090Sstevel@tonic-gate 	}
4100Sstevel@tonic-gate 	return (0);
4110Sstevel@tonic-gate }
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate /*
4140Sstevel@tonic-gate  * Holds event delivery in case of eq_holdmode set or in case the
4150Sstevel@tonic-gate  * event queue is empty. Mutex must be held when called.
4160Sstevel@tonic-gate  * Wakes up a thread waiting for the delivery thread reaching the hold mode.
4170Sstevel@tonic-gate  */
4180Sstevel@tonic-gate static void
evch_delivery_hold(evch_eventq_t * eqp,callb_cpr_t * cpip)4190Sstevel@tonic-gate evch_delivery_hold(evch_eventq_t *eqp, callb_cpr_t *cpip)
4200Sstevel@tonic-gate {
4210Sstevel@tonic-gate 	if (eqp->eq_tabortflag == 0) {
4220Sstevel@tonic-gate 		do {
4230Sstevel@tonic-gate 			if (eqp->eq_holdmode) {
4240Sstevel@tonic-gate 				cv_signal(&eqp->eq_onholdcv);
4250Sstevel@tonic-gate 			}
4260Sstevel@tonic-gate 			CALLB_CPR_SAFE_BEGIN(cpip);
4270Sstevel@tonic-gate 			cv_wait(&eqp->eq_thrsleepcv, &eqp->eq_queuemx);
4280Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(cpip, &eqp->eq_queuemx);
4290Sstevel@tonic-gate 		} while (eqp->eq_holdmode);
4300Sstevel@tonic-gate 	}
4310Sstevel@tonic-gate }
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate /*
4340Sstevel@tonic-gate  * Event delivery thread. Enumerates all subscribers and calls evch_deliver()
4350Sstevel@tonic-gate  * for each one.
4360Sstevel@tonic-gate  */
4370Sstevel@tonic-gate static void
evch_delivery_thr(evch_eventq_t * eqp)4380Sstevel@tonic-gate evch_delivery_thr(evch_eventq_t *eqp)
4390Sstevel@tonic-gate {
4400Sstevel@tonic-gate 	evch_qelem_t	*qep;
4410Sstevel@tonic-gate 	callb_cpr_t	cprinfo;
4420Sstevel@tonic-gate 	int		res;
4430Sstevel@tonic-gate 	evch_evqsub_t	*sub;
4440Sstevel@tonic-gate 	int		deltime;
4450Sstevel@tonic-gate 	int		repeatcount;
4460Sstevel@tonic-gate 	char		thnam[32];
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	(void) snprintf(thnam, sizeof (thnam), "sysevent_chan-%d",
4490Sstevel@tonic-gate 	    (int)eqp->eq_thrid);
4500Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &eqp->eq_queuemx, callb_generic_cpr, thnam);
4510Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
4520Sstevel@tonic-gate 	while (eqp->eq_tabortflag == 0) {
4530Sstevel@tonic-gate 		while (eqp->eq_holdmode == 0 && eqp->eq_tabortflag == 0 &&
4540Sstevel@tonic-gate 		    (qep = evch_q_out(&eqp->eq_eventq)) != NULL) {
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 			/* Filter and deliver event to all subscribers */
4570Sstevel@tonic-gate 			deltime = EVCH_MIN_PAUSE;
4580Sstevel@tonic-gate 			repeatcount = EVCH_MAX_TRY_DELIVERY;
4590Sstevel@tonic-gate 			eqp->eq_curevent = qep->q_objref;
4600Sstevel@tonic-gate 			sub = evch_dl_next(&eqp->eq_subscr, NULL);
4610Sstevel@tonic-gate 			while (sub != NULL) {
4620Sstevel@tonic-gate 				eqp->eq_dactive = 1;
4630Sstevel@tonic-gate 				mutex_exit(&eqp->eq_queuemx);
4640Sstevel@tonic-gate 				res = evch_deliver(sub, qep->q_objref);
4650Sstevel@tonic-gate 				mutex_enter(&eqp->eq_queuemx);
4660Sstevel@tonic-gate 				eqp->eq_dactive = 0;
4670Sstevel@tonic-gate 				cv_signal(&eqp->eq_dactivecv);
4680Sstevel@tonic-gate 				switch (res) {
4690Sstevel@tonic-gate 				case EVQ_SLEEP:
4700Sstevel@tonic-gate 					/*
4710Sstevel@tonic-gate 					 * Wait for subscriber to return.
4720Sstevel@tonic-gate 					 */
4730Sstevel@tonic-gate 					eqp->eq_holdmode = 1;
4740Sstevel@tonic-gate 					evch_delivery_hold(eqp, &cprinfo);
4750Sstevel@tonic-gate 					if (eqp->eq_tabortflag) {
4760Sstevel@tonic-gate 						break;
4770Sstevel@tonic-gate 					}
4780Sstevel@tonic-gate 					continue;
4790Sstevel@tonic-gate 				case EVQ_AGAIN:
4800Sstevel@tonic-gate 					CALLB_CPR_SAFE_BEGIN(&cprinfo);
4810Sstevel@tonic-gate 					mutex_exit(&eqp->eq_queuemx);
4820Sstevel@tonic-gate 					delay(deltime);
4830Sstevel@tonic-gate 					deltime =
4840Sstevel@tonic-gate 					    deltime > EVCH_MAX_PAUSE ?
4850Sstevel@tonic-gate 					    deltime : deltime << 1;
4860Sstevel@tonic-gate 					mutex_enter(&eqp->eq_queuemx);
4870Sstevel@tonic-gate 					CALLB_CPR_SAFE_END(&cprinfo,
4880Sstevel@tonic-gate 					    &eqp->eq_queuemx);
4890Sstevel@tonic-gate 					if (repeatcount-- > 0) {
4900Sstevel@tonic-gate 						continue;
4910Sstevel@tonic-gate 					}
4920Sstevel@tonic-gate 					break;
4930Sstevel@tonic-gate 				}
4940Sstevel@tonic-gate 				if (eqp->eq_tabortflag) {
4950Sstevel@tonic-gate 					break;
4960Sstevel@tonic-gate 				}
4970Sstevel@tonic-gate 				sub = evch_dl_next(&eqp->eq_subscr, sub);
4980Sstevel@tonic-gate 				repeatcount = EVCH_MAX_TRY_DELIVERY;
4990Sstevel@tonic-gate 			}
5000Sstevel@tonic-gate 			eqp->eq_curevent = NULL;
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 			/* Free event data and queue element */
5030Sstevel@tonic-gate 			evch_gevent_free((evch_gevent_t *)qep->q_objref);
5040Sstevel@tonic-gate 			kmem_free(qep, qep->q_objsize);
5050Sstevel@tonic-gate 		}
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 		/* Wait for next event or end of hold mode if set */
5080Sstevel@tonic-gate 		evch_delivery_hold(eqp, &cprinfo);
5090Sstevel@tonic-gate 	}
5100Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);	/* Does mutex_exit of eqp->eq_queuemx */
5110Sstevel@tonic-gate 	thread_exit();
5120Sstevel@tonic-gate }
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate /*
5150Sstevel@tonic-gate  * Create the event delivery thread for an existing event queue.
5160Sstevel@tonic-gate  */
5170Sstevel@tonic-gate static void
evch_evq_thrcreate(evch_eventq_t * eqp)5180Sstevel@tonic-gate evch_evq_thrcreate(evch_eventq_t *eqp)
5190Sstevel@tonic-gate {
5200Sstevel@tonic-gate 	kthread_t *thp;
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	thp = thread_create(NULL, 0, evch_delivery_thr, (char *)eqp, 0, &p0,
5230Sstevel@tonic-gate 	    TS_RUN, minclsyspri);
5240Sstevel@tonic-gate 	eqp->eq_thrid = thp->t_did;
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate /*
5280Sstevel@tonic-gate  * Create event queue.
5290Sstevel@tonic-gate  */
5300Sstevel@tonic-gate static evch_eventq_t *
evch_evq_create()5310Sstevel@tonic-gate evch_evq_create()
5320Sstevel@tonic-gate {
5330Sstevel@tonic-gate 	evch_eventq_t *p;
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 	/* Allocate and initialize event queue descriptor */
5360Sstevel@tonic-gate 	p = kmem_zalloc(sizeof (evch_eventq_t), KM_SLEEP);
5370Sstevel@tonic-gate 	mutex_init(&p->eq_queuemx, NULL, MUTEX_DEFAULT, NULL);
5380Sstevel@tonic-gate 	cv_init(&p->eq_thrsleepcv, NULL, CV_DEFAULT, NULL);
5390Sstevel@tonic-gate 	evch_q_init(&p->eq_eventq);
5400Sstevel@tonic-gate 	evch_dl_init(&p->eq_subscr);
5410Sstevel@tonic-gate 	cv_init(&p->eq_dactivecv, NULL, CV_DEFAULT, NULL);
5420Sstevel@tonic-gate 	cv_init(&p->eq_onholdcv, NULL, CV_DEFAULT, NULL);
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 	/* Create delivery thread */
5450Sstevel@tonic-gate 	if (evq_initcomplete) {
5460Sstevel@tonic-gate 		evch_evq_thrcreate(p);
5470Sstevel@tonic-gate 	}
5480Sstevel@tonic-gate 	return (p);
5490Sstevel@tonic-gate }
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate /*
5520Sstevel@tonic-gate  * Destroy an event queue. All subscribers have to be unsubscribed prior to
5530Sstevel@tonic-gate  * this call.
5540Sstevel@tonic-gate  */
5550Sstevel@tonic-gate static void
evch_evq_destroy(evch_eventq_t * eqp)5560Sstevel@tonic-gate evch_evq_destroy(evch_eventq_t *eqp)
5570Sstevel@tonic-gate {
5580Sstevel@tonic-gate 	evch_qelem_t *qep;
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	ASSERT(evch_dl_getnum(&eqp->eq_subscr) == 0);
5610Sstevel@tonic-gate 	/* Kill delivery thread */
5620Sstevel@tonic-gate 	if (eqp->eq_thrid != NULL) {
5630Sstevel@tonic-gate 		mutex_enter(&eqp->eq_queuemx);
5640Sstevel@tonic-gate 		eqp->eq_tabortflag = 1;
5650Sstevel@tonic-gate 		eqp->eq_holdmode = 0;
5660Sstevel@tonic-gate 		cv_signal(&eqp->eq_thrsleepcv);
5670Sstevel@tonic-gate 		mutex_exit(&eqp->eq_queuemx);
5680Sstevel@tonic-gate 		thread_join(eqp->eq_thrid);
5690Sstevel@tonic-gate 	}
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	/* Get rid of stale events in the event queue */
5720Sstevel@tonic-gate 	while ((qep = (evch_qelem_t *)evch_q_out(&eqp->eq_eventq)) != NULL) {
5730Sstevel@tonic-gate 		evch_gevent_free((evch_gevent_t *)qep->q_objref);
5740Sstevel@tonic-gate 		kmem_free(qep, qep->q_objsize);
5750Sstevel@tonic-gate 	}
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	/* Wrap up event queue structure */
5780Sstevel@tonic-gate 	cv_destroy(&eqp->eq_onholdcv);
5790Sstevel@tonic-gate 	cv_destroy(&eqp->eq_dactivecv);
5800Sstevel@tonic-gate 	cv_destroy(&eqp->eq_thrsleepcv);
5810Sstevel@tonic-gate 	evch_dl_fini(&eqp->eq_subscr);
5820Sstevel@tonic-gate 	mutex_destroy(&eqp->eq_queuemx);
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	/* Free descriptor structure */
5850Sstevel@tonic-gate 	kmem_free(eqp, sizeof (evch_eventq_t));
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate /*
5890Sstevel@tonic-gate  * Subscribe to an event queue. Every subscriber provides a filter callback
5900Sstevel@tonic-gate  * routine and an event delivery callback routine.
5910Sstevel@tonic-gate  */
5920Sstevel@tonic-gate static evch_evqsub_t *
evch_evq_sub(evch_eventq_t * eqp,filter_f filter,void * fcookie,deliver_f callb,void * cbcookie)5930Sstevel@tonic-gate evch_evq_sub(evch_eventq_t *eqp, filter_f filter, void *fcookie,
5940Sstevel@tonic-gate     deliver_f callb, void *cbcookie)
5950Sstevel@tonic-gate {
5960Sstevel@tonic-gate 	evch_evqsub_t *sp = kmem_zalloc(sizeof (evch_evqsub_t), KM_SLEEP);
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	/* Initialize subscriber structure */
5990Sstevel@tonic-gate 	sp->su_filter = filter;
6000Sstevel@tonic-gate 	sp->su_fcookie = fcookie;
6010Sstevel@tonic-gate 	sp->su_callb = callb;
6020Sstevel@tonic-gate 	sp->su_cbcookie = cbcookie;
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate 	/* Add subscription to queue */
6050Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
6060Sstevel@tonic-gate 	evch_dl_add(&eqp->eq_subscr, &sp->su_link);
6070Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
6080Sstevel@tonic-gate 	return (sp);
6090Sstevel@tonic-gate }
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate /*
6120Sstevel@tonic-gate  * Unsubscribe from an event queue.
6130Sstevel@tonic-gate  */
6140Sstevel@tonic-gate static void
evch_evq_unsub(evch_eventq_t * eqp,evch_evqsub_t * sp)6150Sstevel@tonic-gate evch_evq_unsub(evch_eventq_t *eqp, evch_evqsub_t *sp)
6160Sstevel@tonic-gate {
6170Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	/* Wait if delivery is just in progress */
6200Sstevel@tonic-gate 	if (eqp->eq_dactive) {
6210Sstevel@tonic-gate 		cv_wait(&eqp->eq_dactivecv, &eqp->eq_queuemx);
6220Sstevel@tonic-gate 	}
6230Sstevel@tonic-gate 	evch_dl_del(&eqp->eq_subscr, &sp->su_link);
6240Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
6250Sstevel@tonic-gate 	kmem_free(sp, sizeof (evch_evqsub_t));
6260Sstevel@tonic-gate }
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate /*
6290Sstevel@tonic-gate  * Publish an event. Returns 0 on success and -1 if memory alloc failed.
6300Sstevel@tonic-gate  */
6310Sstevel@tonic-gate static int
evch_evq_pub(evch_eventq_t * eqp,void * ev,int flags)6320Sstevel@tonic-gate evch_evq_pub(evch_eventq_t *eqp, void *ev, int flags)
6330Sstevel@tonic-gate {
6340Sstevel@tonic-gate 	size_t size;
6350Sstevel@tonic-gate 	evch_qelem_t	*qep;
6360Sstevel@tonic-gate 	evch_gevent_t	*evp = GEVENT(ev);
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	size = sizeof (evch_qelem_t);
6390Sstevel@tonic-gate 	if (flags & EVCH_TRYHARD) {
6400Sstevel@tonic-gate 		qep = kmem_alloc_tryhard(size, &size, KM_NOSLEEP);
6410Sstevel@tonic-gate 	} else {
6420Sstevel@tonic-gate 		qep = kmem_alloc(size, flags & EVCH_NOSLEEP ?
6430Sstevel@tonic-gate 		    KM_NOSLEEP : KM_SLEEP);
6440Sstevel@tonic-gate 	}
6450Sstevel@tonic-gate 	if (qep == NULL) {
6460Sstevel@tonic-gate 		return (-1);
6470Sstevel@tonic-gate 	}
6480Sstevel@tonic-gate 	qep->q_objref = (void *)evp;
6490Sstevel@tonic-gate 	qep->q_objsize = size;
6500Sstevel@tonic-gate 	atomic_add_32(&evp->ge_refcount, 1);
6510Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
6520Sstevel@tonic-gate 	evch_q_in(&eqp->eq_eventq, qep);
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	/* Wakeup delivery thread */
6550Sstevel@tonic-gate 	cv_signal(&eqp->eq_thrsleepcv);
6560Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
6570Sstevel@tonic-gate 	return (0);
6580Sstevel@tonic-gate }
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate /*
6610Sstevel@tonic-gate  * Enter hold mode of an event queue. Event delivery thread stops event
6620Sstevel@tonic-gate  * handling after delivery of current event (if any).
6630Sstevel@tonic-gate  */
6640Sstevel@tonic-gate static void
evch_evq_stop(evch_eventq_t * eqp)6650Sstevel@tonic-gate evch_evq_stop(evch_eventq_t *eqp)
6660Sstevel@tonic-gate {
6670Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
6680Sstevel@tonic-gate 	eqp->eq_holdmode = 1;
6690Sstevel@tonic-gate 	if (evq_initcomplete) {
6700Sstevel@tonic-gate 		cv_signal(&eqp->eq_thrsleepcv);
6710Sstevel@tonic-gate 		cv_wait(&eqp->eq_onholdcv, &eqp->eq_queuemx);
6720Sstevel@tonic-gate 	}
6730Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
6740Sstevel@tonic-gate }
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate /*
6770Sstevel@tonic-gate  * Continue event delivery.
6780Sstevel@tonic-gate  */
6790Sstevel@tonic-gate static void
evch_evq_continue(evch_eventq_t * eqp)6800Sstevel@tonic-gate evch_evq_continue(evch_eventq_t *eqp)
6810Sstevel@tonic-gate {
6820Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
6830Sstevel@tonic-gate 	eqp->eq_holdmode = 0;
6840Sstevel@tonic-gate 	cv_signal(&eqp->eq_thrsleepcv);
6850Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
6860Sstevel@tonic-gate }
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate /*
6890Sstevel@tonic-gate  * Returns status of delivery thread. 0 if running and 1 if on hold.
6900Sstevel@tonic-gate  */
6910Sstevel@tonic-gate static int
evch_evq_status(evch_eventq_t * eqp)6920Sstevel@tonic-gate evch_evq_status(evch_eventq_t *eqp)
6930Sstevel@tonic-gate {
6940Sstevel@tonic-gate 	return (eqp->eq_holdmode);
6950Sstevel@tonic-gate }
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate /*
6980Sstevel@tonic-gate  * Add a destructor function to an event structure.
6990Sstevel@tonic-gate  */
7000Sstevel@tonic-gate static void
evch_evq_evadd_dest(void * ev,destr_f destructor,void * cookie)7010Sstevel@tonic-gate evch_evq_evadd_dest(void *ev, destr_f destructor, void *cookie)
7020Sstevel@tonic-gate {
7030Sstevel@tonic-gate 	evch_gevent_t *evp = GEVENT(ev);
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	evp->ge_destruct = destructor;
7060Sstevel@tonic-gate 	evp->ge_dstcookie = cookie;
7070Sstevel@tonic-gate }
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate /*
7100Sstevel@tonic-gate  * Allocate evch_gevent_t structure. Return address of payload offset of
7110Sstevel@tonic-gate  * evch_gevent_t.  If EVCH_TRYHARD allocation is requested, we use
7120Sstevel@tonic-gate  * kmem_alloc_tryhard to alloc memory of at least paylsize bytes.
7130Sstevel@tonic-gate  *
7140Sstevel@tonic-gate  * If either memory allocation is unsuccessful, we return NULL.
7150Sstevel@tonic-gate  */
7160Sstevel@tonic-gate static void *
evch_evq_evzalloc(size_t paylsize,int flag)7170Sstevel@tonic-gate evch_evq_evzalloc(size_t paylsize, int flag)
7180Sstevel@tonic-gate {
7190Sstevel@tonic-gate 	evch_gevent_t	*evp;
7205516Sgavinm 	size_t		rsize, evsize, ge_size;
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 	rsize = offsetof(evch_gevent_t, ge_payload) + paylsize;
7230Sstevel@tonic-gate 	if (flag & EVCH_TRYHARD) {
7240Sstevel@tonic-gate 		evp = kmem_alloc_tryhard(rsize, &evsize, KM_NOSLEEP);
7255516Sgavinm 		ge_size = evsize;
7260Sstevel@tonic-gate 	} else {
7270Sstevel@tonic-gate 		evp = kmem_alloc(rsize, flag & EVCH_NOSLEEP ? KM_NOSLEEP :
7280Sstevel@tonic-gate 		    KM_SLEEP);
7295516Sgavinm 		ge_size = rsize;
7300Sstevel@tonic-gate 	}
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 	if (evp) {
7335516Sgavinm 		bzero(evp, rsize);
7345516Sgavinm 		evp->ge_size = ge_size;
7350Sstevel@tonic-gate 		return (&evp->ge_payload);
7360Sstevel@tonic-gate 	}
7370Sstevel@tonic-gate 	return (evp);
7380Sstevel@tonic-gate }
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate /*
7410Sstevel@tonic-gate  * Free event structure. Argument ev is address of payload offset.
7420Sstevel@tonic-gate  */
7430Sstevel@tonic-gate static void
evch_evq_evfree(void * ev)7440Sstevel@tonic-gate evch_evq_evfree(void *ev)
7450Sstevel@tonic-gate {
7460Sstevel@tonic-gate 	evch_gevent_free(GEVENT(ev));
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate /*
7500Sstevel@tonic-gate  * Iterate over all events in the event queue. Begin with an event
7510Sstevel@tonic-gate  * which is currently being delivered. No mutexes are grabbed and no
7520Sstevel@tonic-gate  * resources allocated so that this function can be called in panic
7530Sstevel@tonic-gate  * context too. This function has to be called with ev == NULL initially.
7540Sstevel@tonic-gate  * Actually argument ev is only a flag. Internally the member eq_nextev
7550Sstevel@tonic-gate  * is used to determine the next event. But ev allows for the convenient
7560Sstevel@tonic-gate  * use like
7570Sstevel@tonic-gate  *	ev = NULL;
7580Sstevel@tonic-gate  *	while ((ev = evch_evq_evnext(evp, ev)) != NULL) ...
7590Sstevel@tonic-gate  */
7600Sstevel@tonic-gate static void *
evch_evq_evnext(evch_eventq_t * evq,void * ev)7610Sstevel@tonic-gate evch_evq_evnext(evch_eventq_t *evq, void *ev)
7620Sstevel@tonic-gate {
7630Sstevel@tonic-gate 	if (ev == NULL) {
7640Sstevel@tonic-gate 		evq->eq_nextev = NULL;
7650Sstevel@tonic-gate 		if (evq->eq_curevent != NULL)
7660Sstevel@tonic-gate 			return (&evq->eq_curevent->ge_payload);
7670Sstevel@tonic-gate 	}
7680Sstevel@tonic-gate 	evq->eq_nextev = evch_q_next(&evq->eq_eventq, evq->eq_nextev);
7690Sstevel@tonic-gate 	if (evq->eq_nextev == NULL)
7700Sstevel@tonic-gate 		return (NULL);
7710Sstevel@tonic-gate 	return (&((evch_gevent_t *)evq->eq_nextev->q_objref)->ge_payload);
7720Sstevel@tonic-gate }
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate /*
7750Sstevel@tonic-gate  * Channel handling functions. First some support functions. Functions belonging
7760Sstevel@tonic-gate  * to the channel handling interface start with evch_ch. The following functions
7770Sstevel@tonic-gate  * make up the channel handling internal interfaces:
7780Sstevel@tonic-gate  *
7790Sstevel@tonic-gate  * evch_chinit		- Initialize channel handling
7800Sstevel@tonic-gate  * evch_chinitthr	- Second step init: initialize threads
7810Sstevel@tonic-gate  * evch_chbind		- Bind to a channel
7820Sstevel@tonic-gate  * evch_chunbind	- Unbind from a channel
7830Sstevel@tonic-gate  * evch_chsubscribe	- Subscribe to a sysevent class
7840Sstevel@tonic-gate  * evch_chunsubscribe	- Unsubscribe
7850Sstevel@tonic-gate  * evch_chpublish	- Publish an event
7860Sstevel@tonic-gate  * evch_chgetnames	- Get names of all channels
7870Sstevel@tonic-gate  * evch_chgetchdata	- Get data of a channel
7880Sstevel@tonic-gate  * evch_chrdevent_init  - Init event q traversal
7890Sstevel@tonic-gate  * evch_chgetnextev	- Read out events queued for a subscriber
7900Sstevel@tonic-gate  * evch_chrdevent_fini  - Finish event q traversal
7910Sstevel@tonic-gate  */
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate /*
7940Sstevel@tonic-gate  * Compare channel name. Used for evch_dl_search to find a channel with the
7950Sstevel@tonic-gate  * name s.
7960Sstevel@tonic-gate  */
7970Sstevel@tonic-gate static int
evch_namecmp(evch_dlelem_t * ep,char * s)7980Sstevel@tonic-gate evch_namecmp(evch_dlelem_t *ep, char *s)
7990Sstevel@tonic-gate {
8000Sstevel@tonic-gate 	return (strcmp(((evch_chan_t *)ep)->ch_name, s));
8010Sstevel@tonic-gate }
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate /*
80411102SGavin.Maltby@Sun.COM  * Simple wildcarded match test of event class string 'class' to
80511102SGavin.Maltby@Sun.COM  * wildcarded subscription string 'pat'.  Recursive only if
80611102SGavin.Maltby@Sun.COM  * 'pat' includes a wildcard, otherwise essentially just strcmp.
80711102SGavin.Maltby@Sun.COM  */
80811102SGavin.Maltby@Sun.COM static int
evch_clsmatch(char * class,const char * pat)80911102SGavin.Maltby@Sun.COM evch_clsmatch(char *class, const char *pat)
81011102SGavin.Maltby@Sun.COM {
81111102SGavin.Maltby@Sun.COM 	char c;
81211102SGavin.Maltby@Sun.COM 
81311102SGavin.Maltby@Sun.COM 	do {
81411102SGavin.Maltby@Sun.COM 		if ((c = *pat++) == '\0')
81511102SGavin.Maltby@Sun.COM 			return (*class == '\0');
81611102SGavin.Maltby@Sun.COM 
81711102SGavin.Maltby@Sun.COM 		if (c == '*') {
81811102SGavin.Maltby@Sun.COM 			while (*pat == '*')
81911102SGavin.Maltby@Sun.COM 				pat++; /* consecutive *'s can be collapsed */
82011102SGavin.Maltby@Sun.COM 
82111102SGavin.Maltby@Sun.COM 			if (*pat == '\0')
82211102SGavin.Maltby@Sun.COM 				return (1);
82311102SGavin.Maltby@Sun.COM 
82411102SGavin.Maltby@Sun.COM 			while (*class != '\0') {
82511102SGavin.Maltby@Sun.COM 				if (evch_clsmatch(class++, pat) != 0)
82611102SGavin.Maltby@Sun.COM 					return (1);
82711102SGavin.Maltby@Sun.COM 			}
82811102SGavin.Maltby@Sun.COM 
82911102SGavin.Maltby@Sun.COM 			return (0);
83011102SGavin.Maltby@Sun.COM 		}
83111102SGavin.Maltby@Sun.COM 	} while (c == *class++);
83211102SGavin.Maltby@Sun.COM 
83311102SGavin.Maltby@Sun.COM 	return (0);
83411102SGavin.Maltby@Sun.COM }
83511102SGavin.Maltby@Sun.COM 
83611102SGavin.Maltby@Sun.COM /*
8370Sstevel@tonic-gate  * Sysevent filter callback routine. Enables event delivery only if it matches
83811102SGavin.Maltby@Sun.COM  * the event class pattern string given by parameter cookie.
8390Sstevel@tonic-gate  */
8400Sstevel@tonic-gate static int
evch_class_filter(void * ev,void * cookie)8410Sstevel@tonic-gate evch_class_filter(void *ev, void *cookie)
8420Sstevel@tonic-gate {
84311102SGavin.Maltby@Sun.COM 	const char *pat = (const char *)cookie;
8440Sstevel@tonic-gate 
84511102SGavin.Maltby@Sun.COM 	if (pat == NULL || evch_clsmatch(SE_CLASS_NAME(ev), pat))
8460Sstevel@tonic-gate 		return (EVQ_DELIVER);
84711102SGavin.Maltby@Sun.COM 
8480Sstevel@tonic-gate 	return (EVQ_IGNORE);
8490Sstevel@tonic-gate }
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate /*
8520Sstevel@tonic-gate  * Callback routine to propagate the event into a per subscriber queue.
8530Sstevel@tonic-gate  */
8540Sstevel@tonic-gate static int
evch_subq_deliver(void * evp,void * cookie)8550Sstevel@tonic-gate evch_subq_deliver(void *evp, void *cookie)
8560Sstevel@tonic-gate {
8570Sstevel@tonic-gate 	evch_subd_t *p = (evch_subd_t *)cookie;
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	(void) evch_evq_pub(p->sd_queue, evp, EVCH_SLEEP);
8600Sstevel@tonic-gate 	return (EVQ_CONT);
8610Sstevel@tonic-gate }
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate /*
8640Sstevel@tonic-gate  * Call kernel callback routine for sysevent kernel delivery.
8650Sstevel@tonic-gate  */
8660Sstevel@tonic-gate static int
evch_kern_deliver(void * evp,void * cookie)8670Sstevel@tonic-gate evch_kern_deliver(void *evp, void *cookie)
8680Sstevel@tonic-gate {
8690Sstevel@tonic-gate 	sysevent_impl_t	*ev = (sysevent_impl_t *)evp;
8700Sstevel@tonic-gate 	evch_subd_t	*sdp = (evch_subd_t *)cookie;
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 	return (sdp->sd_callback(ev, sdp->sd_cbcookie));
8730Sstevel@tonic-gate }
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate /*
8760Sstevel@tonic-gate  * Door upcall for user land sysevent delivery.
8770Sstevel@tonic-gate  */
8780Sstevel@tonic-gate static int
evch_door_deliver(void * evp,void * cookie)8790Sstevel@tonic-gate evch_door_deliver(void *evp, void *cookie)
8800Sstevel@tonic-gate {
8810Sstevel@tonic-gate 	int		error;
8820Sstevel@tonic-gate 	size_t		size;
8830Sstevel@tonic-gate 	sysevent_impl_t	*ev = (sysevent_impl_t *)evp;
8840Sstevel@tonic-gate 	door_arg_t	darg;
8850Sstevel@tonic-gate 	evch_subd_t	*sdp = (evch_subd_t *)cookie;
8860Sstevel@tonic-gate 	int		nticks = EVCH_MIN_PAUSE;
8870Sstevel@tonic-gate 	uint32_t	retval;
8880Sstevel@tonic-gate 	int		retry = 20;
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 	/* Initialize door args */
8910Sstevel@tonic-gate 	size = sizeof (sysevent_impl_t) + SE_PAYLOAD_SZ(ev);
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	darg.rbuf = (char *)&retval;
8940Sstevel@tonic-gate 	darg.rsize = sizeof (retval);
8950Sstevel@tonic-gate 	darg.data_ptr = (char *)ev;
8960Sstevel@tonic-gate 	darg.data_size = size;
8970Sstevel@tonic-gate 	darg.desc_ptr = NULL;
8980Sstevel@tonic-gate 	darg.desc_num = 0;
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 	for (;;) {
9016997Sjwadams 		if ((error = door_ki_upcall_limited(sdp->sd_door, &darg,
9026997Sjwadams 		    NULL, SIZE_MAX, 0)) == 0) {
9030Sstevel@tonic-gate 			break;
9040Sstevel@tonic-gate 		}
9050Sstevel@tonic-gate 		switch (error) {
9060Sstevel@tonic-gate 		case EAGAIN:
9070Sstevel@tonic-gate 			/* Cannot deliver event - process may be forking */
9080Sstevel@tonic-gate 			delay(nticks);
9090Sstevel@tonic-gate 			nticks <<= 1;
9100Sstevel@tonic-gate 			if (nticks > EVCH_MAX_PAUSE) {
9110Sstevel@tonic-gate 				nticks = EVCH_MAX_PAUSE;
9120Sstevel@tonic-gate 			}
9130Sstevel@tonic-gate 			if (retry-- <= 0) {
9140Sstevel@tonic-gate 				cmn_err(CE_CONT, "event delivery thread: "
9150Sstevel@tonic-gate 				    "door_ki_upcall error EAGAIN\n");
9160Sstevel@tonic-gate 				return (EVQ_CONT);
9170Sstevel@tonic-gate 			}
9180Sstevel@tonic-gate 			break;
9190Sstevel@tonic-gate 		case EINTR:
9200Sstevel@tonic-gate 		case EBADF:
9210Sstevel@tonic-gate 			/* Process died */
9220Sstevel@tonic-gate 			return (EVQ_SLEEP);
9230Sstevel@tonic-gate 		default:
9240Sstevel@tonic-gate 			cmn_err(CE_CONT,
9250Sstevel@tonic-gate 			    "event delivery thread: door_ki_upcall error %d\n",
9260Sstevel@tonic-gate 			    error);
9270Sstevel@tonic-gate 			return (EVQ_CONT);
9280Sstevel@tonic-gate 		}
9290Sstevel@tonic-gate 	}
9300Sstevel@tonic-gate 	if (retval == EAGAIN) {
9310Sstevel@tonic-gate 		return (EVQ_AGAIN);
9320Sstevel@tonic-gate 	}
9330Sstevel@tonic-gate 	return (EVQ_CONT);
9340Sstevel@tonic-gate }
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate /*
9370Sstevel@tonic-gate  * Callback routine for evch_dl_search() to compare subscriber id's. Used by
9380Sstevel@tonic-gate  * evch_subscribe() and evch_chrdevent_init().
9390Sstevel@tonic-gate  */
9400Sstevel@tonic-gate static int
evch_subidcmp(evch_dlelem_t * ep,char * s)9410Sstevel@tonic-gate evch_subidcmp(evch_dlelem_t *ep, char *s)
9420Sstevel@tonic-gate {
9430Sstevel@tonic-gate 	return (strcmp(((evch_subd_t *)ep)->sd_ident, s));
9440Sstevel@tonic-gate }
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate /*
9470Sstevel@tonic-gate  * Callback routine for evch_dl_search() to find a subscriber with EVCH_SUB_DUMP
9480Sstevel@tonic-gate  * set (indicated by sub->sd_dump != 0). Used by evch_chrdevent_init() and
9490Sstevel@tonic-gate  * evch_subscribe(). Needs to returns 0 if subscriber with sd_dump set is
9500Sstevel@tonic-gate  * found.
9510Sstevel@tonic-gate  */
9520Sstevel@tonic-gate /*ARGSUSED1*/
9530Sstevel@tonic-gate static int
evch_dumpflgcmp(evch_dlelem_t * ep,char * s)9540Sstevel@tonic-gate evch_dumpflgcmp(evch_dlelem_t *ep, char *s)
9550Sstevel@tonic-gate {
9560Sstevel@tonic-gate 	return (((evch_subd_t *)ep)->sd_dump ? 0 : 1);
9570Sstevel@tonic-gate }
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate /*
9600Sstevel@tonic-gate  * Event destructor function. Used to maintain the number of events per channel.
9610Sstevel@tonic-gate  */
9620Sstevel@tonic-gate /*ARGSUSED*/
9630Sstevel@tonic-gate static void
evch_destr_event(void * ev,void * ch)9640Sstevel@tonic-gate evch_destr_event(void *ev, void *ch)
9650Sstevel@tonic-gate {
9660Sstevel@tonic-gate 	evch_chan_t *chp = (evch_chan_t *)ch;
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	mutex_enter(&chp->ch_pubmx);
9690Sstevel@tonic-gate 	chp->ch_nevents--;
9700Sstevel@tonic-gate 	cv_signal(&chp->ch_pubcv);
9710Sstevel@tonic-gate 	mutex_exit(&chp->ch_pubmx);
9720Sstevel@tonic-gate }
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate /*
9750Sstevel@tonic-gate  * Integer square root according to Newton's iteration.
9760Sstevel@tonic-gate  */
9770Sstevel@tonic-gate static uint32_t
evch_isqrt(uint64_t n)9780Sstevel@tonic-gate evch_isqrt(uint64_t n)
9790Sstevel@tonic-gate {
9800Sstevel@tonic-gate 	uint64_t	x = n >> 1;
9810Sstevel@tonic-gate 	uint64_t	xn = x - 1;
9820Sstevel@tonic-gate 	static uint32_t	lowval[] = { 0, 1, 1, 2 };
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 	if (n < 4) {
9850Sstevel@tonic-gate 		return (lowval[n]);
9860Sstevel@tonic-gate 	}
9870Sstevel@tonic-gate 	while (xn < x) {
9880Sstevel@tonic-gate 		x = xn;
9890Sstevel@tonic-gate 		xn = (x + n / x) / 2;
9900Sstevel@tonic-gate 	}
9910Sstevel@tonic-gate 	return ((uint32_t)xn);
9920Sstevel@tonic-gate }
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate /*
9950Sstevel@tonic-gate  * First step sysevent channel initialization. Called when kernel memory
9960Sstevel@tonic-gate  * allocator is initialized.
9970Sstevel@tonic-gate  */
9980Sstevel@tonic-gate static void
evch_chinit()9990Sstevel@tonic-gate evch_chinit()
10000Sstevel@tonic-gate {
10010Sstevel@tonic-gate 	size_t k;
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 	/*
10040Sstevel@tonic-gate 	 * Calculate limits: max no of channels and max no of events per
10050Sstevel@tonic-gate 	 * channel. The smallest machine with 128 MByte will allow for
10060Sstevel@tonic-gate 	 * >= 8 channels and an upper limit of 2048 events per channel.
10070Sstevel@tonic-gate 	 * The event limit is the number of channels times 256 (hence
10080Sstevel@tonic-gate 	 * the shift factor of 8). These number where selected arbitrarily.
10090Sstevel@tonic-gate 	 */
10100Sstevel@tonic-gate 	k = kmem_maxavail() >> 20;
10110Sstevel@tonic-gate 	evch_channels_max = min(evch_isqrt(k), EVCH_MAX_CHANNELS);
10120Sstevel@tonic-gate 	evch_events_max = evch_channels_max << 8;
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	/*
10150Sstevel@tonic-gate 	 * Will trigger creation of the global zone's evch state.
10160Sstevel@tonic-gate 	 */
10170Sstevel@tonic-gate 	zone_key_create(&evch_zone_key, evch_zoneinit, NULL, evch_zonefree);
10180Sstevel@tonic-gate }
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate /*
10210Sstevel@tonic-gate  * Second step sysevent channel initialization. Called when threads are ready.
10220Sstevel@tonic-gate  */
10230Sstevel@tonic-gate static void
evch_chinitthr()10240Sstevel@tonic-gate evch_chinitthr()
10250Sstevel@tonic-gate {
10260Sstevel@tonic-gate 	struct evch_globals *eg;
10270Sstevel@tonic-gate 	evch_chan_t	*chp;
10280Sstevel@tonic-gate 	evch_subd_t	*sdp;
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 	/*
10310Sstevel@tonic-gate 	 * We're early enough in boot that we know that only the global
10320Sstevel@tonic-gate 	 * zone exists; we only need to initialize its threads.
10330Sstevel@tonic-gate 	 */
10340Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, global_zone);
10350Sstevel@tonic-gate 	ASSERT(eg != NULL);
10360Sstevel@tonic-gate 
10370Sstevel@tonic-gate 	for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL;
10380Sstevel@tonic-gate 	    chp = evch_dl_next(&eg->evch_list, chp)) {
10390Sstevel@tonic-gate 		for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp;
10400Sstevel@tonic-gate 		    sdp = evch_dl_next(&chp->ch_subscr, sdp)) {
10410Sstevel@tonic-gate 			evch_evq_thrcreate(sdp->sd_queue);
10420Sstevel@tonic-gate 		}
10430Sstevel@tonic-gate 		evch_evq_thrcreate(chp->ch_queue);
10440Sstevel@tonic-gate 	}
10450Sstevel@tonic-gate 	evq_initcomplete = 1;
10460Sstevel@tonic-gate }
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate /*
10490Sstevel@tonic-gate  * Sysevent channel bind. Create channel and allocate binding structure.
10500Sstevel@tonic-gate  */
10510Sstevel@tonic-gate static int
evch_chbind(const char * chnam,evch_bind_t ** scpp,uint32_t flags)10520Sstevel@tonic-gate evch_chbind(const char *chnam, evch_bind_t **scpp, uint32_t flags)
10530Sstevel@tonic-gate {
10540Sstevel@tonic-gate 	struct evch_globals *eg;
10550Sstevel@tonic-gate 	evch_bind_t	*bp;
10560Sstevel@tonic-gate 	evch_chan_t	*p;
10570Sstevel@tonic-gate 	char		*chn;
10580Sstevel@tonic-gate 	size_t		namlen;
10590Sstevel@tonic-gate 	int		rv;
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, curproc->p_zone);
10620Sstevel@tonic-gate 	ASSERT(eg != NULL);
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate 	/* Create channel if it does not exist */
10650Sstevel@tonic-gate 	ASSERT(evch_dl_is_init(&eg->evch_list));
10660Sstevel@tonic-gate 	if ((namlen = strlen(chnam) + 1) > MAX_CHNAME_LEN) {
10670Sstevel@tonic-gate 		return (EINVAL);
10680Sstevel@tonic-gate 	}
10690Sstevel@tonic-gate 	mutex_enter(&eg->evch_list_lock);
10700Sstevel@tonic-gate 	if ((p = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp,
10710Sstevel@tonic-gate 	    (char *)chnam)) == NULL) {
10720Sstevel@tonic-gate 		if (flags & EVCH_CREAT) {
10730Sstevel@tonic-gate 			if (evch_dl_getnum(&eg->evch_list) >=
10740Sstevel@tonic-gate 			    evch_channels_max) {
10750Sstevel@tonic-gate 				mutex_exit(&eg->evch_list_lock);
10760Sstevel@tonic-gate 				return (ENOMEM);
10770Sstevel@tonic-gate 			}
10780Sstevel@tonic-gate 			chn = kmem_alloc(namlen, KM_SLEEP);
10790Sstevel@tonic-gate 			bcopy(chnam, chn, namlen);
10800Sstevel@tonic-gate 
10810Sstevel@tonic-gate 			/* Allocate and initialize channel descriptor */
10820Sstevel@tonic-gate 			p = kmem_zalloc(sizeof (evch_chan_t), KM_SLEEP);
10830Sstevel@tonic-gate 			p->ch_name = chn;
10840Sstevel@tonic-gate 			p->ch_namelen = namlen;
10850Sstevel@tonic-gate 			mutex_init(&p->ch_mutex, NULL, MUTEX_DEFAULT, NULL);
10860Sstevel@tonic-gate 			p->ch_queue = evch_evq_create();
10870Sstevel@tonic-gate 			evch_dl_init(&p->ch_subscr);
10880Sstevel@tonic-gate 			if (evq_initcomplete) {
10890Sstevel@tonic-gate 				p->ch_uid = crgetuid(curthread->t_cred);
10900Sstevel@tonic-gate 				p->ch_gid = crgetgid(curthread->t_cred);
10910Sstevel@tonic-gate 			}
10920Sstevel@tonic-gate 			cv_init(&p->ch_pubcv, NULL, CV_DEFAULT, NULL);
10930Sstevel@tonic-gate 			mutex_init(&p->ch_pubmx, NULL, MUTEX_DEFAULT, NULL);
10940Sstevel@tonic-gate 			p->ch_maxev = min(EVCH_DEFAULT_EVENTS, evch_events_max);
10950Sstevel@tonic-gate 			p->ch_maxsubscr = EVCH_MAX_SUBSCRIPTIONS;
10960Sstevel@tonic-gate 			p->ch_maxbinds = evch_bindings_max;
10970Sstevel@tonic-gate 			p->ch_ctime = gethrestime_sec();
109811102SGavin.Maltby@Sun.COM 
109911102SGavin.Maltby@Sun.COM 			if (flags & (EVCH_HOLD_PEND | EVCH_HOLD_PEND_INDEF)) {
110011102SGavin.Maltby@Sun.COM 				if (flags & EVCH_HOLD_PEND_INDEF)
110111102SGavin.Maltby@Sun.COM 					p->ch_holdpend = CH_HOLD_PEND_INDEF;
110211102SGavin.Maltby@Sun.COM 				else
110311102SGavin.Maltby@Sun.COM 					p->ch_holdpend = CH_HOLD_PEND;
110411102SGavin.Maltby@Sun.COM 
11050Sstevel@tonic-gate 				evch_evq_stop(p->ch_queue);
11060Sstevel@tonic-gate 			}
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 			/* Put new descriptor into channel list */
11090Sstevel@tonic-gate 			evch_dl_add(&eg->evch_list, (evch_dlelem_t *)p);
11100Sstevel@tonic-gate 		} else {
11110Sstevel@tonic-gate 			mutex_exit(&eg->evch_list_lock);
11120Sstevel@tonic-gate 			return (ENOENT);
11130Sstevel@tonic-gate 		}
11140Sstevel@tonic-gate 	}
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate 	/* Check for max binds and create binding */
11170Sstevel@tonic-gate 	mutex_enter(&p->ch_mutex);
11180Sstevel@tonic-gate 	if (p->ch_bindings >= p->ch_maxbinds) {
11190Sstevel@tonic-gate 		rv = ENOMEM;
11200Sstevel@tonic-gate 		/*
11210Sstevel@tonic-gate 		 * No need to destroy the channel because this call did not
11220Sstevel@tonic-gate 		 * create it. Other bindings will be present if ch_maxbinds
11230Sstevel@tonic-gate 		 * is exceeded.
11240Sstevel@tonic-gate 		 */
11250Sstevel@tonic-gate 		goto errorexit;
11260Sstevel@tonic-gate 	}
11270Sstevel@tonic-gate 	bp = kmem_alloc(sizeof (evch_bind_t), KM_SLEEP);
11280Sstevel@tonic-gate 	bp->bd_channel = p;
11290Sstevel@tonic-gate 	bp->bd_sublst = NULL;
11300Sstevel@tonic-gate 	p->ch_bindings++;
11310Sstevel@tonic-gate 	rv = 0;
11320Sstevel@tonic-gate 	*scpp = bp;
11330Sstevel@tonic-gate errorexit:
11340Sstevel@tonic-gate 	mutex_exit(&p->ch_mutex);
11350Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
11360Sstevel@tonic-gate 	return (rv);
11370Sstevel@tonic-gate }
11380Sstevel@tonic-gate 
11390Sstevel@tonic-gate /*
11400Sstevel@tonic-gate  * Unbind: Free bind structure. Remove channel if last binding was freed.
11410Sstevel@tonic-gate  */
11420Sstevel@tonic-gate static void
evch_chunbind(evch_bind_t * bp)11430Sstevel@tonic-gate evch_chunbind(evch_bind_t *bp)
11440Sstevel@tonic-gate {
11450Sstevel@tonic-gate 	struct evch_globals *eg;
11460Sstevel@tonic-gate 	evch_chan_t *chp = bp->bd_channel;
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, curproc->p_zone);
11490Sstevel@tonic-gate 	ASSERT(eg != NULL);
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate 	mutex_enter(&eg->evch_list_lock);
11520Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
11530Sstevel@tonic-gate 	ASSERT(chp->ch_bindings > 0);
11540Sstevel@tonic-gate 	chp->ch_bindings--;
11550Sstevel@tonic-gate 	kmem_free(bp, sizeof (evch_bind_t));
115611102SGavin.Maltby@Sun.COM 	if (chp->ch_bindings == 0 && evch_dl_getnum(&chp->ch_subscr) == 0 &&
115711102SGavin.Maltby@Sun.COM 	    (chp->ch_nevents == 0 || chp->ch_holdpend != CH_HOLD_PEND_INDEF)) {
11580Sstevel@tonic-gate 		/*
115911102SGavin.Maltby@Sun.COM 		 * No more bindings and no persistent subscriber(s).  If there
116011102SGavin.Maltby@Sun.COM 		 * are no events in the channel then destroy the channel;
116111102SGavin.Maltby@Sun.COM 		 * otherwise destroy the channel only if we're not holding
116211102SGavin.Maltby@Sun.COM 		 * pending events indefinitely.
11630Sstevel@tonic-gate 		 */
11640Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
11650Sstevel@tonic-gate 		evch_dl_del(&eg->evch_list, &chp->ch_link);
11660Sstevel@tonic-gate 		evch_evq_destroy(chp->ch_queue);
1167*12967Sgavin.maltby@oracle.com 		if (chp->ch_propnvl)
1168*12967Sgavin.maltby@oracle.com 			nvlist_free(chp->ch_propnvl);
11690Sstevel@tonic-gate 		mutex_destroy(&chp->ch_mutex);
11700Sstevel@tonic-gate 		mutex_destroy(&chp->ch_pubmx);
11710Sstevel@tonic-gate 		cv_destroy(&chp->ch_pubcv);
11720Sstevel@tonic-gate 		kmem_free(chp->ch_name, chp->ch_namelen);
11730Sstevel@tonic-gate 		kmem_free(chp, sizeof (evch_chan_t));
11740Sstevel@tonic-gate 	} else
11750Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
11760Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
11770Sstevel@tonic-gate }
11780Sstevel@tonic-gate 
117911102SGavin.Maltby@Sun.COM static int
wildcard_count(const char * class)118011102SGavin.Maltby@Sun.COM wildcard_count(const char *class)
118111102SGavin.Maltby@Sun.COM {
118211102SGavin.Maltby@Sun.COM 	int count = 0;
118311102SGavin.Maltby@Sun.COM 	char c;
118411102SGavin.Maltby@Sun.COM 
118511102SGavin.Maltby@Sun.COM 	if (class == NULL)
118611102SGavin.Maltby@Sun.COM 		return (0);
118711102SGavin.Maltby@Sun.COM 
118811102SGavin.Maltby@Sun.COM 	while ((c = *class++) != '\0') {
118911102SGavin.Maltby@Sun.COM 		if (c == '*')
119011102SGavin.Maltby@Sun.COM 			count++;
119111102SGavin.Maltby@Sun.COM 	}
119211102SGavin.Maltby@Sun.COM 
119311102SGavin.Maltby@Sun.COM 	return (count);
119411102SGavin.Maltby@Sun.COM }
119511102SGavin.Maltby@Sun.COM 
11960Sstevel@tonic-gate /*
11970Sstevel@tonic-gate  * Subscribe to a channel. dtype is either EVCH_DELKERN for kernel callbacks
11980Sstevel@tonic-gate  * or EVCH_DELDOOR for door upcall delivery to user land. Depending on dtype
11990Sstevel@tonic-gate  * dinfo gives the call back routine address or the door handle.
12000Sstevel@tonic-gate  */
12010Sstevel@tonic-gate static int
evch_chsubscribe(evch_bind_t * bp,int dtype,const char * sid,const char * class,void * dinfo,void * cookie,int flags,pid_t pid)12020Sstevel@tonic-gate evch_chsubscribe(evch_bind_t *bp, int dtype, const char *sid, const char *class,
12030Sstevel@tonic-gate     void *dinfo, void *cookie, int flags, pid_t pid)
12040Sstevel@tonic-gate {
12050Sstevel@tonic-gate 	evch_chan_t	*chp = bp->bd_channel;
12060Sstevel@tonic-gate 	evch_eventq_t	*eqp = chp->ch_queue;
12070Sstevel@tonic-gate 	evch_subd_t	*sdp;
12080Sstevel@tonic-gate 	evch_subd_t	*esp;
12090Sstevel@tonic-gate 	int		(*delivfkt)();
12100Sstevel@tonic-gate 	char		*clb = NULL;
12110Sstevel@tonic-gate 	int		clblen = 0;
12120Sstevel@tonic-gate 	char		*subid;
12130Sstevel@tonic-gate 	int		subidblen;
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 	/*
12160Sstevel@tonic-gate 	 * Check if only known flags are set.
12170Sstevel@tonic-gate 	 */
12180Sstevel@tonic-gate 	if (flags & ~(EVCH_SUB_KEEP | EVCH_SUB_DUMP))
12190Sstevel@tonic-gate 		return (EINVAL);
122011102SGavin.Maltby@Sun.COM 
122111102SGavin.Maltby@Sun.COM 	/*
122211102SGavin.Maltby@Sun.COM 	 * Enforce a limit on the number of wildcards allowed in the class
122311102SGavin.Maltby@Sun.COM 	 * subscription string (limits recursion in pattern matching).
122411102SGavin.Maltby@Sun.COM 	 */
122511102SGavin.Maltby@Sun.COM 	if (wildcard_count(class) > EVCH_WILDCARD_MAX)
122611102SGavin.Maltby@Sun.COM 		return (EINVAL);
122711102SGavin.Maltby@Sun.COM 
12280Sstevel@tonic-gate 	/*
12290Sstevel@tonic-gate 	 * Check if we have already a subscription with that name and if we
12300Sstevel@tonic-gate 	 * have to reconnect the subscriber to a persistent subscription.
12310Sstevel@tonic-gate 	 */
12320Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
12330Sstevel@tonic-gate 	if ((esp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr,
12340Sstevel@tonic-gate 	    evch_subidcmp, (char *)sid)) != NULL) {
12350Sstevel@tonic-gate 		int error = 0;
12360Sstevel@tonic-gate 		if ((flags & EVCH_SUB_KEEP) && (esp->sd_active == 0)) {
12370Sstevel@tonic-gate 			/*
12380Sstevel@tonic-gate 			 * Subscription with the name on hold, reconnect to
12390Sstevel@tonic-gate 			 * existing queue.
12400Sstevel@tonic-gate 			 */
12410Sstevel@tonic-gate 			ASSERT(dtype == EVCH_DELDOOR);
12420Sstevel@tonic-gate 			esp->sd_subnxt = bp->bd_sublst;
12430Sstevel@tonic-gate 			bp->bd_sublst = esp;
12440Sstevel@tonic-gate 			esp->sd_pid = pid;
12450Sstevel@tonic-gate 			esp->sd_door = (door_handle_t)dinfo;
12460Sstevel@tonic-gate 			esp->sd_active++;
12470Sstevel@tonic-gate 			evch_evq_continue(esp->sd_queue);
12480Sstevel@tonic-gate 		} else {
12490Sstevel@tonic-gate 			/* Subscriber with given name already exists */
12500Sstevel@tonic-gate 			error = EEXIST;
12510Sstevel@tonic-gate 		}
12520Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
12530Sstevel@tonic-gate 		return (error);
12540Sstevel@tonic-gate 	}
12550Sstevel@tonic-gate 
12560Sstevel@tonic-gate 	if (evch_dl_getnum(&chp->ch_subscr) >= chp->ch_maxsubscr) {
12570Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
12580Sstevel@tonic-gate 		return (ENOMEM);
12590Sstevel@tonic-gate 	}
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 	if (flags & EVCH_SUB_DUMP && evch_dl_search(&chp->ch_subscr,
12620Sstevel@tonic-gate 	    evch_dumpflgcmp, NULL) != NULL) {
12630Sstevel@tonic-gate 		/*
12640Sstevel@tonic-gate 		 * Subscription with EVCH_SUB_DUMP flagged already exists.
12650Sstevel@tonic-gate 		 * Only one subscription with EVCH_SUB_DUMP possible. Return
12660Sstevel@tonic-gate 		 * error.
12670Sstevel@tonic-gate 		 */
12680Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
12690Sstevel@tonic-gate 		return (EINVAL);
12700Sstevel@tonic-gate 	}
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 	if (class != NULL) {
12730Sstevel@tonic-gate 		clblen = strlen(class) + 1;
12740Sstevel@tonic-gate 		clb = kmem_alloc(clblen, KM_SLEEP);
12750Sstevel@tonic-gate 		bcopy(class, clb, clblen);
12760Sstevel@tonic-gate 	}
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate 	subidblen = strlen(sid) + 1;
12790Sstevel@tonic-gate 	subid = kmem_alloc(subidblen, KM_SLEEP);
12800Sstevel@tonic-gate 	bcopy(sid, subid, subidblen);
12810Sstevel@tonic-gate 
12820Sstevel@tonic-gate 	/* Create per subscriber queue */
12830Sstevel@tonic-gate 	sdp = kmem_zalloc(sizeof (evch_subd_t), KM_SLEEP);
12840Sstevel@tonic-gate 	sdp->sd_queue = evch_evq_create();
12850Sstevel@tonic-gate 
12860Sstevel@tonic-gate 	/* Subscribe to subscriber queue */
12870Sstevel@tonic-gate 	sdp->sd_persist = flags & EVCH_SUB_KEEP ? 1 : 0;
12880Sstevel@tonic-gate 	sdp->sd_dump = flags & EVCH_SUB_DUMP ? 1 : 0;
12890Sstevel@tonic-gate 	sdp->sd_type = dtype;
12900Sstevel@tonic-gate 	sdp->sd_cbcookie = cookie;
12910Sstevel@tonic-gate 	sdp->sd_ident = subid;
12920Sstevel@tonic-gate 	if (dtype == EVCH_DELKERN) {
12930Sstevel@tonic-gate 		sdp->sd_callback = (kerndlv_f)dinfo;
12940Sstevel@tonic-gate 		delivfkt = evch_kern_deliver;
12950Sstevel@tonic-gate 	} else {
12960Sstevel@tonic-gate 		sdp->sd_door = (door_handle_t)dinfo;
12970Sstevel@tonic-gate 		delivfkt = evch_door_deliver;
12980Sstevel@tonic-gate 	}
12990Sstevel@tonic-gate 	sdp->sd_ssub =
13000Sstevel@tonic-gate 	    evch_evq_sub(sdp->sd_queue, NULL, NULL, delivfkt, (void *)sdp);
13010Sstevel@tonic-gate 
13020Sstevel@tonic-gate 	/* Connect per subscriber queue to main event queue */
13030Sstevel@tonic-gate 	sdp->sd_msub = evch_evq_sub(eqp, evch_class_filter, clb,
13040Sstevel@tonic-gate 	    evch_subq_deliver, (void *)sdp);
13050Sstevel@tonic-gate 	sdp->sd_classname = clb;
13060Sstevel@tonic-gate 	sdp->sd_clnsize = clblen;
13070Sstevel@tonic-gate 	sdp->sd_pid = pid;
13080Sstevel@tonic-gate 	sdp->sd_active++;
13090Sstevel@tonic-gate 
13100Sstevel@tonic-gate 	/* Add subscription to binding */
13110Sstevel@tonic-gate 	sdp->sd_subnxt = bp->bd_sublst;
13120Sstevel@tonic-gate 	bp->bd_sublst = sdp;
13130Sstevel@tonic-gate 
13140Sstevel@tonic-gate 	/* Add subscription to channel */
13150Sstevel@tonic-gate 	evch_dl_add(&chp->ch_subscr, &sdp->sd_link);
13160Sstevel@tonic-gate 	if (chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 1) {
13170Sstevel@tonic-gate 
13180Sstevel@tonic-gate 		/* Let main event queue run in case of HOLDPEND */
13190Sstevel@tonic-gate 		evch_evq_continue(eqp);
13200Sstevel@tonic-gate 	}
13210Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate 	return (0);
13240Sstevel@tonic-gate }
13250Sstevel@tonic-gate 
13260Sstevel@tonic-gate /*
13270Sstevel@tonic-gate  * If flag == EVCH_SUB_KEEP only non-persistent subscriptions are deleted.
13280Sstevel@tonic-gate  * When sid == NULL all subscriptions except the ones with EVCH_SUB_KEEP set
13290Sstevel@tonic-gate  * are removed.
13300Sstevel@tonic-gate  */
13310Sstevel@tonic-gate static void
evch_chunsubscribe(evch_bind_t * bp,const char * sid,uint32_t flags)13320Sstevel@tonic-gate evch_chunsubscribe(evch_bind_t *bp, const char *sid, uint32_t flags)
13330Sstevel@tonic-gate {
13340Sstevel@tonic-gate 	evch_subd_t	*sdp;
13350Sstevel@tonic-gate 	evch_subd_t	*next;
13360Sstevel@tonic-gate 	evch_subd_t	*prev;
13370Sstevel@tonic-gate 	evch_chan_t	*chp = bp->bd_channel;
13380Sstevel@tonic-gate 
13390Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
13400Sstevel@tonic-gate 	if (chp->ch_holdpend) {
13410Sstevel@tonic-gate 		evch_evq_stop(chp->ch_queue);	/* Hold main event queue */
13420Sstevel@tonic-gate 	}
13430Sstevel@tonic-gate 	prev = NULL;
13440Sstevel@tonic-gate 	for (sdp = bp->bd_sublst; sdp; sdp = next) {
13450Sstevel@tonic-gate 		if (sid == NULL || strcmp(sid, sdp->sd_ident) == 0) {
13460Sstevel@tonic-gate 			if (flags == 0 || sdp->sd_persist == 0) {
13470Sstevel@tonic-gate 				/*
13480Sstevel@tonic-gate 				 * Disconnect subscriber queue from main event
13490Sstevel@tonic-gate 				 * queue.
13500Sstevel@tonic-gate 				 */
13510Sstevel@tonic-gate 				evch_evq_unsub(chp->ch_queue, sdp->sd_msub);
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 				/* Destruct per subscriber queue */
13540Sstevel@tonic-gate 				evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub);
13550Sstevel@tonic-gate 				evch_evq_destroy(sdp->sd_queue);
13560Sstevel@tonic-gate 				/*
13570Sstevel@tonic-gate 				 * Eliminate the subscriber data from channel
13580Sstevel@tonic-gate 				 * list.
13590Sstevel@tonic-gate 				 */
13600Sstevel@tonic-gate 				evch_dl_del(&chp->ch_subscr, &sdp->sd_link);
13610Sstevel@tonic-gate 				kmem_free(sdp->sd_classname, sdp->sd_clnsize);
13620Sstevel@tonic-gate 				if (sdp->sd_type == EVCH_DELDOOR) {
13630Sstevel@tonic-gate 					door_ki_rele(sdp->sd_door);
13640Sstevel@tonic-gate 				}
13650Sstevel@tonic-gate 				next = sdp->sd_subnxt;
13660Sstevel@tonic-gate 				if (prev) {
13670Sstevel@tonic-gate 					prev->sd_subnxt = next;
13680Sstevel@tonic-gate 				} else {
13690Sstevel@tonic-gate 					bp->bd_sublst = next;
13700Sstevel@tonic-gate 				}
13710Sstevel@tonic-gate 				kmem_free(sdp->sd_ident,
13720Sstevel@tonic-gate 				    strlen(sdp->sd_ident) + 1);
13730Sstevel@tonic-gate 				kmem_free(sdp, sizeof (evch_subd_t));
13740Sstevel@tonic-gate 			} else {
13750Sstevel@tonic-gate 				/*
13760Sstevel@tonic-gate 				 * EVCH_SUB_KEEP case
13770Sstevel@tonic-gate 				 */
13780Sstevel@tonic-gate 				evch_evq_stop(sdp->sd_queue);
13790Sstevel@tonic-gate 				if (sdp->sd_type == EVCH_DELDOOR) {
13800Sstevel@tonic-gate 					door_ki_rele(sdp->sd_door);
13810Sstevel@tonic-gate 				}
13820Sstevel@tonic-gate 				sdp->sd_active--;
13830Sstevel@tonic-gate 				ASSERT(sdp->sd_active == 0);
13840Sstevel@tonic-gate 				next = sdp->sd_subnxt;
13850Sstevel@tonic-gate 				prev = sdp;
13860Sstevel@tonic-gate 			}
13870Sstevel@tonic-gate 			if (sid != NULL) {
13880Sstevel@tonic-gate 				break;
13890Sstevel@tonic-gate 			}
13900Sstevel@tonic-gate 		} else {
13910Sstevel@tonic-gate 			next = sdp->sd_subnxt;
13920Sstevel@tonic-gate 			prev = sdp;
13930Sstevel@tonic-gate 		}
13940Sstevel@tonic-gate 	}
13950Sstevel@tonic-gate 	if (!(chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 0)) {
13960Sstevel@tonic-gate 		/*
13970Sstevel@tonic-gate 		 * Continue dispatch thread except if no subscribers are present
13980Sstevel@tonic-gate 		 * in HOLDPEND mode.
13990Sstevel@tonic-gate 		 */
14000Sstevel@tonic-gate 		evch_evq_continue(chp->ch_queue);
14010Sstevel@tonic-gate 	}
14020Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
14030Sstevel@tonic-gate }
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate /*
14060Sstevel@tonic-gate  * Publish an event. Returns zero on success and an error code else.
14070Sstevel@tonic-gate  */
14080Sstevel@tonic-gate static int
evch_chpublish(evch_bind_t * bp,sysevent_impl_t * ev,int flags)14090Sstevel@tonic-gate evch_chpublish(evch_bind_t *bp, sysevent_impl_t *ev, int flags)
14100Sstevel@tonic-gate {
14110Sstevel@tonic-gate 	evch_chan_t *chp = bp->bd_channel;
14120Sstevel@tonic-gate 
14133490Seschrock 	DTRACE_SYSEVENT2(post, evch_bind_t *, bp, sysevent_impl_t *, ev);
14143490Seschrock 
14150Sstevel@tonic-gate 	mutex_enter(&chp->ch_pubmx);
14160Sstevel@tonic-gate 	if (chp->ch_nevents >= chp->ch_maxev) {
14170Sstevel@tonic-gate 		if (!(flags & EVCH_QWAIT)) {
14180Sstevel@tonic-gate 			evch_evq_evfree(ev);
14190Sstevel@tonic-gate 			mutex_exit(&chp->ch_pubmx);
14200Sstevel@tonic-gate 			return (EAGAIN);
14210Sstevel@tonic-gate 		} else {
14220Sstevel@tonic-gate 			while (chp->ch_nevents >= chp->ch_maxev) {
14230Sstevel@tonic-gate 				if (cv_wait_sig(&chp->ch_pubcv,
14240Sstevel@tonic-gate 				    &chp->ch_pubmx) == 0) {
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate 					/* Got Signal, return EINTR */
14270Sstevel@tonic-gate 					evch_evq_evfree(ev);
14280Sstevel@tonic-gate 					mutex_exit(&chp->ch_pubmx);
14290Sstevel@tonic-gate 					return (EINTR);
14300Sstevel@tonic-gate 				}
14310Sstevel@tonic-gate 			}
14320Sstevel@tonic-gate 		}
14330Sstevel@tonic-gate 	}
14340Sstevel@tonic-gate 	chp->ch_nevents++;
14350Sstevel@tonic-gate 	mutex_exit(&chp->ch_pubmx);
14360Sstevel@tonic-gate 	SE_TIME(ev) = gethrtime();
14370Sstevel@tonic-gate 	SE_SEQ(ev) = log_sysevent_new_id();
14380Sstevel@tonic-gate 	/*
14390Sstevel@tonic-gate 	 * Add the destructor function to the event structure, now that the
14400Sstevel@tonic-gate 	 * event is accounted for. The only task of the descructor is to
14410Sstevel@tonic-gate 	 * decrement the channel event count. The evq_*() routines (including
14420Sstevel@tonic-gate 	 * the event delivery thread) do not have knowledge of the channel
14430Sstevel@tonic-gate 	 * data. So the anonymous destructor handles the channel data for it.
14440Sstevel@tonic-gate 	 */
14450Sstevel@tonic-gate 	evch_evq_evadd_dest(ev, evch_destr_event, (void *)chp);
14460Sstevel@tonic-gate 	return (evch_evq_pub(chp->ch_queue, ev, flags) == 0 ? 0 : EAGAIN);
14470Sstevel@tonic-gate }
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate /*
14500Sstevel@tonic-gate  * Fills a buffer consecutive with the names of all available channels.
14510Sstevel@tonic-gate  * Returns the length of all name strings or -1 if buffer size was unsufficient.
14520Sstevel@tonic-gate  */
14530Sstevel@tonic-gate static int
evch_chgetnames(char * buf,size_t size)14540Sstevel@tonic-gate evch_chgetnames(char *buf, size_t size)
14550Sstevel@tonic-gate {
14560Sstevel@tonic-gate 	struct evch_globals *eg;
14570Sstevel@tonic-gate 	int		len = 0;
14580Sstevel@tonic-gate 	char		*addr = buf;
14590Sstevel@tonic-gate 	int		max = size;
14600Sstevel@tonic-gate 	evch_chan_t	*chp;
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, curproc->p_zone);
14630Sstevel@tonic-gate 	ASSERT(eg != NULL);
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 	mutex_enter(&eg->evch_list_lock);
14660Sstevel@tonic-gate 	for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL;
14670Sstevel@tonic-gate 	    chp = evch_dl_next(&eg->evch_list, chp)) {
14680Sstevel@tonic-gate 		len += chp->ch_namelen;
14690Sstevel@tonic-gate 		if (len >= max) {
14700Sstevel@tonic-gate 			mutex_exit(&eg->evch_list_lock);
14710Sstevel@tonic-gate 			return (-1);
14720Sstevel@tonic-gate 		}
14730Sstevel@tonic-gate 		bcopy(chp->ch_name, addr, chp->ch_namelen);
14740Sstevel@tonic-gate 		addr += chp->ch_namelen;
14750Sstevel@tonic-gate 	}
14760Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
14770Sstevel@tonic-gate 	addr[0] = 0;
14780Sstevel@tonic-gate 	return (len + 1);
14790Sstevel@tonic-gate }
14800Sstevel@tonic-gate 
14810Sstevel@tonic-gate /*
14820Sstevel@tonic-gate  * Fills the data of one channel and all subscribers of that channel into
14830Sstevel@tonic-gate  * a buffer. Returns -1 if the channel name is invalid and 0 on buffer overflow.
14840Sstevel@tonic-gate  */
14850Sstevel@tonic-gate static int
evch_chgetchdata(char * chname,void * buf,size_t size)14860Sstevel@tonic-gate evch_chgetchdata(char *chname, void *buf, size_t size)
14870Sstevel@tonic-gate {
14880Sstevel@tonic-gate 	struct evch_globals *eg;
14890Sstevel@tonic-gate 	char		*cpaddr;
14900Sstevel@tonic-gate 	int		bufmax;
14910Sstevel@tonic-gate 	int		buflen;
14920Sstevel@tonic-gate 	evch_chan_t	*chp;
14930Sstevel@tonic-gate 	sev_chinfo_t	*p = (sev_chinfo_t *)buf;
14940Sstevel@tonic-gate 	int		chdlen;
14950Sstevel@tonic-gate 	evch_subd_t	*sdp;
14960Sstevel@tonic-gate 	sev_subinfo_t	*subp;
14970Sstevel@tonic-gate 	int		idlen;
14980Sstevel@tonic-gate 	int		len;
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, curproc->p_zone);
15010Sstevel@tonic-gate 	ASSERT(eg != NULL);
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate 	mutex_enter(&eg->evch_list_lock);
15040Sstevel@tonic-gate 	chp = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp,
15050Sstevel@tonic-gate 	    chname);
15060Sstevel@tonic-gate 	if (chp == NULL) {
15070Sstevel@tonic-gate 		mutex_exit(&eg->evch_list_lock);
15080Sstevel@tonic-gate 		return (-1);
15090Sstevel@tonic-gate 	}
15100Sstevel@tonic-gate 	chdlen = offsetof(sev_chinfo_t, cd_subinfo);
15110Sstevel@tonic-gate 	if (size < chdlen) {
15120Sstevel@tonic-gate 		mutex_exit(&eg->evch_list_lock);
15130Sstevel@tonic-gate 		return (0);
15140Sstevel@tonic-gate 	}
15150Sstevel@tonic-gate 	p->cd_version = 0;
15160Sstevel@tonic-gate 	p->cd_suboffs = chdlen;
15170Sstevel@tonic-gate 	p->cd_uid = chp->ch_uid;
15180Sstevel@tonic-gate 	p->cd_gid = chp->ch_gid;
15190Sstevel@tonic-gate 	p->cd_perms = 0;
15200Sstevel@tonic-gate 	p->cd_ctime = chp->ch_ctime;
15210Sstevel@tonic-gate 	p->cd_maxev = chp->ch_maxev;
15220Sstevel@tonic-gate 	p->cd_evhwm = EVCH_EVQ_HIGHWM(chp->ch_queue);
15230Sstevel@tonic-gate 	p->cd_nevents = EVCH_EVQ_EVCOUNT(chp->ch_queue);
15240Sstevel@tonic-gate 	p->cd_maxsub = chp->ch_maxsubscr;
15250Sstevel@tonic-gate 	p->cd_nsub = evch_dl_getnum(&chp->ch_subscr);
15260Sstevel@tonic-gate 	p->cd_maxbinds = chp->ch_maxbinds;
15270Sstevel@tonic-gate 	p->cd_nbinds = chp->ch_bindings;
15280Sstevel@tonic-gate 	p->cd_holdpend = chp->ch_holdpend;
15290Sstevel@tonic-gate 	p->cd_limev = evch_events_max;
15300Sstevel@tonic-gate 	cpaddr = (char *)p + chdlen;
15310Sstevel@tonic-gate 	bufmax = size - chdlen;
15320Sstevel@tonic-gate 	buflen = 0;
15330Sstevel@tonic-gate 
15340Sstevel@tonic-gate 	for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp != NULL;
15350Sstevel@tonic-gate 	    sdp = evch_dl_next(&chp->ch_subscr, sdp)) {
15360Sstevel@tonic-gate 		idlen = strlen(sdp->sd_ident) + 1;
15370Sstevel@tonic-gate 		len = SE_ALIGN(offsetof(sev_subinfo_t, sb_strings) + idlen +
15380Sstevel@tonic-gate 		    sdp->sd_clnsize);
15390Sstevel@tonic-gate 		buflen += len;
15400Sstevel@tonic-gate 		if (buflen >= bufmax) {
15410Sstevel@tonic-gate 			mutex_exit(&eg->evch_list_lock);
15420Sstevel@tonic-gate 			return (0);
15430Sstevel@tonic-gate 		}
15440Sstevel@tonic-gate 		subp = (sev_subinfo_t *)cpaddr;
15450Sstevel@tonic-gate 		subp->sb_nextoff = len;
15460Sstevel@tonic-gate 		subp->sb_stroff = offsetof(sev_subinfo_t, sb_strings);
15470Sstevel@tonic-gate 		if (sdp->sd_classname) {
15480Sstevel@tonic-gate 			bcopy(sdp->sd_classname, subp->sb_strings + idlen,
15490Sstevel@tonic-gate 			    sdp->sd_clnsize);
15500Sstevel@tonic-gate 			subp->sb_clnamoff = idlen;
15510Sstevel@tonic-gate 		} else {
15520Sstevel@tonic-gate 			subp->sb_clnamoff = idlen - 1;
15530Sstevel@tonic-gate 		}
15540Sstevel@tonic-gate 		subp->sb_pid = sdp->sd_pid;
15550Sstevel@tonic-gate 		subp->sb_nevents = EVCH_EVQ_EVCOUNT(sdp->sd_queue);
15560Sstevel@tonic-gate 		subp->sb_evhwm = EVCH_EVQ_HIGHWM(sdp->sd_queue);
15570Sstevel@tonic-gate 		subp->sb_persist = sdp->sd_persist;
15580Sstevel@tonic-gate 		subp->sb_status = evch_evq_status(sdp->sd_queue);
15590Sstevel@tonic-gate 		subp->sb_active = sdp->sd_active;
15600Sstevel@tonic-gate 		subp->sb_dump = sdp->sd_dump;
15610Sstevel@tonic-gate 		bcopy(sdp->sd_ident, subp->sb_strings, idlen);
15620Sstevel@tonic-gate 		cpaddr += len;
15630Sstevel@tonic-gate 	}
15640Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
15650Sstevel@tonic-gate 	return (chdlen + buflen);
15660Sstevel@tonic-gate }
15670Sstevel@tonic-gate 
1568*12967Sgavin.maltby@oracle.com static void
evch_chsetpropnvl(evch_bind_t * bp,nvlist_t * nvl)1569*12967Sgavin.maltby@oracle.com evch_chsetpropnvl(evch_bind_t *bp, nvlist_t *nvl)
1570*12967Sgavin.maltby@oracle.com {
1571*12967Sgavin.maltby@oracle.com 	evch_chan_t *chp = bp->bd_channel;
1572*12967Sgavin.maltby@oracle.com 
1573*12967Sgavin.maltby@oracle.com 	mutex_enter(&chp->ch_mutex);
1574*12967Sgavin.maltby@oracle.com 
1575*12967Sgavin.maltby@oracle.com 	if (chp->ch_propnvl)
1576*12967Sgavin.maltby@oracle.com 		nvlist_free(chp->ch_propnvl);
1577*12967Sgavin.maltby@oracle.com 
1578*12967Sgavin.maltby@oracle.com 	chp->ch_propnvl = nvl;
1579*12967Sgavin.maltby@oracle.com 	chp->ch_propnvlgen++;
1580*12967Sgavin.maltby@oracle.com 
1581*12967Sgavin.maltby@oracle.com 	mutex_exit(&chp->ch_mutex);
1582*12967Sgavin.maltby@oracle.com }
1583*12967Sgavin.maltby@oracle.com 
1584*12967Sgavin.maltby@oracle.com static int
evch_chgetpropnvl(evch_bind_t * bp,nvlist_t ** nvlp,int64_t * genp)1585*12967Sgavin.maltby@oracle.com evch_chgetpropnvl(evch_bind_t *bp, nvlist_t **nvlp, int64_t *genp)
1586*12967Sgavin.maltby@oracle.com {
1587*12967Sgavin.maltby@oracle.com 	evch_chan_t *chp = bp->bd_channel;
1588*12967Sgavin.maltby@oracle.com 	int rc = 0;
1589*12967Sgavin.maltby@oracle.com 
1590*12967Sgavin.maltby@oracle.com 	mutex_enter(&chp->ch_mutex);
1591*12967Sgavin.maltby@oracle.com 
1592*12967Sgavin.maltby@oracle.com 	if (chp->ch_propnvl != NULL)
1593*12967Sgavin.maltby@oracle.com 		rc = (nvlist_dup(chp->ch_propnvl, nvlp, 0) == 0) ? 0 : ENOMEM;
1594*12967Sgavin.maltby@oracle.com 	else
1595*12967Sgavin.maltby@oracle.com 		*nvlp = NULL;	/* rc still 0 */
1596*12967Sgavin.maltby@oracle.com 
1597*12967Sgavin.maltby@oracle.com 	if (genp)
1598*12967Sgavin.maltby@oracle.com 		*genp = chp->ch_propnvlgen;
1599*12967Sgavin.maltby@oracle.com 
1600*12967Sgavin.maltby@oracle.com 	mutex_exit(&chp->ch_mutex);
1601*12967Sgavin.maltby@oracle.com 
1602*12967Sgavin.maltby@oracle.com 	if (rc != 0)
1603*12967Sgavin.maltby@oracle.com 		*nvlp = NULL;
1604*12967Sgavin.maltby@oracle.com 
1605*12967Sgavin.maltby@oracle.com 	return (rc);
1606*12967Sgavin.maltby@oracle.com 
1607*12967Sgavin.maltby@oracle.com }
1608*12967Sgavin.maltby@oracle.com 
16090Sstevel@tonic-gate /*
16100Sstevel@tonic-gate  * Init iteration of all events of a channel. This function creates a new
16110Sstevel@tonic-gate  * event queue and puts all events from the channel into that queue.
16120Sstevel@tonic-gate  * Subsequent calls to evch_chgetnextev will deliver the events from that
16130Sstevel@tonic-gate  * queue. Only one thread per channel is allowed to read through the events.
16140Sstevel@tonic-gate  * Returns 0 on success and 1 if there is already someone reading the
16150Sstevel@tonic-gate  * events.
16160Sstevel@tonic-gate  * If argument subid == NULL, we look for a subscriber which has
16170Sstevel@tonic-gate  * flag EVCH_SUB_DUMP set.
16180Sstevel@tonic-gate  */
16190Sstevel@tonic-gate /*
16200Sstevel@tonic-gate  * Static variables that are used to traverse events of a channel in panic case.
16210Sstevel@tonic-gate  */
16220Sstevel@tonic-gate static evch_chan_t	*evch_chan;
16230Sstevel@tonic-gate static evch_eventq_t	*evch_subq;
16240Sstevel@tonic-gate static sysevent_impl_t	*evch_curev;
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate static evchanq_t *
evch_chrdevent_init(evch_chan_t * chp,char * subid)16270Sstevel@tonic-gate evch_chrdevent_init(evch_chan_t *chp, char *subid)
16280Sstevel@tonic-gate {
16290Sstevel@tonic-gate 	evch_subd_t	*sdp;
16300Sstevel@tonic-gate 	void		*ev;
16310Sstevel@tonic-gate 	int		pmqstat;	/* Prev status of main queue */
16320Sstevel@tonic-gate 	int		psqstat;	/* Prev status of subscriber queue */
16330Sstevel@tonic-gate 	evchanq_t	*snp;		/* Pointer to q with snapshot of ev */
16340Sstevel@tonic-gate 	compare_f	compfunc;
16350Sstevel@tonic-gate 
16360Sstevel@tonic-gate 	compfunc = subid == NULL ? evch_dumpflgcmp : evch_subidcmp;
16370Sstevel@tonic-gate 	if (panicstr != NULL) {
16380Sstevel@tonic-gate 		evch_chan = chp;
16390Sstevel@tonic-gate 		evch_subq = NULL;
16400Sstevel@tonic-gate 		evch_curev = NULL;
16410Sstevel@tonic-gate 		if ((sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr,
16420Sstevel@tonic-gate 		    compfunc, subid)) != NULL) {
16430Sstevel@tonic-gate 			evch_subq = sdp->sd_queue;
16440Sstevel@tonic-gate 		}
16450Sstevel@tonic-gate 		return (NULL);
16460Sstevel@tonic-gate 	}
16470Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
16480Sstevel@tonic-gate 	sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr, compfunc, subid);
16490Sstevel@tonic-gate 	/*
16500Sstevel@tonic-gate 	 * Stop main event queue and subscriber queue if not already
16510Sstevel@tonic-gate 	 * in stop mode.
16520Sstevel@tonic-gate 	 */
16530Sstevel@tonic-gate 	pmqstat = evch_evq_status(chp->ch_queue);
16540Sstevel@tonic-gate 	if (pmqstat == 0)
16550Sstevel@tonic-gate 		evch_evq_stop(chp->ch_queue);
16560Sstevel@tonic-gate 	if (sdp != NULL) {
16570Sstevel@tonic-gate 		psqstat = evch_evq_status(sdp->sd_queue);
16580Sstevel@tonic-gate 		if (psqstat == 0)
16590Sstevel@tonic-gate 			evch_evq_stop(sdp->sd_queue);
16600Sstevel@tonic-gate 	}
16610Sstevel@tonic-gate 	/*
16620Sstevel@tonic-gate 	 * Create event queue to make a snapshot of all events in the
16630Sstevel@tonic-gate 	 * channel.
16640Sstevel@tonic-gate 	 */
16650Sstevel@tonic-gate 	snp = kmem_alloc(sizeof (evchanq_t), KM_SLEEP);
16660Sstevel@tonic-gate 	snp->sn_queue = evch_evq_create();
16670Sstevel@tonic-gate 	evch_evq_stop(snp->sn_queue);
16680Sstevel@tonic-gate 	/*
16690Sstevel@tonic-gate 	 * Make a snapshot of the subscriber queue and the main event queue.
16700Sstevel@tonic-gate 	 */
16710Sstevel@tonic-gate 	if (sdp != NULL) {
16720Sstevel@tonic-gate 		ev = NULL;
16730Sstevel@tonic-gate 		while ((ev = evch_evq_evnext(sdp->sd_queue, ev)) != NULL) {
16740Sstevel@tonic-gate 			(void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP);
16750Sstevel@tonic-gate 		}
16760Sstevel@tonic-gate 	}
16770Sstevel@tonic-gate 	ev = NULL;
16780Sstevel@tonic-gate 	while ((ev = evch_evq_evnext(chp->ch_queue, ev)) != NULL) {
16790Sstevel@tonic-gate 		(void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP);
16800Sstevel@tonic-gate 	}
16810Sstevel@tonic-gate 	snp->sn_nxtev = NULL;
16820Sstevel@tonic-gate 	/*
16830Sstevel@tonic-gate 	 * Restart main and subscriber queue if previously stopped
16840Sstevel@tonic-gate 	 */
16850Sstevel@tonic-gate 	if (sdp != NULL && psqstat == 0)
16860Sstevel@tonic-gate 		evch_evq_continue(sdp->sd_queue);
16870Sstevel@tonic-gate 	if (pmqstat == 0)
16880Sstevel@tonic-gate 		evch_evq_continue(chp->ch_queue);
16890Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
16900Sstevel@tonic-gate 	return (snp);
16910Sstevel@tonic-gate }
16920Sstevel@tonic-gate 
16930Sstevel@tonic-gate /*
16940Sstevel@tonic-gate  * Free all resources of the event queue snapshot. In case of panic
16950Sstevel@tonic-gate  * context snp must be NULL and no resources need to be free'ed.
16960Sstevel@tonic-gate  */
16970Sstevel@tonic-gate static void
evch_chrdevent_fini(evchanq_t * snp)16980Sstevel@tonic-gate evch_chrdevent_fini(evchanq_t *snp)
16990Sstevel@tonic-gate {
17000Sstevel@tonic-gate 	if (snp != NULL) {
17010Sstevel@tonic-gate 		evch_evq_destroy(snp->sn_queue);
17020Sstevel@tonic-gate 		kmem_free(snp, sizeof (evchanq_t));
17030Sstevel@tonic-gate 	}
17040Sstevel@tonic-gate }
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate /*
17070Sstevel@tonic-gate  * Get address of next event from an event channel.
17080Sstevel@tonic-gate  * This function might be called in a panic context. In that case
17090Sstevel@tonic-gate  * no resources will be allocated and no locks grabbed.
17100Sstevel@tonic-gate  * In normal operation context a snapshot of the event queues of the
17110Sstevel@tonic-gate  * specified event channel will be taken.
17120Sstevel@tonic-gate  */
17130Sstevel@tonic-gate static sysevent_impl_t *
evch_chgetnextev(evchanq_t * snp)17140Sstevel@tonic-gate evch_chgetnextev(evchanq_t *snp)
17150Sstevel@tonic-gate {
17160Sstevel@tonic-gate 	if (panicstr != NULL) {
17170Sstevel@tonic-gate 		if (evch_chan == NULL)
17180Sstevel@tonic-gate 			return (NULL);
17190Sstevel@tonic-gate 		if (evch_subq != NULL) {
17200Sstevel@tonic-gate 			/*
17210Sstevel@tonic-gate 			 * We have a subscriber queue. Traverse this queue
17220Sstevel@tonic-gate 			 * first.
17230Sstevel@tonic-gate 			 */
17240Sstevel@tonic-gate 			if ((evch_curev = (sysevent_impl_t *)
17250Sstevel@tonic-gate 			    evch_evq_evnext(evch_subq, evch_curev)) != NULL) {
17260Sstevel@tonic-gate 				return (evch_curev);
17270Sstevel@tonic-gate 			} else {
17280Sstevel@tonic-gate 				/*
17290Sstevel@tonic-gate 				 * All subscriber events traversed. evch_subq
17300Sstevel@tonic-gate 				 * == NULL indicates to take the main event
17310Sstevel@tonic-gate 				 * queue now.
17320Sstevel@tonic-gate 				 */
17330Sstevel@tonic-gate 				evch_subq = NULL;
17340Sstevel@tonic-gate 			}
17350Sstevel@tonic-gate 		}
17360Sstevel@tonic-gate 		/*
17370Sstevel@tonic-gate 		 * Traverse the main event queue.
17380Sstevel@tonic-gate 		 */
17390Sstevel@tonic-gate 		if ((evch_curev = (sysevent_impl_t *)
17400Sstevel@tonic-gate 		    evch_evq_evnext(evch_chan->ch_queue, evch_curev)) ==
17410Sstevel@tonic-gate 		    NULL) {
17420Sstevel@tonic-gate 			evch_chan = NULL;
17430Sstevel@tonic-gate 		}
17440Sstevel@tonic-gate 		return (evch_curev);
17450Sstevel@tonic-gate 	}
17460Sstevel@tonic-gate 	ASSERT(snp != NULL);
17470Sstevel@tonic-gate 	snp->sn_nxtev = (sysevent_impl_t *)evch_evq_evnext(snp->sn_queue,
17480Sstevel@tonic-gate 	    snp->sn_nxtev);
17490Sstevel@tonic-gate 	return (snp->sn_nxtev);
17500Sstevel@tonic-gate }
17510Sstevel@tonic-gate 
17520Sstevel@tonic-gate /*
17530Sstevel@tonic-gate  * The functions below build up the interface for the kernel to bind/unbind,
17540Sstevel@tonic-gate  * subscribe/unsubscribe and publish to event channels. It consists of the
17550Sstevel@tonic-gate  * following functions:
17560Sstevel@tonic-gate  *
17570Sstevel@tonic-gate  * sysevent_evc_bind	    - Bind to a channel. Create a channel if required
17580Sstevel@tonic-gate  * sysevent_evc_unbind	    - Unbind from a channel. Destroy ch. if last unbind
17590Sstevel@tonic-gate  * sysevent_evc_subscribe   - Subscribe to events from a channel
17600Sstevel@tonic-gate  * sysevent_evc_unsubscribe - Unsubscribe from an event class
17610Sstevel@tonic-gate  * sysevent_evc_publish	    - Publish an event to an event channel
17620Sstevel@tonic-gate  * sysevent_evc_control	    - Various control operation on event channel
1763*12967Sgavin.maltby@oracle.com  * sysevent_evc_setpropnvl  - Set channel property nvlist
1764*12967Sgavin.maltby@oracle.com  * sysevent_evc_getpropnvl  - Get channel property nvlist
17650Sstevel@tonic-gate  *
17660Sstevel@tonic-gate  * The function below are for evaluating a sysevent:
17670Sstevel@tonic-gate  *
17680Sstevel@tonic-gate  * sysevent_get_class_name  - Get pointer to event class string
17690Sstevel@tonic-gate  * sysevent_get_subclass_name - Get pointer to event subclass string
17700Sstevel@tonic-gate  * sysevent_get_seq	    - Get unique event sequence number
17710Sstevel@tonic-gate  * sysevent_get_time	    - Get hrestime of event publish
17720Sstevel@tonic-gate  * sysevent_get_size	    - Get size of event structure
17730Sstevel@tonic-gate  * sysevent_get_pub	    - Get publisher string
17740Sstevel@tonic-gate  * sysevent_get_attr_list   - Get copy of attribute list
17750Sstevel@tonic-gate  *
17760Sstevel@tonic-gate  * The following interfaces represent stability level project privat
17770Sstevel@tonic-gate  * and allow to save the events of an event channel even in a panic case.
17780Sstevel@tonic-gate  *
17790Sstevel@tonic-gate  * sysevent_evc_walk_init   - Take a snapshot of the events in a channel
17800Sstevel@tonic-gate  * sysevent_evc_walk_step   - Read next event from snapshot
17810Sstevel@tonic-gate  * sysevent_evc_walk_fini   - Free resources from event channel snapshot
17820Sstevel@tonic-gate  * sysevent_evc_event_attr  - Get event payload address and size
17830Sstevel@tonic-gate  */
17840Sstevel@tonic-gate /*
17850Sstevel@tonic-gate  * allocate sysevent structure with optional space for attributes
17860Sstevel@tonic-gate  */
17870Sstevel@tonic-gate static sysevent_impl_t *
sysevent_evc_alloc(const char * class,const char * subclass,const char * pub,size_t pub_sz,size_t atsz,uint32_t flag)17880Sstevel@tonic-gate sysevent_evc_alloc(const char *class, const char *subclass, const char *pub,
17890Sstevel@tonic-gate     size_t pub_sz, size_t atsz, uint32_t flag)
17900Sstevel@tonic-gate {
17910Sstevel@tonic-gate 	int		payload_sz;
17920Sstevel@tonic-gate 	int		class_sz, subclass_sz;
17930Sstevel@tonic-gate 	int 		aligned_class_sz, aligned_subclass_sz, aligned_pub_sz;
17940Sstevel@tonic-gate 	sysevent_impl_t	*ev;
17950Sstevel@tonic-gate 
17960Sstevel@tonic-gate 	/*
17970Sstevel@tonic-gate 	 * Calculate and reserve space for the class, subclass and
17980Sstevel@tonic-gate 	 * publisher strings in the event buffer
17990Sstevel@tonic-gate 	 */
18000Sstevel@tonic-gate 	class_sz = strlen(class) + 1;
18010Sstevel@tonic-gate 	subclass_sz = strlen(subclass) + 1;
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 	ASSERT((class_sz <= MAX_CLASS_LEN) && (subclass_sz <=
18040Sstevel@tonic-gate 	    MAX_SUBCLASS_LEN) && (pub_sz <= MAX_PUB_LEN));
18050Sstevel@tonic-gate 
18060Sstevel@tonic-gate 	/* String sizes must be 64-bit aligned in the event buffer */
18070Sstevel@tonic-gate 	aligned_class_sz = SE_ALIGN(class_sz);
18080Sstevel@tonic-gate 	aligned_subclass_sz = SE_ALIGN(subclass_sz);
18090Sstevel@tonic-gate 	aligned_pub_sz = SE_ALIGN(pub_sz);
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate 	/*
18120Sstevel@tonic-gate 	 * Calculate payload size. Consider the space needed for alignment
18130Sstevel@tonic-gate 	 * and subtract the size of the uint64_t placeholder variables of
18140Sstevel@tonic-gate 	 * sysevent_impl_t.
18150Sstevel@tonic-gate 	 */
18160Sstevel@tonic-gate 	payload_sz = (aligned_class_sz - sizeof (uint64_t)) +
18170Sstevel@tonic-gate 	    (aligned_subclass_sz - sizeof (uint64_t)) +
18180Sstevel@tonic-gate 	    (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) +
18190Sstevel@tonic-gate 	    atsz;
18200Sstevel@tonic-gate 
18210Sstevel@tonic-gate 	/*
18220Sstevel@tonic-gate 	 * Allocate event buffer plus additional payload overhead
18230Sstevel@tonic-gate 	 */
18240Sstevel@tonic-gate 	if ((ev = evch_evq_evzalloc(sizeof (sysevent_impl_t) +
18250Sstevel@tonic-gate 	    payload_sz, flag)) == NULL) {
18260Sstevel@tonic-gate 		return (NULL);
18270Sstevel@tonic-gate 	}
18280Sstevel@tonic-gate 
18290Sstevel@tonic-gate 	/* Initialize the event buffer data */
18300Sstevel@tonic-gate 	SE_VERSION(ev) = SYS_EVENT_VERSION;
18310Sstevel@tonic-gate 	bcopy(class, SE_CLASS_NAME(ev), class_sz);
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate 	SE_SUBCLASS_OFF(ev) = SE_ALIGN(offsetof(sysevent_impl_t,
18340Sstevel@tonic-gate 	    se_class_name)) + aligned_class_sz;
18350Sstevel@tonic-gate 	bcopy(subclass, SE_SUBCLASS_NAME(ev), subclass_sz);
18360Sstevel@tonic-gate 
18370Sstevel@tonic-gate 	SE_PUB_OFF(ev) = SE_SUBCLASS_OFF(ev) + aligned_subclass_sz;
18380Sstevel@tonic-gate 	bcopy(pub, SE_PUB_NAME(ev), pub_sz);
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate 	SE_ATTR_PTR(ev) = (uint64_t)0;
18410Sstevel@tonic-gate 	SE_PAYLOAD_SZ(ev) = payload_sz;
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 	return (ev);
18440Sstevel@tonic-gate }
18450Sstevel@tonic-gate 
18460Sstevel@tonic-gate /*
18470Sstevel@tonic-gate  * Initialize event channel handling queues.
18480Sstevel@tonic-gate  */
18490Sstevel@tonic-gate void
sysevent_evc_init()18500Sstevel@tonic-gate sysevent_evc_init()
18510Sstevel@tonic-gate {
18520Sstevel@tonic-gate 	evch_chinit();
18530Sstevel@tonic-gate }
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate /*
18560Sstevel@tonic-gate  * Second initialization step: create threads, if event channels are already
18570Sstevel@tonic-gate  * created
18580Sstevel@tonic-gate  */
18590Sstevel@tonic-gate void
sysevent_evc_thrinit()18600Sstevel@tonic-gate sysevent_evc_thrinit()
18610Sstevel@tonic-gate {
18620Sstevel@tonic-gate 	evch_chinitthr();
18630Sstevel@tonic-gate }
18640Sstevel@tonic-gate 
18650Sstevel@tonic-gate int
sysevent_evc_bind(const char * ch_name,evchan_t ** scpp,uint32_t flags)18660Sstevel@tonic-gate sysevent_evc_bind(const char *ch_name, evchan_t **scpp, uint32_t flags)
18670Sstevel@tonic-gate {
18680Sstevel@tonic-gate 	ASSERT(ch_name != NULL && scpp != NULL);
18690Sstevel@tonic-gate 	ASSERT((flags & ~EVCH_B_FLAGS) == 0);
18700Sstevel@tonic-gate 	return (evch_chbind(ch_name, (evch_bind_t **)scpp, flags));
18710Sstevel@tonic-gate }
18720Sstevel@tonic-gate 
187311102SGavin.Maltby@Sun.COM int
sysevent_evc_unbind(evchan_t * scp)18740Sstevel@tonic-gate sysevent_evc_unbind(evchan_t *scp)
18750Sstevel@tonic-gate {
18760Sstevel@tonic-gate 	evch_bind_t *bp = (evch_bind_t *)scp;
18770Sstevel@tonic-gate 
18780Sstevel@tonic-gate 	ASSERT(scp != NULL);
18790Sstevel@tonic-gate 	evch_chunsubscribe(bp, NULL, 0);
18800Sstevel@tonic-gate 	evch_chunbind(bp);
188111102SGavin.Maltby@Sun.COM 
188211102SGavin.Maltby@Sun.COM 	return (0);
18830Sstevel@tonic-gate }
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate int
sysevent_evc_subscribe(evchan_t * scp,const char * sid,const char * class,int (* callb)(sysevent_t * ev,void * cookie),void * cookie,uint32_t flags)18860Sstevel@tonic-gate sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class,
18870Sstevel@tonic-gate     int (*callb)(sysevent_t *ev, void *cookie),
18880Sstevel@tonic-gate     void *cookie, uint32_t flags)
18890Sstevel@tonic-gate {
18900Sstevel@tonic-gate 	ASSERT(scp != NULL && sid != NULL && class != NULL && callb != NULL);
18910Sstevel@tonic-gate 	ASSERT(flags == 0);
18920Sstevel@tonic-gate 	if (strlen(sid) > MAX_SUBID_LEN) {
18930Sstevel@tonic-gate 		return (EINVAL);
18940Sstevel@tonic-gate 	}
18950Sstevel@tonic-gate 	if (strcmp(class, EC_ALL) == 0) {
18960Sstevel@tonic-gate 		class = NULL;
18970Sstevel@tonic-gate 	}
18980Sstevel@tonic-gate 	return (evch_chsubscribe((evch_bind_t *)scp, EVCH_DELKERN, sid, class,
18990Sstevel@tonic-gate 	    (void *)callb, cookie, 0, 0));
19000Sstevel@tonic-gate }
19010Sstevel@tonic-gate 
190211102SGavin.Maltby@Sun.COM int
sysevent_evc_unsubscribe(evchan_t * scp,const char * sid)19030Sstevel@tonic-gate sysevent_evc_unsubscribe(evchan_t *scp, const char *sid)
19040Sstevel@tonic-gate {
19050Sstevel@tonic-gate 	ASSERT(scp != NULL && sid != NULL);
19060Sstevel@tonic-gate 	if (strcmp(sid, EVCH_ALLSUB) == 0) {
19070Sstevel@tonic-gate 		sid = NULL;
19080Sstevel@tonic-gate 	}
19090Sstevel@tonic-gate 	evch_chunsubscribe((evch_bind_t *)scp, sid, 0);
191011102SGavin.Maltby@Sun.COM 
191111102SGavin.Maltby@Sun.COM 	return (0);
19120Sstevel@tonic-gate }
19130Sstevel@tonic-gate 
19140Sstevel@tonic-gate /*
19150Sstevel@tonic-gate  * Publish kernel event. Returns 0 on success, error code else.
19160Sstevel@tonic-gate  * Optional attribute data is packed into the event structure.
19170Sstevel@tonic-gate  */
19180Sstevel@tonic-gate int
sysevent_evc_publish(evchan_t * scp,const char * class,const char * subclass,const char * vendor,const char * pubs,nvlist_t * attr,uint32_t flags)19190Sstevel@tonic-gate sysevent_evc_publish(evchan_t *scp, const char *class, const char *subclass,
19200Sstevel@tonic-gate     const char *vendor, const char *pubs, nvlist_t *attr, uint32_t flags)
19210Sstevel@tonic-gate {
19220Sstevel@tonic-gate 	sysevent_impl_t	*evp;
19230Sstevel@tonic-gate 	char		pub[MAX_PUB_LEN];
19240Sstevel@tonic-gate 	int		pub_sz;		/* includes terminating 0 */
19250Sstevel@tonic-gate 	int		km_flags;
19260Sstevel@tonic-gate 	size_t		asz = 0;
19270Sstevel@tonic-gate 	uint64_t	attr_offset;
19280Sstevel@tonic-gate 	caddr_t		patt;
19290Sstevel@tonic-gate 	int		err;
19300Sstevel@tonic-gate 
19310Sstevel@tonic-gate 	ASSERT(scp != NULL && class != NULL && subclass != NULL &&
19320Sstevel@tonic-gate 	    vendor != NULL && pubs != NULL);
19330Sstevel@tonic-gate 
19340Sstevel@tonic-gate 	ASSERT((flags & ~(EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD |
19350Sstevel@tonic-gate 	    EVCH_QWAIT)) == 0);
19360Sstevel@tonic-gate 
19370Sstevel@tonic-gate 	km_flags = flags & (EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD);
19380Sstevel@tonic-gate 	ASSERT(km_flags == EVCH_SLEEP || km_flags == EVCH_NOSLEEP ||
19390Sstevel@tonic-gate 	    km_flags == EVCH_TRYHARD);
19400Sstevel@tonic-gate 
19410Sstevel@tonic-gate 	pub_sz = snprintf(pub, MAX_PUB_LEN, "%s:kern:%s", vendor, pubs) + 1;
19420Sstevel@tonic-gate 	if (pub_sz > MAX_PUB_LEN)
19430Sstevel@tonic-gate 		return (EINVAL);
19440Sstevel@tonic-gate 
19450Sstevel@tonic-gate 	if (attr != NULL) {
19460Sstevel@tonic-gate 		if ((err = nvlist_size(attr, &asz, NV_ENCODE_NATIVE)) != 0) {
19470Sstevel@tonic-gate 			return (err);
19480Sstevel@tonic-gate 		}
19490Sstevel@tonic-gate 	}
19500Sstevel@tonic-gate 	evp = sysevent_evc_alloc(class, subclass, pub, pub_sz, asz, km_flags);
19510Sstevel@tonic-gate 	if (evp == NULL) {
19520Sstevel@tonic-gate 		return (ENOMEM);
19530Sstevel@tonic-gate 	}
19540Sstevel@tonic-gate 	if (attr != NULL) {
19550Sstevel@tonic-gate 		/*
19560Sstevel@tonic-gate 		 * Pack attributes into event buffer. Event buffer already
19570Sstevel@tonic-gate 		 * has enough room for the packed nvlist.
19580Sstevel@tonic-gate 		 */
19590Sstevel@tonic-gate 		attr_offset = SE_ATTR_OFF(evp);
19600Sstevel@tonic-gate 		patt = (caddr_t)evp + attr_offset;
19610Sstevel@tonic-gate 
19620Sstevel@tonic-gate 		err = nvlist_pack(attr, &patt, &asz, NV_ENCODE_NATIVE,
19630Sstevel@tonic-gate 		    km_flags & EVCH_SLEEP ? KM_SLEEP : KM_NOSLEEP);
19640Sstevel@tonic-gate 
19650Sstevel@tonic-gate 		ASSERT(err != ENOMEM);
19660Sstevel@tonic-gate 
19670Sstevel@tonic-gate 		if (err != 0) {
19680Sstevel@tonic-gate 			return (EINVAL);
19690Sstevel@tonic-gate 		}
19700Sstevel@tonic-gate 
19710Sstevel@tonic-gate 		evp->seh_attr_off = attr_offset;
19720Sstevel@tonic-gate 		SE_FLAG(evp) = SE_PACKED_BUF;
19730Sstevel@tonic-gate 	}
19740Sstevel@tonic-gate 	return (evch_chpublish((evch_bind_t *)scp, evp, flags));
19750Sstevel@tonic-gate }
19760Sstevel@tonic-gate 
19770Sstevel@tonic-gate int
sysevent_evc_control(evchan_t * scp,int cmd,...)19780Sstevel@tonic-gate sysevent_evc_control(evchan_t *scp, int cmd, ...)
19790Sstevel@tonic-gate {
19800Sstevel@tonic-gate 	va_list		ap;
19810Sstevel@tonic-gate 	evch_chan_t	*chp = ((evch_bind_t *)scp)->bd_channel;
19820Sstevel@tonic-gate 	uint32_t	*chlenp;
19830Sstevel@tonic-gate 	uint32_t	chlen;
19840Sstevel@tonic-gate 	uint32_t	ochlen;
19850Sstevel@tonic-gate 	int		rc = 0;
19860Sstevel@tonic-gate 
19870Sstevel@tonic-gate 	if (scp == NULL) {
19880Sstevel@tonic-gate 		return (EINVAL);
19890Sstevel@tonic-gate 	}
19900Sstevel@tonic-gate 
19910Sstevel@tonic-gate 	va_start(ap, cmd);
19920Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
19930Sstevel@tonic-gate 	switch (cmd) {
19940Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN:
19950Sstevel@tonic-gate 		chlenp = va_arg(ap, uint32_t *);
19960Sstevel@tonic-gate 		*chlenp = chp->ch_maxev;
19970Sstevel@tonic-gate 		break;
19980Sstevel@tonic-gate 	case EVCH_SET_CHAN_LEN:
19990Sstevel@tonic-gate 		chlen = va_arg(ap, uint32_t);
20000Sstevel@tonic-gate 		ochlen = chp->ch_maxev;
20010Sstevel@tonic-gate 		chp->ch_maxev = min(chlen, evch_events_max);
20020Sstevel@tonic-gate 		if (ochlen < chp->ch_maxev) {
20030Sstevel@tonic-gate 			cv_signal(&chp->ch_pubcv);
20040Sstevel@tonic-gate 		}
20050Sstevel@tonic-gate 		break;
20060Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN_MAX:
20070Sstevel@tonic-gate 		*va_arg(ap, uint32_t *) = evch_events_max;
20080Sstevel@tonic-gate 		break;
20090Sstevel@tonic-gate 	default:
20100Sstevel@tonic-gate 		rc = EINVAL;
20110Sstevel@tonic-gate 	}
20120Sstevel@tonic-gate 
20130Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
20140Sstevel@tonic-gate 	va_end(ap);
20150Sstevel@tonic-gate 	return (rc);
20160Sstevel@tonic-gate }
20170Sstevel@tonic-gate 
2018*12967Sgavin.maltby@oracle.com int
sysevent_evc_setpropnvl(evchan_t * scp,nvlist_t * nvl)2019*12967Sgavin.maltby@oracle.com sysevent_evc_setpropnvl(evchan_t *scp, nvlist_t *nvl)
2020*12967Sgavin.maltby@oracle.com {
2021*12967Sgavin.maltby@oracle.com 	nvlist_t *nvlcp = nvl;
2022*12967Sgavin.maltby@oracle.com 
2023*12967Sgavin.maltby@oracle.com 	if (nvl != NULL && nvlist_dup(nvl, &nvlcp, 0) != 0)
2024*12967Sgavin.maltby@oracle.com 		return (ENOMEM);
2025*12967Sgavin.maltby@oracle.com 
2026*12967Sgavin.maltby@oracle.com 	evch_chsetpropnvl((evch_bind_t *)scp, nvlcp);
2027*12967Sgavin.maltby@oracle.com 
2028*12967Sgavin.maltby@oracle.com 	return (0);
2029*12967Sgavin.maltby@oracle.com }
2030*12967Sgavin.maltby@oracle.com 
2031*12967Sgavin.maltby@oracle.com int
sysevent_evc_getpropnvl(evchan_t * scp,nvlist_t ** nvlp)2032*12967Sgavin.maltby@oracle.com sysevent_evc_getpropnvl(evchan_t *scp, nvlist_t **nvlp)
2033*12967Sgavin.maltby@oracle.com {
2034*12967Sgavin.maltby@oracle.com 	return (evch_chgetpropnvl((evch_bind_t *)scp, nvlp, NULL));
2035*12967Sgavin.maltby@oracle.com }
2036*12967Sgavin.maltby@oracle.com 
20370Sstevel@tonic-gate /*
20380Sstevel@tonic-gate  * Project private interface to take a snapshot of all events of the
20390Sstevel@tonic-gate  * specified event channel. Argument subscr may be a subscriber id, the empty
20400Sstevel@tonic-gate  * string "", or NULL. The empty string indicates that no subscriber is
20410Sstevel@tonic-gate  * selected, for example if a previous subscriber died. sysevent_evc_walk_next()
20420Sstevel@tonic-gate  * will deliver events from the main event queue in this case. If subscr is
20430Sstevel@tonic-gate  * NULL, the subscriber with the EVCH_SUB_DUMP flag set (subd->sd_dump != 0)
20440Sstevel@tonic-gate  * will be selected.
20450Sstevel@tonic-gate  *
20460Sstevel@tonic-gate  * In panic case this function returns NULL. This is legal. The NULL has
20470Sstevel@tonic-gate  * to be delivered to sysevent_evc_walk_step() and sysevent_evc_walk_fini().
20480Sstevel@tonic-gate  */
20490Sstevel@tonic-gate evchanq_t *
sysevent_evc_walk_init(evchan_t * scp,char * subscr)20500Sstevel@tonic-gate sysevent_evc_walk_init(evchan_t *scp, char *subscr)
20510Sstevel@tonic-gate {
20520Sstevel@tonic-gate 	if (panicstr != NULL && scp == NULL)
20530Sstevel@tonic-gate 		return (NULL);
20540Sstevel@tonic-gate 	ASSERT(scp != NULL);
20550Sstevel@tonic-gate 	return (evch_chrdevent_init(((evch_bind_t *)scp)->bd_channel, subscr));
20560Sstevel@tonic-gate }
20570Sstevel@tonic-gate 
20580Sstevel@tonic-gate /*
20590Sstevel@tonic-gate  * Project private interface to read events from a previously taken
20600Sstevel@tonic-gate  * snapshot (with sysevent_evc_walk_init). In case of panic events
20610Sstevel@tonic-gate  * are retrieved directly from the channel data structures. No resources
20620Sstevel@tonic-gate  * are allocated and no mutexes are grabbed in panic context.
20630Sstevel@tonic-gate  */
20640Sstevel@tonic-gate sysevent_t *
sysevent_evc_walk_step(evchanq_t * evcq)20650Sstevel@tonic-gate sysevent_evc_walk_step(evchanq_t *evcq)
20660Sstevel@tonic-gate {
20670Sstevel@tonic-gate 	return ((sysevent_t *)evch_chgetnextev(evcq));
20680Sstevel@tonic-gate }
20690Sstevel@tonic-gate 
20700Sstevel@tonic-gate /*
20710Sstevel@tonic-gate  * Project private interface to free a previously taken snapshot.
20720Sstevel@tonic-gate  */
20730Sstevel@tonic-gate void
sysevent_evc_walk_fini(evchanq_t * evcq)20740Sstevel@tonic-gate sysevent_evc_walk_fini(evchanq_t *evcq)
20750Sstevel@tonic-gate {
20760Sstevel@tonic-gate 	evch_chrdevent_fini(evcq);
20770Sstevel@tonic-gate }
20780Sstevel@tonic-gate 
20790Sstevel@tonic-gate /*
20800Sstevel@tonic-gate  * Get address and size of an event payload. Returns NULL when no
20810Sstevel@tonic-gate  * payload present.
20820Sstevel@tonic-gate  */
20830Sstevel@tonic-gate char *
sysevent_evc_event_attr(sysevent_t * ev,size_t * plsize)20840Sstevel@tonic-gate sysevent_evc_event_attr(sysevent_t *ev, size_t *plsize)
20850Sstevel@tonic-gate {
20860Sstevel@tonic-gate 	char	*attrp;
20870Sstevel@tonic-gate 	size_t	aoff;
20880Sstevel@tonic-gate 	size_t	asz;
20890Sstevel@tonic-gate 
20900Sstevel@tonic-gate 	aoff = SE_ATTR_OFF(ev);
20910Sstevel@tonic-gate 	attrp = (char *)ev + aoff;
20920Sstevel@tonic-gate 	asz = *plsize = SE_SIZE(ev) - aoff;
20930Sstevel@tonic-gate 	return (asz ? attrp : NULL);
20940Sstevel@tonic-gate }
20950Sstevel@tonic-gate 
20960Sstevel@tonic-gate /*
20970Sstevel@tonic-gate  * sysevent_get_class_name - Get class name string
20980Sstevel@tonic-gate  */
20990Sstevel@tonic-gate char *
sysevent_get_class_name(sysevent_t * ev)21000Sstevel@tonic-gate sysevent_get_class_name(sysevent_t *ev)
21010Sstevel@tonic-gate {
21020Sstevel@tonic-gate 	return (SE_CLASS_NAME(ev));
21030Sstevel@tonic-gate }
21040Sstevel@tonic-gate 
21050Sstevel@tonic-gate /*
21060Sstevel@tonic-gate  * sysevent_get_subclass_name - Get subclass name string
21070Sstevel@tonic-gate  */
21080Sstevel@tonic-gate char *
sysevent_get_subclass_name(sysevent_t * ev)21090Sstevel@tonic-gate sysevent_get_subclass_name(sysevent_t *ev)
21100Sstevel@tonic-gate {
21110Sstevel@tonic-gate 	return (SE_SUBCLASS_NAME(ev));
21120Sstevel@tonic-gate }
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate /*
21150Sstevel@tonic-gate  * sysevent_get_seq - Get event sequence id
21160Sstevel@tonic-gate  */
21170Sstevel@tonic-gate uint64_t
sysevent_get_seq(sysevent_t * ev)21180Sstevel@tonic-gate sysevent_get_seq(sysevent_t *ev)
21190Sstevel@tonic-gate {
21200Sstevel@tonic-gate 	return (SE_SEQ(ev));
21210Sstevel@tonic-gate }
21220Sstevel@tonic-gate 
21230Sstevel@tonic-gate /*
21240Sstevel@tonic-gate  * sysevent_get_time - Get event timestamp
21250Sstevel@tonic-gate  */
21260Sstevel@tonic-gate void
sysevent_get_time(sysevent_t * ev,hrtime_t * etime)21270Sstevel@tonic-gate sysevent_get_time(sysevent_t *ev, hrtime_t *etime)
21280Sstevel@tonic-gate {
21290Sstevel@tonic-gate 	*etime = SE_TIME(ev);
21300Sstevel@tonic-gate }
21310Sstevel@tonic-gate 
21320Sstevel@tonic-gate /*
21330Sstevel@tonic-gate  * sysevent_get_size - Get event buffer size
21340Sstevel@tonic-gate  */
21350Sstevel@tonic-gate size_t
sysevent_get_size(sysevent_t * ev)21360Sstevel@tonic-gate sysevent_get_size(sysevent_t *ev)
21370Sstevel@tonic-gate {
21380Sstevel@tonic-gate 	return ((size_t)SE_SIZE(ev));
21390Sstevel@tonic-gate }
21400Sstevel@tonic-gate 
21410Sstevel@tonic-gate /*
21420Sstevel@tonic-gate  * sysevent_get_pub - Get publisher name string
21430Sstevel@tonic-gate  */
21440Sstevel@tonic-gate char *
sysevent_get_pub(sysevent_t * ev)21450Sstevel@tonic-gate sysevent_get_pub(sysevent_t *ev)
21460Sstevel@tonic-gate {
21470Sstevel@tonic-gate 	return (SE_PUB_NAME(ev));
21480Sstevel@tonic-gate }
21490Sstevel@tonic-gate 
21500Sstevel@tonic-gate /*
21510Sstevel@tonic-gate  * sysevent_get_attr_list - stores address of a copy of the attribute list
21520Sstevel@tonic-gate  * associated with the given sysevent buffer. The list must be freed by the
21530Sstevel@tonic-gate  * caller.
21540Sstevel@tonic-gate  */
21550Sstevel@tonic-gate int
sysevent_get_attr_list(sysevent_t * ev,nvlist_t ** nvlist)21560Sstevel@tonic-gate sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist)
21570Sstevel@tonic-gate {
21580Sstevel@tonic-gate 	int		error;
21590Sstevel@tonic-gate 	caddr_t		attr;
21600Sstevel@tonic-gate 	size_t		attr_len;
21610Sstevel@tonic-gate 	uint64_t	attr_offset;
21620Sstevel@tonic-gate 
21630Sstevel@tonic-gate 	*nvlist = NULL;
21640Sstevel@tonic-gate 	if (SE_FLAG(ev) != SE_PACKED_BUF) {
21650Sstevel@tonic-gate 		return (EINVAL);
21660Sstevel@tonic-gate 	}
21670Sstevel@tonic-gate 	attr_offset = SE_ATTR_OFF(ev);
21680Sstevel@tonic-gate 	if (SE_SIZE(ev) == attr_offset) {
21690Sstevel@tonic-gate 		return (EINVAL);
21700Sstevel@tonic-gate 	}
21710Sstevel@tonic-gate 
21720Sstevel@tonic-gate 	/* unpack nvlist */
21730Sstevel@tonic-gate 	attr = (caddr_t)ev + attr_offset;
21740Sstevel@tonic-gate 	attr_len = SE_SIZE(ev) - attr_offset;
21750Sstevel@tonic-gate 	if ((error = nvlist_unpack(attr, attr_len, nvlist, 0)) != 0) {
21760Sstevel@tonic-gate 		error = error != ENOMEM ? EINVAL : error;
21770Sstevel@tonic-gate 		return (error);
21780Sstevel@tonic-gate 	}
21790Sstevel@tonic-gate 	return (0);
21800Sstevel@tonic-gate }
21810Sstevel@tonic-gate 
21820Sstevel@tonic-gate /*
21830Sstevel@tonic-gate  * Functions called by the sysevent driver for general purpose event channels
21840Sstevel@tonic-gate  *
21850Sstevel@tonic-gate  * evch_usrchanopen	- Create/Bind to an event channel
21860Sstevel@tonic-gate  * evch_usrchanclose	- Unbind/Destroy event channel
21870Sstevel@tonic-gate  * evch_usrallocev	- Allocate event data structure
21880Sstevel@tonic-gate  * evch_usrfreeev	- Free event data structure
21890Sstevel@tonic-gate  * evch_usrpostevent	- Publish event
21900Sstevel@tonic-gate  * evch_usrsubscribe	- Subscribe (register callback function)
21910Sstevel@tonic-gate  * evch_usrunsubscribe	- Unsubscribe
21920Sstevel@tonic-gate  * evch_usrcontrol_set	- Set channel properties
21930Sstevel@tonic-gate  * evch_usrcontrol_get	- Get channel properties
21940Sstevel@tonic-gate  * evch_usrgetchnames	- Get list of channel names
21950Sstevel@tonic-gate  * evch_usrgetchdata	- Get data of an event channel
2196*12967Sgavin.maltby@oracle.com  * evch_usrsetpropnvl	- Set channel properties nvlist
2197*12967Sgavin.maltby@oracle.com  * evch_usrgetpropnvl	- Get channel properties nvlist
21980Sstevel@tonic-gate  */
21990Sstevel@tonic-gate evchan_t *
evch_usrchanopen(const char * name,uint32_t flags,int * err)22000Sstevel@tonic-gate evch_usrchanopen(const char *name, uint32_t flags, int *err)
22010Sstevel@tonic-gate {
22020Sstevel@tonic-gate 	evch_bind_t *bp = NULL;
22030Sstevel@tonic-gate 
22040Sstevel@tonic-gate 	*err = evch_chbind(name, &bp, flags);
22050Sstevel@tonic-gate 	return ((evchan_t *)bp);
22060Sstevel@tonic-gate }
22070Sstevel@tonic-gate 
22080Sstevel@tonic-gate /*
22090Sstevel@tonic-gate  * Unbind from the channel.
22100Sstevel@tonic-gate  */
22110Sstevel@tonic-gate void
evch_usrchanclose(evchan_t * cbp)22120Sstevel@tonic-gate evch_usrchanclose(evchan_t *cbp)
22130Sstevel@tonic-gate {
22140Sstevel@tonic-gate 	evch_chunbind((evch_bind_t *)cbp);
22150Sstevel@tonic-gate }
22160Sstevel@tonic-gate 
22170Sstevel@tonic-gate /*
22180Sstevel@tonic-gate  * Allocates log_evch_eventq_t structure but returns the pointer of the embedded
22190Sstevel@tonic-gate  * sysevent_impl_t structure as the opaque sysevent_t * data type
22200Sstevel@tonic-gate  */
22210Sstevel@tonic-gate sysevent_impl_t *
evch_usrallocev(size_t evsize,uint32_t flags)22220Sstevel@tonic-gate evch_usrallocev(size_t evsize, uint32_t flags)
22230Sstevel@tonic-gate {
22240Sstevel@tonic-gate 	return ((sysevent_impl_t *)evch_evq_evzalloc(evsize, flags));
22250Sstevel@tonic-gate }
22260Sstevel@tonic-gate 
22270Sstevel@tonic-gate /*
22280Sstevel@tonic-gate  * Free evch_eventq_t structure
22290Sstevel@tonic-gate  */
22300Sstevel@tonic-gate void
evch_usrfreeev(sysevent_impl_t * ev)22310Sstevel@tonic-gate evch_usrfreeev(sysevent_impl_t *ev)
22320Sstevel@tonic-gate {
22330Sstevel@tonic-gate 	evch_evq_evfree((void *)ev);
22340Sstevel@tonic-gate }
22350Sstevel@tonic-gate 
22360Sstevel@tonic-gate /*
22370Sstevel@tonic-gate  * Posts an event to the given channel. The event structure has to be
22380Sstevel@tonic-gate  * allocated by evch_usrallocev(). Returns zero on success and an error
22390Sstevel@tonic-gate  * code else. Attributes have to be packed and included in the event structure.
22400Sstevel@tonic-gate  *
22410Sstevel@tonic-gate  */
22420Sstevel@tonic-gate int
evch_usrpostevent(evchan_t * bp,sysevent_impl_t * ev,uint32_t flags)22430Sstevel@tonic-gate evch_usrpostevent(evchan_t *bp, sysevent_impl_t *ev, uint32_t flags)
22440Sstevel@tonic-gate {
22450Sstevel@tonic-gate 	return (evch_chpublish((evch_bind_t *)bp, ev, flags));
22460Sstevel@tonic-gate }
22470Sstevel@tonic-gate 
22480Sstevel@tonic-gate /*
22490Sstevel@tonic-gate  * Subscribe function for user land subscriptions
22500Sstevel@tonic-gate  */
22510Sstevel@tonic-gate int
evch_usrsubscribe(evchan_t * bp,const char * sid,const char * class,int d,uint32_t flags)22520Sstevel@tonic-gate evch_usrsubscribe(evchan_t *bp, const char *sid, const char *class,
22530Sstevel@tonic-gate     int d, uint32_t flags)
22540Sstevel@tonic-gate {
22550Sstevel@tonic-gate 	door_handle_t	dh = door_ki_lookup(d);
22560Sstevel@tonic-gate 	int		rv;
22570Sstevel@tonic-gate 
22580Sstevel@tonic-gate 	if (dh == NULL) {
22590Sstevel@tonic-gate 		return (EINVAL);
22600Sstevel@tonic-gate 	}
22610Sstevel@tonic-gate 	if ((rv = evch_chsubscribe((evch_bind_t *)bp, EVCH_DELDOOR, sid, class,
22620Sstevel@tonic-gate 	    (void *)dh, NULL, flags, curproc->p_pid)) != 0) {
22630Sstevel@tonic-gate 		door_ki_rele(dh);
22640Sstevel@tonic-gate 	}
22650Sstevel@tonic-gate 	return (rv);
22660Sstevel@tonic-gate }
22670Sstevel@tonic-gate 
22680Sstevel@tonic-gate /*
22690Sstevel@tonic-gate  * Flag can be EVCH_SUB_KEEP or 0. EVCH_SUB_KEEP preserves persistent
22700Sstevel@tonic-gate  * subscribers
22710Sstevel@tonic-gate  */
22720Sstevel@tonic-gate void
evch_usrunsubscribe(evchan_t * bp,const char * subid,uint32_t flags)22730Sstevel@tonic-gate evch_usrunsubscribe(evchan_t *bp, const char *subid, uint32_t flags)
22740Sstevel@tonic-gate {
22750Sstevel@tonic-gate 	evch_chunsubscribe((evch_bind_t *)bp, subid, flags);
22760Sstevel@tonic-gate }
22770Sstevel@tonic-gate 
22780Sstevel@tonic-gate /*ARGSUSED*/
22790Sstevel@tonic-gate int
evch_usrcontrol_set(evchan_t * bp,int cmd,uint32_t value)22800Sstevel@tonic-gate evch_usrcontrol_set(evchan_t *bp, int cmd, uint32_t value)
22810Sstevel@tonic-gate {
22820Sstevel@tonic-gate 	evch_chan_t	*chp = ((evch_bind_t *)bp)->bd_channel;
22830Sstevel@tonic-gate 	uid_t		uid = crgetuid(curthread->t_cred);
22840Sstevel@tonic-gate 	int		rc = 0;
22850Sstevel@tonic-gate 
22860Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
22870Sstevel@tonic-gate 	switch (cmd) {
22880Sstevel@tonic-gate 	case EVCH_SET_CHAN_LEN:
22890Sstevel@tonic-gate 		if (uid && uid != chp->ch_uid) {
22900Sstevel@tonic-gate 			rc = EACCES;
22910Sstevel@tonic-gate 			break;
22920Sstevel@tonic-gate 		}
22930Sstevel@tonic-gate 		chp->ch_maxev = min(value, evch_events_max);
22940Sstevel@tonic-gate 		break;
22950Sstevel@tonic-gate 	default:
22960Sstevel@tonic-gate 		rc = EINVAL;
22970Sstevel@tonic-gate 	}
22980Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
22990Sstevel@tonic-gate 	return (rc);
23000Sstevel@tonic-gate }
23010Sstevel@tonic-gate 
23020Sstevel@tonic-gate /*ARGSUSED*/
23030Sstevel@tonic-gate int
evch_usrcontrol_get(evchan_t * bp,int cmd,uint32_t * value)23040Sstevel@tonic-gate evch_usrcontrol_get(evchan_t *bp, int cmd, uint32_t *value)
23050Sstevel@tonic-gate {
23060Sstevel@tonic-gate 	evch_chan_t	*chp = ((evch_bind_t *)bp)->bd_channel;
23070Sstevel@tonic-gate 	int		rc = 0;
23080Sstevel@tonic-gate 
23090Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
23100Sstevel@tonic-gate 	switch (cmd) {
23110Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN:
23120Sstevel@tonic-gate 		*value = chp->ch_maxev;
23130Sstevel@tonic-gate 		break;
23140Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN_MAX:
23150Sstevel@tonic-gate 		*value = evch_events_max;
23160Sstevel@tonic-gate 		break;
23170Sstevel@tonic-gate 	default:
23180Sstevel@tonic-gate 		rc = EINVAL;
23190Sstevel@tonic-gate 	}
23200Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
23210Sstevel@tonic-gate 	return (rc);
23220Sstevel@tonic-gate }
23230Sstevel@tonic-gate 
23240Sstevel@tonic-gate int
evch_usrgetchnames(char * buf,size_t size)23250Sstevel@tonic-gate evch_usrgetchnames(char *buf, size_t size)
23260Sstevel@tonic-gate {
23270Sstevel@tonic-gate 	return (evch_chgetnames(buf, size));
23280Sstevel@tonic-gate }
23290Sstevel@tonic-gate 
23300Sstevel@tonic-gate int
evch_usrgetchdata(char * chname,void * buf,size_t size)23310Sstevel@tonic-gate evch_usrgetchdata(char *chname, void *buf, size_t size)
23320Sstevel@tonic-gate {
23330Sstevel@tonic-gate 	return (evch_chgetchdata(chname, buf, size));
23340Sstevel@tonic-gate }
2335*12967Sgavin.maltby@oracle.com 
2336*12967Sgavin.maltby@oracle.com void
evch_usrsetpropnvl(evchan_t * bp,nvlist_t * nvl)2337*12967Sgavin.maltby@oracle.com evch_usrsetpropnvl(evchan_t *bp, nvlist_t *nvl)
2338*12967Sgavin.maltby@oracle.com {
2339*12967Sgavin.maltby@oracle.com 	evch_chsetpropnvl((evch_bind_t *)bp, nvl);
2340*12967Sgavin.maltby@oracle.com }
2341*12967Sgavin.maltby@oracle.com 
2342*12967Sgavin.maltby@oracle.com int
evch_usrgetpropnvl(evchan_t * bp,nvlist_t ** nvlp,int64_t * genp)2343*12967Sgavin.maltby@oracle.com evch_usrgetpropnvl(evchan_t *bp, nvlist_t **nvlp, int64_t *genp)
2344*12967Sgavin.maltby@oracle.com {
2345*12967Sgavin.maltby@oracle.com 	return (evch_chgetpropnvl((evch_bind_t *)bp, nvlp, genp));
2346*12967Sgavin.maltby@oracle.com }
2347