xref: /titanic_52/usr/src/uts/common/os/evchannels.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * This file contains the source of the general purpose event channel extension
31*7c478bd9Sstevel@tonic-gate  * to the sysevent framework. This implementation is made up mainly of four
32*7c478bd9Sstevel@tonic-gate  * layers of functionality: the event queues (evch_evq_*()), the handling of
33*7c478bd9Sstevel@tonic-gate  * channels (evch_ch*()), the kernel interface (sysevent_evc_*()) and the
34*7c478bd9Sstevel@tonic-gate  * interface for the sysevent pseudo driver (evch_usr*()).
35*7c478bd9Sstevel@tonic-gate  * Libsysevent.so uses the pseudo driver sysevent's ioctl to access the event
36*7c478bd9Sstevel@tonic-gate  * channel extensions. The driver in turn uses the evch_usr*() functions below.
37*7c478bd9Sstevel@tonic-gate  *
38*7c478bd9Sstevel@tonic-gate  * The interfaces for user land and kernel are declared in sys/sysevent.h
39*7c478bd9Sstevel@tonic-gate  * Internal data structures for event channels are defined in
40*7c478bd9Sstevel@tonic-gate  * sys/sysevent_impl.h.
41*7c478bd9Sstevel@tonic-gate  *
42*7c478bd9Sstevel@tonic-gate  * The basic data structure for an event channel is of type evch_chan_t.
43*7c478bd9Sstevel@tonic-gate  * All channels are maintained by a list named evch_list. The list head
44*7c478bd9Sstevel@tonic-gate  * is of type evch_dlist_t.
45*7c478bd9Sstevel@tonic-gate  */
46*7c478bd9Sstevel@tonic-gate 
47*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/vmem.h>
53*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
54*7c478bd9Sstevel@tonic-gate #include <sys/callb.h>
55*7c478bd9Sstevel@tonic-gate #include <sys/sysevent.h>
56*7c478bd9Sstevel@tonic-gate #include <sys/sysevent_impl.h>
57*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
58*7c478bd9Sstevel@tonic-gate #include <sys/disp.h>
59*7c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
60*7c478bd9Sstevel@tonic-gate #include <sys/door.h>
61*7c478bd9Sstevel@tonic-gate #include <sys/zone.h>
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate /* Back-off delay for door_ki_upcall */
64*7c478bd9Sstevel@tonic-gate #define	EVCH_MIN_PAUSE	8
65*7c478bd9Sstevel@tonic-gate #define	EVCH_MAX_PAUSE	128
66*7c478bd9Sstevel@tonic-gate 
67*7c478bd9Sstevel@tonic-gate #define	GEVENT(ev)	((evch_gevent_t *)((char *)ev - \
68*7c478bd9Sstevel@tonic-gate 			    offsetof(evch_gevent_t, ge_payload)))
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate #define	EVCH_EVQ_EVCOUNT(x)	((&(x)->eq_eventq)->sq_count)
71*7c478bd9Sstevel@tonic-gate #define	EVCH_EVQ_HIGHWM(x)	((&(x)->eq_eventq)->sq_highwm)
72*7c478bd9Sstevel@tonic-gate 
73*7c478bd9Sstevel@tonic-gate struct evch_globals {
74*7c478bd9Sstevel@tonic-gate 	evch_dlist_t evch_list;
75*7c478bd9Sstevel@tonic-gate 	kmutex_t evch_list_lock;
76*7c478bd9Sstevel@tonic-gate };
77*7c478bd9Sstevel@tonic-gate 
78*7c478bd9Sstevel@tonic-gate /* Variables used by event channel routines */
79*7c478bd9Sstevel@tonic-gate static int		evq_initcomplete = 0;
80*7c478bd9Sstevel@tonic-gate static zone_key_t	evch_zone_key;
81*7c478bd9Sstevel@tonic-gate static uint32_t		evch_channels_max;
82*7c478bd9Sstevel@tonic-gate static uint32_t		evch_bindings_max = EVCH_MAX_BINDS_PER_CHANNEL;
83*7c478bd9Sstevel@tonic-gate static uint32_t		evch_events_max;
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate static void evch_evq_unsub(evch_eventq_t *, evch_evqsub_t *);
86*7c478bd9Sstevel@tonic-gate static void evch_evq_destroy(evch_eventq_t *);
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate /*
89*7c478bd9Sstevel@tonic-gate  * List handling. These functions handle a doubly linked list. The list has
90*7c478bd9Sstevel@tonic-gate  * to be protected by the calling functions. evch_dlist_t is the list head.
91*7c478bd9Sstevel@tonic-gate  * Every node of the list has to put a evch_dlelem_t data type in its data
92*7c478bd9Sstevel@tonic-gate  * structure as its first element.
93*7c478bd9Sstevel@tonic-gate  *
94*7c478bd9Sstevel@tonic-gate  * evch_dl_init		- Initialize list head
95*7c478bd9Sstevel@tonic-gate  * evch_dl_fini		- Terminate list handling
96*7c478bd9Sstevel@tonic-gate  * evch_dl_is_init	- Returns one if list is initialized
97*7c478bd9Sstevel@tonic-gate  * evch_dl_add		- Add element to end of list
98*7c478bd9Sstevel@tonic-gate  * evch_dl_del		- Remove given element from list
99*7c478bd9Sstevel@tonic-gate  * evch_dl_search	- Lookup element in list
100*7c478bd9Sstevel@tonic-gate  * evch_dl_getnum	- Get number of elements in list
101*7c478bd9Sstevel@tonic-gate  * evch_dl_next		- Get next elements of list
102*7c478bd9Sstevel@tonic-gate  */
103*7c478bd9Sstevel@tonic-gate 
104*7c478bd9Sstevel@tonic-gate static void
105*7c478bd9Sstevel@tonic-gate evch_dl_init(evch_dlist_t *hp)
106*7c478bd9Sstevel@tonic-gate {
107*7c478bd9Sstevel@tonic-gate 	hp->dh_head.dl_prev = hp->dh_head.dl_next = &hp->dh_head;
108*7c478bd9Sstevel@tonic-gate 	hp->dh_count = 0;
109*7c478bd9Sstevel@tonic-gate }
110*7c478bd9Sstevel@tonic-gate 
111*7c478bd9Sstevel@tonic-gate /*
112*7c478bd9Sstevel@tonic-gate  * Assumes that list is empty.
113*7c478bd9Sstevel@tonic-gate  */
114*7c478bd9Sstevel@tonic-gate static void
115*7c478bd9Sstevel@tonic-gate evch_dl_fini(evch_dlist_t *hp)
116*7c478bd9Sstevel@tonic-gate {
117*7c478bd9Sstevel@tonic-gate 	hp->dh_head.dl_prev = hp->dh_head.dl_next = NULL;
118*7c478bd9Sstevel@tonic-gate }
119*7c478bd9Sstevel@tonic-gate 
120*7c478bd9Sstevel@tonic-gate static int
121*7c478bd9Sstevel@tonic-gate evch_dl_is_init(evch_dlist_t *hp)
122*7c478bd9Sstevel@tonic-gate {
123*7c478bd9Sstevel@tonic-gate 	return (hp->dh_head.dl_next != NULL ? 1 : 0);
124*7c478bd9Sstevel@tonic-gate }
125*7c478bd9Sstevel@tonic-gate 
126*7c478bd9Sstevel@tonic-gate /*
127*7c478bd9Sstevel@tonic-gate  * Add an element at the end of the list.
128*7c478bd9Sstevel@tonic-gate  */
129*7c478bd9Sstevel@tonic-gate static void
130*7c478bd9Sstevel@tonic-gate evch_dl_add(evch_dlist_t *hp, evch_dlelem_t *el)
131*7c478bd9Sstevel@tonic-gate {
132*7c478bd9Sstevel@tonic-gate 	evch_dlelem_t	*x = hp->dh_head.dl_prev;
133*7c478bd9Sstevel@tonic-gate 	evch_dlelem_t	*y = &hp->dh_head;
134*7c478bd9Sstevel@tonic-gate 
135*7c478bd9Sstevel@tonic-gate 	x->dl_next = el;
136*7c478bd9Sstevel@tonic-gate 	y->dl_prev = el;
137*7c478bd9Sstevel@tonic-gate 	el->dl_next = y;
138*7c478bd9Sstevel@tonic-gate 	el->dl_prev = x;
139*7c478bd9Sstevel@tonic-gate 	hp->dh_count++;
140*7c478bd9Sstevel@tonic-gate }
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate /*
143*7c478bd9Sstevel@tonic-gate  * Remove arbitrary element out of dlist.
144*7c478bd9Sstevel@tonic-gate  */
145*7c478bd9Sstevel@tonic-gate static void
146*7c478bd9Sstevel@tonic-gate evch_dl_del(evch_dlist_t *hp, evch_dlelem_t *p)
147*7c478bd9Sstevel@tonic-gate {
148*7c478bd9Sstevel@tonic-gate 	ASSERT(hp->dh_count > 0 && p != &hp->dh_head);
149*7c478bd9Sstevel@tonic-gate 	p->dl_prev->dl_next = p->dl_next;
150*7c478bd9Sstevel@tonic-gate 	p->dl_next->dl_prev = p->dl_prev;
151*7c478bd9Sstevel@tonic-gate 	p->dl_prev = NULL;
152*7c478bd9Sstevel@tonic-gate 	p->dl_next = NULL;
153*7c478bd9Sstevel@tonic-gate 	hp->dh_count--;
154*7c478bd9Sstevel@tonic-gate }
155*7c478bd9Sstevel@tonic-gate 
156*7c478bd9Sstevel@tonic-gate /*
157*7c478bd9Sstevel@tonic-gate  * Search an element in a list. Caller provides comparison callback function.
158*7c478bd9Sstevel@tonic-gate  */
159*7c478bd9Sstevel@tonic-gate static evch_dlelem_t *
160*7c478bd9Sstevel@tonic-gate evch_dl_search(evch_dlist_t *hp, int (*cmp)(evch_dlelem_t *, char *), char *s)
161*7c478bd9Sstevel@tonic-gate {
162*7c478bd9Sstevel@tonic-gate 	evch_dlelem_t *p;
163*7c478bd9Sstevel@tonic-gate 
164*7c478bd9Sstevel@tonic-gate 	for (p = hp->dh_head.dl_next; p != &hp->dh_head; p = p->dl_next) {
165*7c478bd9Sstevel@tonic-gate 		if (cmp(p, s) == 0) {
166*7c478bd9Sstevel@tonic-gate 			return (p);
167*7c478bd9Sstevel@tonic-gate 		}
168*7c478bd9Sstevel@tonic-gate 	}
169*7c478bd9Sstevel@tonic-gate 	return (NULL);
170*7c478bd9Sstevel@tonic-gate }
171*7c478bd9Sstevel@tonic-gate 
172*7c478bd9Sstevel@tonic-gate /*
173*7c478bd9Sstevel@tonic-gate  * Return number of elements in the list.
174*7c478bd9Sstevel@tonic-gate  */
175*7c478bd9Sstevel@tonic-gate static int
176*7c478bd9Sstevel@tonic-gate evch_dl_getnum(evch_dlist_t *hp)
177*7c478bd9Sstevel@tonic-gate {
178*7c478bd9Sstevel@tonic-gate 	return (hp->dh_count);
179*7c478bd9Sstevel@tonic-gate }
180*7c478bd9Sstevel@tonic-gate 
181*7c478bd9Sstevel@tonic-gate /*
182*7c478bd9Sstevel@tonic-gate  * Find next element of a evch_dlist_t list. Find first element if el == NULL.
183*7c478bd9Sstevel@tonic-gate  * Returns NULL if end of list is reached.
184*7c478bd9Sstevel@tonic-gate  */
185*7c478bd9Sstevel@tonic-gate static void *
186*7c478bd9Sstevel@tonic-gate evch_dl_next(evch_dlist_t *hp, void *el)
187*7c478bd9Sstevel@tonic-gate {
188*7c478bd9Sstevel@tonic-gate 	evch_dlelem_t *ep = (evch_dlelem_t *)el;
189*7c478bd9Sstevel@tonic-gate 
190*7c478bd9Sstevel@tonic-gate 	if (hp->dh_count == 0) {
191*7c478bd9Sstevel@tonic-gate 		return (NULL);
192*7c478bd9Sstevel@tonic-gate 	}
193*7c478bd9Sstevel@tonic-gate 	if (ep == NULL) {
194*7c478bd9Sstevel@tonic-gate 		return (hp->dh_head.dl_next);
195*7c478bd9Sstevel@tonic-gate 	}
196*7c478bd9Sstevel@tonic-gate 	if ((ep = ep->dl_next) == (evch_dlelem_t *)hp) {
197*7c478bd9Sstevel@tonic-gate 		return (NULL);
198*7c478bd9Sstevel@tonic-gate 	}
199*7c478bd9Sstevel@tonic-gate 	return ((void *)ep);
200*7c478bd9Sstevel@tonic-gate }
201*7c478bd9Sstevel@tonic-gate 
202*7c478bd9Sstevel@tonic-gate /*
203*7c478bd9Sstevel@tonic-gate  * Queue handling routines. Mutexes have to be entered previously.
204*7c478bd9Sstevel@tonic-gate  *
205*7c478bd9Sstevel@tonic-gate  * evch_q_init	- Initialize queue head
206*7c478bd9Sstevel@tonic-gate  * evch_q_in	- Put element into queue
207*7c478bd9Sstevel@tonic-gate  * evch_q_out	- Get element out of queue
208*7c478bd9Sstevel@tonic-gate  * evch_q_next	- Iterate over the elements of a queue
209*7c478bd9Sstevel@tonic-gate  */
210*7c478bd9Sstevel@tonic-gate static void
211*7c478bd9Sstevel@tonic-gate evch_q_init(evch_squeue_t *q)
212*7c478bd9Sstevel@tonic-gate {
213*7c478bd9Sstevel@tonic-gate 	q->sq_head = NULL;
214*7c478bd9Sstevel@tonic-gate 	q->sq_tail = (evch_qelem_t *)q;
215*7c478bd9Sstevel@tonic-gate 	q->sq_count = 0;
216*7c478bd9Sstevel@tonic-gate 	q->sq_highwm = 0;
217*7c478bd9Sstevel@tonic-gate }
218*7c478bd9Sstevel@tonic-gate 
219*7c478bd9Sstevel@tonic-gate /*
220*7c478bd9Sstevel@tonic-gate  * Put element into the queue q
221*7c478bd9Sstevel@tonic-gate  */
222*7c478bd9Sstevel@tonic-gate static void
223*7c478bd9Sstevel@tonic-gate evch_q_in(evch_squeue_t *q, evch_qelem_t *el)
224*7c478bd9Sstevel@tonic-gate {
225*7c478bd9Sstevel@tonic-gate 	q->sq_tail->q_next = el;
226*7c478bd9Sstevel@tonic-gate 	el->q_next = NULL;
227*7c478bd9Sstevel@tonic-gate 	q->sq_tail = el;
228*7c478bd9Sstevel@tonic-gate 	q->sq_count++;
229*7c478bd9Sstevel@tonic-gate 	if (q->sq_count > q->sq_highwm) {
230*7c478bd9Sstevel@tonic-gate 		q->sq_highwm = q->sq_count;
231*7c478bd9Sstevel@tonic-gate 	}
232*7c478bd9Sstevel@tonic-gate }
233*7c478bd9Sstevel@tonic-gate 
234*7c478bd9Sstevel@tonic-gate /*
235*7c478bd9Sstevel@tonic-gate  * Returns NULL if queue is empty.
236*7c478bd9Sstevel@tonic-gate  */
237*7c478bd9Sstevel@tonic-gate static evch_qelem_t *
238*7c478bd9Sstevel@tonic-gate evch_q_out(evch_squeue_t *q)
239*7c478bd9Sstevel@tonic-gate {
240*7c478bd9Sstevel@tonic-gate 	evch_qelem_t *el;
241*7c478bd9Sstevel@tonic-gate 
242*7c478bd9Sstevel@tonic-gate 	if ((el = q->sq_head) != NULL) {
243*7c478bd9Sstevel@tonic-gate 		q->sq_head = el->q_next;
244*7c478bd9Sstevel@tonic-gate 		q->sq_count--;
245*7c478bd9Sstevel@tonic-gate 		if (q->sq_head == NULL) {
246*7c478bd9Sstevel@tonic-gate 			q->sq_tail = (evch_qelem_t *)q;
247*7c478bd9Sstevel@tonic-gate 		}
248*7c478bd9Sstevel@tonic-gate 	}
249*7c478bd9Sstevel@tonic-gate 	return (el);
250*7c478bd9Sstevel@tonic-gate }
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate /*
253*7c478bd9Sstevel@tonic-gate  * Returns element after *el or first if el == NULL. NULL is returned
254*7c478bd9Sstevel@tonic-gate  * if queue is empty or *el points to the last element in the queue.
255*7c478bd9Sstevel@tonic-gate  */
256*7c478bd9Sstevel@tonic-gate static evch_qelem_t *
257*7c478bd9Sstevel@tonic-gate evch_q_next(evch_squeue_t *q, evch_qelem_t *el)
258*7c478bd9Sstevel@tonic-gate {
259*7c478bd9Sstevel@tonic-gate 	if (el == NULL)
260*7c478bd9Sstevel@tonic-gate 		return (q->sq_head);
261*7c478bd9Sstevel@tonic-gate 	return (el->q_next);
262*7c478bd9Sstevel@tonic-gate }
263*7c478bd9Sstevel@tonic-gate 
264*7c478bd9Sstevel@tonic-gate /*
265*7c478bd9Sstevel@tonic-gate  * Event queue handling functions. An event queue is the basic building block
266*7c478bd9Sstevel@tonic-gate  * of an event channel. One event queue makes up the publisher-side event queue.
267*7c478bd9Sstevel@tonic-gate  * Further event queues build the per-subscriber queues of an event channel.
268*7c478bd9Sstevel@tonic-gate  * Each queue is associated an event delivery thread.
269*7c478bd9Sstevel@tonic-gate  * These functions support a two-step initialization. First step, when kernel
270*7c478bd9Sstevel@tonic-gate  * memory is ready and second when threads are ready.
271*7c478bd9Sstevel@tonic-gate  * Events consist of an administrating evch_gevent_t structure with the event
272*7c478bd9Sstevel@tonic-gate  * data appended as variable length payload.
273*7c478bd9Sstevel@tonic-gate  * The internal interface functions for the event queue handling are:
274*7c478bd9Sstevel@tonic-gate  *
275*7c478bd9Sstevel@tonic-gate  * evch_evq_create	- create an event queue
276*7c478bd9Sstevel@tonic-gate  * evch_evq_thrcreate	- create thread for an event queue.
277*7c478bd9Sstevel@tonic-gate  * evch_evq_destroy	- delete an event queue
278*7c478bd9Sstevel@tonic-gate  * evch_evq_sub		- Subscribe to event delivery from an event queue
279*7c478bd9Sstevel@tonic-gate  * evch_evq_unsub	- Unsubscribe
280*7c478bd9Sstevel@tonic-gate  * evch_evq_pub		- Post an event into an event queue
281*7c478bd9Sstevel@tonic-gate  * evch_evq_stop	- Put delivery thread on hold
282*7c478bd9Sstevel@tonic-gate  * evch_evq_continue	- Resume event delivery thread
283*7c478bd9Sstevel@tonic-gate  * evch_evq_status	- Return status of delivery thread, running or on hold
284*7c478bd9Sstevel@tonic-gate  * evch_evq_evzalloc	- Allocate an event structure
285*7c478bd9Sstevel@tonic-gate  * evch_evq_evfree	- Free an event structure
286*7c478bd9Sstevel@tonic-gate  * evch_evq_evadd_dest	- Add a destructor function to an event structure
287*7c478bd9Sstevel@tonic-gate  * evch_evq_evnext	- Iterate over events non-destructive
288*7c478bd9Sstevel@tonic-gate  */
289*7c478bd9Sstevel@tonic-gate 
290*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
291*7c478bd9Sstevel@tonic-gate static void *
292*7c478bd9Sstevel@tonic-gate evch_zoneinit(zoneid_t zoneid)
293*7c478bd9Sstevel@tonic-gate {
294*7c478bd9Sstevel@tonic-gate 	struct evch_globals *eg;
295*7c478bd9Sstevel@tonic-gate 
296*7c478bd9Sstevel@tonic-gate 	eg = kmem_zalloc(sizeof (*eg), KM_SLEEP);
297*7c478bd9Sstevel@tonic-gate 	evch_dl_init(&eg->evch_list);
298*7c478bd9Sstevel@tonic-gate 	return (eg);
299*7c478bd9Sstevel@tonic-gate }
300*7c478bd9Sstevel@tonic-gate 
301*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
302*7c478bd9Sstevel@tonic-gate static void
303*7c478bd9Sstevel@tonic-gate evch_zonefree(zoneid_t zoneid, void *arg)
304*7c478bd9Sstevel@tonic-gate {
305*7c478bd9Sstevel@tonic-gate 	struct evch_globals *eg = arg;
306*7c478bd9Sstevel@tonic-gate 	evch_chan_t *chp;
307*7c478bd9Sstevel@tonic-gate 	evch_subd_t *sdp;
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eg->evch_list_lock);
310*7c478bd9Sstevel@tonic-gate 
311*7c478bd9Sstevel@tonic-gate 	/*
312*7c478bd9Sstevel@tonic-gate 	 * Keep picking the head element off the list until there are no
313*7c478bd9Sstevel@tonic-gate 	 * more.
314*7c478bd9Sstevel@tonic-gate 	 */
315*7c478bd9Sstevel@tonic-gate 	while ((chp = evch_dl_next(&eg->evch_list, NULL)) != NULL) {
316*7c478bd9Sstevel@tonic-gate 
317*7c478bd9Sstevel@tonic-gate 		/*
318*7c478bd9Sstevel@tonic-gate 		 * Since all processes are gone, all bindings should be gone,
319*7c478bd9Sstevel@tonic-gate 		 * and only channels with SUB_KEEP subscribers should remain.
320*7c478bd9Sstevel@tonic-gate 		 */
321*7c478bd9Sstevel@tonic-gate 		mutex_enter(&chp->ch_mutex);
322*7c478bd9Sstevel@tonic-gate 		ASSERT(chp->ch_bindings == 0);
323*7c478bd9Sstevel@tonic-gate 		ASSERT(evch_dl_getnum(&chp->ch_subscr) != 0);
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 		/* Forcibly unsubscribe each remaining subscription */
326*7c478bd9Sstevel@tonic-gate 		while ((sdp = evch_dl_next(&chp->ch_subscr, NULL)) != NULL) {
327*7c478bd9Sstevel@tonic-gate 			/*
328*7c478bd9Sstevel@tonic-gate 			 * We should only be tearing down persistent
329*7c478bd9Sstevel@tonic-gate 			 * subscribers at this point, since all processes
330*7c478bd9Sstevel@tonic-gate 			 * from this zone are gone.
331*7c478bd9Sstevel@tonic-gate 			 */
332*7c478bd9Sstevel@tonic-gate 			ASSERT(sdp->sd_active == 0);
333*7c478bd9Sstevel@tonic-gate 			ASSERT((sdp->sd_persist & EVCH_SUB_KEEP) != 0);
334*7c478bd9Sstevel@tonic-gate 			/*
335*7c478bd9Sstevel@tonic-gate 			 * Disconnect subscriber queue from main event queue.
336*7c478bd9Sstevel@tonic-gate 			 */
337*7c478bd9Sstevel@tonic-gate 			evch_evq_unsub(chp->ch_queue, sdp->sd_msub);
338*7c478bd9Sstevel@tonic-gate 
339*7c478bd9Sstevel@tonic-gate 			/* Destruct per subscriber queue */
340*7c478bd9Sstevel@tonic-gate 			evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub);
341*7c478bd9Sstevel@tonic-gate 			evch_evq_destroy(sdp->sd_queue);
342*7c478bd9Sstevel@tonic-gate 			/*
343*7c478bd9Sstevel@tonic-gate 			 * Eliminate the subscriber data from channel list.
344*7c478bd9Sstevel@tonic-gate 			 */
345*7c478bd9Sstevel@tonic-gate 			evch_dl_del(&chp->ch_subscr, &sdp->sd_link);
346*7c478bd9Sstevel@tonic-gate 			kmem_free(sdp->sd_classname, sdp->sd_clnsize);
347*7c478bd9Sstevel@tonic-gate 			kmem_free(sdp->sd_ident, strlen(sdp->sd_ident) + 1);
348*7c478bd9Sstevel@tonic-gate 			kmem_free(sdp, sizeof (evch_subd_t));
349*7c478bd9Sstevel@tonic-gate 		}
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 		/* Channel must now have no subscribers */
352*7c478bd9Sstevel@tonic-gate 		ASSERT(evch_dl_getnum(&chp->ch_subscr) == 0);
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate 		/* Just like unbind */
355*7c478bd9Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
356*7c478bd9Sstevel@tonic-gate 		evch_dl_del(&eg->evch_list, &chp->ch_link);
357*7c478bd9Sstevel@tonic-gate 		evch_evq_destroy(chp->ch_queue);
358*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&chp->ch_mutex);
359*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&chp->ch_pubmx);
360*7c478bd9Sstevel@tonic-gate 		cv_destroy(&chp->ch_pubcv);
361*7c478bd9Sstevel@tonic-gate 		kmem_free(chp->ch_name, chp->ch_namelen);
362*7c478bd9Sstevel@tonic-gate 		kmem_free(chp, sizeof (evch_chan_t));
363*7c478bd9Sstevel@tonic-gate 	}
364*7c478bd9Sstevel@tonic-gate 
365*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
366*7c478bd9Sstevel@tonic-gate 	/* all channels should now be gone */
367*7c478bd9Sstevel@tonic-gate 	ASSERT(evch_dl_getnum(&eg->evch_list) == 0);
368*7c478bd9Sstevel@tonic-gate 	kmem_free(eg, sizeof (*eg));
369*7c478bd9Sstevel@tonic-gate }
370*7c478bd9Sstevel@tonic-gate 
371*7c478bd9Sstevel@tonic-gate /*
372*7c478bd9Sstevel@tonic-gate  * Frees evch_gevent_t structure including the payload, if the reference count
373*7c478bd9Sstevel@tonic-gate  * drops to or below zero. Below zero happens when the event is freed
374*7c478bd9Sstevel@tonic-gate  * without beeing queued into a queue.
375*7c478bd9Sstevel@tonic-gate  */
376*7c478bd9Sstevel@tonic-gate static void
377*7c478bd9Sstevel@tonic-gate evch_gevent_free(evch_gevent_t *evp)
378*7c478bd9Sstevel@tonic-gate {
379*7c478bd9Sstevel@tonic-gate 	int32_t refcnt;
380*7c478bd9Sstevel@tonic-gate 
381*7c478bd9Sstevel@tonic-gate 	refcnt = (int32_t)atomic_add_32_nv(&evp->ge_refcount, -1);
382*7c478bd9Sstevel@tonic-gate 	if (refcnt <= 0) {
383*7c478bd9Sstevel@tonic-gate 		if (evp->ge_destruct != NULL) {
384*7c478bd9Sstevel@tonic-gate 			evp->ge_destruct((void *)&(evp->ge_payload),
385*7c478bd9Sstevel@tonic-gate 			    evp->ge_dstcookie);
386*7c478bd9Sstevel@tonic-gate 		}
387*7c478bd9Sstevel@tonic-gate 		kmem_free(evp, evp->ge_size);
388*7c478bd9Sstevel@tonic-gate 	}
389*7c478bd9Sstevel@tonic-gate }
390*7c478bd9Sstevel@tonic-gate 
391*7c478bd9Sstevel@tonic-gate /*
392*7c478bd9Sstevel@tonic-gate  * Deliver is called for every subscription to the current event
393*7c478bd9Sstevel@tonic-gate  * It calls the registered filter function and then the registered delivery
394*7c478bd9Sstevel@tonic-gate  * callback routine. Returns 0 on success. The callback routine returns
395*7c478bd9Sstevel@tonic-gate  * EVQ_AGAIN or EVQ_SLEEP in case the event could not be delivered.
396*7c478bd9Sstevel@tonic-gate  */
397*7c478bd9Sstevel@tonic-gate static int
398*7c478bd9Sstevel@tonic-gate evch_deliver(evch_evqsub_t *sp, evch_gevent_t *ep)
399*7c478bd9Sstevel@tonic-gate {
400*7c478bd9Sstevel@tonic-gate 	void		*uep = &ep->ge_payload;
401*7c478bd9Sstevel@tonic-gate 	int		res = EVQ_DELIVER;
402*7c478bd9Sstevel@tonic-gate 
403*7c478bd9Sstevel@tonic-gate 	if (sp->su_filter != NULL) {
404*7c478bd9Sstevel@tonic-gate 		res = sp->su_filter(uep, sp->su_fcookie);
405*7c478bd9Sstevel@tonic-gate 	}
406*7c478bd9Sstevel@tonic-gate 	if (res == EVQ_DELIVER) {
407*7c478bd9Sstevel@tonic-gate 		return (sp->su_callb(uep, sp->su_cbcookie));
408*7c478bd9Sstevel@tonic-gate 	}
409*7c478bd9Sstevel@tonic-gate 	return (0);
410*7c478bd9Sstevel@tonic-gate }
411*7c478bd9Sstevel@tonic-gate 
412*7c478bd9Sstevel@tonic-gate /*
413*7c478bd9Sstevel@tonic-gate  * Holds event delivery in case of eq_holdmode set or in case the
414*7c478bd9Sstevel@tonic-gate  * event queue is empty. Mutex must be held when called.
415*7c478bd9Sstevel@tonic-gate  * Wakes up a thread waiting for the delivery thread reaching the hold mode.
416*7c478bd9Sstevel@tonic-gate  */
417*7c478bd9Sstevel@tonic-gate static void
418*7c478bd9Sstevel@tonic-gate evch_delivery_hold(evch_eventq_t *eqp, callb_cpr_t *cpip)
419*7c478bd9Sstevel@tonic-gate {
420*7c478bd9Sstevel@tonic-gate 	if (eqp->eq_tabortflag == 0) {
421*7c478bd9Sstevel@tonic-gate 		do {
422*7c478bd9Sstevel@tonic-gate 			if (eqp->eq_holdmode) {
423*7c478bd9Sstevel@tonic-gate 				cv_signal(&eqp->eq_onholdcv);
424*7c478bd9Sstevel@tonic-gate 			}
425*7c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_BEGIN(cpip);
426*7c478bd9Sstevel@tonic-gate 			cv_wait(&eqp->eq_thrsleepcv, &eqp->eq_queuemx);
427*7c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(cpip, &eqp->eq_queuemx);
428*7c478bd9Sstevel@tonic-gate 		} while (eqp->eq_holdmode);
429*7c478bd9Sstevel@tonic-gate 	}
430*7c478bd9Sstevel@tonic-gate }
431*7c478bd9Sstevel@tonic-gate 
432*7c478bd9Sstevel@tonic-gate /*
433*7c478bd9Sstevel@tonic-gate  * Event delivery thread. Enumerates all subscribers and calls evch_deliver()
434*7c478bd9Sstevel@tonic-gate  * for each one.
435*7c478bd9Sstevel@tonic-gate  */
436*7c478bd9Sstevel@tonic-gate static void
437*7c478bd9Sstevel@tonic-gate evch_delivery_thr(evch_eventq_t *eqp)
438*7c478bd9Sstevel@tonic-gate {
439*7c478bd9Sstevel@tonic-gate 	evch_qelem_t	*qep;
440*7c478bd9Sstevel@tonic-gate 	callb_cpr_t	cprinfo;
441*7c478bd9Sstevel@tonic-gate 	int		res;
442*7c478bd9Sstevel@tonic-gate 	evch_evqsub_t	*sub;
443*7c478bd9Sstevel@tonic-gate 	int		deltime;
444*7c478bd9Sstevel@tonic-gate 	int		repeatcount;
445*7c478bd9Sstevel@tonic-gate 	char		thnam[32];
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate 	(void) snprintf(thnam, sizeof (thnam), "sysevent_chan-%d",
448*7c478bd9Sstevel@tonic-gate 	    (int)eqp->eq_thrid);
449*7c478bd9Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &eqp->eq_queuemx, callb_generic_cpr, thnam);
450*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
451*7c478bd9Sstevel@tonic-gate 	while (eqp->eq_tabortflag == 0) {
452*7c478bd9Sstevel@tonic-gate 		while (eqp->eq_holdmode == 0 && eqp->eq_tabortflag == 0 &&
453*7c478bd9Sstevel@tonic-gate 		    (qep = evch_q_out(&eqp->eq_eventq)) != NULL) {
454*7c478bd9Sstevel@tonic-gate 
455*7c478bd9Sstevel@tonic-gate 			/* Filter and deliver event to all subscribers */
456*7c478bd9Sstevel@tonic-gate 			deltime = EVCH_MIN_PAUSE;
457*7c478bd9Sstevel@tonic-gate 			repeatcount = EVCH_MAX_TRY_DELIVERY;
458*7c478bd9Sstevel@tonic-gate 			eqp->eq_curevent = qep->q_objref;
459*7c478bd9Sstevel@tonic-gate 			sub = evch_dl_next(&eqp->eq_subscr, NULL);
460*7c478bd9Sstevel@tonic-gate 			while (sub != NULL) {
461*7c478bd9Sstevel@tonic-gate 				eqp->eq_dactive = 1;
462*7c478bd9Sstevel@tonic-gate 				mutex_exit(&eqp->eq_queuemx);
463*7c478bd9Sstevel@tonic-gate 				res = evch_deliver(sub, qep->q_objref);
464*7c478bd9Sstevel@tonic-gate 				mutex_enter(&eqp->eq_queuemx);
465*7c478bd9Sstevel@tonic-gate 				eqp->eq_dactive = 0;
466*7c478bd9Sstevel@tonic-gate 				cv_signal(&eqp->eq_dactivecv);
467*7c478bd9Sstevel@tonic-gate 				switch (res) {
468*7c478bd9Sstevel@tonic-gate 				case EVQ_SLEEP:
469*7c478bd9Sstevel@tonic-gate 					/*
470*7c478bd9Sstevel@tonic-gate 					 * Wait for subscriber to return.
471*7c478bd9Sstevel@tonic-gate 					 */
472*7c478bd9Sstevel@tonic-gate 					eqp->eq_holdmode = 1;
473*7c478bd9Sstevel@tonic-gate 					evch_delivery_hold(eqp, &cprinfo);
474*7c478bd9Sstevel@tonic-gate 					if (eqp->eq_tabortflag) {
475*7c478bd9Sstevel@tonic-gate 						break;
476*7c478bd9Sstevel@tonic-gate 					}
477*7c478bd9Sstevel@tonic-gate 					continue;
478*7c478bd9Sstevel@tonic-gate 				case EVQ_AGAIN:
479*7c478bd9Sstevel@tonic-gate 					CALLB_CPR_SAFE_BEGIN(&cprinfo);
480*7c478bd9Sstevel@tonic-gate 					mutex_exit(&eqp->eq_queuemx);
481*7c478bd9Sstevel@tonic-gate 					delay(deltime);
482*7c478bd9Sstevel@tonic-gate 					deltime =
483*7c478bd9Sstevel@tonic-gate 					    deltime > EVCH_MAX_PAUSE ?
484*7c478bd9Sstevel@tonic-gate 					    deltime : deltime << 1;
485*7c478bd9Sstevel@tonic-gate 					mutex_enter(&eqp->eq_queuemx);
486*7c478bd9Sstevel@tonic-gate 					CALLB_CPR_SAFE_END(&cprinfo,
487*7c478bd9Sstevel@tonic-gate 					    &eqp->eq_queuemx);
488*7c478bd9Sstevel@tonic-gate 					if (repeatcount-- > 0) {
489*7c478bd9Sstevel@tonic-gate 						continue;
490*7c478bd9Sstevel@tonic-gate 					}
491*7c478bd9Sstevel@tonic-gate 					break;
492*7c478bd9Sstevel@tonic-gate 				}
493*7c478bd9Sstevel@tonic-gate 				if (eqp->eq_tabortflag) {
494*7c478bd9Sstevel@tonic-gate 					break;
495*7c478bd9Sstevel@tonic-gate 				}
496*7c478bd9Sstevel@tonic-gate 				sub = evch_dl_next(&eqp->eq_subscr, sub);
497*7c478bd9Sstevel@tonic-gate 				repeatcount = EVCH_MAX_TRY_DELIVERY;
498*7c478bd9Sstevel@tonic-gate 			}
499*7c478bd9Sstevel@tonic-gate 			eqp->eq_curevent = NULL;
500*7c478bd9Sstevel@tonic-gate 
501*7c478bd9Sstevel@tonic-gate 			/* Free event data and queue element */
502*7c478bd9Sstevel@tonic-gate 			evch_gevent_free((evch_gevent_t *)qep->q_objref);
503*7c478bd9Sstevel@tonic-gate 			kmem_free(qep, qep->q_objsize);
504*7c478bd9Sstevel@tonic-gate 		}
505*7c478bd9Sstevel@tonic-gate 
506*7c478bd9Sstevel@tonic-gate 		/* Wait for next event or end of hold mode if set */
507*7c478bd9Sstevel@tonic-gate 		evch_delivery_hold(eqp, &cprinfo);
508*7c478bd9Sstevel@tonic-gate 	}
509*7c478bd9Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);	/* Does mutex_exit of eqp->eq_queuemx */
510*7c478bd9Sstevel@tonic-gate 	thread_exit();
511*7c478bd9Sstevel@tonic-gate }
512*7c478bd9Sstevel@tonic-gate 
513*7c478bd9Sstevel@tonic-gate /*
514*7c478bd9Sstevel@tonic-gate  * Create the event delivery thread for an existing event queue.
515*7c478bd9Sstevel@tonic-gate  */
516*7c478bd9Sstevel@tonic-gate static void
517*7c478bd9Sstevel@tonic-gate evch_evq_thrcreate(evch_eventq_t *eqp)
518*7c478bd9Sstevel@tonic-gate {
519*7c478bd9Sstevel@tonic-gate 	kthread_t *thp;
520*7c478bd9Sstevel@tonic-gate 
521*7c478bd9Sstevel@tonic-gate 	thp = thread_create(NULL, 0, evch_delivery_thr, (char *)eqp, 0, &p0,
522*7c478bd9Sstevel@tonic-gate 	    TS_RUN, minclsyspri);
523*7c478bd9Sstevel@tonic-gate 	eqp->eq_thrid = thp->t_did;
524*7c478bd9Sstevel@tonic-gate }
525*7c478bd9Sstevel@tonic-gate 
526*7c478bd9Sstevel@tonic-gate /*
527*7c478bd9Sstevel@tonic-gate  * Create event queue.
528*7c478bd9Sstevel@tonic-gate  */
529*7c478bd9Sstevel@tonic-gate static evch_eventq_t *
530*7c478bd9Sstevel@tonic-gate evch_evq_create()
531*7c478bd9Sstevel@tonic-gate {
532*7c478bd9Sstevel@tonic-gate 	evch_eventq_t *p;
533*7c478bd9Sstevel@tonic-gate 
534*7c478bd9Sstevel@tonic-gate 	/* Allocate and initialize event queue descriptor */
535*7c478bd9Sstevel@tonic-gate 	p = kmem_zalloc(sizeof (evch_eventq_t), KM_SLEEP);
536*7c478bd9Sstevel@tonic-gate 	mutex_init(&p->eq_queuemx, NULL, MUTEX_DEFAULT, NULL);
537*7c478bd9Sstevel@tonic-gate 	cv_init(&p->eq_thrsleepcv, NULL, CV_DEFAULT, NULL);
538*7c478bd9Sstevel@tonic-gate 	evch_q_init(&p->eq_eventq);
539*7c478bd9Sstevel@tonic-gate 	evch_dl_init(&p->eq_subscr);
540*7c478bd9Sstevel@tonic-gate 	cv_init(&p->eq_dactivecv, NULL, CV_DEFAULT, NULL);
541*7c478bd9Sstevel@tonic-gate 	cv_init(&p->eq_onholdcv, NULL, CV_DEFAULT, NULL);
542*7c478bd9Sstevel@tonic-gate 
543*7c478bd9Sstevel@tonic-gate 	/* Create delivery thread */
544*7c478bd9Sstevel@tonic-gate 	if (evq_initcomplete) {
545*7c478bd9Sstevel@tonic-gate 		evch_evq_thrcreate(p);
546*7c478bd9Sstevel@tonic-gate 	}
547*7c478bd9Sstevel@tonic-gate 	return (p);
548*7c478bd9Sstevel@tonic-gate }
549*7c478bd9Sstevel@tonic-gate 
550*7c478bd9Sstevel@tonic-gate /*
551*7c478bd9Sstevel@tonic-gate  * Destroy an event queue. All subscribers have to be unsubscribed prior to
552*7c478bd9Sstevel@tonic-gate  * this call.
553*7c478bd9Sstevel@tonic-gate  */
554*7c478bd9Sstevel@tonic-gate static void
555*7c478bd9Sstevel@tonic-gate evch_evq_destroy(evch_eventq_t *eqp)
556*7c478bd9Sstevel@tonic-gate {
557*7c478bd9Sstevel@tonic-gate 	evch_qelem_t *qep;
558*7c478bd9Sstevel@tonic-gate 
559*7c478bd9Sstevel@tonic-gate 	ASSERT(evch_dl_getnum(&eqp->eq_subscr) == 0);
560*7c478bd9Sstevel@tonic-gate 	/* Kill delivery thread */
561*7c478bd9Sstevel@tonic-gate 	if (eqp->eq_thrid != NULL) {
562*7c478bd9Sstevel@tonic-gate 		mutex_enter(&eqp->eq_queuemx);
563*7c478bd9Sstevel@tonic-gate 		eqp->eq_tabortflag = 1;
564*7c478bd9Sstevel@tonic-gate 		eqp->eq_holdmode = 0;
565*7c478bd9Sstevel@tonic-gate 		cv_signal(&eqp->eq_thrsleepcv);
566*7c478bd9Sstevel@tonic-gate 		mutex_exit(&eqp->eq_queuemx);
567*7c478bd9Sstevel@tonic-gate 		thread_join(eqp->eq_thrid);
568*7c478bd9Sstevel@tonic-gate 	}
569*7c478bd9Sstevel@tonic-gate 
570*7c478bd9Sstevel@tonic-gate 	/* Get rid of stale events in the event queue */
571*7c478bd9Sstevel@tonic-gate 	while ((qep = (evch_qelem_t *)evch_q_out(&eqp->eq_eventq)) != NULL) {
572*7c478bd9Sstevel@tonic-gate 		evch_gevent_free((evch_gevent_t *)qep->q_objref);
573*7c478bd9Sstevel@tonic-gate 		kmem_free(qep, qep->q_objsize);
574*7c478bd9Sstevel@tonic-gate 	}
575*7c478bd9Sstevel@tonic-gate 
576*7c478bd9Sstevel@tonic-gate 	/* Wrap up event queue structure */
577*7c478bd9Sstevel@tonic-gate 	cv_destroy(&eqp->eq_onholdcv);
578*7c478bd9Sstevel@tonic-gate 	cv_destroy(&eqp->eq_dactivecv);
579*7c478bd9Sstevel@tonic-gate 	cv_destroy(&eqp->eq_thrsleepcv);
580*7c478bd9Sstevel@tonic-gate 	evch_dl_fini(&eqp->eq_subscr);
581*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&eqp->eq_queuemx);
582*7c478bd9Sstevel@tonic-gate 
583*7c478bd9Sstevel@tonic-gate 	/* Free descriptor structure */
584*7c478bd9Sstevel@tonic-gate 	kmem_free(eqp, sizeof (evch_eventq_t));
585*7c478bd9Sstevel@tonic-gate }
586*7c478bd9Sstevel@tonic-gate 
587*7c478bd9Sstevel@tonic-gate /*
588*7c478bd9Sstevel@tonic-gate  * Subscribe to an event queue. Every subscriber provides a filter callback
589*7c478bd9Sstevel@tonic-gate  * routine and an event delivery callback routine.
590*7c478bd9Sstevel@tonic-gate  */
591*7c478bd9Sstevel@tonic-gate static evch_evqsub_t *
592*7c478bd9Sstevel@tonic-gate evch_evq_sub(evch_eventq_t *eqp, filter_f filter, void *fcookie,
593*7c478bd9Sstevel@tonic-gate     deliver_f callb, void *cbcookie)
594*7c478bd9Sstevel@tonic-gate {
595*7c478bd9Sstevel@tonic-gate 	evch_evqsub_t *sp = kmem_zalloc(sizeof (evch_evqsub_t), KM_SLEEP);
596*7c478bd9Sstevel@tonic-gate 
597*7c478bd9Sstevel@tonic-gate 	/* Initialize subscriber structure */
598*7c478bd9Sstevel@tonic-gate 	sp->su_filter = filter;
599*7c478bd9Sstevel@tonic-gate 	sp->su_fcookie = fcookie;
600*7c478bd9Sstevel@tonic-gate 	sp->su_callb = callb;
601*7c478bd9Sstevel@tonic-gate 	sp->su_cbcookie = cbcookie;
602*7c478bd9Sstevel@tonic-gate 
603*7c478bd9Sstevel@tonic-gate 	/* Add subscription to queue */
604*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
605*7c478bd9Sstevel@tonic-gate 	evch_dl_add(&eqp->eq_subscr, &sp->su_link);
606*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
607*7c478bd9Sstevel@tonic-gate 	return (sp);
608*7c478bd9Sstevel@tonic-gate }
609*7c478bd9Sstevel@tonic-gate 
610*7c478bd9Sstevel@tonic-gate /*
611*7c478bd9Sstevel@tonic-gate  * Unsubscribe from an event queue.
612*7c478bd9Sstevel@tonic-gate  */
613*7c478bd9Sstevel@tonic-gate static void
614*7c478bd9Sstevel@tonic-gate evch_evq_unsub(evch_eventq_t *eqp, evch_evqsub_t *sp)
615*7c478bd9Sstevel@tonic-gate {
616*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
617*7c478bd9Sstevel@tonic-gate 
618*7c478bd9Sstevel@tonic-gate 	/* Wait if delivery is just in progress */
619*7c478bd9Sstevel@tonic-gate 	if (eqp->eq_dactive) {
620*7c478bd9Sstevel@tonic-gate 		cv_wait(&eqp->eq_dactivecv, &eqp->eq_queuemx);
621*7c478bd9Sstevel@tonic-gate 	}
622*7c478bd9Sstevel@tonic-gate 	evch_dl_del(&eqp->eq_subscr, &sp->su_link);
623*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
624*7c478bd9Sstevel@tonic-gate 	kmem_free(sp, sizeof (evch_evqsub_t));
625*7c478bd9Sstevel@tonic-gate }
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate /*
628*7c478bd9Sstevel@tonic-gate  * Publish an event. Returns 0 on success and -1 if memory alloc failed.
629*7c478bd9Sstevel@tonic-gate  */
630*7c478bd9Sstevel@tonic-gate static int
631*7c478bd9Sstevel@tonic-gate evch_evq_pub(evch_eventq_t *eqp, void *ev, int flags)
632*7c478bd9Sstevel@tonic-gate {
633*7c478bd9Sstevel@tonic-gate 	size_t size;
634*7c478bd9Sstevel@tonic-gate 	evch_qelem_t	*qep;
635*7c478bd9Sstevel@tonic-gate 	evch_gevent_t	*evp = GEVENT(ev);
636*7c478bd9Sstevel@tonic-gate 
637*7c478bd9Sstevel@tonic-gate 	size = sizeof (evch_qelem_t);
638*7c478bd9Sstevel@tonic-gate 	if (flags & EVCH_TRYHARD) {
639*7c478bd9Sstevel@tonic-gate 		qep = kmem_alloc_tryhard(size, &size, KM_NOSLEEP);
640*7c478bd9Sstevel@tonic-gate 	} else {
641*7c478bd9Sstevel@tonic-gate 		qep = kmem_alloc(size, flags & EVCH_NOSLEEP ?
642*7c478bd9Sstevel@tonic-gate 		    KM_NOSLEEP : KM_SLEEP);
643*7c478bd9Sstevel@tonic-gate 	}
644*7c478bd9Sstevel@tonic-gate 	if (qep == NULL) {
645*7c478bd9Sstevel@tonic-gate 		return (-1);
646*7c478bd9Sstevel@tonic-gate 	}
647*7c478bd9Sstevel@tonic-gate 	qep->q_objref = (void *)evp;
648*7c478bd9Sstevel@tonic-gate 	qep->q_objsize = size;
649*7c478bd9Sstevel@tonic-gate 	atomic_add_32(&evp->ge_refcount, 1);
650*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
651*7c478bd9Sstevel@tonic-gate 	evch_q_in(&eqp->eq_eventq, qep);
652*7c478bd9Sstevel@tonic-gate 
653*7c478bd9Sstevel@tonic-gate 	/* Wakeup delivery thread */
654*7c478bd9Sstevel@tonic-gate 	cv_signal(&eqp->eq_thrsleepcv);
655*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
656*7c478bd9Sstevel@tonic-gate 	return (0);
657*7c478bd9Sstevel@tonic-gate }
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate /*
660*7c478bd9Sstevel@tonic-gate  * Enter hold mode of an event queue. Event delivery thread stops event
661*7c478bd9Sstevel@tonic-gate  * handling after delivery of current event (if any).
662*7c478bd9Sstevel@tonic-gate  */
663*7c478bd9Sstevel@tonic-gate static void
664*7c478bd9Sstevel@tonic-gate evch_evq_stop(evch_eventq_t *eqp)
665*7c478bd9Sstevel@tonic-gate {
666*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
667*7c478bd9Sstevel@tonic-gate 	eqp->eq_holdmode = 1;
668*7c478bd9Sstevel@tonic-gate 	if (evq_initcomplete) {
669*7c478bd9Sstevel@tonic-gate 		cv_signal(&eqp->eq_thrsleepcv);
670*7c478bd9Sstevel@tonic-gate 		cv_wait(&eqp->eq_onholdcv, &eqp->eq_queuemx);
671*7c478bd9Sstevel@tonic-gate 	}
672*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
673*7c478bd9Sstevel@tonic-gate }
674*7c478bd9Sstevel@tonic-gate 
675*7c478bd9Sstevel@tonic-gate /*
676*7c478bd9Sstevel@tonic-gate  * Continue event delivery.
677*7c478bd9Sstevel@tonic-gate  */
678*7c478bd9Sstevel@tonic-gate static void
679*7c478bd9Sstevel@tonic-gate evch_evq_continue(evch_eventq_t *eqp)
680*7c478bd9Sstevel@tonic-gate {
681*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eqp->eq_queuemx);
682*7c478bd9Sstevel@tonic-gate 	eqp->eq_holdmode = 0;
683*7c478bd9Sstevel@tonic-gate 	cv_signal(&eqp->eq_thrsleepcv);
684*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eqp->eq_queuemx);
685*7c478bd9Sstevel@tonic-gate }
686*7c478bd9Sstevel@tonic-gate 
687*7c478bd9Sstevel@tonic-gate /*
688*7c478bd9Sstevel@tonic-gate  * Returns status of delivery thread. 0 if running and 1 if on hold.
689*7c478bd9Sstevel@tonic-gate  */
690*7c478bd9Sstevel@tonic-gate static int
691*7c478bd9Sstevel@tonic-gate evch_evq_status(evch_eventq_t *eqp)
692*7c478bd9Sstevel@tonic-gate {
693*7c478bd9Sstevel@tonic-gate 	return (eqp->eq_holdmode);
694*7c478bd9Sstevel@tonic-gate }
695*7c478bd9Sstevel@tonic-gate 
696*7c478bd9Sstevel@tonic-gate /*
697*7c478bd9Sstevel@tonic-gate  * Add a destructor function to an event structure.
698*7c478bd9Sstevel@tonic-gate  */
699*7c478bd9Sstevel@tonic-gate static void
700*7c478bd9Sstevel@tonic-gate evch_evq_evadd_dest(void *ev, destr_f destructor, void *cookie)
701*7c478bd9Sstevel@tonic-gate {
702*7c478bd9Sstevel@tonic-gate 	evch_gevent_t *evp = GEVENT(ev);
703*7c478bd9Sstevel@tonic-gate 
704*7c478bd9Sstevel@tonic-gate 	evp->ge_destruct = destructor;
705*7c478bd9Sstevel@tonic-gate 	evp->ge_dstcookie = cookie;
706*7c478bd9Sstevel@tonic-gate }
707*7c478bd9Sstevel@tonic-gate 
708*7c478bd9Sstevel@tonic-gate /*
709*7c478bd9Sstevel@tonic-gate  * Allocate evch_gevent_t structure. Return address of payload offset of
710*7c478bd9Sstevel@tonic-gate  * evch_gevent_t.  If EVCH_TRYHARD allocation is requested, we use
711*7c478bd9Sstevel@tonic-gate  * kmem_alloc_tryhard to alloc memory of at least paylsize bytes.
712*7c478bd9Sstevel@tonic-gate  *
713*7c478bd9Sstevel@tonic-gate  * If either memory allocation is unsuccessful, we return NULL.
714*7c478bd9Sstevel@tonic-gate  */
715*7c478bd9Sstevel@tonic-gate static void *
716*7c478bd9Sstevel@tonic-gate evch_evq_evzalloc(size_t paylsize, int flag)
717*7c478bd9Sstevel@tonic-gate {
718*7c478bd9Sstevel@tonic-gate 	evch_gevent_t	*evp;
719*7c478bd9Sstevel@tonic-gate 	size_t		rsize, evsize;
720*7c478bd9Sstevel@tonic-gate 
721*7c478bd9Sstevel@tonic-gate 	rsize = offsetof(evch_gevent_t, ge_payload) + paylsize;
722*7c478bd9Sstevel@tonic-gate 	if (flag & EVCH_TRYHARD) {
723*7c478bd9Sstevel@tonic-gate 		evp = kmem_alloc_tryhard(rsize, &evsize, KM_NOSLEEP);
724*7c478bd9Sstevel@tonic-gate 		bzero(evp, rsize);
725*7c478bd9Sstevel@tonic-gate 		evp->ge_size = evsize;
726*7c478bd9Sstevel@tonic-gate 	} else {
727*7c478bd9Sstevel@tonic-gate 		evp = kmem_alloc(rsize, flag & EVCH_NOSLEEP ? KM_NOSLEEP :
728*7c478bd9Sstevel@tonic-gate 		    KM_SLEEP);
729*7c478bd9Sstevel@tonic-gate 		bzero(evp, rsize);
730*7c478bd9Sstevel@tonic-gate 		evp->ge_size = rsize;
731*7c478bd9Sstevel@tonic-gate 	}
732*7c478bd9Sstevel@tonic-gate 
733*7c478bd9Sstevel@tonic-gate 	if (evp) {
734*7c478bd9Sstevel@tonic-gate 		return (&evp->ge_payload);
735*7c478bd9Sstevel@tonic-gate 	}
736*7c478bd9Sstevel@tonic-gate 	return (evp);
737*7c478bd9Sstevel@tonic-gate }
738*7c478bd9Sstevel@tonic-gate 
739*7c478bd9Sstevel@tonic-gate /*
740*7c478bd9Sstevel@tonic-gate  * Free event structure. Argument ev is address of payload offset.
741*7c478bd9Sstevel@tonic-gate  */
742*7c478bd9Sstevel@tonic-gate static void
743*7c478bd9Sstevel@tonic-gate evch_evq_evfree(void *ev)
744*7c478bd9Sstevel@tonic-gate {
745*7c478bd9Sstevel@tonic-gate 	evch_gevent_free(GEVENT(ev));
746*7c478bd9Sstevel@tonic-gate }
747*7c478bd9Sstevel@tonic-gate 
748*7c478bd9Sstevel@tonic-gate /*
749*7c478bd9Sstevel@tonic-gate  * Iterate over all events in the event queue. Begin with an event
750*7c478bd9Sstevel@tonic-gate  * which is currently being delivered. No mutexes are grabbed and no
751*7c478bd9Sstevel@tonic-gate  * resources allocated so that this function can be called in panic
752*7c478bd9Sstevel@tonic-gate  * context too. This function has to be called with ev == NULL initially.
753*7c478bd9Sstevel@tonic-gate  * Actually argument ev is only a flag. Internally the member eq_nextev
754*7c478bd9Sstevel@tonic-gate  * is used to determine the next event. But ev allows for the convenient
755*7c478bd9Sstevel@tonic-gate  * use like
756*7c478bd9Sstevel@tonic-gate  *	ev = NULL;
757*7c478bd9Sstevel@tonic-gate  *	while ((ev = evch_evq_evnext(evp, ev)) != NULL) ...
758*7c478bd9Sstevel@tonic-gate  */
759*7c478bd9Sstevel@tonic-gate static void *
760*7c478bd9Sstevel@tonic-gate evch_evq_evnext(evch_eventq_t *evq, void *ev)
761*7c478bd9Sstevel@tonic-gate {
762*7c478bd9Sstevel@tonic-gate 	if (ev == NULL) {
763*7c478bd9Sstevel@tonic-gate 		evq->eq_nextev = NULL;
764*7c478bd9Sstevel@tonic-gate 		if (evq->eq_curevent != NULL)
765*7c478bd9Sstevel@tonic-gate 			return (&evq->eq_curevent->ge_payload);
766*7c478bd9Sstevel@tonic-gate 	}
767*7c478bd9Sstevel@tonic-gate 	evq->eq_nextev = evch_q_next(&evq->eq_eventq, evq->eq_nextev);
768*7c478bd9Sstevel@tonic-gate 	if (evq->eq_nextev == NULL)
769*7c478bd9Sstevel@tonic-gate 		return (NULL);
770*7c478bd9Sstevel@tonic-gate 	return (&((evch_gevent_t *)evq->eq_nextev->q_objref)->ge_payload);
771*7c478bd9Sstevel@tonic-gate }
772*7c478bd9Sstevel@tonic-gate 
773*7c478bd9Sstevel@tonic-gate /*
774*7c478bd9Sstevel@tonic-gate  * Channel handling functions. First some support functions. Functions belonging
775*7c478bd9Sstevel@tonic-gate  * to the channel handling interface start with evch_ch. The following functions
776*7c478bd9Sstevel@tonic-gate  * make up the channel handling internal interfaces:
777*7c478bd9Sstevel@tonic-gate  *
778*7c478bd9Sstevel@tonic-gate  * evch_chinit		- Initialize channel handling
779*7c478bd9Sstevel@tonic-gate  * evch_chinitthr	- Second step init: initialize threads
780*7c478bd9Sstevel@tonic-gate  * evch_chbind		- Bind to a channel
781*7c478bd9Sstevel@tonic-gate  * evch_chunbind	- Unbind from a channel
782*7c478bd9Sstevel@tonic-gate  * evch_chsubscribe	- Subscribe to a sysevent class
783*7c478bd9Sstevel@tonic-gate  * evch_chunsubscribe	- Unsubscribe
784*7c478bd9Sstevel@tonic-gate  * evch_chpublish	- Publish an event
785*7c478bd9Sstevel@tonic-gate  * evch_chgetnames	- Get names of all channels
786*7c478bd9Sstevel@tonic-gate  * evch_chgetchdata	- Get data of a channel
787*7c478bd9Sstevel@tonic-gate  * evch_chrdevent_init  - Init event q traversal
788*7c478bd9Sstevel@tonic-gate  * evch_chgetnextev	- Read out events queued for a subscriber
789*7c478bd9Sstevel@tonic-gate  * evch_chrdevent_fini  - Finish event q traversal
790*7c478bd9Sstevel@tonic-gate  */
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate /*
793*7c478bd9Sstevel@tonic-gate  * Compare channel name. Used for evch_dl_search to find a channel with the
794*7c478bd9Sstevel@tonic-gate  * name s.
795*7c478bd9Sstevel@tonic-gate  */
796*7c478bd9Sstevel@tonic-gate static int
797*7c478bd9Sstevel@tonic-gate evch_namecmp(evch_dlelem_t *ep, char *s)
798*7c478bd9Sstevel@tonic-gate {
799*7c478bd9Sstevel@tonic-gate 	return (strcmp(((evch_chan_t *)ep)->ch_name, s));
800*7c478bd9Sstevel@tonic-gate }
801*7c478bd9Sstevel@tonic-gate 
802*7c478bd9Sstevel@tonic-gate /*
803*7c478bd9Sstevel@tonic-gate  * Sysevent filter callback routine. Enables event delivery only if it matches
804*7c478bd9Sstevel@tonic-gate  * the event class string given by parameter cookie.
805*7c478bd9Sstevel@tonic-gate  */
806*7c478bd9Sstevel@tonic-gate static int
807*7c478bd9Sstevel@tonic-gate evch_class_filter(void *ev, void *cookie)
808*7c478bd9Sstevel@tonic-gate {
809*7c478bd9Sstevel@tonic-gate 	char *class = (char *)cookie;
810*7c478bd9Sstevel@tonic-gate 
811*7c478bd9Sstevel@tonic-gate 	if (class == NULL || strcmp(SE_CLASS_NAME(ev), class) == 0) {
812*7c478bd9Sstevel@tonic-gate 		return (EVQ_DELIVER);
813*7c478bd9Sstevel@tonic-gate 	}
814*7c478bd9Sstevel@tonic-gate 	return (EVQ_IGNORE);
815*7c478bd9Sstevel@tonic-gate }
816*7c478bd9Sstevel@tonic-gate 
817*7c478bd9Sstevel@tonic-gate /*
818*7c478bd9Sstevel@tonic-gate  * Callback routine to propagate the event into a per subscriber queue.
819*7c478bd9Sstevel@tonic-gate  */
820*7c478bd9Sstevel@tonic-gate static int
821*7c478bd9Sstevel@tonic-gate evch_subq_deliver(void *evp, void *cookie)
822*7c478bd9Sstevel@tonic-gate {
823*7c478bd9Sstevel@tonic-gate 	evch_subd_t *p = (evch_subd_t *)cookie;
824*7c478bd9Sstevel@tonic-gate 
825*7c478bd9Sstevel@tonic-gate 	(void) evch_evq_pub(p->sd_queue, evp, EVCH_SLEEP);
826*7c478bd9Sstevel@tonic-gate 	return (EVQ_CONT);
827*7c478bd9Sstevel@tonic-gate }
828*7c478bd9Sstevel@tonic-gate 
829*7c478bd9Sstevel@tonic-gate /*
830*7c478bd9Sstevel@tonic-gate  * Call kernel callback routine for sysevent kernel delivery.
831*7c478bd9Sstevel@tonic-gate  */
832*7c478bd9Sstevel@tonic-gate static int
833*7c478bd9Sstevel@tonic-gate evch_kern_deliver(void *evp, void *cookie)
834*7c478bd9Sstevel@tonic-gate {
835*7c478bd9Sstevel@tonic-gate 	sysevent_impl_t	*ev = (sysevent_impl_t *)evp;
836*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*sdp = (evch_subd_t *)cookie;
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate 	return (sdp->sd_callback(ev, sdp->sd_cbcookie));
839*7c478bd9Sstevel@tonic-gate }
840*7c478bd9Sstevel@tonic-gate 
841*7c478bd9Sstevel@tonic-gate /*
842*7c478bd9Sstevel@tonic-gate  * Door upcall for user land sysevent delivery.
843*7c478bd9Sstevel@tonic-gate  */
844*7c478bd9Sstevel@tonic-gate static int
845*7c478bd9Sstevel@tonic-gate evch_door_deliver(void *evp, void *cookie)
846*7c478bd9Sstevel@tonic-gate {
847*7c478bd9Sstevel@tonic-gate 	int		error;
848*7c478bd9Sstevel@tonic-gate 	size_t		size;
849*7c478bd9Sstevel@tonic-gate 	sysevent_impl_t	*ev = (sysevent_impl_t *)evp;
850*7c478bd9Sstevel@tonic-gate 	door_arg_t	darg;
851*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*sdp = (evch_subd_t *)cookie;
852*7c478bd9Sstevel@tonic-gate 	int		nticks = EVCH_MIN_PAUSE;
853*7c478bd9Sstevel@tonic-gate 	uint32_t	retval;
854*7c478bd9Sstevel@tonic-gate 	int		retry = 20;
855*7c478bd9Sstevel@tonic-gate 
856*7c478bd9Sstevel@tonic-gate 	/* Initialize door args */
857*7c478bd9Sstevel@tonic-gate 	size = sizeof (sysevent_impl_t) + SE_PAYLOAD_SZ(ev);
858*7c478bd9Sstevel@tonic-gate 
859*7c478bd9Sstevel@tonic-gate 	darg.rbuf = (char *)&retval;
860*7c478bd9Sstevel@tonic-gate 	darg.rsize = sizeof (retval);
861*7c478bd9Sstevel@tonic-gate 	darg.data_ptr = (char *)ev;
862*7c478bd9Sstevel@tonic-gate 	darg.data_size = size;
863*7c478bd9Sstevel@tonic-gate 	darg.desc_ptr = NULL;
864*7c478bd9Sstevel@tonic-gate 	darg.desc_num = 0;
865*7c478bd9Sstevel@tonic-gate 
866*7c478bd9Sstevel@tonic-gate 	for (;;) {
867*7c478bd9Sstevel@tonic-gate 		if ((error = door_ki_upcall(sdp->sd_door, &darg)) == 0) {
868*7c478bd9Sstevel@tonic-gate 			break;
869*7c478bd9Sstevel@tonic-gate 		}
870*7c478bd9Sstevel@tonic-gate 		switch (error) {
871*7c478bd9Sstevel@tonic-gate 		case EAGAIN:
872*7c478bd9Sstevel@tonic-gate 			/* Cannot deliver event - process may be forking */
873*7c478bd9Sstevel@tonic-gate 			delay(nticks);
874*7c478bd9Sstevel@tonic-gate 			nticks <<= 1;
875*7c478bd9Sstevel@tonic-gate 			if (nticks > EVCH_MAX_PAUSE) {
876*7c478bd9Sstevel@tonic-gate 				nticks = EVCH_MAX_PAUSE;
877*7c478bd9Sstevel@tonic-gate 			}
878*7c478bd9Sstevel@tonic-gate 			if (retry-- <= 0) {
879*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_CONT, "event delivery thread: "
880*7c478bd9Sstevel@tonic-gate 				    "door_ki_upcall error EAGAIN\n");
881*7c478bd9Sstevel@tonic-gate 				return (EVQ_CONT);
882*7c478bd9Sstevel@tonic-gate 			}
883*7c478bd9Sstevel@tonic-gate 			break;
884*7c478bd9Sstevel@tonic-gate 		case EINTR:
885*7c478bd9Sstevel@tonic-gate 		case EBADF:
886*7c478bd9Sstevel@tonic-gate 			/* Process died */
887*7c478bd9Sstevel@tonic-gate 			return (EVQ_SLEEP);
888*7c478bd9Sstevel@tonic-gate 		default:
889*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT,
890*7c478bd9Sstevel@tonic-gate 			    "event delivery thread: door_ki_upcall error %d\n",
891*7c478bd9Sstevel@tonic-gate 			    error);
892*7c478bd9Sstevel@tonic-gate 			return (EVQ_CONT);
893*7c478bd9Sstevel@tonic-gate 		}
894*7c478bd9Sstevel@tonic-gate 	}
895*7c478bd9Sstevel@tonic-gate 	if (retval == EAGAIN) {
896*7c478bd9Sstevel@tonic-gate 		return (EVQ_AGAIN);
897*7c478bd9Sstevel@tonic-gate 	}
898*7c478bd9Sstevel@tonic-gate 	return (EVQ_CONT);
899*7c478bd9Sstevel@tonic-gate }
900*7c478bd9Sstevel@tonic-gate 
901*7c478bd9Sstevel@tonic-gate /*
902*7c478bd9Sstevel@tonic-gate  * Callback routine for evch_dl_search() to compare subscriber id's. Used by
903*7c478bd9Sstevel@tonic-gate  * evch_subscribe() and evch_chrdevent_init().
904*7c478bd9Sstevel@tonic-gate  */
905*7c478bd9Sstevel@tonic-gate static int
906*7c478bd9Sstevel@tonic-gate evch_subidcmp(evch_dlelem_t *ep, char *s)
907*7c478bd9Sstevel@tonic-gate {
908*7c478bd9Sstevel@tonic-gate 	return (strcmp(((evch_subd_t *)ep)->sd_ident, s));
909*7c478bd9Sstevel@tonic-gate }
910*7c478bd9Sstevel@tonic-gate 
911*7c478bd9Sstevel@tonic-gate /*
912*7c478bd9Sstevel@tonic-gate  * Callback routine for evch_dl_search() to find a subscriber with EVCH_SUB_DUMP
913*7c478bd9Sstevel@tonic-gate  * set (indicated by sub->sd_dump != 0). Used by evch_chrdevent_init() and
914*7c478bd9Sstevel@tonic-gate  * evch_subscribe(). Needs to returns 0 if subscriber with sd_dump set is
915*7c478bd9Sstevel@tonic-gate  * found.
916*7c478bd9Sstevel@tonic-gate  */
917*7c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
918*7c478bd9Sstevel@tonic-gate static int
919*7c478bd9Sstevel@tonic-gate evch_dumpflgcmp(evch_dlelem_t *ep, char *s)
920*7c478bd9Sstevel@tonic-gate {
921*7c478bd9Sstevel@tonic-gate 	return (((evch_subd_t *)ep)->sd_dump ? 0 : 1);
922*7c478bd9Sstevel@tonic-gate }
923*7c478bd9Sstevel@tonic-gate 
924*7c478bd9Sstevel@tonic-gate /*
925*7c478bd9Sstevel@tonic-gate  * Event destructor function. Used to maintain the number of events per channel.
926*7c478bd9Sstevel@tonic-gate  */
927*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
928*7c478bd9Sstevel@tonic-gate static void
929*7c478bd9Sstevel@tonic-gate evch_destr_event(void *ev, void *ch)
930*7c478bd9Sstevel@tonic-gate {
931*7c478bd9Sstevel@tonic-gate 	evch_chan_t *chp = (evch_chan_t *)ch;
932*7c478bd9Sstevel@tonic-gate 
933*7c478bd9Sstevel@tonic-gate 	mutex_enter(&chp->ch_pubmx);
934*7c478bd9Sstevel@tonic-gate 	chp->ch_nevents--;
935*7c478bd9Sstevel@tonic-gate 	cv_signal(&chp->ch_pubcv);
936*7c478bd9Sstevel@tonic-gate 	mutex_exit(&chp->ch_pubmx);
937*7c478bd9Sstevel@tonic-gate }
938*7c478bd9Sstevel@tonic-gate 
939*7c478bd9Sstevel@tonic-gate /*
940*7c478bd9Sstevel@tonic-gate  * Integer square root according to Newton's iteration.
941*7c478bd9Sstevel@tonic-gate  */
942*7c478bd9Sstevel@tonic-gate static uint32_t
943*7c478bd9Sstevel@tonic-gate evch_isqrt(uint64_t n)
944*7c478bd9Sstevel@tonic-gate {
945*7c478bd9Sstevel@tonic-gate 	uint64_t	x = n >> 1;
946*7c478bd9Sstevel@tonic-gate 	uint64_t	xn = x - 1;
947*7c478bd9Sstevel@tonic-gate 	static uint32_t	lowval[] = { 0, 1, 1, 2 };
948*7c478bd9Sstevel@tonic-gate 
949*7c478bd9Sstevel@tonic-gate 	if (n < 4) {
950*7c478bd9Sstevel@tonic-gate 		return (lowval[n]);
951*7c478bd9Sstevel@tonic-gate 	}
952*7c478bd9Sstevel@tonic-gate 	while (xn < x) {
953*7c478bd9Sstevel@tonic-gate 		x = xn;
954*7c478bd9Sstevel@tonic-gate 		xn = (x + n / x) / 2;
955*7c478bd9Sstevel@tonic-gate 	}
956*7c478bd9Sstevel@tonic-gate 	return ((uint32_t)xn);
957*7c478bd9Sstevel@tonic-gate }
958*7c478bd9Sstevel@tonic-gate 
959*7c478bd9Sstevel@tonic-gate /*
960*7c478bd9Sstevel@tonic-gate  * First step sysevent channel initialization. Called when kernel memory
961*7c478bd9Sstevel@tonic-gate  * allocator is initialized.
962*7c478bd9Sstevel@tonic-gate  */
963*7c478bd9Sstevel@tonic-gate static void
964*7c478bd9Sstevel@tonic-gate evch_chinit()
965*7c478bd9Sstevel@tonic-gate {
966*7c478bd9Sstevel@tonic-gate 	size_t k;
967*7c478bd9Sstevel@tonic-gate 
968*7c478bd9Sstevel@tonic-gate 	/*
969*7c478bd9Sstevel@tonic-gate 	 * Calculate limits: max no of channels and max no of events per
970*7c478bd9Sstevel@tonic-gate 	 * channel. The smallest machine with 128 MByte will allow for
971*7c478bd9Sstevel@tonic-gate 	 * >= 8 channels and an upper limit of 2048 events per channel.
972*7c478bd9Sstevel@tonic-gate 	 * The event limit is the number of channels times 256 (hence
973*7c478bd9Sstevel@tonic-gate 	 * the shift factor of 8). These number where selected arbitrarily.
974*7c478bd9Sstevel@tonic-gate 	 */
975*7c478bd9Sstevel@tonic-gate 	k = kmem_maxavail() >> 20;
976*7c478bd9Sstevel@tonic-gate 	evch_channels_max = min(evch_isqrt(k), EVCH_MAX_CHANNELS);
977*7c478bd9Sstevel@tonic-gate 	evch_events_max = evch_channels_max << 8;
978*7c478bd9Sstevel@tonic-gate 
979*7c478bd9Sstevel@tonic-gate 	/*
980*7c478bd9Sstevel@tonic-gate 	 * Will trigger creation of the global zone's evch state.
981*7c478bd9Sstevel@tonic-gate 	 */
982*7c478bd9Sstevel@tonic-gate 	zone_key_create(&evch_zone_key, evch_zoneinit, NULL, evch_zonefree);
983*7c478bd9Sstevel@tonic-gate }
984*7c478bd9Sstevel@tonic-gate 
985*7c478bd9Sstevel@tonic-gate /*
986*7c478bd9Sstevel@tonic-gate  * Second step sysevent channel initialization. Called when threads are ready.
987*7c478bd9Sstevel@tonic-gate  */
988*7c478bd9Sstevel@tonic-gate static void
989*7c478bd9Sstevel@tonic-gate evch_chinitthr()
990*7c478bd9Sstevel@tonic-gate {
991*7c478bd9Sstevel@tonic-gate 	struct evch_globals *eg;
992*7c478bd9Sstevel@tonic-gate 	evch_chan_t	*chp;
993*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*sdp;
994*7c478bd9Sstevel@tonic-gate 
995*7c478bd9Sstevel@tonic-gate 	/*
996*7c478bd9Sstevel@tonic-gate 	 * We're early enough in boot that we know that only the global
997*7c478bd9Sstevel@tonic-gate 	 * zone exists; we only need to initialize its threads.
998*7c478bd9Sstevel@tonic-gate 	 */
999*7c478bd9Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, global_zone);
1000*7c478bd9Sstevel@tonic-gate 	ASSERT(eg != NULL);
1001*7c478bd9Sstevel@tonic-gate 
1002*7c478bd9Sstevel@tonic-gate 	for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL;
1003*7c478bd9Sstevel@tonic-gate 	    chp = evch_dl_next(&eg->evch_list, chp)) {
1004*7c478bd9Sstevel@tonic-gate 		for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp;
1005*7c478bd9Sstevel@tonic-gate 		    sdp = evch_dl_next(&chp->ch_subscr, sdp)) {
1006*7c478bd9Sstevel@tonic-gate 			evch_evq_thrcreate(sdp->sd_queue);
1007*7c478bd9Sstevel@tonic-gate 		}
1008*7c478bd9Sstevel@tonic-gate 		evch_evq_thrcreate(chp->ch_queue);
1009*7c478bd9Sstevel@tonic-gate 	}
1010*7c478bd9Sstevel@tonic-gate 	evq_initcomplete = 1;
1011*7c478bd9Sstevel@tonic-gate }
1012*7c478bd9Sstevel@tonic-gate 
1013*7c478bd9Sstevel@tonic-gate /*
1014*7c478bd9Sstevel@tonic-gate  * Sysevent channel bind. Create channel and allocate binding structure.
1015*7c478bd9Sstevel@tonic-gate  */
1016*7c478bd9Sstevel@tonic-gate static int
1017*7c478bd9Sstevel@tonic-gate evch_chbind(const char *chnam, evch_bind_t **scpp, uint32_t flags)
1018*7c478bd9Sstevel@tonic-gate {
1019*7c478bd9Sstevel@tonic-gate 	struct evch_globals *eg;
1020*7c478bd9Sstevel@tonic-gate 	evch_bind_t	*bp;
1021*7c478bd9Sstevel@tonic-gate 	evch_chan_t	*p;
1022*7c478bd9Sstevel@tonic-gate 	char		*chn;
1023*7c478bd9Sstevel@tonic-gate 	size_t		namlen;
1024*7c478bd9Sstevel@tonic-gate 	int		rv;
1025*7c478bd9Sstevel@tonic-gate 
1026*7c478bd9Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, curproc->p_zone);
1027*7c478bd9Sstevel@tonic-gate 	ASSERT(eg != NULL);
1028*7c478bd9Sstevel@tonic-gate 
1029*7c478bd9Sstevel@tonic-gate 	/* Create channel if it does not exist */
1030*7c478bd9Sstevel@tonic-gate 	ASSERT(evch_dl_is_init(&eg->evch_list));
1031*7c478bd9Sstevel@tonic-gate 	if ((namlen = strlen(chnam) + 1) > MAX_CHNAME_LEN) {
1032*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1033*7c478bd9Sstevel@tonic-gate 	}
1034*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eg->evch_list_lock);
1035*7c478bd9Sstevel@tonic-gate 	if ((p = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp,
1036*7c478bd9Sstevel@tonic-gate 	    (char *)chnam)) == NULL) {
1037*7c478bd9Sstevel@tonic-gate 		if (flags & EVCH_CREAT) {
1038*7c478bd9Sstevel@tonic-gate 			if (evch_dl_getnum(&eg->evch_list) >=
1039*7c478bd9Sstevel@tonic-gate 			    evch_channels_max) {
1040*7c478bd9Sstevel@tonic-gate 				mutex_exit(&eg->evch_list_lock);
1041*7c478bd9Sstevel@tonic-gate 				return (ENOMEM);
1042*7c478bd9Sstevel@tonic-gate 			}
1043*7c478bd9Sstevel@tonic-gate 			chn = kmem_alloc(namlen, KM_SLEEP);
1044*7c478bd9Sstevel@tonic-gate 			bcopy(chnam, chn, namlen);
1045*7c478bd9Sstevel@tonic-gate 
1046*7c478bd9Sstevel@tonic-gate 			/* Allocate and initialize channel descriptor */
1047*7c478bd9Sstevel@tonic-gate 			p = kmem_zalloc(sizeof (evch_chan_t), KM_SLEEP);
1048*7c478bd9Sstevel@tonic-gate 			p->ch_name = chn;
1049*7c478bd9Sstevel@tonic-gate 			p->ch_namelen = namlen;
1050*7c478bd9Sstevel@tonic-gate 			mutex_init(&p->ch_mutex, NULL, MUTEX_DEFAULT, NULL);
1051*7c478bd9Sstevel@tonic-gate 			p->ch_queue = evch_evq_create();
1052*7c478bd9Sstevel@tonic-gate 			evch_dl_init(&p->ch_subscr);
1053*7c478bd9Sstevel@tonic-gate 			if (evq_initcomplete) {
1054*7c478bd9Sstevel@tonic-gate 				p->ch_uid = crgetuid(curthread->t_cred);
1055*7c478bd9Sstevel@tonic-gate 				p->ch_gid = crgetgid(curthread->t_cred);
1056*7c478bd9Sstevel@tonic-gate 			}
1057*7c478bd9Sstevel@tonic-gate 			cv_init(&p->ch_pubcv, NULL, CV_DEFAULT, NULL);
1058*7c478bd9Sstevel@tonic-gate 			mutex_init(&p->ch_pubmx, NULL, MUTEX_DEFAULT, NULL);
1059*7c478bd9Sstevel@tonic-gate 			p->ch_maxev = min(EVCH_DEFAULT_EVENTS, evch_events_max);
1060*7c478bd9Sstevel@tonic-gate 			p->ch_maxsubscr = EVCH_MAX_SUBSCRIPTIONS;
1061*7c478bd9Sstevel@tonic-gate 			p->ch_maxbinds = evch_bindings_max;
1062*7c478bd9Sstevel@tonic-gate 			p->ch_ctime = gethrestime_sec();
1063*7c478bd9Sstevel@tonic-gate 			if (flags & EVCH_HOLD_PEND) {
1064*7c478bd9Sstevel@tonic-gate 				p->ch_holdpend = 1;
1065*7c478bd9Sstevel@tonic-gate 				evch_evq_stop(p->ch_queue);
1066*7c478bd9Sstevel@tonic-gate 			}
1067*7c478bd9Sstevel@tonic-gate 
1068*7c478bd9Sstevel@tonic-gate 			/* Put new descriptor into channel list */
1069*7c478bd9Sstevel@tonic-gate 			evch_dl_add(&eg->evch_list, (evch_dlelem_t *)p);
1070*7c478bd9Sstevel@tonic-gate 		} else {
1071*7c478bd9Sstevel@tonic-gate 			mutex_exit(&eg->evch_list_lock);
1072*7c478bd9Sstevel@tonic-gate 			return (ENOENT);
1073*7c478bd9Sstevel@tonic-gate 		}
1074*7c478bd9Sstevel@tonic-gate 	}
1075*7c478bd9Sstevel@tonic-gate 
1076*7c478bd9Sstevel@tonic-gate 	/* Check for max binds and create binding */
1077*7c478bd9Sstevel@tonic-gate 	mutex_enter(&p->ch_mutex);
1078*7c478bd9Sstevel@tonic-gate 	if (p->ch_bindings >= p->ch_maxbinds) {
1079*7c478bd9Sstevel@tonic-gate 		rv = ENOMEM;
1080*7c478bd9Sstevel@tonic-gate 		/*
1081*7c478bd9Sstevel@tonic-gate 		 * No need to destroy the channel because this call did not
1082*7c478bd9Sstevel@tonic-gate 		 * create it. Other bindings will be present if ch_maxbinds
1083*7c478bd9Sstevel@tonic-gate 		 * is exceeded.
1084*7c478bd9Sstevel@tonic-gate 		 */
1085*7c478bd9Sstevel@tonic-gate 		goto errorexit;
1086*7c478bd9Sstevel@tonic-gate 	}
1087*7c478bd9Sstevel@tonic-gate 	bp = kmem_alloc(sizeof (evch_bind_t), KM_SLEEP);
1088*7c478bd9Sstevel@tonic-gate 	bp->bd_channel = p;
1089*7c478bd9Sstevel@tonic-gate 	bp->bd_sublst = NULL;
1090*7c478bd9Sstevel@tonic-gate 	p->ch_bindings++;
1091*7c478bd9Sstevel@tonic-gate 	rv = 0;
1092*7c478bd9Sstevel@tonic-gate 	*scpp = bp;
1093*7c478bd9Sstevel@tonic-gate errorexit:
1094*7c478bd9Sstevel@tonic-gate 	mutex_exit(&p->ch_mutex);
1095*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
1096*7c478bd9Sstevel@tonic-gate 	return (rv);
1097*7c478bd9Sstevel@tonic-gate }
1098*7c478bd9Sstevel@tonic-gate 
1099*7c478bd9Sstevel@tonic-gate /*
1100*7c478bd9Sstevel@tonic-gate  * Unbind: Free bind structure. Remove channel if last binding was freed.
1101*7c478bd9Sstevel@tonic-gate  */
1102*7c478bd9Sstevel@tonic-gate static void
1103*7c478bd9Sstevel@tonic-gate evch_chunbind(evch_bind_t *bp)
1104*7c478bd9Sstevel@tonic-gate {
1105*7c478bd9Sstevel@tonic-gate 	struct evch_globals *eg;
1106*7c478bd9Sstevel@tonic-gate 	evch_chan_t *chp = bp->bd_channel;
1107*7c478bd9Sstevel@tonic-gate 
1108*7c478bd9Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, curproc->p_zone);
1109*7c478bd9Sstevel@tonic-gate 	ASSERT(eg != NULL);
1110*7c478bd9Sstevel@tonic-gate 
1111*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eg->evch_list_lock);
1112*7c478bd9Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
1113*7c478bd9Sstevel@tonic-gate 	ASSERT(chp->ch_bindings > 0);
1114*7c478bd9Sstevel@tonic-gate 	chp->ch_bindings--;
1115*7c478bd9Sstevel@tonic-gate 	kmem_free(bp, sizeof (evch_bind_t));
1116*7c478bd9Sstevel@tonic-gate 	if (chp->ch_bindings == 0 && evch_dl_getnum(&chp->ch_subscr) == 0) {
1117*7c478bd9Sstevel@tonic-gate 		/*
1118*7c478bd9Sstevel@tonic-gate 		 * No more bindings or persistent subscriber, destroy channel.
1119*7c478bd9Sstevel@tonic-gate 		 */
1120*7c478bd9Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
1121*7c478bd9Sstevel@tonic-gate 		evch_dl_del(&eg->evch_list, &chp->ch_link);
1122*7c478bd9Sstevel@tonic-gate 		evch_evq_destroy(chp->ch_queue);
1123*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&chp->ch_mutex);
1124*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&chp->ch_pubmx);
1125*7c478bd9Sstevel@tonic-gate 		cv_destroy(&chp->ch_pubcv);
1126*7c478bd9Sstevel@tonic-gate 		kmem_free(chp->ch_name, chp->ch_namelen);
1127*7c478bd9Sstevel@tonic-gate 		kmem_free(chp, sizeof (evch_chan_t));
1128*7c478bd9Sstevel@tonic-gate 	} else
1129*7c478bd9Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
1130*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
1131*7c478bd9Sstevel@tonic-gate }
1132*7c478bd9Sstevel@tonic-gate 
1133*7c478bd9Sstevel@tonic-gate /*
1134*7c478bd9Sstevel@tonic-gate  * Subscribe to a channel. dtype is either EVCH_DELKERN for kernel callbacks
1135*7c478bd9Sstevel@tonic-gate  * or EVCH_DELDOOR for door upcall delivery to user land. Depending on dtype
1136*7c478bd9Sstevel@tonic-gate  * dinfo gives the call back routine address or the door handle.
1137*7c478bd9Sstevel@tonic-gate  */
1138*7c478bd9Sstevel@tonic-gate static int
1139*7c478bd9Sstevel@tonic-gate evch_chsubscribe(evch_bind_t *bp, int dtype, const char *sid, const char *class,
1140*7c478bd9Sstevel@tonic-gate     void *dinfo, void *cookie, int flags, pid_t pid)
1141*7c478bd9Sstevel@tonic-gate {
1142*7c478bd9Sstevel@tonic-gate 	evch_chan_t	*chp = bp->bd_channel;
1143*7c478bd9Sstevel@tonic-gate 	evch_eventq_t	*eqp = chp->ch_queue;
1144*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*sdp;
1145*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*esp;
1146*7c478bd9Sstevel@tonic-gate 	int		(*delivfkt)();
1147*7c478bd9Sstevel@tonic-gate 	char		*clb = NULL;
1148*7c478bd9Sstevel@tonic-gate 	int		clblen = 0;
1149*7c478bd9Sstevel@tonic-gate 	char		*subid;
1150*7c478bd9Sstevel@tonic-gate 	int		subidblen;
1151*7c478bd9Sstevel@tonic-gate 
1152*7c478bd9Sstevel@tonic-gate 	/*
1153*7c478bd9Sstevel@tonic-gate 	 * Check if only known flags are set.
1154*7c478bd9Sstevel@tonic-gate 	 */
1155*7c478bd9Sstevel@tonic-gate 	if (flags & ~(EVCH_SUB_KEEP | EVCH_SUB_DUMP))
1156*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1157*7c478bd9Sstevel@tonic-gate 	/*
1158*7c478bd9Sstevel@tonic-gate 	 * Check if we have already a subscription with that name and if we
1159*7c478bd9Sstevel@tonic-gate 	 * have to reconnect the subscriber to a persistent subscription.
1160*7c478bd9Sstevel@tonic-gate 	 */
1161*7c478bd9Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
1162*7c478bd9Sstevel@tonic-gate 	if ((esp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr,
1163*7c478bd9Sstevel@tonic-gate 	    evch_subidcmp, (char *)sid)) != NULL) {
1164*7c478bd9Sstevel@tonic-gate 		int error = 0;
1165*7c478bd9Sstevel@tonic-gate 		if ((flags & EVCH_SUB_KEEP) && (esp->sd_active == 0)) {
1166*7c478bd9Sstevel@tonic-gate 			/*
1167*7c478bd9Sstevel@tonic-gate 			 * Subscription with the name on hold, reconnect to
1168*7c478bd9Sstevel@tonic-gate 			 * existing queue.
1169*7c478bd9Sstevel@tonic-gate 			 */
1170*7c478bd9Sstevel@tonic-gate 			ASSERT(dtype == EVCH_DELDOOR);
1171*7c478bd9Sstevel@tonic-gate 			esp->sd_subnxt = bp->bd_sublst;
1172*7c478bd9Sstevel@tonic-gate 			bp->bd_sublst = esp;
1173*7c478bd9Sstevel@tonic-gate 			esp->sd_pid = pid;
1174*7c478bd9Sstevel@tonic-gate 			esp->sd_door = (door_handle_t)dinfo;
1175*7c478bd9Sstevel@tonic-gate 			esp->sd_active++;
1176*7c478bd9Sstevel@tonic-gate 			evch_evq_continue(esp->sd_queue);
1177*7c478bd9Sstevel@tonic-gate 		} else {
1178*7c478bd9Sstevel@tonic-gate 			/* Subscriber with given name already exists */
1179*7c478bd9Sstevel@tonic-gate 			error = EEXIST;
1180*7c478bd9Sstevel@tonic-gate 		}
1181*7c478bd9Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
1182*7c478bd9Sstevel@tonic-gate 		return (error);
1183*7c478bd9Sstevel@tonic-gate 	}
1184*7c478bd9Sstevel@tonic-gate 
1185*7c478bd9Sstevel@tonic-gate 	if (evch_dl_getnum(&chp->ch_subscr) >= chp->ch_maxsubscr) {
1186*7c478bd9Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
1187*7c478bd9Sstevel@tonic-gate 		return (ENOMEM);
1188*7c478bd9Sstevel@tonic-gate 	}
1189*7c478bd9Sstevel@tonic-gate 
1190*7c478bd9Sstevel@tonic-gate 	if (flags & EVCH_SUB_DUMP && evch_dl_search(&chp->ch_subscr,
1191*7c478bd9Sstevel@tonic-gate 	    evch_dumpflgcmp, NULL) != NULL) {
1192*7c478bd9Sstevel@tonic-gate 		/*
1193*7c478bd9Sstevel@tonic-gate 		 * Subscription with EVCH_SUB_DUMP flagged already exists.
1194*7c478bd9Sstevel@tonic-gate 		 * Only one subscription with EVCH_SUB_DUMP possible. Return
1195*7c478bd9Sstevel@tonic-gate 		 * error.
1196*7c478bd9Sstevel@tonic-gate 		 */
1197*7c478bd9Sstevel@tonic-gate 		mutex_exit(&chp->ch_mutex);
1198*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1199*7c478bd9Sstevel@tonic-gate 	}
1200*7c478bd9Sstevel@tonic-gate 
1201*7c478bd9Sstevel@tonic-gate 	if (class != NULL) {
1202*7c478bd9Sstevel@tonic-gate 		clblen = strlen(class) + 1;
1203*7c478bd9Sstevel@tonic-gate 		clb = kmem_alloc(clblen, KM_SLEEP);
1204*7c478bd9Sstevel@tonic-gate 		bcopy(class, clb, clblen);
1205*7c478bd9Sstevel@tonic-gate 	}
1206*7c478bd9Sstevel@tonic-gate 
1207*7c478bd9Sstevel@tonic-gate 	subidblen = strlen(sid) + 1;
1208*7c478bd9Sstevel@tonic-gate 	subid = kmem_alloc(subidblen, KM_SLEEP);
1209*7c478bd9Sstevel@tonic-gate 	bcopy(sid, subid, subidblen);
1210*7c478bd9Sstevel@tonic-gate 
1211*7c478bd9Sstevel@tonic-gate 	/* Create per subscriber queue */
1212*7c478bd9Sstevel@tonic-gate 	sdp = kmem_zalloc(sizeof (evch_subd_t), KM_SLEEP);
1213*7c478bd9Sstevel@tonic-gate 	sdp->sd_queue = evch_evq_create();
1214*7c478bd9Sstevel@tonic-gate 
1215*7c478bd9Sstevel@tonic-gate 	/* Subscribe to subscriber queue */
1216*7c478bd9Sstevel@tonic-gate 	sdp->sd_persist = flags & EVCH_SUB_KEEP ? 1 : 0;
1217*7c478bd9Sstevel@tonic-gate 	sdp->sd_dump = flags & EVCH_SUB_DUMP ? 1 : 0;
1218*7c478bd9Sstevel@tonic-gate 	sdp->sd_type = dtype;
1219*7c478bd9Sstevel@tonic-gate 	sdp->sd_cbcookie = cookie;
1220*7c478bd9Sstevel@tonic-gate 	sdp->sd_ident = subid;
1221*7c478bd9Sstevel@tonic-gate 	if (dtype == EVCH_DELKERN) {
1222*7c478bd9Sstevel@tonic-gate 		sdp->sd_callback = (kerndlv_f)dinfo;
1223*7c478bd9Sstevel@tonic-gate 		delivfkt = evch_kern_deliver;
1224*7c478bd9Sstevel@tonic-gate 	} else {
1225*7c478bd9Sstevel@tonic-gate 		sdp->sd_door = (door_handle_t)dinfo;
1226*7c478bd9Sstevel@tonic-gate 		delivfkt = evch_door_deliver;
1227*7c478bd9Sstevel@tonic-gate 	}
1228*7c478bd9Sstevel@tonic-gate 	sdp->sd_ssub =
1229*7c478bd9Sstevel@tonic-gate 	    evch_evq_sub(sdp->sd_queue, NULL, NULL, delivfkt, (void *)sdp);
1230*7c478bd9Sstevel@tonic-gate 
1231*7c478bd9Sstevel@tonic-gate 	/* Connect per subscriber queue to main event queue */
1232*7c478bd9Sstevel@tonic-gate 	sdp->sd_msub = evch_evq_sub(eqp, evch_class_filter, clb,
1233*7c478bd9Sstevel@tonic-gate 	    evch_subq_deliver, (void *)sdp);
1234*7c478bd9Sstevel@tonic-gate 	sdp->sd_classname = clb;
1235*7c478bd9Sstevel@tonic-gate 	sdp->sd_clnsize = clblen;
1236*7c478bd9Sstevel@tonic-gate 	sdp->sd_pid = pid;
1237*7c478bd9Sstevel@tonic-gate 	sdp->sd_active++;
1238*7c478bd9Sstevel@tonic-gate 
1239*7c478bd9Sstevel@tonic-gate 	/* Add subscription to binding */
1240*7c478bd9Sstevel@tonic-gate 	sdp->sd_subnxt = bp->bd_sublst;
1241*7c478bd9Sstevel@tonic-gate 	bp->bd_sublst = sdp;
1242*7c478bd9Sstevel@tonic-gate 
1243*7c478bd9Sstevel@tonic-gate 	/* Add subscription to channel */
1244*7c478bd9Sstevel@tonic-gate 	evch_dl_add(&chp->ch_subscr, &sdp->sd_link);
1245*7c478bd9Sstevel@tonic-gate 	if (chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 1) {
1246*7c478bd9Sstevel@tonic-gate 
1247*7c478bd9Sstevel@tonic-gate 		/* Let main event queue run in case of HOLDPEND */
1248*7c478bd9Sstevel@tonic-gate 		evch_evq_continue(eqp);
1249*7c478bd9Sstevel@tonic-gate 	}
1250*7c478bd9Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
1251*7c478bd9Sstevel@tonic-gate 
1252*7c478bd9Sstevel@tonic-gate 	return (0);
1253*7c478bd9Sstevel@tonic-gate }
1254*7c478bd9Sstevel@tonic-gate 
1255*7c478bd9Sstevel@tonic-gate /*
1256*7c478bd9Sstevel@tonic-gate  * If flag == EVCH_SUB_KEEP only non-persistent subscriptions are deleted.
1257*7c478bd9Sstevel@tonic-gate  * When sid == NULL all subscriptions except the ones with EVCH_SUB_KEEP set
1258*7c478bd9Sstevel@tonic-gate  * are removed.
1259*7c478bd9Sstevel@tonic-gate  */
1260*7c478bd9Sstevel@tonic-gate static void
1261*7c478bd9Sstevel@tonic-gate evch_chunsubscribe(evch_bind_t *bp, const char *sid, uint32_t flags)
1262*7c478bd9Sstevel@tonic-gate {
1263*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*sdp;
1264*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*next;
1265*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*prev;
1266*7c478bd9Sstevel@tonic-gate 	evch_chan_t	*chp = bp->bd_channel;
1267*7c478bd9Sstevel@tonic-gate 
1268*7c478bd9Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
1269*7c478bd9Sstevel@tonic-gate 	if (chp->ch_holdpend) {
1270*7c478bd9Sstevel@tonic-gate 		evch_evq_stop(chp->ch_queue);	/* Hold main event queue */
1271*7c478bd9Sstevel@tonic-gate 	}
1272*7c478bd9Sstevel@tonic-gate 	prev = NULL;
1273*7c478bd9Sstevel@tonic-gate 	for (sdp = bp->bd_sublst; sdp; sdp = next) {
1274*7c478bd9Sstevel@tonic-gate 		if (sid == NULL || strcmp(sid, sdp->sd_ident) == 0) {
1275*7c478bd9Sstevel@tonic-gate 			if (flags == 0 || sdp->sd_persist == 0) {
1276*7c478bd9Sstevel@tonic-gate 				/*
1277*7c478bd9Sstevel@tonic-gate 				 * Disconnect subscriber queue from main event
1278*7c478bd9Sstevel@tonic-gate 				 * queue.
1279*7c478bd9Sstevel@tonic-gate 				 */
1280*7c478bd9Sstevel@tonic-gate 				evch_evq_unsub(chp->ch_queue, sdp->sd_msub);
1281*7c478bd9Sstevel@tonic-gate 
1282*7c478bd9Sstevel@tonic-gate 				/* Destruct per subscriber queue */
1283*7c478bd9Sstevel@tonic-gate 				evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub);
1284*7c478bd9Sstevel@tonic-gate 				evch_evq_destroy(sdp->sd_queue);
1285*7c478bd9Sstevel@tonic-gate 				/*
1286*7c478bd9Sstevel@tonic-gate 				 * Eliminate the subscriber data from channel
1287*7c478bd9Sstevel@tonic-gate 				 * list.
1288*7c478bd9Sstevel@tonic-gate 				 */
1289*7c478bd9Sstevel@tonic-gate 				evch_dl_del(&chp->ch_subscr, &sdp->sd_link);
1290*7c478bd9Sstevel@tonic-gate 				kmem_free(sdp->sd_classname, sdp->sd_clnsize);
1291*7c478bd9Sstevel@tonic-gate 				if (sdp->sd_type == EVCH_DELDOOR) {
1292*7c478bd9Sstevel@tonic-gate 					door_ki_rele(sdp->sd_door);
1293*7c478bd9Sstevel@tonic-gate 				}
1294*7c478bd9Sstevel@tonic-gate 				next = sdp->sd_subnxt;
1295*7c478bd9Sstevel@tonic-gate 				if (prev) {
1296*7c478bd9Sstevel@tonic-gate 					prev->sd_subnxt = next;
1297*7c478bd9Sstevel@tonic-gate 				} else {
1298*7c478bd9Sstevel@tonic-gate 					bp->bd_sublst = next;
1299*7c478bd9Sstevel@tonic-gate 				}
1300*7c478bd9Sstevel@tonic-gate 				kmem_free(sdp->sd_ident,
1301*7c478bd9Sstevel@tonic-gate 				    strlen(sdp->sd_ident) + 1);
1302*7c478bd9Sstevel@tonic-gate 				kmem_free(sdp, sizeof (evch_subd_t));
1303*7c478bd9Sstevel@tonic-gate 			} else {
1304*7c478bd9Sstevel@tonic-gate 				/*
1305*7c478bd9Sstevel@tonic-gate 				 * EVCH_SUB_KEEP case
1306*7c478bd9Sstevel@tonic-gate 				 */
1307*7c478bd9Sstevel@tonic-gate 				evch_evq_stop(sdp->sd_queue);
1308*7c478bd9Sstevel@tonic-gate 				if (sdp->sd_type == EVCH_DELDOOR) {
1309*7c478bd9Sstevel@tonic-gate 					door_ki_rele(sdp->sd_door);
1310*7c478bd9Sstevel@tonic-gate 				}
1311*7c478bd9Sstevel@tonic-gate 				sdp->sd_active--;
1312*7c478bd9Sstevel@tonic-gate 				ASSERT(sdp->sd_active == 0);
1313*7c478bd9Sstevel@tonic-gate 				next = sdp->sd_subnxt;
1314*7c478bd9Sstevel@tonic-gate 				prev = sdp;
1315*7c478bd9Sstevel@tonic-gate 			}
1316*7c478bd9Sstevel@tonic-gate 			if (sid != NULL) {
1317*7c478bd9Sstevel@tonic-gate 				break;
1318*7c478bd9Sstevel@tonic-gate 			}
1319*7c478bd9Sstevel@tonic-gate 		} else {
1320*7c478bd9Sstevel@tonic-gate 			next = sdp->sd_subnxt;
1321*7c478bd9Sstevel@tonic-gate 			prev = sdp;
1322*7c478bd9Sstevel@tonic-gate 		}
1323*7c478bd9Sstevel@tonic-gate 	}
1324*7c478bd9Sstevel@tonic-gate 	if (!(chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 0)) {
1325*7c478bd9Sstevel@tonic-gate 		/*
1326*7c478bd9Sstevel@tonic-gate 		 * Continue dispatch thread except if no subscribers are present
1327*7c478bd9Sstevel@tonic-gate 		 * in HOLDPEND mode.
1328*7c478bd9Sstevel@tonic-gate 		 */
1329*7c478bd9Sstevel@tonic-gate 		evch_evq_continue(chp->ch_queue);
1330*7c478bd9Sstevel@tonic-gate 	}
1331*7c478bd9Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
1332*7c478bd9Sstevel@tonic-gate }
1333*7c478bd9Sstevel@tonic-gate 
1334*7c478bd9Sstevel@tonic-gate /*
1335*7c478bd9Sstevel@tonic-gate  * Publish an event. Returns zero on success and an error code else.
1336*7c478bd9Sstevel@tonic-gate  */
1337*7c478bd9Sstevel@tonic-gate static int
1338*7c478bd9Sstevel@tonic-gate evch_chpublish(evch_bind_t *bp, sysevent_impl_t *ev, int flags)
1339*7c478bd9Sstevel@tonic-gate {
1340*7c478bd9Sstevel@tonic-gate 	evch_chan_t *chp = bp->bd_channel;
1341*7c478bd9Sstevel@tonic-gate 
1342*7c478bd9Sstevel@tonic-gate 	mutex_enter(&chp->ch_pubmx);
1343*7c478bd9Sstevel@tonic-gate 	if (chp->ch_nevents >= chp->ch_maxev) {
1344*7c478bd9Sstevel@tonic-gate 		if (!(flags & EVCH_QWAIT)) {
1345*7c478bd9Sstevel@tonic-gate 			evch_evq_evfree(ev);
1346*7c478bd9Sstevel@tonic-gate 			mutex_exit(&chp->ch_pubmx);
1347*7c478bd9Sstevel@tonic-gate 			return (EAGAIN);
1348*7c478bd9Sstevel@tonic-gate 		} else {
1349*7c478bd9Sstevel@tonic-gate 			while (chp->ch_nevents >= chp->ch_maxev) {
1350*7c478bd9Sstevel@tonic-gate 				if (cv_wait_sig(&chp->ch_pubcv,
1351*7c478bd9Sstevel@tonic-gate 				    &chp->ch_pubmx) == 0) {
1352*7c478bd9Sstevel@tonic-gate 
1353*7c478bd9Sstevel@tonic-gate 					/* Got Signal, return EINTR */
1354*7c478bd9Sstevel@tonic-gate 					evch_evq_evfree(ev);
1355*7c478bd9Sstevel@tonic-gate 					mutex_exit(&chp->ch_pubmx);
1356*7c478bd9Sstevel@tonic-gate 					return (EINTR);
1357*7c478bd9Sstevel@tonic-gate 				}
1358*7c478bd9Sstevel@tonic-gate 			}
1359*7c478bd9Sstevel@tonic-gate 		}
1360*7c478bd9Sstevel@tonic-gate 	}
1361*7c478bd9Sstevel@tonic-gate 	chp->ch_nevents++;
1362*7c478bd9Sstevel@tonic-gate 	mutex_exit(&chp->ch_pubmx);
1363*7c478bd9Sstevel@tonic-gate 	SE_TIME(ev) = gethrtime();
1364*7c478bd9Sstevel@tonic-gate 	SE_SEQ(ev) = log_sysevent_new_id();
1365*7c478bd9Sstevel@tonic-gate 	/*
1366*7c478bd9Sstevel@tonic-gate 	 * Add the destructor function to the event structure, now that the
1367*7c478bd9Sstevel@tonic-gate 	 * event is accounted for. The only task of the descructor is to
1368*7c478bd9Sstevel@tonic-gate 	 * decrement the channel event count. The evq_*() routines (including
1369*7c478bd9Sstevel@tonic-gate 	 * the event delivery thread) do not have knowledge of the channel
1370*7c478bd9Sstevel@tonic-gate 	 * data. So the anonymous destructor handles the channel data for it.
1371*7c478bd9Sstevel@tonic-gate 	 */
1372*7c478bd9Sstevel@tonic-gate 	evch_evq_evadd_dest(ev, evch_destr_event, (void *)chp);
1373*7c478bd9Sstevel@tonic-gate 	return (evch_evq_pub(chp->ch_queue, ev, flags) == 0 ? 0 : EAGAIN);
1374*7c478bd9Sstevel@tonic-gate }
1375*7c478bd9Sstevel@tonic-gate 
1376*7c478bd9Sstevel@tonic-gate /*
1377*7c478bd9Sstevel@tonic-gate  * Fills a buffer consecutive with the names of all available channels.
1378*7c478bd9Sstevel@tonic-gate  * Returns the length of all name strings or -1 if buffer size was unsufficient.
1379*7c478bd9Sstevel@tonic-gate  */
1380*7c478bd9Sstevel@tonic-gate static int
1381*7c478bd9Sstevel@tonic-gate evch_chgetnames(char *buf, size_t size)
1382*7c478bd9Sstevel@tonic-gate {
1383*7c478bd9Sstevel@tonic-gate 	struct evch_globals *eg;
1384*7c478bd9Sstevel@tonic-gate 	int		len = 0;
1385*7c478bd9Sstevel@tonic-gate 	char		*addr = buf;
1386*7c478bd9Sstevel@tonic-gate 	int		max = size;
1387*7c478bd9Sstevel@tonic-gate 	evch_chan_t	*chp;
1388*7c478bd9Sstevel@tonic-gate 
1389*7c478bd9Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, curproc->p_zone);
1390*7c478bd9Sstevel@tonic-gate 	ASSERT(eg != NULL);
1391*7c478bd9Sstevel@tonic-gate 
1392*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eg->evch_list_lock);
1393*7c478bd9Sstevel@tonic-gate 	for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL;
1394*7c478bd9Sstevel@tonic-gate 	    chp = evch_dl_next(&eg->evch_list, chp)) {
1395*7c478bd9Sstevel@tonic-gate 		len += chp->ch_namelen;
1396*7c478bd9Sstevel@tonic-gate 		if (len >= max) {
1397*7c478bd9Sstevel@tonic-gate 			mutex_exit(&eg->evch_list_lock);
1398*7c478bd9Sstevel@tonic-gate 			return (-1);
1399*7c478bd9Sstevel@tonic-gate 		}
1400*7c478bd9Sstevel@tonic-gate 		bcopy(chp->ch_name, addr, chp->ch_namelen);
1401*7c478bd9Sstevel@tonic-gate 		addr += chp->ch_namelen;
1402*7c478bd9Sstevel@tonic-gate 	}
1403*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
1404*7c478bd9Sstevel@tonic-gate 	addr[0] = 0;
1405*7c478bd9Sstevel@tonic-gate 	return (len + 1);
1406*7c478bd9Sstevel@tonic-gate }
1407*7c478bd9Sstevel@tonic-gate 
1408*7c478bd9Sstevel@tonic-gate /*
1409*7c478bd9Sstevel@tonic-gate  * Fills the data of one channel and all subscribers of that channel into
1410*7c478bd9Sstevel@tonic-gate  * a buffer. Returns -1 if the channel name is invalid and 0 on buffer overflow.
1411*7c478bd9Sstevel@tonic-gate  */
1412*7c478bd9Sstevel@tonic-gate static int
1413*7c478bd9Sstevel@tonic-gate evch_chgetchdata(char *chname, void *buf, size_t size)
1414*7c478bd9Sstevel@tonic-gate {
1415*7c478bd9Sstevel@tonic-gate 	struct evch_globals *eg;
1416*7c478bd9Sstevel@tonic-gate 	char		*cpaddr;
1417*7c478bd9Sstevel@tonic-gate 	int		bufmax;
1418*7c478bd9Sstevel@tonic-gate 	int		buflen;
1419*7c478bd9Sstevel@tonic-gate 	evch_chan_t	*chp;
1420*7c478bd9Sstevel@tonic-gate 	sev_chinfo_t	*p = (sev_chinfo_t *)buf;
1421*7c478bd9Sstevel@tonic-gate 	int		chdlen;
1422*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*sdp;
1423*7c478bd9Sstevel@tonic-gate 	sev_subinfo_t	*subp;
1424*7c478bd9Sstevel@tonic-gate 	int		idlen;
1425*7c478bd9Sstevel@tonic-gate 	int		len;
1426*7c478bd9Sstevel@tonic-gate 
1427*7c478bd9Sstevel@tonic-gate 	eg = zone_getspecific(evch_zone_key, curproc->p_zone);
1428*7c478bd9Sstevel@tonic-gate 	ASSERT(eg != NULL);
1429*7c478bd9Sstevel@tonic-gate 
1430*7c478bd9Sstevel@tonic-gate 	mutex_enter(&eg->evch_list_lock);
1431*7c478bd9Sstevel@tonic-gate 	chp = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp,
1432*7c478bd9Sstevel@tonic-gate 	    chname);
1433*7c478bd9Sstevel@tonic-gate 	if (chp == NULL) {
1434*7c478bd9Sstevel@tonic-gate 		mutex_exit(&eg->evch_list_lock);
1435*7c478bd9Sstevel@tonic-gate 		return (-1);
1436*7c478bd9Sstevel@tonic-gate 	}
1437*7c478bd9Sstevel@tonic-gate 	chdlen = offsetof(sev_chinfo_t, cd_subinfo);
1438*7c478bd9Sstevel@tonic-gate 	if (size < chdlen) {
1439*7c478bd9Sstevel@tonic-gate 		mutex_exit(&eg->evch_list_lock);
1440*7c478bd9Sstevel@tonic-gate 		return (0);
1441*7c478bd9Sstevel@tonic-gate 	}
1442*7c478bd9Sstevel@tonic-gate 	p->cd_version = 0;
1443*7c478bd9Sstevel@tonic-gate 	p->cd_suboffs = chdlen;
1444*7c478bd9Sstevel@tonic-gate 	p->cd_uid = chp->ch_uid;
1445*7c478bd9Sstevel@tonic-gate 	p->cd_gid = chp->ch_gid;
1446*7c478bd9Sstevel@tonic-gate 	p->cd_perms = 0;
1447*7c478bd9Sstevel@tonic-gate 	p->cd_ctime = chp->ch_ctime;
1448*7c478bd9Sstevel@tonic-gate 	p->cd_maxev = chp->ch_maxev;
1449*7c478bd9Sstevel@tonic-gate 	p->cd_evhwm = EVCH_EVQ_HIGHWM(chp->ch_queue);
1450*7c478bd9Sstevel@tonic-gate 	p->cd_nevents = EVCH_EVQ_EVCOUNT(chp->ch_queue);
1451*7c478bd9Sstevel@tonic-gate 	p->cd_maxsub = chp->ch_maxsubscr;
1452*7c478bd9Sstevel@tonic-gate 	p->cd_nsub = evch_dl_getnum(&chp->ch_subscr);
1453*7c478bd9Sstevel@tonic-gate 	p->cd_maxbinds = chp->ch_maxbinds;
1454*7c478bd9Sstevel@tonic-gate 	p->cd_nbinds = chp->ch_bindings;
1455*7c478bd9Sstevel@tonic-gate 	p->cd_holdpend = chp->ch_holdpend;
1456*7c478bd9Sstevel@tonic-gate 	p->cd_limev = evch_events_max;
1457*7c478bd9Sstevel@tonic-gate 	cpaddr = (char *)p + chdlen;
1458*7c478bd9Sstevel@tonic-gate 	bufmax = size - chdlen;
1459*7c478bd9Sstevel@tonic-gate 	buflen = 0;
1460*7c478bd9Sstevel@tonic-gate 
1461*7c478bd9Sstevel@tonic-gate 	for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp != NULL;
1462*7c478bd9Sstevel@tonic-gate 	    sdp = evch_dl_next(&chp->ch_subscr, sdp)) {
1463*7c478bd9Sstevel@tonic-gate 		idlen = strlen(sdp->sd_ident) + 1;
1464*7c478bd9Sstevel@tonic-gate 		len = SE_ALIGN(offsetof(sev_subinfo_t, sb_strings) + idlen +
1465*7c478bd9Sstevel@tonic-gate 		    sdp->sd_clnsize);
1466*7c478bd9Sstevel@tonic-gate 		buflen += len;
1467*7c478bd9Sstevel@tonic-gate 		if (buflen >= bufmax) {
1468*7c478bd9Sstevel@tonic-gate 			mutex_exit(&eg->evch_list_lock);
1469*7c478bd9Sstevel@tonic-gate 			return (0);
1470*7c478bd9Sstevel@tonic-gate 		}
1471*7c478bd9Sstevel@tonic-gate 		subp = (sev_subinfo_t *)cpaddr;
1472*7c478bd9Sstevel@tonic-gate 		subp->sb_nextoff = len;
1473*7c478bd9Sstevel@tonic-gate 		subp->sb_stroff = offsetof(sev_subinfo_t, sb_strings);
1474*7c478bd9Sstevel@tonic-gate 		if (sdp->sd_classname) {
1475*7c478bd9Sstevel@tonic-gate 			bcopy(sdp->sd_classname, subp->sb_strings + idlen,
1476*7c478bd9Sstevel@tonic-gate 			    sdp->sd_clnsize);
1477*7c478bd9Sstevel@tonic-gate 			subp->sb_clnamoff = idlen;
1478*7c478bd9Sstevel@tonic-gate 		} else {
1479*7c478bd9Sstevel@tonic-gate 			subp->sb_clnamoff = idlen - 1;
1480*7c478bd9Sstevel@tonic-gate 		}
1481*7c478bd9Sstevel@tonic-gate 		subp->sb_pid = sdp->sd_pid;
1482*7c478bd9Sstevel@tonic-gate 		subp->sb_nevents = EVCH_EVQ_EVCOUNT(sdp->sd_queue);
1483*7c478bd9Sstevel@tonic-gate 		subp->sb_evhwm = EVCH_EVQ_HIGHWM(sdp->sd_queue);
1484*7c478bd9Sstevel@tonic-gate 		subp->sb_persist = sdp->sd_persist;
1485*7c478bd9Sstevel@tonic-gate 		subp->sb_status = evch_evq_status(sdp->sd_queue);
1486*7c478bd9Sstevel@tonic-gate 		subp->sb_active = sdp->sd_active;
1487*7c478bd9Sstevel@tonic-gate 		subp->sb_dump = sdp->sd_dump;
1488*7c478bd9Sstevel@tonic-gate 		bcopy(sdp->sd_ident, subp->sb_strings, idlen);
1489*7c478bd9Sstevel@tonic-gate 		cpaddr += len;
1490*7c478bd9Sstevel@tonic-gate 	}
1491*7c478bd9Sstevel@tonic-gate 	mutex_exit(&eg->evch_list_lock);
1492*7c478bd9Sstevel@tonic-gate 	return (chdlen + buflen);
1493*7c478bd9Sstevel@tonic-gate }
1494*7c478bd9Sstevel@tonic-gate 
1495*7c478bd9Sstevel@tonic-gate /*
1496*7c478bd9Sstevel@tonic-gate  * Init iteration of all events of a channel. This function creates a new
1497*7c478bd9Sstevel@tonic-gate  * event queue and puts all events from the channel into that queue.
1498*7c478bd9Sstevel@tonic-gate  * Subsequent calls to evch_chgetnextev will deliver the events from that
1499*7c478bd9Sstevel@tonic-gate  * queue. Only one thread per channel is allowed to read through the events.
1500*7c478bd9Sstevel@tonic-gate  * Returns 0 on success and 1 if there is already someone reading the
1501*7c478bd9Sstevel@tonic-gate  * events.
1502*7c478bd9Sstevel@tonic-gate  * If argument subid == NULL, we look for a subscriber which has
1503*7c478bd9Sstevel@tonic-gate  * flag EVCH_SUB_DUMP set.
1504*7c478bd9Sstevel@tonic-gate  */
1505*7c478bd9Sstevel@tonic-gate /*
1506*7c478bd9Sstevel@tonic-gate  * Static variables that are used to traverse events of a channel in panic case.
1507*7c478bd9Sstevel@tonic-gate  */
1508*7c478bd9Sstevel@tonic-gate static evch_chan_t	*evch_chan;
1509*7c478bd9Sstevel@tonic-gate static evch_eventq_t	*evch_subq;
1510*7c478bd9Sstevel@tonic-gate static sysevent_impl_t	*evch_curev;
1511*7c478bd9Sstevel@tonic-gate 
1512*7c478bd9Sstevel@tonic-gate static evchanq_t *
1513*7c478bd9Sstevel@tonic-gate evch_chrdevent_init(evch_chan_t *chp, char *subid)
1514*7c478bd9Sstevel@tonic-gate {
1515*7c478bd9Sstevel@tonic-gate 	evch_subd_t	*sdp;
1516*7c478bd9Sstevel@tonic-gate 	void		*ev;
1517*7c478bd9Sstevel@tonic-gate 	int		pmqstat;	/* Prev status of main queue */
1518*7c478bd9Sstevel@tonic-gate 	int		psqstat;	/* Prev status of subscriber queue */
1519*7c478bd9Sstevel@tonic-gate 	evchanq_t	*snp;		/* Pointer to q with snapshot of ev */
1520*7c478bd9Sstevel@tonic-gate 	compare_f	compfunc;
1521*7c478bd9Sstevel@tonic-gate 
1522*7c478bd9Sstevel@tonic-gate 	compfunc = subid == NULL ? evch_dumpflgcmp : evch_subidcmp;
1523*7c478bd9Sstevel@tonic-gate 	if (panicstr != NULL) {
1524*7c478bd9Sstevel@tonic-gate 		evch_chan = chp;
1525*7c478bd9Sstevel@tonic-gate 		evch_subq = NULL;
1526*7c478bd9Sstevel@tonic-gate 		evch_curev = NULL;
1527*7c478bd9Sstevel@tonic-gate 		if ((sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr,
1528*7c478bd9Sstevel@tonic-gate 		    compfunc, subid)) != NULL) {
1529*7c478bd9Sstevel@tonic-gate 			evch_subq = sdp->sd_queue;
1530*7c478bd9Sstevel@tonic-gate 		}
1531*7c478bd9Sstevel@tonic-gate 		return (NULL);
1532*7c478bd9Sstevel@tonic-gate 	}
1533*7c478bd9Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
1534*7c478bd9Sstevel@tonic-gate 	sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr, compfunc, subid);
1535*7c478bd9Sstevel@tonic-gate 	/*
1536*7c478bd9Sstevel@tonic-gate 	 * Stop main event queue and subscriber queue if not already
1537*7c478bd9Sstevel@tonic-gate 	 * in stop mode.
1538*7c478bd9Sstevel@tonic-gate 	 */
1539*7c478bd9Sstevel@tonic-gate 	pmqstat = evch_evq_status(chp->ch_queue);
1540*7c478bd9Sstevel@tonic-gate 	if (pmqstat == 0)
1541*7c478bd9Sstevel@tonic-gate 		evch_evq_stop(chp->ch_queue);
1542*7c478bd9Sstevel@tonic-gate 	if (sdp != NULL) {
1543*7c478bd9Sstevel@tonic-gate 		psqstat = evch_evq_status(sdp->sd_queue);
1544*7c478bd9Sstevel@tonic-gate 		if (psqstat == 0)
1545*7c478bd9Sstevel@tonic-gate 			evch_evq_stop(sdp->sd_queue);
1546*7c478bd9Sstevel@tonic-gate 	}
1547*7c478bd9Sstevel@tonic-gate 	/*
1548*7c478bd9Sstevel@tonic-gate 	 * Create event queue to make a snapshot of all events in the
1549*7c478bd9Sstevel@tonic-gate 	 * channel.
1550*7c478bd9Sstevel@tonic-gate 	 */
1551*7c478bd9Sstevel@tonic-gate 	snp = kmem_alloc(sizeof (evchanq_t), KM_SLEEP);
1552*7c478bd9Sstevel@tonic-gate 	snp->sn_queue = evch_evq_create();
1553*7c478bd9Sstevel@tonic-gate 	evch_evq_stop(snp->sn_queue);
1554*7c478bd9Sstevel@tonic-gate 	/*
1555*7c478bd9Sstevel@tonic-gate 	 * Make a snapshot of the subscriber queue and the main event queue.
1556*7c478bd9Sstevel@tonic-gate 	 */
1557*7c478bd9Sstevel@tonic-gate 	if (sdp != NULL) {
1558*7c478bd9Sstevel@tonic-gate 		ev = NULL;
1559*7c478bd9Sstevel@tonic-gate 		while ((ev = evch_evq_evnext(sdp->sd_queue, ev)) != NULL) {
1560*7c478bd9Sstevel@tonic-gate 			(void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP);
1561*7c478bd9Sstevel@tonic-gate 		}
1562*7c478bd9Sstevel@tonic-gate 	}
1563*7c478bd9Sstevel@tonic-gate 	ev = NULL;
1564*7c478bd9Sstevel@tonic-gate 	while ((ev = evch_evq_evnext(chp->ch_queue, ev)) != NULL) {
1565*7c478bd9Sstevel@tonic-gate 		(void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP);
1566*7c478bd9Sstevel@tonic-gate 	}
1567*7c478bd9Sstevel@tonic-gate 	snp->sn_nxtev = NULL;
1568*7c478bd9Sstevel@tonic-gate 	/*
1569*7c478bd9Sstevel@tonic-gate 	 * Restart main and subscriber queue if previously stopped
1570*7c478bd9Sstevel@tonic-gate 	 */
1571*7c478bd9Sstevel@tonic-gate 	if (sdp != NULL && psqstat == 0)
1572*7c478bd9Sstevel@tonic-gate 		evch_evq_continue(sdp->sd_queue);
1573*7c478bd9Sstevel@tonic-gate 	if (pmqstat == 0)
1574*7c478bd9Sstevel@tonic-gate 		evch_evq_continue(chp->ch_queue);
1575*7c478bd9Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
1576*7c478bd9Sstevel@tonic-gate 	return (snp);
1577*7c478bd9Sstevel@tonic-gate }
1578*7c478bd9Sstevel@tonic-gate 
1579*7c478bd9Sstevel@tonic-gate /*
1580*7c478bd9Sstevel@tonic-gate  * Free all resources of the event queue snapshot. In case of panic
1581*7c478bd9Sstevel@tonic-gate  * context snp must be NULL and no resources need to be free'ed.
1582*7c478bd9Sstevel@tonic-gate  */
1583*7c478bd9Sstevel@tonic-gate static void
1584*7c478bd9Sstevel@tonic-gate evch_chrdevent_fini(evchanq_t *snp)
1585*7c478bd9Sstevel@tonic-gate {
1586*7c478bd9Sstevel@tonic-gate 	if (snp != NULL) {
1587*7c478bd9Sstevel@tonic-gate 		evch_evq_destroy(snp->sn_queue);
1588*7c478bd9Sstevel@tonic-gate 		kmem_free(snp, sizeof (evchanq_t));
1589*7c478bd9Sstevel@tonic-gate 	}
1590*7c478bd9Sstevel@tonic-gate }
1591*7c478bd9Sstevel@tonic-gate 
1592*7c478bd9Sstevel@tonic-gate /*
1593*7c478bd9Sstevel@tonic-gate  * Get address of next event from an event channel.
1594*7c478bd9Sstevel@tonic-gate  * This function might be called in a panic context. In that case
1595*7c478bd9Sstevel@tonic-gate  * no resources will be allocated and no locks grabbed.
1596*7c478bd9Sstevel@tonic-gate  * In normal operation context a snapshot of the event queues of the
1597*7c478bd9Sstevel@tonic-gate  * specified event channel will be taken.
1598*7c478bd9Sstevel@tonic-gate  */
1599*7c478bd9Sstevel@tonic-gate static sysevent_impl_t *
1600*7c478bd9Sstevel@tonic-gate evch_chgetnextev(evchanq_t *snp)
1601*7c478bd9Sstevel@tonic-gate {
1602*7c478bd9Sstevel@tonic-gate 	if (panicstr != NULL) {
1603*7c478bd9Sstevel@tonic-gate 		if (evch_chan == NULL)
1604*7c478bd9Sstevel@tonic-gate 			return (NULL);
1605*7c478bd9Sstevel@tonic-gate 		if (evch_subq != NULL) {
1606*7c478bd9Sstevel@tonic-gate 			/*
1607*7c478bd9Sstevel@tonic-gate 			 * We have a subscriber queue. Traverse this queue
1608*7c478bd9Sstevel@tonic-gate 			 * first.
1609*7c478bd9Sstevel@tonic-gate 			 */
1610*7c478bd9Sstevel@tonic-gate 			if ((evch_curev = (sysevent_impl_t *)
1611*7c478bd9Sstevel@tonic-gate 			    evch_evq_evnext(evch_subq, evch_curev)) != NULL) {
1612*7c478bd9Sstevel@tonic-gate 				return (evch_curev);
1613*7c478bd9Sstevel@tonic-gate 			} else {
1614*7c478bd9Sstevel@tonic-gate 				/*
1615*7c478bd9Sstevel@tonic-gate 				 * All subscriber events traversed. evch_subq
1616*7c478bd9Sstevel@tonic-gate 				 * == NULL indicates to take the main event
1617*7c478bd9Sstevel@tonic-gate 				 * queue now.
1618*7c478bd9Sstevel@tonic-gate 				 */
1619*7c478bd9Sstevel@tonic-gate 				evch_subq = NULL;
1620*7c478bd9Sstevel@tonic-gate 			}
1621*7c478bd9Sstevel@tonic-gate 		}
1622*7c478bd9Sstevel@tonic-gate 		/*
1623*7c478bd9Sstevel@tonic-gate 		 * Traverse the main event queue.
1624*7c478bd9Sstevel@tonic-gate 		 */
1625*7c478bd9Sstevel@tonic-gate 		if ((evch_curev = (sysevent_impl_t *)
1626*7c478bd9Sstevel@tonic-gate 		    evch_evq_evnext(evch_chan->ch_queue, evch_curev)) ==
1627*7c478bd9Sstevel@tonic-gate 		    NULL) {
1628*7c478bd9Sstevel@tonic-gate 			evch_chan = NULL;
1629*7c478bd9Sstevel@tonic-gate 		}
1630*7c478bd9Sstevel@tonic-gate 		return (evch_curev);
1631*7c478bd9Sstevel@tonic-gate 	}
1632*7c478bd9Sstevel@tonic-gate 	ASSERT(snp != NULL);
1633*7c478bd9Sstevel@tonic-gate 	snp->sn_nxtev = (sysevent_impl_t *)evch_evq_evnext(snp->sn_queue,
1634*7c478bd9Sstevel@tonic-gate 	    snp->sn_nxtev);
1635*7c478bd9Sstevel@tonic-gate 	return (snp->sn_nxtev);
1636*7c478bd9Sstevel@tonic-gate }
1637*7c478bd9Sstevel@tonic-gate 
1638*7c478bd9Sstevel@tonic-gate /*
1639*7c478bd9Sstevel@tonic-gate  * The functions below build up the interface for the kernel to bind/unbind,
1640*7c478bd9Sstevel@tonic-gate  * subscribe/unsubscribe and publish to event channels. It consists of the
1641*7c478bd9Sstevel@tonic-gate  * following functions:
1642*7c478bd9Sstevel@tonic-gate  *
1643*7c478bd9Sstevel@tonic-gate  * sysevent_evc_bind	    - Bind to a channel. Create a channel if required
1644*7c478bd9Sstevel@tonic-gate  * sysevent_evc_unbind	    - Unbind from a channel. Destroy ch. if last unbind
1645*7c478bd9Sstevel@tonic-gate  * sysevent_evc_subscribe   - Subscribe to events from a channel
1646*7c478bd9Sstevel@tonic-gate  * sysevent_evc_unsubscribe - Unsubscribe from an event class
1647*7c478bd9Sstevel@tonic-gate  * sysevent_evc_publish	    - Publish an event to an event channel
1648*7c478bd9Sstevel@tonic-gate  * sysevent_evc_control	    - Various control operation on event channel
1649*7c478bd9Sstevel@tonic-gate  *
1650*7c478bd9Sstevel@tonic-gate  * The function below are for evaluating a sysevent:
1651*7c478bd9Sstevel@tonic-gate  *
1652*7c478bd9Sstevel@tonic-gate  * sysevent_get_class_name  - Get pointer to event class string
1653*7c478bd9Sstevel@tonic-gate  * sysevent_get_subclass_name - Get pointer to event subclass string
1654*7c478bd9Sstevel@tonic-gate  * sysevent_get_seq	    - Get unique event sequence number
1655*7c478bd9Sstevel@tonic-gate  * sysevent_get_time	    - Get hrestime of event publish
1656*7c478bd9Sstevel@tonic-gate  * sysevent_get_size	    - Get size of event structure
1657*7c478bd9Sstevel@tonic-gate  * sysevent_get_pub	    - Get publisher string
1658*7c478bd9Sstevel@tonic-gate  * sysevent_get_attr_list   - Get copy of attribute list
1659*7c478bd9Sstevel@tonic-gate  *
1660*7c478bd9Sstevel@tonic-gate  * The following interfaces represent stability level project privat
1661*7c478bd9Sstevel@tonic-gate  * and allow to save the events of an event channel even in a panic case.
1662*7c478bd9Sstevel@tonic-gate  *
1663*7c478bd9Sstevel@tonic-gate  * sysevent_evc_walk_init   - Take a snapshot of the events in a channel
1664*7c478bd9Sstevel@tonic-gate  * sysevent_evc_walk_step   - Read next event from snapshot
1665*7c478bd9Sstevel@tonic-gate  * sysevent_evc_walk_fini   - Free resources from event channel snapshot
1666*7c478bd9Sstevel@tonic-gate  * sysevent_evc_event_attr  - Get event payload address and size
1667*7c478bd9Sstevel@tonic-gate  */
1668*7c478bd9Sstevel@tonic-gate /*
1669*7c478bd9Sstevel@tonic-gate  * allocate sysevent structure with optional space for attributes
1670*7c478bd9Sstevel@tonic-gate  */
1671*7c478bd9Sstevel@tonic-gate static sysevent_impl_t *
1672*7c478bd9Sstevel@tonic-gate sysevent_evc_alloc(const char *class, const char *subclass, const char *pub,
1673*7c478bd9Sstevel@tonic-gate     size_t pub_sz, size_t atsz, uint32_t flag)
1674*7c478bd9Sstevel@tonic-gate {
1675*7c478bd9Sstevel@tonic-gate 	int		payload_sz;
1676*7c478bd9Sstevel@tonic-gate 	int		class_sz, subclass_sz;
1677*7c478bd9Sstevel@tonic-gate 	int 		aligned_class_sz, aligned_subclass_sz, aligned_pub_sz;
1678*7c478bd9Sstevel@tonic-gate 	sysevent_impl_t	*ev;
1679*7c478bd9Sstevel@tonic-gate 
1680*7c478bd9Sstevel@tonic-gate 	/*
1681*7c478bd9Sstevel@tonic-gate 	 * Calculate and reserve space for the class, subclass and
1682*7c478bd9Sstevel@tonic-gate 	 * publisher strings in the event buffer
1683*7c478bd9Sstevel@tonic-gate 	 */
1684*7c478bd9Sstevel@tonic-gate 	class_sz = strlen(class) + 1;
1685*7c478bd9Sstevel@tonic-gate 	subclass_sz = strlen(subclass) + 1;
1686*7c478bd9Sstevel@tonic-gate 
1687*7c478bd9Sstevel@tonic-gate 	ASSERT((class_sz <= MAX_CLASS_LEN) && (subclass_sz <=
1688*7c478bd9Sstevel@tonic-gate 	    MAX_SUBCLASS_LEN) && (pub_sz <= MAX_PUB_LEN));
1689*7c478bd9Sstevel@tonic-gate 
1690*7c478bd9Sstevel@tonic-gate 	/* String sizes must be 64-bit aligned in the event buffer */
1691*7c478bd9Sstevel@tonic-gate 	aligned_class_sz = SE_ALIGN(class_sz);
1692*7c478bd9Sstevel@tonic-gate 	aligned_subclass_sz = SE_ALIGN(subclass_sz);
1693*7c478bd9Sstevel@tonic-gate 	aligned_pub_sz = SE_ALIGN(pub_sz);
1694*7c478bd9Sstevel@tonic-gate 
1695*7c478bd9Sstevel@tonic-gate 	/*
1696*7c478bd9Sstevel@tonic-gate 	 * Calculate payload size. Consider the space needed for alignment
1697*7c478bd9Sstevel@tonic-gate 	 * and subtract the size of the uint64_t placeholder variables of
1698*7c478bd9Sstevel@tonic-gate 	 * sysevent_impl_t.
1699*7c478bd9Sstevel@tonic-gate 	 */
1700*7c478bd9Sstevel@tonic-gate 	payload_sz = (aligned_class_sz - sizeof (uint64_t)) +
1701*7c478bd9Sstevel@tonic-gate 	    (aligned_subclass_sz - sizeof (uint64_t)) +
1702*7c478bd9Sstevel@tonic-gate 	    (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) +
1703*7c478bd9Sstevel@tonic-gate 	    atsz;
1704*7c478bd9Sstevel@tonic-gate 
1705*7c478bd9Sstevel@tonic-gate 	/*
1706*7c478bd9Sstevel@tonic-gate 	 * Allocate event buffer plus additional payload overhead
1707*7c478bd9Sstevel@tonic-gate 	 */
1708*7c478bd9Sstevel@tonic-gate 	if ((ev = evch_evq_evzalloc(sizeof (sysevent_impl_t) +
1709*7c478bd9Sstevel@tonic-gate 	    payload_sz, flag)) == NULL) {
1710*7c478bd9Sstevel@tonic-gate 		return (NULL);
1711*7c478bd9Sstevel@tonic-gate 	}
1712*7c478bd9Sstevel@tonic-gate 
1713*7c478bd9Sstevel@tonic-gate 	/* Initialize the event buffer data */
1714*7c478bd9Sstevel@tonic-gate 	SE_VERSION(ev) = SYS_EVENT_VERSION;
1715*7c478bd9Sstevel@tonic-gate 	bcopy(class, SE_CLASS_NAME(ev), class_sz);
1716*7c478bd9Sstevel@tonic-gate 
1717*7c478bd9Sstevel@tonic-gate 	SE_SUBCLASS_OFF(ev) = SE_ALIGN(offsetof(sysevent_impl_t,
1718*7c478bd9Sstevel@tonic-gate 	    se_class_name)) + aligned_class_sz;
1719*7c478bd9Sstevel@tonic-gate 	bcopy(subclass, SE_SUBCLASS_NAME(ev), subclass_sz);
1720*7c478bd9Sstevel@tonic-gate 
1721*7c478bd9Sstevel@tonic-gate 	SE_PUB_OFF(ev) = SE_SUBCLASS_OFF(ev) + aligned_subclass_sz;
1722*7c478bd9Sstevel@tonic-gate 	bcopy(pub, SE_PUB_NAME(ev), pub_sz);
1723*7c478bd9Sstevel@tonic-gate 
1724*7c478bd9Sstevel@tonic-gate 	SE_ATTR_PTR(ev) = (uint64_t)0;
1725*7c478bd9Sstevel@tonic-gate 	SE_PAYLOAD_SZ(ev) = payload_sz;
1726*7c478bd9Sstevel@tonic-gate 
1727*7c478bd9Sstevel@tonic-gate 	return (ev);
1728*7c478bd9Sstevel@tonic-gate }
1729*7c478bd9Sstevel@tonic-gate 
1730*7c478bd9Sstevel@tonic-gate /*
1731*7c478bd9Sstevel@tonic-gate  * Initialize event channel handling queues.
1732*7c478bd9Sstevel@tonic-gate  */
1733*7c478bd9Sstevel@tonic-gate void
1734*7c478bd9Sstevel@tonic-gate sysevent_evc_init()
1735*7c478bd9Sstevel@tonic-gate {
1736*7c478bd9Sstevel@tonic-gate 	evch_chinit();
1737*7c478bd9Sstevel@tonic-gate }
1738*7c478bd9Sstevel@tonic-gate 
1739*7c478bd9Sstevel@tonic-gate /*
1740*7c478bd9Sstevel@tonic-gate  * Second initialization step: create threads, if event channels are already
1741*7c478bd9Sstevel@tonic-gate  * created
1742*7c478bd9Sstevel@tonic-gate  */
1743*7c478bd9Sstevel@tonic-gate void
1744*7c478bd9Sstevel@tonic-gate sysevent_evc_thrinit()
1745*7c478bd9Sstevel@tonic-gate {
1746*7c478bd9Sstevel@tonic-gate 	evch_chinitthr();
1747*7c478bd9Sstevel@tonic-gate }
1748*7c478bd9Sstevel@tonic-gate 
1749*7c478bd9Sstevel@tonic-gate int
1750*7c478bd9Sstevel@tonic-gate sysevent_evc_bind(const char *ch_name, evchan_t **scpp, uint32_t flags)
1751*7c478bd9Sstevel@tonic-gate {
1752*7c478bd9Sstevel@tonic-gate 	ASSERT(ch_name != NULL && scpp != NULL);
1753*7c478bd9Sstevel@tonic-gate 	ASSERT((flags & ~EVCH_B_FLAGS) == 0);
1754*7c478bd9Sstevel@tonic-gate 	return (evch_chbind(ch_name, (evch_bind_t **)scpp, flags));
1755*7c478bd9Sstevel@tonic-gate }
1756*7c478bd9Sstevel@tonic-gate 
1757*7c478bd9Sstevel@tonic-gate void
1758*7c478bd9Sstevel@tonic-gate sysevent_evc_unbind(evchan_t *scp)
1759*7c478bd9Sstevel@tonic-gate {
1760*7c478bd9Sstevel@tonic-gate 	evch_bind_t *bp = (evch_bind_t *)scp;
1761*7c478bd9Sstevel@tonic-gate 
1762*7c478bd9Sstevel@tonic-gate 	ASSERT(scp != NULL);
1763*7c478bd9Sstevel@tonic-gate 	evch_chunsubscribe(bp, NULL, 0);
1764*7c478bd9Sstevel@tonic-gate 	evch_chunbind(bp);
1765*7c478bd9Sstevel@tonic-gate }
1766*7c478bd9Sstevel@tonic-gate 
1767*7c478bd9Sstevel@tonic-gate int
1768*7c478bd9Sstevel@tonic-gate sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class,
1769*7c478bd9Sstevel@tonic-gate     int (*callb)(sysevent_t *ev, void *cookie),
1770*7c478bd9Sstevel@tonic-gate     void *cookie, uint32_t flags)
1771*7c478bd9Sstevel@tonic-gate {
1772*7c478bd9Sstevel@tonic-gate 	ASSERT(scp != NULL && sid != NULL && class != NULL && callb != NULL);
1773*7c478bd9Sstevel@tonic-gate 	ASSERT(flags == 0);
1774*7c478bd9Sstevel@tonic-gate 	if (strlen(sid) > MAX_SUBID_LEN) {
1775*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1776*7c478bd9Sstevel@tonic-gate 	}
1777*7c478bd9Sstevel@tonic-gate 	if (strcmp(class, EC_ALL) == 0) {
1778*7c478bd9Sstevel@tonic-gate 		class = NULL;
1779*7c478bd9Sstevel@tonic-gate 	}
1780*7c478bd9Sstevel@tonic-gate 	return (evch_chsubscribe((evch_bind_t *)scp, EVCH_DELKERN, sid, class,
1781*7c478bd9Sstevel@tonic-gate 	    (void *)callb, cookie, 0, 0));
1782*7c478bd9Sstevel@tonic-gate }
1783*7c478bd9Sstevel@tonic-gate 
1784*7c478bd9Sstevel@tonic-gate void
1785*7c478bd9Sstevel@tonic-gate sysevent_evc_unsubscribe(evchan_t *scp, const char *sid)
1786*7c478bd9Sstevel@tonic-gate {
1787*7c478bd9Sstevel@tonic-gate 	ASSERT(scp != NULL && sid != NULL);
1788*7c478bd9Sstevel@tonic-gate 	if (strcmp(sid, EVCH_ALLSUB) == 0) {
1789*7c478bd9Sstevel@tonic-gate 		sid = NULL;
1790*7c478bd9Sstevel@tonic-gate 	}
1791*7c478bd9Sstevel@tonic-gate 	evch_chunsubscribe((evch_bind_t *)scp, sid, 0);
1792*7c478bd9Sstevel@tonic-gate }
1793*7c478bd9Sstevel@tonic-gate 
1794*7c478bd9Sstevel@tonic-gate /*
1795*7c478bd9Sstevel@tonic-gate  * Publish kernel event. Returns 0 on success, error code else.
1796*7c478bd9Sstevel@tonic-gate  * Optional attribute data is packed into the event structure.
1797*7c478bd9Sstevel@tonic-gate  */
1798*7c478bd9Sstevel@tonic-gate int
1799*7c478bd9Sstevel@tonic-gate sysevent_evc_publish(evchan_t *scp, const char *class, const char *subclass,
1800*7c478bd9Sstevel@tonic-gate     const char *vendor, const char *pubs, nvlist_t *attr, uint32_t flags)
1801*7c478bd9Sstevel@tonic-gate {
1802*7c478bd9Sstevel@tonic-gate 	sysevent_impl_t	*evp;
1803*7c478bd9Sstevel@tonic-gate 	char		pub[MAX_PUB_LEN];
1804*7c478bd9Sstevel@tonic-gate 	int		pub_sz;		/* includes terminating 0 */
1805*7c478bd9Sstevel@tonic-gate 	int		km_flags;
1806*7c478bd9Sstevel@tonic-gate 	size_t		asz = 0;
1807*7c478bd9Sstevel@tonic-gate 	uint64_t	attr_offset;
1808*7c478bd9Sstevel@tonic-gate 	caddr_t		patt;
1809*7c478bd9Sstevel@tonic-gate 	int		err;
1810*7c478bd9Sstevel@tonic-gate 
1811*7c478bd9Sstevel@tonic-gate 	ASSERT(scp != NULL && class != NULL && subclass != NULL &&
1812*7c478bd9Sstevel@tonic-gate 	    vendor != NULL && pubs != NULL);
1813*7c478bd9Sstevel@tonic-gate 
1814*7c478bd9Sstevel@tonic-gate 	ASSERT((flags & ~(EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD |
1815*7c478bd9Sstevel@tonic-gate 	    EVCH_QWAIT)) == 0);
1816*7c478bd9Sstevel@tonic-gate 
1817*7c478bd9Sstevel@tonic-gate 	km_flags = flags & (EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD);
1818*7c478bd9Sstevel@tonic-gate 	ASSERT(km_flags == EVCH_SLEEP || km_flags == EVCH_NOSLEEP ||
1819*7c478bd9Sstevel@tonic-gate 	    km_flags == EVCH_TRYHARD);
1820*7c478bd9Sstevel@tonic-gate 
1821*7c478bd9Sstevel@tonic-gate 	pub_sz = snprintf(pub, MAX_PUB_LEN, "%s:kern:%s", vendor, pubs) + 1;
1822*7c478bd9Sstevel@tonic-gate 	if (pub_sz > MAX_PUB_LEN)
1823*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1824*7c478bd9Sstevel@tonic-gate 
1825*7c478bd9Sstevel@tonic-gate 	if (attr != NULL) {
1826*7c478bd9Sstevel@tonic-gate 		if ((err = nvlist_size(attr, &asz, NV_ENCODE_NATIVE)) != 0) {
1827*7c478bd9Sstevel@tonic-gate 			return (err);
1828*7c478bd9Sstevel@tonic-gate 		}
1829*7c478bd9Sstevel@tonic-gate 	}
1830*7c478bd9Sstevel@tonic-gate 	evp = sysevent_evc_alloc(class, subclass, pub, pub_sz, asz, km_flags);
1831*7c478bd9Sstevel@tonic-gate 	if (evp == NULL) {
1832*7c478bd9Sstevel@tonic-gate 		return (ENOMEM);
1833*7c478bd9Sstevel@tonic-gate 	}
1834*7c478bd9Sstevel@tonic-gate 	if (attr != NULL) {
1835*7c478bd9Sstevel@tonic-gate 		/*
1836*7c478bd9Sstevel@tonic-gate 		 * Pack attributes into event buffer. Event buffer already
1837*7c478bd9Sstevel@tonic-gate 		 * has enough room for the packed nvlist.
1838*7c478bd9Sstevel@tonic-gate 		 */
1839*7c478bd9Sstevel@tonic-gate 		attr_offset = SE_ATTR_OFF(evp);
1840*7c478bd9Sstevel@tonic-gate 		patt = (caddr_t)evp + attr_offset;
1841*7c478bd9Sstevel@tonic-gate 
1842*7c478bd9Sstevel@tonic-gate 		err = nvlist_pack(attr, &patt, &asz, NV_ENCODE_NATIVE,
1843*7c478bd9Sstevel@tonic-gate 		    km_flags & EVCH_SLEEP ? KM_SLEEP : KM_NOSLEEP);
1844*7c478bd9Sstevel@tonic-gate 
1845*7c478bd9Sstevel@tonic-gate 		ASSERT(err != ENOMEM);
1846*7c478bd9Sstevel@tonic-gate 
1847*7c478bd9Sstevel@tonic-gate 		if (err != 0) {
1848*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
1849*7c478bd9Sstevel@tonic-gate 		}
1850*7c478bd9Sstevel@tonic-gate 
1851*7c478bd9Sstevel@tonic-gate 		evp->seh_attr_off = attr_offset;
1852*7c478bd9Sstevel@tonic-gate 		SE_FLAG(evp) = SE_PACKED_BUF;
1853*7c478bd9Sstevel@tonic-gate 	}
1854*7c478bd9Sstevel@tonic-gate 	return (evch_chpublish((evch_bind_t *)scp, evp, flags));
1855*7c478bd9Sstevel@tonic-gate }
1856*7c478bd9Sstevel@tonic-gate 
1857*7c478bd9Sstevel@tonic-gate int
1858*7c478bd9Sstevel@tonic-gate sysevent_evc_control(evchan_t *scp, int cmd, ...)
1859*7c478bd9Sstevel@tonic-gate {
1860*7c478bd9Sstevel@tonic-gate 	va_list		ap;
1861*7c478bd9Sstevel@tonic-gate 	evch_chan_t	*chp = ((evch_bind_t *)scp)->bd_channel;
1862*7c478bd9Sstevel@tonic-gate 	uint32_t	*chlenp;
1863*7c478bd9Sstevel@tonic-gate 	uint32_t	chlen;
1864*7c478bd9Sstevel@tonic-gate 	uint32_t	ochlen;
1865*7c478bd9Sstevel@tonic-gate 	int		rc = 0;
1866*7c478bd9Sstevel@tonic-gate 
1867*7c478bd9Sstevel@tonic-gate 	if (scp == NULL) {
1868*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1869*7c478bd9Sstevel@tonic-gate 	}
1870*7c478bd9Sstevel@tonic-gate 
1871*7c478bd9Sstevel@tonic-gate 	va_start(ap, cmd);
1872*7c478bd9Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
1873*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
1874*7c478bd9Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN:
1875*7c478bd9Sstevel@tonic-gate 		chlenp = va_arg(ap, uint32_t *);
1876*7c478bd9Sstevel@tonic-gate 		*chlenp = chp->ch_maxev;
1877*7c478bd9Sstevel@tonic-gate 		break;
1878*7c478bd9Sstevel@tonic-gate 	case EVCH_SET_CHAN_LEN:
1879*7c478bd9Sstevel@tonic-gate 		chlen = va_arg(ap, uint32_t);
1880*7c478bd9Sstevel@tonic-gate 		ochlen = chp->ch_maxev;
1881*7c478bd9Sstevel@tonic-gate 		chp->ch_maxev = min(chlen, evch_events_max);
1882*7c478bd9Sstevel@tonic-gate 		if (ochlen < chp->ch_maxev) {
1883*7c478bd9Sstevel@tonic-gate 			cv_signal(&chp->ch_pubcv);
1884*7c478bd9Sstevel@tonic-gate 		}
1885*7c478bd9Sstevel@tonic-gate 		break;
1886*7c478bd9Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN_MAX:
1887*7c478bd9Sstevel@tonic-gate 		*va_arg(ap, uint32_t *) = evch_events_max;
1888*7c478bd9Sstevel@tonic-gate 		break;
1889*7c478bd9Sstevel@tonic-gate 	default:
1890*7c478bd9Sstevel@tonic-gate 		rc = EINVAL;
1891*7c478bd9Sstevel@tonic-gate 	}
1892*7c478bd9Sstevel@tonic-gate 
1893*7c478bd9Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
1894*7c478bd9Sstevel@tonic-gate 	va_end(ap);
1895*7c478bd9Sstevel@tonic-gate 	return (rc);
1896*7c478bd9Sstevel@tonic-gate }
1897*7c478bd9Sstevel@tonic-gate 
1898*7c478bd9Sstevel@tonic-gate /*
1899*7c478bd9Sstevel@tonic-gate  * Project private interface to take a snapshot of all events of the
1900*7c478bd9Sstevel@tonic-gate  * specified event channel. Argument subscr may be a subscriber id, the empty
1901*7c478bd9Sstevel@tonic-gate  * string "", or NULL. The empty string indicates that no subscriber is
1902*7c478bd9Sstevel@tonic-gate  * selected, for example if a previous subscriber died. sysevent_evc_walk_next()
1903*7c478bd9Sstevel@tonic-gate  * will deliver events from the main event queue in this case. If subscr is
1904*7c478bd9Sstevel@tonic-gate  * NULL, the subscriber with the EVCH_SUB_DUMP flag set (subd->sd_dump != 0)
1905*7c478bd9Sstevel@tonic-gate  * will be selected.
1906*7c478bd9Sstevel@tonic-gate  *
1907*7c478bd9Sstevel@tonic-gate  * In panic case this function returns NULL. This is legal. The NULL has
1908*7c478bd9Sstevel@tonic-gate  * to be delivered to sysevent_evc_walk_step() and sysevent_evc_walk_fini().
1909*7c478bd9Sstevel@tonic-gate  */
1910*7c478bd9Sstevel@tonic-gate evchanq_t *
1911*7c478bd9Sstevel@tonic-gate sysevent_evc_walk_init(evchan_t *scp, char *subscr)
1912*7c478bd9Sstevel@tonic-gate {
1913*7c478bd9Sstevel@tonic-gate 	if (panicstr != NULL && scp == NULL)
1914*7c478bd9Sstevel@tonic-gate 		return (NULL);
1915*7c478bd9Sstevel@tonic-gate 	ASSERT(scp != NULL);
1916*7c478bd9Sstevel@tonic-gate 	return (evch_chrdevent_init(((evch_bind_t *)scp)->bd_channel, subscr));
1917*7c478bd9Sstevel@tonic-gate }
1918*7c478bd9Sstevel@tonic-gate 
1919*7c478bd9Sstevel@tonic-gate /*
1920*7c478bd9Sstevel@tonic-gate  * Project private interface to read events from a previously taken
1921*7c478bd9Sstevel@tonic-gate  * snapshot (with sysevent_evc_walk_init). In case of panic events
1922*7c478bd9Sstevel@tonic-gate  * are retrieved directly from the channel data structures. No resources
1923*7c478bd9Sstevel@tonic-gate  * are allocated and no mutexes are grabbed in panic context.
1924*7c478bd9Sstevel@tonic-gate  */
1925*7c478bd9Sstevel@tonic-gate sysevent_t *
1926*7c478bd9Sstevel@tonic-gate sysevent_evc_walk_step(evchanq_t *evcq)
1927*7c478bd9Sstevel@tonic-gate {
1928*7c478bd9Sstevel@tonic-gate 	return ((sysevent_t *)evch_chgetnextev(evcq));
1929*7c478bd9Sstevel@tonic-gate }
1930*7c478bd9Sstevel@tonic-gate 
1931*7c478bd9Sstevel@tonic-gate /*
1932*7c478bd9Sstevel@tonic-gate  * Project private interface to free a previously taken snapshot.
1933*7c478bd9Sstevel@tonic-gate  */
1934*7c478bd9Sstevel@tonic-gate void
1935*7c478bd9Sstevel@tonic-gate sysevent_evc_walk_fini(evchanq_t *evcq)
1936*7c478bd9Sstevel@tonic-gate {
1937*7c478bd9Sstevel@tonic-gate 	evch_chrdevent_fini(evcq);
1938*7c478bd9Sstevel@tonic-gate }
1939*7c478bd9Sstevel@tonic-gate 
1940*7c478bd9Sstevel@tonic-gate /*
1941*7c478bd9Sstevel@tonic-gate  * Get address and size of an event payload. Returns NULL when no
1942*7c478bd9Sstevel@tonic-gate  * payload present.
1943*7c478bd9Sstevel@tonic-gate  */
1944*7c478bd9Sstevel@tonic-gate char *
1945*7c478bd9Sstevel@tonic-gate sysevent_evc_event_attr(sysevent_t *ev, size_t *plsize)
1946*7c478bd9Sstevel@tonic-gate {
1947*7c478bd9Sstevel@tonic-gate 	char	*attrp;
1948*7c478bd9Sstevel@tonic-gate 	size_t	aoff;
1949*7c478bd9Sstevel@tonic-gate 	size_t	asz;
1950*7c478bd9Sstevel@tonic-gate 
1951*7c478bd9Sstevel@tonic-gate 	aoff = SE_ATTR_OFF(ev);
1952*7c478bd9Sstevel@tonic-gate 	attrp = (char *)ev + aoff;
1953*7c478bd9Sstevel@tonic-gate 	asz = *plsize = SE_SIZE(ev) - aoff;
1954*7c478bd9Sstevel@tonic-gate 	return (asz ? attrp : NULL);
1955*7c478bd9Sstevel@tonic-gate }
1956*7c478bd9Sstevel@tonic-gate 
1957*7c478bd9Sstevel@tonic-gate /*
1958*7c478bd9Sstevel@tonic-gate  * sysevent_get_class_name - Get class name string
1959*7c478bd9Sstevel@tonic-gate  */
1960*7c478bd9Sstevel@tonic-gate char *
1961*7c478bd9Sstevel@tonic-gate sysevent_get_class_name(sysevent_t *ev)
1962*7c478bd9Sstevel@tonic-gate {
1963*7c478bd9Sstevel@tonic-gate 	return (SE_CLASS_NAME(ev));
1964*7c478bd9Sstevel@tonic-gate }
1965*7c478bd9Sstevel@tonic-gate 
1966*7c478bd9Sstevel@tonic-gate /*
1967*7c478bd9Sstevel@tonic-gate  * sysevent_get_subclass_name - Get subclass name string
1968*7c478bd9Sstevel@tonic-gate  */
1969*7c478bd9Sstevel@tonic-gate char *
1970*7c478bd9Sstevel@tonic-gate sysevent_get_subclass_name(sysevent_t *ev)
1971*7c478bd9Sstevel@tonic-gate {
1972*7c478bd9Sstevel@tonic-gate 	return (SE_SUBCLASS_NAME(ev));
1973*7c478bd9Sstevel@tonic-gate }
1974*7c478bd9Sstevel@tonic-gate 
1975*7c478bd9Sstevel@tonic-gate /*
1976*7c478bd9Sstevel@tonic-gate  * sysevent_get_seq - Get event sequence id
1977*7c478bd9Sstevel@tonic-gate  */
1978*7c478bd9Sstevel@tonic-gate uint64_t
1979*7c478bd9Sstevel@tonic-gate sysevent_get_seq(sysevent_t *ev)
1980*7c478bd9Sstevel@tonic-gate {
1981*7c478bd9Sstevel@tonic-gate 	return (SE_SEQ(ev));
1982*7c478bd9Sstevel@tonic-gate }
1983*7c478bd9Sstevel@tonic-gate 
1984*7c478bd9Sstevel@tonic-gate /*
1985*7c478bd9Sstevel@tonic-gate  * sysevent_get_time - Get event timestamp
1986*7c478bd9Sstevel@tonic-gate  */
1987*7c478bd9Sstevel@tonic-gate void
1988*7c478bd9Sstevel@tonic-gate sysevent_get_time(sysevent_t *ev, hrtime_t *etime)
1989*7c478bd9Sstevel@tonic-gate {
1990*7c478bd9Sstevel@tonic-gate 	*etime = SE_TIME(ev);
1991*7c478bd9Sstevel@tonic-gate }
1992*7c478bd9Sstevel@tonic-gate 
1993*7c478bd9Sstevel@tonic-gate /*
1994*7c478bd9Sstevel@tonic-gate  * sysevent_get_size - Get event buffer size
1995*7c478bd9Sstevel@tonic-gate  */
1996*7c478bd9Sstevel@tonic-gate size_t
1997*7c478bd9Sstevel@tonic-gate sysevent_get_size(sysevent_t *ev)
1998*7c478bd9Sstevel@tonic-gate {
1999*7c478bd9Sstevel@tonic-gate 	return ((size_t)SE_SIZE(ev));
2000*7c478bd9Sstevel@tonic-gate }
2001*7c478bd9Sstevel@tonic-gate 
2002*7c478bd9Sstevel@tonic-gate /*
2003*7c478bd9Sstevel@tonic-gate  * sysevent_get_pub - Get publisher name string
2004*7c478bd9Sstevel@tonic-gate  */
2005*7c478bd9Sstevel@tonic-gate char *
2006*7c478bd9Sstevel@tonic-gate sysevent_get_pub(sysevent_t *ev)
2007*7c478bd9Sstevel@tonic-gate {
2008*7c478bd9Sstevel@tonic-gate 	return (SE_PUB_NAME(ev));
2009*7c478bd9Sstevel@tonic-gate }
2010*7c478bd9Sstevel@tonic-gate 
2011*7c478bd9Sstevel@tonic-gate /*
2012*7c478bd9Sstevel@tonic-gate  * sysevent_get_attr_list - stores address of a copy of the attribute list
2013*7c478bd9Sstevel@tonic-gate  * associated with the given sysevent buffer. The list must be freed by the
2014*7c478bd9Sstevel@tonic-gate  * caller.
2015*7c478bd9Sstevel@tonic-gate  */
2016*7c478bd9Sstevel@tonic-gate int
2017*7c478bd9Sstevel@tonic-gate sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist)
2018*7c478bd9Sstevel@tonic-gate {
2019*7c478bd9Sstevel@tonic-gate 	int		error;
2020*7c478bd9Sstevel@tonic-gate 	caddr_t		attr;
2021*7c478bd9Sstevel@tonic-gate 	size_t		attr_len;
2022*7c478bd9Sstevel@tonic-gate 	uint64_t	attr_offset;
2023*7c478bd9Sstevel@tonic-gate 
2024*7c478bd9Sstevel@tonic-gate 	*nvlist = NULL;
2025*7c478bd9Sstevel@tonic-gate 	if (SE_FLAG(ev) != SE_PACKED_BUF) {
2026*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
2027*7c478bd9Sstevel@tonic-gate 	}
2028*7c478bd9Sstevel@tonic-gate 	attr_offset = SE_ATTR_OFF(ev);
2029*7c478bd9Sstevel@tonic-gate 	if (SE_SIZE(ev) == attr_offset) {
2030*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
2031*7c478bd9Sstevel@tonic-gate 	}
2032*7c478bd9Sstevel@tonic-gate 
2033*7c478bd9Sstevel@tonic-gate 	/* unpack nvlist */
2034*7c478bd9Sstevel@tonic-gate 	attr = (caddr_t)ev + attr_offset;
2035*7c478bd9Sstevel@tonic-gate 	attr_len = SE_SIZE(ev) - attr_offset;
2036*7c478bd9Sstevel@tonic-gate 	if ((error = nvlist_unpack(attr, attr_len, nvlist, 0)) != 0) {
2037*7c478bd9Sstevel@tonic-gate 		error = error != ENOMEM ? EINVAL : error;
2038*7c478bd9Sstevel@tonic-gate 		return (error);
2039*7c478bd9Sstevel@tonic-gate 	}
2040*7c478bd9Sstevel@tonic-gate 	return (0);
2041*7c478bd9Sstevel@tonic-gate }
2042*7c478bd9Sstevel@tonic-gate 
2043*7c478bd9Sstevel@tonic-gate /*
2044*7c478bd9Sstevel@tonic-gate  * Functions called by the sysevent driver for general purpose event channels
2045*7c478bd9Sstevel@tonic-gate  *
2046*7c478bd9Sstevel@tonic-gate  * evch_usrchanopen	- Create/Bind to an event channel
2047*7c478bd9Sstevel@tonic-gate  * evch_usrchanclose	- Unbind/Destroy event channel
2048*7c478bd9Sstevel@tonic-gate  * evch_usrallocev	- Allocate event data structure
2049*7c478bd9Sstevel@tonic-gate  * evch_usrfreeev	- Free event data structure
2050*7c478bd9Sstevel@tonic-gate  * evch_usrpostevent	- Publish event
2051*7c478bd9Sstevel@tonic-gate  * evch_usrsubscribe	- Subscribe (register callback function)
2052*7c478bd9Sstevel@tonic-gate  * evch_usrunsubscribe	- Unsubscribe
2053*7c478bd9Sstevel@tonic-gate  * evch_usrcontrol_set	- Set channel properties
2054*7c478bd9Sstevel@tonic-gate  * evch_usrcontrol_get	- Get channel properties
2055*7c478bd9Sstevel@tonic-gate  * evch_usrgetchnames	- Get list of channel names
2056*7c478bd9Sstevel@tonic-gate  * evch_usrgetchdata	- Get data of an event channel
2057*7c478bd9Sstevel@tonic-gate  */
2058*7c478bd9Sstevel@tonic-gate evchan_t *
2059*7c478bd9Sstevel@tonic-gate evch_usrchanopen(const char *name, uint32_t flags, int *err)
2060*7c478bd9Sstevel@tonic-gate {
2061*7c478bd9Sstevel@tonic-gate 	evch_bind_t *bp = NULL;
2062*7c478bd9Sstevel@tonic-gate 
2063*7c478bd9Sstevel@tonic-gate 	*err = evch_chbind(name, &bp, flags);
2064*7c478bd9Sstevel@tonic-gate 	return ((evchan_t *)bp);
2065*7c478bd9Sstevel@tonic-gate }
2066*7c478bd9Sstevel@tonic-gate 
2067*7c478bd9Sstevel@tonic-gate /*
2068*7c478bd9Sstevel@tonic-gate  * Unbind from the channel.
2069*7c478bd9Sstevel@tonic-gate  */
2070*7c478bd9Sstevel@tonic-gate void
2071*7c478bd9Sstevel@tonic-gate evch_usrchanclose(evchan_t *cbp)
2072*7c478bd9Sstevel@tonic-gate {
2073*7c478bd9Sstevel@tonic-gate 	evch_chunbind((evch_bind_t *)cbp);
2074*7c478bd9Sstevel@tonic-gate }
2075*7c478bd9Sstevel@tonic-gate 
2076*7c478bd9Sstevel@tonic-gate /*
2077*7c478bd9Sstevel@tonic-gate  * Allocates log_evch_eventq_t structure but returns the pointer of the embedded
2078*7c478bd9Sstevel@tonic-gate  * sysevent_impl_t structure as the opaque sysevent_t * data type
2079*7c478bd9Sstevel@tonic-gate  */
2080*7c478bd9Sstevel@tonic-gate sysevent_impl_t *
2081*7c478bd9Sstevel@tonic-gate evch_usrallocev(size_t evsize, uint32_t flags)
2082*7c478bd9Sstevel@tonic-gate {
2083*7c478bd9Sstevel@tonic-gate 	return ((sysevent_impl_t *)evch_evq_evzalloc(evsize, flags));
2084*7c478bd9Sstevel@tonic-gate }
2085*7c478bd9Sstevel@tonic-gate 
2086*7c478bd9Sstevel@tonic-gate /*
2087*7c478bd9Sstevel@tonic-gate  * Free evch_eventq_t structure
2088*7c478bd9Sstevel@tonic-gate  */
2089*7c478bd9Sstevel@tonic-gate void
2090*7c478bd9Sstevel@tonic-gate evch_usrfreeev(sysevent_impl_t *ev)
2091*7c478bd9Sstevel@tonic-gate {
2092*7c478bd9Sstevel@tonic-gate 	evch_evq_evfree((void *)ev);
2093*7c478bd9Sstevel@tonic-gate }
2094*7c478bd9Sstevel@tonic-gate 
2095*7c478bd9Sstevel@tonic-gate /*
2096*7c478bd9Sstevel@tonic-gate  * Posts an event to the given channel. The event structure has to be
2097*7c478bd9Sstevel@tonic-gate  * allocated by evch_usrallocev(). Returns zero on success and an error
2098*7c478bd9Sstevel@tonic-gate  * code else. Attributes have to be packed and included in the event structure.
2099*7c478bd9Sstevel@tonic-gate  *
2100*7c478bd9Sstevel@tonic-gate  */
2101*7c478bd9Sstevel@tonic-gate int
2102*7c478bd9Sstevel@tonic-gate evch_usrpostevent(evchan_t *bp, sysevent_impl_t *ev, uint32_t flags)
2103*7c478bd9Sstevel@tonic-gate {
2104*7c478bd9Sstevel@tonic-gate 	return (evch_chpublish((evch_bind_t *)bp, ev, flags));
2105*7c478bd9Sstevel@tonic-gate }
2106*7c478bd9Sstevel@tonic-gate 
2107*7c478bd9Sstevel@tonic-gate /*
2108*7c478bd9Sstevel@tonic-gate  * Subscribe function for user land subscriptions
2109*7c478bd9Sstevel@tonic-gate  */
2110*7c478bd9Sstevel@tonic-gate int
2111*7c478bd9Sstevel@tonic-gate evch_usrsubscribe(evchan_t *bp, const char *sid, const char *class,
2112*7c478bd9Sstevel@tonic-gate     int d, uint32_t flags)
2113*7c478bd9Sstevel@tonic-gate {
2114*7c478bd9Sstevel@tonic-gate 	door_handle_t	dh = door_ki_lookup(d);
2115*7c478bd9Sstevel@tonic-gate 	int		rv;
2116*7c478bd9Sstevel@tonic-gate 
2117*7c478bd9Sstevel@tonic-gate 	if (dh == NULL) {
2118*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
2119*7c478bd9Sstevel@tonic-gate 	}
2120*7c478bd9Sstevel@tonic-gate 	if ((rv = evch_chsubscribe((evch_bind_t *)bp, EVCH_DELDOOR, sid, class,
2121*7c478bd9Sstevel@tonic-gate 	    (void *)dh, NULL, flags, curproc->p_pid)) != 0) {
2122*7c478bd9Sstevel@tonic-gate 		door_ki_rele(dh);
2123*7c478bd9Sstevel@tonic-gate 	}
2124*7c478bd9Sstevel@tonic-gate 	return (rv);
2125*7c478bd9Sstevel@tonic-gate }
2126*7c478bd9Sstevel@tonic-gate 
2127*7c478bd9Sstevel@tonic-gate /*
2128*7c478bd9Sstevel@tonic-gate  * Flag can be EVCH_SUB_KEEP or 0. EVCH_SUB_KEEP preserves persistent
2129*7c478bd9Sstevel@tonic-gate  * subscribers
2130*7c478bd9Sstevel@tonic-gate  */
2131*7c478bd9Sstevel@tonic-gate void
2132*7c478bd9Sstevel@tonic-gate evch_usrunsubscribe(evchan_t *bp, const char *subid, uint32_t flags)
2133*7c478bd9Sstevel@tonic-gate {
2134*7c478bd9Sstevel@tonic-gate 	evch_chunsubscribe((evch_bind_t *)bp, subid, flags);
2135*7c478bd9Sstevel@tonic-gate }
2136*7c478bd9Sstevel@tonic-gate 
2137*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2138*7c478bd9Sstevel@tonic-gate int
2139*7c478bd9Sstevel@tonic-gate evch_usrcontrol_set(evchan_t *bp, int cmd, uint32_t value)
2140*7c478bd9Sstevel@tonic-gate {
2141*7c478bd9Sstevel@tonic-gate 	evch_chan_t	*chp = ((evch_bind_t *)bp)->bd_channel;
2142*7c478bd9Sstevel@tonic-gate 	uid_t		uid = crgetuid(curthread->t_cred);
2143*7c478bd9Sstevel@tonic-gate 	int		rc = 0;
2144*7c478bd9Sstevel@tonic-gate 
2145*7c478bd9Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
2146*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
2147*7c478bd9Sstevel@tonic-gate 	case EVCH_SET_CHAN_LEN:
2148*7c478bd9Sstevel@tonic-gate 		if (uid && uid != chp->ch_uid) {
2149*7c478bd9Sstevel@tonic-gate 			rc = EACCES;
2150*7c478bd9Sstevel@tonic-gate 			break;
2151*7c478bd9Sstevel@tonic-gate 		}
2152*7c478bd9Sstevel@tonic-gate 		chp->ch_maxev = min(value, evch_events_max);
2153*7c478bd9Sstevel@tonic-gate 		break;
2154*7c478bd9Sstevel@tonic-gate 	default:
2155*7c478bd9Sstevel@tonic-gate 		rc = EINVAL;
2156*7c478bd9Sstevel@tonic-gate 	}
2157*7c478bd9Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
2158*7c478bd9Sstevel@tonic-gate 	return (rc);
2159*7c478bd9Sstevel@tonic-gate }
2160*7c478bd9Sstevel@tonic-gate 
2161*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2162*7c478bd9Sstevel@tonic-gate int
2163*7c478bd9Sstevel@tonic-gate evch_usrcontrol_get(evchan_t *bp, int cmd, uint32_t *value)
2164*7c478bd9Sstevel@tonic-gate {
2165*7c478bd9Sstevel@tonic-gate 	evch_chan_t	*chp = ((evch_bind_t *)bp)->bd_channel;
2166*7c478bd9Sstevel@tonic-gate 	int		rc = 0;
2167*7c478bd9Sstevel@tonic-gate 
2168*7c478bd9Sstevel@tonic-gate 	mutex_enter(&chp->ch_mutex);
2169*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
2170*7c478bd9Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN:
2171*7c478bd9Sstevel@tonic-gate 		*value = chp->ch_maxev;
2172*7c478bd9Sstevel@tonic-gate 		break;
2173*7c478bd9Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN_MAX:
2174*7c478bd9Sstevel@tonic-gate 		*value = evch_events_max;
2175*7c478bd9Sstevel@tonic-gate 		break;
2176*7c478bd9Sstevel@tonic-gate 	default:
2177*7c478bd9Sstevel@tonic-gate 		rc = EINVAL;
2178*7c478bd9Sstevel@tonic-gate 	}
2179*7c478bd9Sstevel@tonic-gate 	mutex_exit(&chp->ch_mutex);
2180*7c478bd9Sstevel@tonic-gate 	return (rc);
2181*7c478bd9Sstevel@tonic-gate }
2182*7c478bd9Sstevel@tonic-gate 
2183*7c478bd9Sstevel@tonic-gate int
2184*7c478bd9Sstevel@tonic-gate evch_usrgetchnames(char *buf, size_t size)
2185*7c478bd9Sstevel@tonic-gate {
2186*7c478bd9Sstevel@tonic-gate 	return (evch_chgetnames(buf, size));
2187*7c478bd9Sstevel@tonic-gate }
2188*7c478bd9Sstevel@tonic-gate 
2189*7c478bd9Sstevel@tonic-gate int
2190*7c478bd9Sstevel@tonic-gate evch_usrgetchdata(char *chname, void *buf, size_t size)
2191*7c478bd9Sstevel@tonic-gate {
2192*7c478bd9Sstevel@tonic-gate 	return (evch_chgetchdata(chname, buf, size));
2193*7c478bd9Sstevel@tonic-gate }
2194