Merge commit 'ea01a15a654b9e1c7b37d958f4d1911882ed7781'
[unleashed.git] / kernel / os / evchannels.c
blob0bcf1257225c24dc1877bb016cc38b6d1ff6ea28
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
26 * This file contains the source of the general purpose event channel extension
27 * to the sysevent framework. This implementation is made up mainly of four
28 * layers of functionality: the event queues (evch_evq_*()), the handling of
29 * channels (evch_ch*()), the kernel interface (sysevent_evc_*()) and the
30 * interface for the sysevent pseudo driver (evch_usr*()).
31 * Libsysevent.so uses the pseudo driver sysevent's ioctl to access the event
32 * channel extensions. The driver in turn uses the evch_usr*() functions below.
34 * The interfaces for user land and kernel are declared in sys/sysevent.h
35 * Internal data structures for event channels are defined in
36 * sys/sysevent_impl.h.
38 * The basic data structure for an event channel is of type evch_chan_t.
39 * All channels are maintained by a list named evch_list. The list head
40 * is of type evch_dlist_t.
43 #include <sys/types.h>
44 #include <sys/errno.h>
45 #include <sys/stropts.h>
46 #include <sys/debug.h>
47 #include <sys/ddi.h>
48 #include <sys/vmem.h>
49 #include <sys/cmn_err.h>
50 #include <sys/callb.h>
51 #include <sys/sysevent.h>
52 #include <sys/sysevent_impl.h>
53 #include <sys/sysmacros.h>
54 #include <sys/disp.h>
55 #include <sys/atomic.h>
56 #include <sys/door.h>
57 #include <sys/zone.h>
58 #include <sys/sdt.h>
60 /* Back-off delay for door_ki_upcall */
61 #define EVCH_MIN_PAUSE 8
62 #define EVCH_MAX_PAUSE 128
64 #define GEVENT(ev) ((evch_gevent_t *)((char *)ev - \
65 offsetof(evch_gevent_t, ge_payload)))
67 #define EVCH_EVQ_EVCOUNT(x) ((&(x)->eq_eventq)->sq_count)
68 #define EVCH_EVQ_HIGHWM(x) ((&(x)->eq_eventq)->sq_highwm)
70 #define CH_HOLD_PEND 1
71 #define CH_HOLD_PEND_INDEF 2
73 struct evch_globals {
74 evch_dlist_t evch_list;
75 kmutex_t evch_list_lock;
78 /* Variables used by event channel routines */
79 static int evq_initcomplete = 0;
80 static zone_key_t evch_zone_key;
81 static uint32_t evch_channels_max;
82 static uint32_t evch_bindings_max = EVCH_MAX_BINDS_PER_CHANNEL;
83 static uint32_t evch_events_max;
85 static void evch_evq_unsub(evch_eventq_t *, evch_evqsub_t *);
86 static void evch_evq_destroy(evch_eventq_t *);
89 * List handling. These functions handle a doubly linked list. The list has
90 * to be protected by the calling functions. evch_dlist_t is the list head.
91 * Every node of the list has to put a evch_dlelem_t data type in its data
92 * structure as its first element.
94 * evch_dl_init - Initialize list head
95 * evch_dl_fini - Terminate list handling
96 * evch_dl_is_init - Returns one if list is initialized
97 * evch_dl_add - Add element to end of list
98 * evch_dl_del - Remove given element from list
99 * evch_dl_search - Lookup element in list
100 * evch_dl_getnum - Get number of elements in list
101 * evch_dl_next - Get next elements of list
104 static void
105 evch_dl_init(evch_dlist_t *hp)
107 hp->dh_head.dl_prev = hp->dh_head.dl_next = &hp->dh_head;
108 hp->dh_count = 0;
112 * Assumes that list is empty.
114 static void
115 evch_dl_fini(evch_dlist_t *hp)
117 hp->dh_head.dl_prev = hp->dh_head.dl_next = NULL;
120 static int
121 evch_dl_is_init(evch_dlist_t *hp)
123 return (hp->dh_head.dl_next != NULL ? 1 : 0);
127 * Add an element at the end of the list.
129 static void
130 evch_dl_add(evch_dlist_t *hp, evch_dlelem_t *el)
132 evch_dlelem_t *x = hp->dh_head.dl_prev;
133 evch_dlelem_t *y = &hp->dh_head;
135 x->dl_next = el;
136 y->dl_prev = el;
137 el->dl_next = y;
138 el->dl_prev = x;
139 hp->dh_count++;
143 * Remove arbitrary element out of dlist.
145 static void
146 evch_dl_del(evch_dlist_t *hp, evch_dlelem_t *p)
148 ASSERT(hp->dh_count > 0 && p != &hp->dh_head);
149 p->dl_prev->dl_next = p->dl_next;
150 p->dl_next->dl_prev = p->dl_prev;
151 p->dl_prev = NULL;
152 p->dl_next = NULL;
153 hp->dh_count--;
157 * Search an element in a list. Caller provides comparison callback function.
159 static evch_dlelem_t *
160 evch_dl_search(evch_dlist_t *hp, int (*cmp)(evch_dlelem_t *, char *), char *s)
162 evch_dlelem_t *p;
164 for (p = hp->dh_head.dl_next; p != &hp->dh_head; p = p->dl_next) {
165 if (cmp(p, s) == 0) {
166 return (p);
169 return (NULL);
173 * Return number of elements in the list.
175 static int
176 evch_dl_getnum(evch_dlist_t *hp)
178 return (hp->dh_count);
182 * Find next element of a evch_dlist_t list. Find first element if el == NULL.
183 * Returns NULL if end of list is reached.
185 static void *
186 evch_dl_next(evch_dlist_t *hp, void *el)
188 evch_dlelem_t *ep = (evch_dlelem_t *)el;
190 if (hp->dh_count == 0) {
191 return (NULL);
193 if (ep == NULL) {
194 return (hp->dh_head.dl_next);
196 if ((ep = ep->dl_next) == (evch_dlelem_t *)hp) {
197 return (NULL);
199 return ((void *)ep);
203 * Queue handling routines. Mutexes have to be entered previously.
205 * evch_q_init - Initialize queue head
206 * evch_q_in - Put element into queue
207 * evch_q_out - Get element out of queue
208 * evch_q_next - Iterate over the elements of a queue
210 static void
211 evch_q_init(evch_squeue_t *q)
213 q->sq_head = NULL;
214 q->sq_tail = (evch_qelem_t *)q;
215 q->sq_count = 0;
216 q->sq_highwm = 0;
220 * Put element into the queue q
222 static void
223 evch_q_in(evch_squeue_t *q, evch_qelem_t *el)
225 q->sq_tail->q_next = el;
226 el->q_next = NULL;
227 q->sq_tail = el;
228 q->sq_count++;
229 if (q->sq_count > q->sq_highwm) {
230 q->sq_highwm = q->sq_count;
235 * Returns NULL if queue is empty.
237 static evch_qelem_t *
238 evch_q_out(evch_squeue_t *q)
240 evch_qelem_t *el;
242 if ((el = q->sq_head) != NULL) {
243 q->sq_head = el->q_next;
244 q->sq_count--;
245 if (q->sq_head == NULL) {
246 q->sq_tail = (evch_qelem_t *)q;
249 return (el);
253 * Returns element after *el or first if el == NULL. NULL is returned
254 * if queue is empty or *el points to the last element in the queue.
256 static evch_qelem_t *
257 evch_q_next(evch_squeue_t *q, evch_qelem_t *el)
259 if (el == NULL)
260 return (q->sq_head);
261 return (el->q_next);
265 * Event queue handling functions. An event queue is the basic building block
266 * of an event channel. One event queue makes up the publisher-side event queue.
267 * Further event queues build the per-subscriber queues of an event channel.
268 * Each queue is associated an event delivery thread.
269 * These functions support a two-step initialization. First step, when kernel
270 * memory is ready and second when threads are ready.
271 * Events consist of an administrating evch_gevent_t structure with the event
272 * data appended as variable length payload.
273 * The internal interface functions for the event queue handling are:
275 * evch_evq_create - create an event queue
276 * evch_evq_thrcreate - create thread for an event queue.
277 * evch_evq_destroy - delete an event queue
278 * evch_evq_sub - Subscribe to event delivery from an event queue
279 * evch_evq_unsub - Unsubscribe
280 * evch_evq_pub - Post an event into an event queue
281 * evch_evq_stop - Put delivery thread on hold
282 * evch_evq_continue - Resume event delivery thread
283 * evch_evq_status - Return status of delivery thread, running or on hold
284 * evch_evq_evzalloc - Allocate an event structure
285 * evch_evq_evfree - Free an event structure
286 * evch_evq_evadd_dest - Add a destructor function to an event structure
287 * evch_evq_evnext - Iterate over events non-destructive
290 /*ARGSUSED*/
291 static void *
292 evch_zoneinit(zoneid_t zoneid)
294 struct evch_globals *eg;
296 eg = kmem_zalloc(sizeof (*eg), KM_SLEEP);
297 evch_dl_init(&eg->evch_list);
298 return (eg);
301 /*ARGSUSED*/
302 static void
303 evch_zonefree(zoneid_t zoneid, void *arg)
305 struct evch_globals *eg = arg;
306 evch_chan_t *chp;
307 evch_subd_t *sdp;
309 mutex_enter(&eg->evch_list_lock);
312 * Keep picking the head element off the list until there are no
313 * more.
315 while ((chp = evch_dl_next(&eg->evch_list, NULL)) != NULL) {
318 * Since all processes are gone, all bindings should be gone,
319 * and only channels with SUB_KEEP subscribers should remain.
321 mutex_enter(&chp->ch_mutex);
322 ASSERT(chp->ch_bindings == 0);
323 ASSERT(evch_dl_getnum(&chp->ch_subscr) != 0 ||
324 chp->ch_holdpend == CH_HOLD_PEND_INDEF);
326 /* Forcibly unsubscribe each remaining subscription */
327 while ((sdp = evch_dl_next(&chp->ch_subscr, NULL)) != NULL) {
329 * We should only be tearing down persistent
330 * subscribers at this point, since all processes
331 * from this zone are gone.
333 ASSERT(sdp->sd_active == 0);
334 ASSERT((sdp->sd_persist & EVCH_SUB_KEEP) != 0);
336 * Disconnect subscriber queue from main event queue.
338 evch_evq_unsub(chp->ch_queue, sdp->sd_msub);
340 /* Destruct per subscriber queue */
341 evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub);
342 evch_evq_destroy(sdp->sd_queue);
344 * Eliminate the subscriber data from channel list.
346 evch_dl_del(&chp->ch_subscr, &sdp->sd_link);
347 kmem_free(sdp->sd_classname, sdp->sd_clnsize);
348 kmem_free(sdp->sd_ident, strlen(sdp->sd_ident) + 1);
349 kmem_free(sdp, sizeof (evch_subd_t));
352 /* Channel must now have no subscribers */
353 ASSERT(evch_dl_getnum(&chp->ch_subscr) == 0);
355 /* Just like unbind */
356 mutex_exit(&chp->ch_mutex);
357 evch_dl_del(&eg->evch_list, &chp->ch_link);
358 evch_evq_destroy(chp->ch_queue);
359 mutex_destroy(&chp->ch_mutex);
360 mutex_destroy(&chp->ch_pubmx);
361 cv_destroy(&chp->ch_pubcv);
362 kmem_free(chp->ch_name, chp->ch_namelen);
363 kmem_free(chp, sizeof (evch_chan_t));
366 mutex_exit(&eg->evch_list_lock);
367 /* all channels should now be gone */
368 ASSERT(evch_dl_getnum(&eg->evch_list) == 0);
369 kmem_free(eg, sizeof (*eg));
373 * Frees evch_gevent_t structure including the payload, if the reference count
374 * drops to or below zero. Below zero happens when the event is freed
375 * without beeing queued into a queue.
377 static void
378 evch_gevent_free(evch_gevent_t *evp)
380 int32_t refcnt;
382 refcnt = (int32_t)atomic_dec_32_nv(&evp->ge_refcount);
383 if (refcnt <= 0) {
384 if (evp->ge_destruct != NULL) {
385 evp->ge_destruct((void *)&(evp->ge_payload),
386 evp->ge_dstcookie);
388 kmem_free(evp, evp->ge_size);
393 * Deliver is called for every subscription to the current event
394 * It calls the registered filter function and then the registered delivery
395 * callback routine. Returns 0 on success. The callback routine returns
396 * EVQ_AGAIN or EVQ_SLEEP in case the event could not be delivered.
398 static int
399 evch_deliver(evch_evqsub_t *sp, evch_gevent_t *ep)
401 void *uep = &ep->ge_payload;
402 int res = EVQ_DELIVER;
404 if (sp->su_filter != NULL) {
405 res = sp->su_filter(uep, sp->su_fcookie);
407 if (res == EVQ_DELIVER) {
408 return (sp->su_callb(uep, sp->su_cbcookie));
410 return (0);
414 * Holds event delivery in case of eq_holdmode set or in case the
415 * event queue is empty. Mutex must be held when called.
416 * Wakes up a thread waiting for the delivery thread reaching the hold mode.
418 static void
419 evch_delivery_hold(evch_eventq_t *eqp, callb_cpr_t *cpip)
421 if (eqp->eq_tabortflag == 0) {
422 do {
423 if (eqp->eq_holdmode) {
424 cv_signal(&eqp->eq_onholdcv);
426 CALLB_CPR_SAFE_BEGIN(cpip);
427 cv_wait(&eqp->eq_thrsleepcv, &eqp->eq_queuemx);
428 CALLB_CPR_SAFE_END(cpip, &eqp->eq_queuemx);
429 } while (eqp->eq_holdmode);
434 * Event delivery thread. Enumerates all subscribers and calls evch_deliver()
435 * for each one.
437 static void
438 evch_delivery_thr(evch_eventq_t *eqp)
440 evch_qelem_t *qep;
441 callb_cpr_t cprinfo;
442 int res;
443 evch_evqsub_t *sub;
444 int deltime;
445 int repeatcount;
446 char thnam[32];
448 (void) snprintf(thnam, sizeof (thnam), "sysevent_chan-%d",
449 (int)eqp->eq_thrid);
450 CALLB_CPR_INIT(&cprinfo, &eqp->eq_queuemx, callb_generic_cpr, thnam);
451 mutex_enter(&eqp->eq_queuemx);
452 while (eqp->eq_tabortflag == 0) {
453 while (eqp->eq_holdmode == 0 && eqp->eq_tabortflag == 0 &&
454 (qep = evch_q_out(&eqp->eq_eventq)) != NULL) {
456 /* Filter and deliver event to all subscribers */
457 deltime = EVCH_MIN_PAUSE;
458 repeatcount = EVCH_MAX_TRY_DELIVERY;
459 eqp->eq_curevent = qep->q_objref;
460 sub = evch_dl_next(&eqp->eq_subscr, NULL);
461 while (sub != NULL) {
462 eqp->eq_dactive = 1;
463 mutex_exit(&eqp->eq_queuemx);
464 res = evch_deliver(sub, qep->q_objref);
465 mutex_enter(&eqp->eq_queuemx);
466 eqp->eq_dactive = 0;
467 cv_signal(&eqp->eq_dactivecv);
468 switch (res) {
469 case EVQ_SLEEP:
471 * Wait for subscriber to return.
473 eqp->eq_holdmode = 1;
474 evch_delivery_hold(eqp, &cprinfo);
475 if (eqp->eq_tabortflag) {
476 break;
478 continue;
479 case EVQ_AGAIN:
480 CALLB_CPR_SAFE_BEGIN(&cprinfo);
481 mutex_exit(&eqp->eq_queuemx);
482 delay(deltime);
483 deltime =
484 deltime > EVCH_MAX_PAUSE ?
485 deltime : deltime << 1;
486 mutex_enter(&eqp->eq_queuemx);
487 CALLB_CPR_SAFE_END(&cprinfo,
488 &eqp->eq_queuemx);
489 if (repeatcount-- > 0) {
490 continue;
492 break;
494 if (eqp->eq_tabortflag) {
495 break;
497 sub = evch_dl_next(&eqp->eq_subscr, sub);
498 repeatcount = EVCH_MAX_TRY_DELIVERY;
500 eqp->eq_curevent = NULL;
502 /* Free event data and queue element */
503 evch_gevent_free((evch_gevent_t *)qep->q_objref);
504 kmem_free(qep, qep->q_objsize);
507 /* Wait for next event or end of hold mode if set */
508 evch_delivery_hold(eqp, &cprinfo);
510 CALLB_CPR_EXIT(&cprinfo); /* Does mutex_exit of eqp->eq_queuemx */
511 thread_exit();
515 * Create the event delivery thread for an existing event queue.
517 static void
518 evch_evq_thrcreate(evch_eventq_t *eqp)
520 kthread_t *thp;
522 thp = thread_create(NULL, 0, evch_delivery_thr, (char *)eqp, 0, &p0,
523 TS_RUN, minclsyspri);
524 eqp->eq_thrid = thp->t_did;
528 * Create event queue.
530 static evch_eventq_t *
531 evch_evq_create()
533 evch_eventq_t *p;
535 /* Allocate and initialize event queue descriptor */
536 p = kmem_zalloc(sizeof (evch_eventq_t), KM_SLEEP);
537 mutex_init(&p->eq_queuemx, NULL, MUTEX_DEFAULT, NULL);
538 cv_init(&p->eq_thrsleepcv, NULL, CV_DEFAULT, NULL);
539 evch_q_init(&p->eq_eventq);
540 evch_dl_init(&p->eq_subscr);
541 cv_init(&p->eq_dactivecv, NULL, CV_DEFAULT, NULL);
542 cv_init(&p->eq_onholdcv, NULL, CV_DEFAULT, NULL);
544 /* Create delivery thread */
545 if (evq_initcomplete) {
546 evch_evq_thrcreate(p);
548 return (p);
552 * Destroy an event queue. All subscribers have to be unsubscribed prior to
553 * this call.
555 static void
556 evch_evq_destroy(evch_eventq_t *eqp)
558 evch_qelem_t *qep;
560 ASSERT(evch_dl_getnum(&eqp->eq_subscr) == 0);
561 /* Kill delivery thread */
562 if (eqp->eq_thrid != 0) {
563 mutex_enter(&eqp->eq_queuemx);
564 eqp->eq_tabortflag = 1;
565 eqp->eq_holdmode = 0;
566 cv_signal(&eqp->eq_thrsleepcv);
567 mutex_exit(&eqp->eq_queuemx);
568 thread_join(eqp->eq_thrid);
571 /* Get rid of stale events in the event queue */
572 while ((qep = (evch_qelem_t *)evch_q_out(&eqp->eq_eventq)) != NULL) {
573 evch_gevent_free((evch_gevent_t *)qep->q_objref);
574 kmem_free(qep, qep->q_objsize);
577 /* Wrap up event queue structure */
578 cv_destroy(&eqp->eq_onholdcv);
579 cv_destroy(&eqp->eq_dactivecv);
580 cv_destroy(&eqp->eq_thrsleepcv);
581 evch_dl_fini(&eqp->eq_subscr);
582 mutex_destroy(&eqp->eq_queuemx);
584 /* Free descriptor structure */
585 kmem_free(eqp, sizeof (evch_eventq_t));
589 * Subscribe to an event queue. Every subscriber provides a filter callback
590 * routine and an event delivery callback routine.
592 static evch_evqsub_t *
593 evch_evq_sub(evch_eventq_t *eqp, filter_f filter, void *fcookie,
594 deliver_f callb, void *cbcookie)
596 evch_evqsub_t *sp = kmem_zalloc(sizeof (evch_evqsub_t), KM_SLEEP);
598 /* Initialize subscriber structure */
599 sp->su_filter = filter;
600 sp->su_fcookie = fcookie;
601 sp->su_callb = callb;
602 sp->su_cbcookie = cbcookie;
604 /* Add subscription to queue */
605 mutex_enter(&eqp->eq_queuemx);
606 evch_dl_add(&eqp->eq_subscr, &sp->su_link);
607 mutex_exit(&eqp->eq_queuemx);
608 return (sp);
612 * Unsubscribe from an event queue.
614 static void
615 evch_evq_unsub(evch_eventq_t *eqp, evch_evqsub_t *sp)
617 mutex_enter(&eqp->eq_queuemx);
619 /* Wait if delivery is just in progress */
620 if (eqp->eq_dactive) {
621 cv_wait(&eqp->eq_dactivecv, &eqp->eq_queuemx);
623 evch_dl_del(&eqp->eq_subscr, &sp->su_link);
624 mutex_exit(&eqp->eq_queuemx);
625 kmem_free(sp, sizeof (evch_evqsub_t));
629 * Publish an event. Returns 0 on success and -1 if memory alloc failed.
631 static int
632 evch_evq_pub(evch_eventq_t *eqp, void *ev, int flags)
634 size_t size;
635 evch_qelem_t *qep;
636 evch_gevent_t *evp = GEVENT(ev);
638 size = sizeof (evch_qelem_t);
639 if (flags & EVCH_TRYHARD) {
640 qep = kmem_alloc_tryhard(size, &size, KM_NOSLEEP);
641 } else {
642 qep = kmem_alloc(size, flags & EVCH_NOSLEEP ?
643 KM_NOSLEEP : KM_SLEEP);
645 if (qep == NULL) {
646 return (-1);
648 qep->q_objref = (void *)evp;
649 qep->q_objsize = size;
650 atomic_inc_32(&evp->ge_refcount);
651 mutex_enter(&eqp->eq_queuemx);
652 evch_q_in(&eqp->eq_eventq, qep);
654 /* Wakeup delivery thread */
655 cv_signal(&eqp->eq_thrsleepcv);
656 mutex_exit(&eqp->eq_queuemx);
657 return (0);
661 * Enter hold mode of an event queue. Event delivery thread stops event
662 * handling after delivery of current event (if any).
664 static void
665 evch_evq_stop(evch_eventq_t *eqp)
667 mutex_enter(&eqp->eq_queuemx);
668 eqp->eq_holdmode = 1;
669 if (evq_initcomplete) {
670 cv_signal(&eqp->eq_thrsleepcv);
671 cv_wait(&eqp->eq_onholdcv, &eqp->eq_queuemx);
673 mutex_exit(&eqp->eq_queuemx);
677 * Continue event delivery.
679 static void
680 evch_evq_continue(evch_eventq_t *eqp)
682 mutex_enter(&eqp->eq_queuemx);
683 eqp->eq_holdmode = 0;
684 cv_signal(&eqp->eq_thrsleepcv);
685 mutex_exit(&eqp->eq_queuemx);
689 * Returns status of delivery thread. 0 if running and 1 if on hold.
691 static int
692 evch_evq_status(evch_eventq_t *eqp)
694 return (eqp->eq_holdmode);
698 * Add a destructor function to an event structure.
700 static void
701 evch_evq_evadd_dest(void *ev, destr_f destructor, void *cookie)
703 evch_gevent_t *evp = GEVENT(ev);
705 evp->ge_destruct = destructor;
706 evp->ge_dstcookie = cookie;
710 * Allocate evch_gevent_t structure. Return address of payload offset of
711 * evch_gevent_t. If EVCH_TRYHARD allocation is requested, we use
712 * kmem_alloc_tryhard to alloc memory of at least paylsize bytes.
714 * If either memory allocation is unsuccessful, we return NULL.
716 static void *
717 evch_evq_evzalloc(size_t paylsize, int flag)
719 evch_gevent_t *evp;
720 size_t rsize, evsize, ge_size;
722 rsize = offsetof(evch_gevent_t, ge_payload) + paylsize;
723 if (flag & EVCH_TRYHARD) {
724 evp = kmem_alloc_tryhard(rsize, &evsize, KM_NOSLEEP);
725 ge_size = evsize;
726 } else {
727 evp = kmem_alloc(rsize, flag & EVCH_NOSLEEP ? KM_NOSLEEP :
728 KM_SLEEP);
729 ge_size = rsize;
732 if (evp) {
733 bzero(evp, rsize);
734 evp->ge_size = ge_size;
735 return (&evp->ge_payload);
737 return (evp);
741 * Free event structure. Argument ev is address of payload offset.
743 static void
744 evch_evq_evfree(void *ev)
746 evch_gevent_free(GEVENT(ev));
750 * Iterate over all events in the event queue. Begin with an event
751 * which is currently being delivered. No mutexes are grabbed and no
752 * resources allocated so that this function can be called in panic
753 * context too. This function has to be called with ev == NULL initially.
754 * Actually argument ev is only a flag. Internally the member eq_nextev
755 * is used to determine the next event. But ev allows for the convenient
756 * use like
757 * ev = NULL;
758 * while ((ev = evch_evq_evnext(evp, ev)) != NULL) ...
760 static void *
761 evch_evq_evnext(evch_eventq_t *evq, void *ev)
763 if (ev == NULL) {
764 evq->eq_nextev = NULL;
765 if (evq->eq_curevent != NULL)
766 return (&evq->eq_curevent->ge_payload);
768 evq->eq_nextev = evch_q_next(&evq->eq_eventq, evq->eq_nextev);
769 if (evq->eq_nextev == NULL)
770 return (NULL);
771 return (&((evch_gevent_t *)evq->eq_nextev->q_objref)->ge_payload);
775 * Channel handling functions. First some support functions. Functions belonging
776 * to the channel handling interface start with evch_ch. The following functions
777 * make up the channel handling internal interfaces:
779 * evch_chinit - Initialize channel handling
780 * evch_chinitthr - Second step init: initialize threads
781 * evch_chbind - Bind to a channel
782 * evch_chunbind - Unbind from a channel
783 * evch_chsubscribe - Subscribe to a sysevent class
784 * evch_chunsubscribe - Unsubscribe
785 * evch_chpublish - Publish an event
786 * evch_chgetnames - Get names of all channels
787 * evch_chgetchdata - Get data of a channel
788 * evch_chrdevent_init - Init event q traversal
789 * evch_chgetnextev - Read out events queued for a subscriber
790 * evch_chrdevent_fini - Finish event q traversal
794 * Compare channel name. Used for evch_dl_search to find a channel with the
795 * name s.
797 static int
798 evch_namecmp(evch_dlelem_t *ep, char *s)
800 return (strcmp(((evch_chan_t *)ep)->ch_name, s));
804 * Simple wildcarded match test of event class string 'class' to
805 * wildcarded subscription string 'pat'. Recursive only if
806 * 'pat' includes a wildcard, otherwise essentially just strcmp.
808 static int
809 evch_clsmatch(char *class, const char *pat)
811 char c;
813 do {
814 if ((c = *pat++) == '\0')
815 return (*class == '\0');
817 if (c == '*') {
818 while (*pat == '*')
819 pat++; /* consecutive *'s can be collapsed */
821 if (*pat == '\0')
822 return (1);
824 while (*class != '\0') {
825 if (evch_clsmatch(class++, pat) != 0)
826 return (1);
829 return (0);
831 } while (c == *class++);
833 return (0);
837 * Sysevent filter callback routine. Enables event delivery only if it matches
838 * the event class pattern string given by parameter cookie.
840 static int
841 evch_class_filter(void *ev, void *cookie)
843 const char *pat = (const char *)cookie;
845 if (pat == NULL || evch_clsmatch(SE_CLASS_NAME(ev), pat))
846 return (EVQ_DELIVER);
848 return (EVQ_IGNORE);
852 * Callback routine to propagate the event into a per subscriber queue.
854 static int
855 evch_subq_deliver(void *evp, void *cookie)
857 evch_subd_t *p = (evch_subd_t *)cookie;
859 (void) evch_evq_pub(p->sd_queue, evp, EVCH_SLEEP);
860 return (EVQ_CONT);
864 * Call kernel callback routine for sysevent kernel delivery.
866 static int
867 evch_kern_deliver(void *evp, void *cookie)
869 sysevent_impl_t *ev = (sysevent_impl_t *)evp;
870 evch_subd_t *sdp = (evch_subd_t *)cookie;
872 return (sdp->sd_callback(ev, sdp->sd_cbcookie));
876 * Door upcall for user land sysevent delivery.
878 static int
879 evch_door_deliver(void *evp, void *cookie)
881 int error;
882 size_t size;
883 sysevent_impl_t *ev = (sysevent_impl_t *)evp;
884 door_arg_t darg;
885 evch_subd_t *sdp = (evch_subd_t *)cookie;
886 int nticks = EVCH_MIN_PAUSE;
887 uint32_t retval;
888 int retry = 20;
890 /* Initialize door args */
891 size = sizeof (sysevent_impl_t) + SE_PAYLOAD_SZ(ev);
893 darg.rbuf = (char *)&retval;
894 darg.rsize = sizeof (retval);
895 darg.data_ptr = (char *)ev;
896 darg.data_size = size;
897 darg.desc_ptr = NULL;
898 darg.desc_num = 0;
900 for (;;) {
901 if ((error = door_ki_upcall_limited(sdp->sd_door, &darg,
902 NULL, SIZE_MAX, 0)) == 0) {
903 break;
905 switch (error) {
906 case EAGAIN:
907 /* Cannot deliver event - process may be forking */
908 delay(nticks);
909 nticks <<= 1;
910 if (nticks > EVCH_MAX_PAUSE) {
911 nticks = EVCH_MAX_PAUSE;
913 if (retry-- <= 0) {
914 cmn_err(CE_CONT, "event delivery thread: "
915 "door_ki_upcall error EAGAIN\n");
916 return (EVQ_CONT);
918 break;
919 case EINTR:
920 case EBADF:
921 /* Process died */
922 return (EVQ_SLEEP);
923 default:
924 cmn_err(CE_CONT,
925 "event delivery thread: door_ki_upcall error %d\n",
926 error);
927 return (EVQ_CONT);
930 if (retval == EAGAIN) {
931 return (EVQ_AGAIN);
933 return (EVQ_CONT);
937 * Callback routine for evch_dl_search() to compare subscriber id's. Used by
938 * evch_subscribe() and evch_chrdevent_init().
940 static int
941 evch_subidcmp(evch_dlelem_t *ep, char *s)
943 return (strcmp(((evch_subd_t *)ep)->sd_ident, s));
947 * Callback routine for evch_dl_search() to find a subscriber with EVCH_SUB_DUMP
948 * set (indicated by sub->sd_dump != 0). Used by evch_chrdevent_init() and
949 * evch_subscribe(). Needs to returns 0 if subscriber with sd_dump set is
950 * found.
952 /*ARGSUSED1*/
953 static int
954 evch_dumpflgcmp(evch_dlelem_t *ep, char *s)
956 return (((evch_subd_t *)ep)->sd_dump ? 0 : 1);
960 * Event destructor function. Used to maintain the number of events per channel.
962 /*ARGSUSED*/
963 static void
964 evch_destr_event(void *ev, void *ch)
966 evch_chan_t *chp = (evch_chan_t *)ch;
968 mutex_enter(&chp->ch_pubmx);
969 chp->ch_nevents--;
970 cv_signal(&chp->ch_pubcv);
971 mutex_exit(&chp->ch_pubmx);
975 * Integer square root according to Newton's iteration.
977 static uint32_t
978 evch_isqrt(uint64_t n)
980 uint64_t x = n >> 1;
981 uint64_t xn = x - 1;
982 static uint32_t lowval[] = { 0, 1, 1, 2 };
984 if (n < 4) {
985 return (lowval[n]);
987 while (xn < x) {
988 x = xn;
989 xn = (x + n / x) / 2;
991 return ((uint32_t)xn);
995 * First step sysevent channel initialization. Called when kernel memory
996 * allocator is initialized.
998 static void
999 evch_chinit()
1001 size_t k;
1004 * Calculate limits: max no of channels and max no of events per
1005 * channel. The smallest machine with 128 MByte will allow for
1006 * >= 8 channels and an upper limit of 2048 events per channel.
1007 * The event limit is the number of channels times 256 (hence
1008 * the shift factor of 8). These number where selected arbitrarily.
1010 k = kmem_maxavail() >> 20;
1011 evch_channels_max = min(evch_isqrt(k), EVCH_MAX_CHANNELS);
1012 evch_events_max = evch_channels_max << 8;
1015 * Will trigger creation of the global zone's evch state.
1017 zone_key_create(&evch_zone_key, evch_zoneinit, NULL, evch_zonefree);
1021 * Second step sysevent channel initialization. Called when threads are ready.
1023 static void
1024 evch_chinitthr()
1026 struct evch_globals *eg;
1027 evch_chan_t *chp;
1028 evch_subd_t *sdp;
1031 * We're early enough in boot that we know that only the global
1032 * zone exists; we only need to initialize its threads.
1034 eg = zone_getspecific(evch_zone_key, global_zone);
1035 ASSERT(eg != NULL);
1037 for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL;
1038 chp = evch_dl_next(&eg->evch_list, chp)) {
1039 for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp;
1040 sdp = evch_dl_next(&chp->ch_subscr, sdp)) {
1041 evch_evq_thrcreate(sdp->sd_queue);
1043 evch_evq_thrcreate(chp->ch_queue);
1045 evq_initcomplete = 1;
1049 * Sysevent channel bind. Create channel and allocate binding structure.
1051 static int
1052 evch_chbind(const char *chnam, evch_bind_t **scpp, uint32_t flags)
1054 struct evch_globals *eg;
1055 evch_bind_t *bp;
1056 evch_chan_t *p;
1057 char *chn;
1058 size_t namlen;
1059 int rv;
1061 eg = zone_getspecific(evch_zone_key, curproc->p_zone);
1062 ASSERT(eg != NULL);
1064 /* Create channel if it does not exist */
1065 ASSERT(evch_dl_is_init(&eg->evch_list));
1066 if ((namlen = strlen(chnam) + 1) > MAX_CHNAME_LEN) {
1067 return (EINVAL);
1069 mutex_enter(&eg->evch_list_lock);
1070 if ((p = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp,
1071 (char *)chnam)) == NULL) {
1072 if (flags & EVCH_CREAT) {
1073 if (evch_dl_getnum(&eg->evch_list) >=
1074 evch_channels_max) {
1075 mutex_exit(&eg->evch_list_lock);
1076 return (ENOMEM);
1078 chn = kmem_alloc(namlen, KM_SLEEP);
1079 bcopy(chnam, chn, namlen);
1081 /* Allocate and initialize channel descriptor */
1082 p = kmem_zalloc(sizeof (evch_chan_t), KM_SLEEP);
1083 p->ch_name = chn;
1084 p->ch_namelen = namlen;
1085 mutex_init(&p->ch_mutex, NULL, MUTEX_DEFAULT, NULL);
1086 p->ch_queue = evch_evq_create();
1087 evch_dl_init(&p->ch_subscr);
1088 if (evq_initcomplete) {
1089 p->ch_uid = crgetuid(curthread->t_cred);
1090 p->ch_gid = crgetgid(curthread->t_cred);
1092 cv_init(&p->ch_pubcv, NULL, CV_DEFAULT, NULL);
1093 mutex_init(&p->ch_pubmx, NULL, MUTEX_DEFAULT, NULL);
1094 p->ch_maxev = min(EVCH_DEFAULT_EVENTS, evch_events_max);
1095 p->ch_maxsubscr = EVCH_MAX_SUBSCRIPTIONS;
1096 p->ch_maxbinds = evch_bindings_max;
1097 p->ch_ctime = gethrestime_sec();
1099 if (flags & (EVCH_HOLD_PEND | EVCH_HOLD_PEND_INDEF)) {
1100 if (flags & EVCH_HOLD_PEND_INDEF)
1101 p->ch_holdpend = CH_HOLD_PEND_INDEF;
1102 else
1103 p->ch_holdpend = CH_HOLD_PEND;
1105 evch_evq_stop(p->ch_queue);
1108 /* Put new descriptor into channel list */
1109 evch_dl_add(&eg->evch_list, (evch_dlelem_t *)p);
1110 } else {
1111 mutex_exit(&eg->evch_list_lock);
1112 return (ENOENT);
1116 /* Check for max binds and create binding */
1117 mutex_enter(&p->ch_mutex);
1118 if (p->ch_bindings >= p->ch_maxbinds) {
1119 rv = ENOMEM;
1121 * No need to destroy the channel because this call did not
1122 * create it. Other bindings will be present if ch_maxbinds
1123 * is exceeded.
1125 goto errorexit;
1127 bp = kmem_alloc(sizeof (evch_bind_t), KM_SLEEP);
1128 bp->bd_channel = p;
1129 bp->bd_sublst = NULL;
1130 p->ch_bindings++;
1131 rv = 0;
1132 *scpp = bp;
1133 errorexit:
1134 mutex_exit(&p->ch_mutex);
1135 mutex_exit(&eg->evch_list_lock);
1136 return (rv);
1140 * Unbind: Free bind structure. Remove channel if last binding was freed.
1142 static void
1143 evch_chunbind(evch_bind_t *bp)
1145 struct evch_globals *eg;
1146 evch_chan_t *chp = bp->bd_channel;
1148 eg = zone_getspecific(evch_zone_key, curproc->p_zone);
1149 ASSERT(eg != NULL);
1151 mutex_enter(&eg->evch_list_lock);
1152 mutex_enter(&chp->ch_mutex);
1153 ASSERT(chp->ch_bindings > 0);
1154 chp->ch_bindings--;
1155 kmem_free(bp, sizeof (evch_bind_t));
1156 if (chp->ch_bindings == 0 && evch_dl_getnum(&chp->ch_subscr) == 0 &&
1157 (chp->ch_nevents == 0 || chp->ch_holdpend != CH_HOLD_PEND_INDEF)) {
1159 * No more bindings and no persistent subscriber(s). If there
1160 * are no events in the channel then destroy the channel;
1161 * otherwise destroy the channel only if we're not holding
1162 * pending events indefinitely.
1164 mutex_exit(&chp->ch_mutex);
1165 evch_dl_del(&eg->evch_list, &chp->ch_link);
1166 evch_evq_destroy(chp->ch_queue);
1167 nvlist_free(chp->ch_propnvl);
1168 mutex_destroy(&chp->ch_mutex);
1169 mutex_destroy(&chp->ch_pubmx);
1170 cv_destroy(&chp->ch_pubcv);
1171 kmem_free(chp->ch_name, chp->ch_namelen);
1172 kmem_free(chp, sizeof (evch_chan_t));
1173 } else
1174 mutex_exit(&chp->ch_mutex);
1175 mutex_exit(&eg->evch_list_lock);
1178 static int
1179 wildcard_count(const char *class)
1181 int count = 0;
1182 char c;
1184 if (class == NULL)
1185 return (0);
1187 while ((c = *class++) != '\0') {
1188 if (c == '*')
1189 count++;
1192 return (count);
1196 * Subscribe to a channel. dtype is either EVCH_DELKERN for kernel callbacks
1197 * or EVCH_DELDOOR for door upcall delivery to user land. Depending on dtype
1198 * dinfo gives the call back routine address or the door handle.
1200 static int
1201 evch_chsubscribe(evch_bind_t *bp, int dtype, const char *sid, const char *class,
1202 void *dinfo, void *cookie, int flags, pid_t pid)
1204 evch_chan_t *chp = bp->bd_channel;
1205 evch_eventq_t *eqp = chp->ch_queue;
1206 evch_subd_t *sdp;
1207 evch_subd_t *esp;
1208 int (*delivfkt)();
1209 char *clb = NULL;
1210 int clblen = 0;
1211 char *subid;
1212 int subidblen;
1215 * Check if only known flags are set.
1217 if (flags & ~(EVCH_SUB_KEEP | EVCH_SUB_DUMP))
1218 return (EINVAL);
1221 * Enforce a limit on the number of wildcards allowed in the class
1222 * subscription string (limits recursion in pattern matching).
1224 if (wildcard_count(class) > EVCH_WILDCARD_MAX)
1225 return (EINVAL);
1228 * Check if we have already a subscription with that name and if we
1229 * have to reconnect the subscriber to a persistent subscription.
1231 mutex_enter(&chp->ch_mutex);
1232 if ((esp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr,
1233 evch_subidcmp, (char *)sid)) != NULL) {
1234 int error = 0;
1235 if ((flags & EVCH_SUB_KEEP) && (esp->sd_active == 0)) {
1237 * Subscription with the name on hold, reconnect to
1238 * existing queue.
1240 ASSERT(dtype == EVCH_DELDOOR);
1241 esp->sd_subnxt = bp->bd_sublst;
1242 bp->bd_sublst = esp;
1243 esp->sd_pid = pid;
1244 esp->sd_door = (door_handle_t)dinfo;
1245 esp->sd_active++;
1246 evch_evq_continue(esp->sd_queue);
1247 } else {
1248 /* Subscriber with given name already exists */
1249 error = EEXIST;
1251 mutex_exit(&chp->ch_mutex);
1252 return (error);
1255 if (evch_dl_getnum(&chp->ch_subscr) >= chp->ch_maxsubscr) {
1256 mutex_exit(&chp->ch_mutex);
1257 return (ENOMEM);
1260 if (flags & EVCH_SUB_DUMP && evch_dl_search(&chp->ch_subscr,
1261 evch_dumpflgcmp, NULL) != NULL) {
1263 * Subscription with EVCH_SUB_DUMP flagged already exists.
1264 * Only one subscription with EVCH_SUB_DUMP possible. Return
1265 * error.
1267 mutex_exit(&chp->ch_mutex);
1268 return (EINVAL);
1271 if (class != NULL) {
1272 clblen = strlen(class) + 1;
1273 clb = kmem_alloc(clblen, KM_SLEEP);
1274 bcopy(class, clb, clblen);
1277 subidblen = strlen(sid) + 1;
1278 subid = kmem_alloc(subidblen, KM_SLEEP);
1279 bcopy(sid, subid, subidblen);
1281 /* Create per subscriber queue */
1282 sdp = kmem_zalloc(sizeof (evch_subd_t), KM_SLEEP);
1283 sdp->sd_queue = evch_evq_create();
1285 /* Subscribe to subscriber queue */
1286 sdp->sd_persist = flags & EVCH_SUB_KEEP ? 1 : 0;
1287 sdp->sd_dump = flags & EVCH_SUB_DUMP ? 1 : 0;
1288 sdp->sd_type = dtype;
1289 sdp->sd_cbcookie = cookie;
1290 sdp->sd_ident = subid;
1291 if (dtype == EVCH_DELKERN) {
1292 sdp->sd_callback = (kerndlv_f)dinfo;
1293 delivfkt = evch_kern_deliver;
1294 } else {
1295 sdp->sd_door = (door_handle_t)dinfo;
1296 delivfkt = evch_door_deliver;
1298 sdp->sd_ssub =
1299 evch_evq_sub(sdp->sd_queue, NULL, NULL, delivfkt, (void *)sdp);
1301 /* Connect per subscriber queue to main event queue */
1302 sdp->sd_msub = evch_evq_sub(eqp, evch_class_filter, clb,
1303 evch_subq_deliver, (void *)sdp);
1304 sdp->sd_classname = clb;
1305 sdp->sd_clnsize = clblen;
1306 sdp->sd_pid = pid;
1307 sdp->sd_active++;
1309 /* Add subscription to binding */
1310 sdp->sd_subnxt = bp->bd_sublst;
1311 bp->bd_sublst = sdp;
1313 /* Add subscription to channel */
1314 evch_dl_add(&chp->ch_subscr, &sdp->sd_link);
1315 if (chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 1) {
1317 /* Let main event queue run in case of HOLDPEND */
1318 evch_evq_continue(eqp);
1320 mutex_exit(&chp->ch_mutex);
1322 return (0);
1326 * If flag == EVCH_SUB_KEEP only non-persistent subscriptions are deleted.
1327 * When sid == NULL all subscriptions except the ones with EVCH_SUB_KEEP set
1328 * are removed.
1330 static void
1331 evch_chunsubscribe(evch_bind_t *bp, const char *sid, uint32_t flags)
1333 evch_subd_t *sdp;
1334 evch_subd_t *next;
1335 evch_subd_t *prev;
1336 evch_chan_t *chp = bp->bd_channel;
1338 mutex_enter(&chp->ch_mutex);
1339 if (chp->ch_holdpend) {
1340 evch_evq_stop(chp->ch_queue); /* Hold main event queue */
1342 prev = NULL;
1343 for (sdp = bp->bd_sublst; sdp; sdp = next) {
1344 if (sid == NULL || strcmp(sid, sdp->sd_ident) == 0) {
1345 if (flags == 0 || sdp->sd_persist == 0) {
1347 * Disconnect subscriber queue from main event
1348 * queue.
1350 evch_evq_unsub(chp->ch_queue, sdp->sd_msub);
1352 /* Destruct per subscriber queue */
1353 evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub);
1354 evch_evq_destroy(sdp->sd_queue);
1356 * Eliminate the subscriber data from channel
1357 * list.
1359 evch_dl_del(&chp->ch_subscr, &sdp->sd_link);
1360 kmem_free(sdp->sd_classname, sdp->sd_clnsize);
1361 if (sdp->sd_type == EVCH_DELDOOR) {
1362 door_ki_rele(sdp->sd_door);
1364 next = sdp->sd_subnxt;
1365 if (prev) {
1366 prev->sd_subnxt = next;
1367 } else {
1368 bp->bd_sublst = next;
1370 kmem_free(sdp->sd_ident,
1371 strlen(sdp->sd_ident) + 1);
1372 kmem_free(sdp, sizeof (evch_subd_t));
1373 } else {
1375 * EVCH_SUB_KEEP case
1377 evch_evq_stop(sdp->sd_queue);
1378 if (sdp->sd_type == EVCH_DELDOOR) {
1379 door_ki_rele(sdp->sd_door);
1381 sdp->sd_active--;
1382 ASSERT(sdp->sd_active == 0);
1383 next = sdp->sd_subnxt;
1384 prev = sdp;
1386 if (sid != NULL) {
1387 break;
1389 } else {
1390 next = sdp->sd_subnxt;
1391 prev = sdp;
1394 if (!(chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 0)) {
1396 * Continue dispatch thread except if no subscribers are present
1397 * in HOLDPEND mode.
1399 evch_evq_continue(chp->ch_queue);
1401 mutex_exit(&chp->ch_mutex);
1405 * Publish an event. Returns zero on success and an error code else.
1407 static int
1408 evch_chpublish(evch_bind_t *bp, sysevent_impl_t *ev, int flags)
1410 evch_chan_t *chp = bp->bd_channel;
1412 DTRACE_SYSEVENT2(post, evch_bind_t *, bp, sysevent_impl_t *, ev);
1414 mutex_enter(&chp->ch_pubmx);
1415 if (chp->ch_nevents >= chp->ch_maxev) {
1416 if (!(flags & EVCH_QWAIT)) {
1417 evch_evq_evfree(ev);
1418 mutex_exit(&chp->ch_pubmx);
1419 return (EAGAIN);
1420 } else {
1421 while (chp->ch_nevents >= chp->ch_maxev) {
1422 if (cv_wait_sig(&chp->ch_pubcv,
1423 &chp->ch_pubmx) == 0) {
1425 /* Got Signal, return EINTR */
1426 evch_evq_evfree(ev);
1427 mutex_exit(&chp->ch_pubmx);
1428 return (EINTR);
1433 chp->ch_nevents++;
1434 mutex_exit(&chp->ch_pubmx);
1435 SE_TIME(ev) = gethrtime();
1436 SE_SEQ(ev) = log_sysevent_new_id();
1438 * Add the destructor function to the event structure, now that the
1439 * event is accounted for. The only task of the descructor is to
1440 * decrement the channel event count. The evq_*() routines (including
1441 * the event delivery thread) do not have knowledge of the channel
1442 * data. So the anonymous destructor handles the channel data for it.
1444 evch_evq_evadd_dest(ev, evch_destr_event, (void *)chp);
1445 return (evch_evq_pub(chp->ch_queue, ev, flags) == 0 ? 0 : EAGAIN);
1449 * Fills a buffer consecutive with the names of all available channels.
1450 * Returns the length of all name strings or -1 if buffer size was unsufficient.
1452 static int
1453 evch_chgetnames(char *buf, size_t size)
1455 struct evch_globals *eg;
1456 int len = 0;
1457 char *addr = buf;
1458 int max = size;
1459 evch_chan_t *chp;
1461 eg = zone_getspecific(evch_zone_key, curproc->p_zone);
1462 ASSERT(eg != NULL);
1464 mutex_enter(&eg->evch_list_lock);
1465 for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL;
1466 chp = evch_dl_next(&eg->evch_list, chp)) {
1467 len += chp->ch_namelen;
1468 if (len >= max) {
1469 mutex_exit(&eg->evch_list_lock);
1470 return (-1);
1472 bcopy(chp->ch_name, addr, chp->ch_namelen);
1473 addr += chp->ch_namelen;
1475 mutex_exit(&eg->evch_list_lock);
1476 addr[0] = 0;
1477 return (len + 1);
1481 * Fills the data of one channel and all subscribers of that channel into
1482 * a buffer. Returns -1 if the channel name is invalid and 0 on buffer overflow.
1484 static int
1485 evch_chgetchdata(char *chname, void *buf, size_t size)
1487 struct evch_globals *eg;
1488 char *cpaddr;
1489 int bufmax;
1490 int buflen;
1491 evch_chan_t *chp;
1492 sev_chinfo_t *p = (sev_chinfo_t *)buf;
1493 int chdlen;
1494 evch_subd_t *sdp;
1495 sev_subinfo_t *subp;
1496 int idlen;
1497 int len;
1499 eg = zone_getspecific(evch_zone_key, curproc->p_zone);
1500 ASSERT(eg != NULL);
1502 mutex_enter(&eg->evch_list_lock);
1503 chp = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp,
1504 chname);
1505 if (chp == NULL) {
1506 mutex_exit(&eg->evch_list_lock);
1507 return (-1);
1509 chdlen = offsetof(sev_chinfo_t, cd_subinfo);
1510 if (size < chdlen) {
1511 mutex_exit(&eg->evch_list_lock);
1512 return (0);
1514 p->cd_version = 0;
1515 p->cd_suboffs = chdlen;
1516 p->cd_uid = chp->ch_uid;
1517 p->cd_gid = chp->ch_gid;
1518 p->cd_perms = 0;
1519 p->cd_ctime = chp->ch_ctime;
1520 p->cd_maxev = chp->ch_maxev;
1521 p->cd_evhwm = EVCH_EVQ_HIGHWM(chp->ch_queue);
1522 p->cd_nevents = EVCH_EVQ_EVCOUNT(chp->ch_queue);
1523 p->cd_maxsub = chp->ch_maxsubscr;
1524 p->cd_nsub = evch_dl_getnum(&chp->ch_subscr);
1525 p->cd_maxbinds = chp->ch_maxbinds;
1526 p->cd_nbinds = chp->ch_bindings;
1527 p->cd_holdpend = chp->ch_holdpend;
1528 p->cd_limev = evch_events_max;
1529 cpaddr = (char *)p + chdlen;
1530 bufmax = size - chdlen;
1531 buflen = 0;
1533 for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp != NULL;
1534 sdp = evch_dl_next(&chp->ch_subscr, sdp)) {
1535 idlen = strlen(sdp->sd_ident) + 1;
1536 len = SE_ALIGN(offsetof(sev_subinfo_t, sb_strings) + idlen +
1537 sdp->sd_clnsize);
1538 buflen += len;
1539 if (buflen >= bufmax) {
1540 mutex_exit(&eg->evch_list_lock);
1541 return (0);
1543 subp = (sev_subinfo_t *)cpaddr;
1544 subp->sb_nextoff = len;
1545 subp->sb_stroff = offsetof(sev_subinfo_t, sb_strings);
1546 if (sdp->sd_classname) {
1547 bcopy(sdp->sd_classname, subp->sb_strings + idlen,
1548 sdp->sd_clnsize);
1549 subp->sb_clnamoff = idlen;
1550 } else {
1551 subp->sb_clnamoff = idlen - 1;
1553 subp->sb_pid = sdp->sd_pid;
1554 subp->sb_nevents = EVCH_EVQ_EVCOUNT(sdp->sd_queue);
1555 subp->sb_evhwm = EVCH_EVQ_HIGHWM(sdp->sd_queue);
1556 subp->sb_persist = sdp->sd_persist;
1557 subp->sb_status = evch_evq_status(sdp->sd_queue);
1558 subp->sb_active = sdp->sd_active;
1559 subp->sb_dump = sdp->sd_dump;
1560 bcopy(sdp->sd_ident, subp->sb_strings, idlen);
1561 cpaddr += len;
1563 mutex_exit(&eg->evch_list_lock);
1564 return (chdlen + buflen);
1567 static void
1568 evch_chsetpropnvl(evch_bind_t *bp, nvlist_t *nvl)
1570 evch_chan_t *chp = bp->bd_channel;
1572 mutex_enter(&chp->ch_mutex);
1574 nvlist_free(chp->ch_propnvl);
1576 chp->ch_propnvl = nvl;
1577 chp->ch_propnvlgen++;
1579 mutex_exit(&chp->ch_mutex);
1582 static int
1583 evch_chgetpropnvl(evch_bind_t *bp, nvlist_t **nvlp, int64_t *genp)
1585 evch_chan_t *chp = bp->bd_channel;
1586 int rc = 0;
1588 mutex_enter(&chp->ch_mutex);
1590 if (chp->ch_propnvl != NULL)
1591 rc = (nvlist_dup(chp->ch_propnvl, nvlp, 0) == 0) ? 0 : ENOMEM;
1592 else
1593 *nvlp = NULL; /* rc still 0 */
1595 if (genp)
1596 *genp = chp->ch_propnvlgen;
1598 mutex_exit(&chp->ch_mutex);
1600 if (rc != 0)
1601 *nvlp = NULL;
1603 return (rc);
1608 * Init iteration of all events of a channel. This function creates a new
1609 * event queue and puts all events from the channel into that queue.
1610 * Subsequent calls to evch_chgetnextev will deliver the events from that
1611 * queue. Only one thread per channel is allowed to read through the events.
1612 * Returns 0 on success and 1 if there is already someone reading the
1613 * events.
1614 * If argument subid == NULL, we look for a subscriber which has
1615 * flag EVCH_SUB_DUMP set.
1618 * Static variables that are used to traverse events of a channel in panic case.
1620 static evch_chan_t *evch_chan;
1621 static evch_eventq_t *evch_subq;
1622 static sysevent_impl_t *evch_curev;
1624 static evchanq_t *
1625 evch_chrdevent_init(evch_chan_t *chp, char *subid)
1627 evch_subd_t *sdp;
1628 void *ev;
1629 int pmqstat; /* Prev status of main queue */
1630 int psqstat; /* Prev status of subscriber queue */
1631 evchanq_t *snp; /* Pointer to q with snapshot of ev */
1632 compare_f compfunc;
1634 compfunc = subid == NULL ? evch_dumpflgcmp : evch_subidcmp;
1635 if (panicstr != NULL) {
1636 evch_chan = chp;
1637 evch_subq = NULL;
1638 evch_curev = NULL;
1639 if ((sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr,
1640 compfunc, subid)) != NULL) {
1641 evch_subq = sdp->sd_queue;
1643 return (NULL);
1645 mutex_enter(&chp->ch_mutex);
1646 sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr, compfunc, subid);
1648 * Stop main event queue and subscriber queue if not already
1649 * in stop mode.
1651 pmqstat = evch_evq_status(chp->ch_queue);
1652 if (pmqstat == 0)
1653 evch_evq_stop(chp->ch_queue);
1654 if (sdp != NULL) {
1655 psqstat = evch_evq_status(sdp->sd_queue);
1656 if (psqstat == 0)
1657 evch_evq_stop(sdp->sd_queue);
1660 * Create event queue to make a snapshot of all events in the
1661 * channel.
1663 snp = kmem_alloc(sizeof (evchanq_t), KM_SLEEP);
1664 snp->sn_queue = evch_evq_create();
1665 evch_evq_stop(snp->sn_queue);
1667 * Make a snapshot of the subscriber queue and the main event queue.
1669 if (sdp != NULL) {
1670 ev = NULL;
1671 while ((ev = evch_evq_evnext(sdp->sd_queue, ev)) != NULL) {
1672 (void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP);
1675 ev = NULL;
1676 while ((ev = evch_evq_evnext(chp->ch_queue, ev)) != NULL) {
1677 (void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP);
1679 snp->sn_nxtev = NULL;
1681 * Restart main and subscriber queue if previously stopped
1683 if (sdp != NULL && psqstat == 0)
1684 evch_evq_continue(sdp->sd_queue);
1685 if (pmqstat == 0)
1686 evch_evq_continue(chp->ch_queue);
1687 mutex_exit(&chp->ch_mutex);
1688 return (snp);
1692 * Free all resources of the event queue snapshot. In case of panic
1693 * context snp must be NULL and no resources need to be free'ed.
1695 static void
1696 evch_chrdevent_fini(evchanq_t *snp)
1698 if (snp != NULL) {
1699 evch_evq_destroy(snp->sn_queue);
1700 kmem_free(snp, sizeof (evchanq_t));
1705 * Get address of next event from an event channel.
1706 * This function might be called in a panic context. In that case
1707 * no resources will be allocated and no locks grabbed.
1708 * In normal operation context a snapshot of the event queues of the
1709 * specified event channel will be taken.
1711 static sysevent_impl_t *
1712 evch_chgetnextev(evchanq_t *snp)
1714 if (panicstr != NULL) {
1715 if (evch_chan == NULL)
1716 return (NULL);
1717 if (evch_subq != NULL) {
1719 * We have a subscriber queue. Traverse this queue
1720 * first.
1722 if ((evch_curev = (sysevent_impl_t *)
1723 evch_evq_evnext(evch_subq, evch_curev)) != NULL) {
1724 return (evch_curev);
1725 } else {
1727 * All subscriber events traversed. evch_subq
1728 * == NULL indicates to take the main event
1729 * queue now.
1731 evch_subq = NULL;
1735 * Traverse the main event queue.
1737 if ((evch_curev = (sysevent_impl_t *)
1738 evch_evq_evnext(evch_chan->ch_queue, evch_curev)) ==
1739 NULL) {
1740 evch_chan = NULL;
1742 return (evch_curev);
1744 ASSERT(snp != NULL);
1745 snp->sn_nxtev = (sysevent_impl_t *)evch_evq_evnext(snp->sn_queue,
1746 snp->sn_nxtev);
1747 return (snp->sn_nxtev);
1751 * The functions below build up the interface for the kernel to bind/unbind,
1752 * subscribe/unsubscribe and publish to event channels. It consists of the
1753 * following functions:
1755 * sysevent_evc_bind - Bind to a channel. Create a channel if required
1756 * sysevent_evc_unbind - Unbind from a channel. Destroy ch. if last unbind
1757 * sysevent_evc_subscribe - Subscribe to events from a channel
1758 * sysevent_evc_unsubscribe - Unsubscribe from an event class
1759 * sysevent_evc_publish - Publish an event to an event channel
1760 * sysevent_evc_control - Various control operation on event channel
1761 * sysevent_evc_setpropnvl - Set channel property nvlist
1762 * sysevent_evc_getpropnvl - Get channel property nvlist
1764 * The function below are for evaluating a sysevent:
1766 * sysevent_get_class_name - Get pointer to event class string
1767 * sysevent_get_subclass_name - Get pointer to event subclass string
1768 * sysevent_get_seq - Get unique event sequence number
1769 * sysevent_get_time - Get hrestime of event publish
1770 * sysevent_get_size - Get size of event structure
1771 * sysevent_get_pub - Get publisher string
1772 * sysevent_get_attr_list - Get copy of attribute list
1774 * The following interfaces represent stability level project privat
1775 * and allow to save the events of an event channel even in a panic case.
1777 * sysevent_evc_walk_init - Take a snapshot of the events in a channel
1778 * sysevent_evc_walk_step - Read next event from snapshot
1779 * sysevent_evc_walk_fini - Free resources from event channel snapshot
1780 * sysevent_evc_event_attr - Get event payload address and size
1783 * allocate sysevent structure with optional space for attributes
1785 static sysevent_impl_t *
1786 sysevent_evc_alloc(const char *class, const char *subclass, const char *pub,
1787 size_t pub_sz, size_t atsz, uint32_t flag)
1789 int payload_sz;
1790 int class_sz, subclass_sz;
1791 int aligned_class_sz, aligned_subclass_sz, aligned_pub_sz;
1792 sysevent_impl_t *ev;
1795 * Calculate and reserve space for the class, subclass and
1796 * publisher strings in the event buffer
1798 class_sz = strlen(class) + 1;
1799 subclass_sz = strlen(subclass) + 1;
1801 ASSERT((class_sz <= MAX_CLASS_LEN) && (subclass_sz <=
1802 MAX_SUBCLASS_LEN) && (pub_sz <= MAX_PUB_LEN));
1804 /* String sizes must be 64-bit aligned in the event buffer */
1805 aligned_class_sz = SE_ALIGN(class_sz);
1806 aligned_subclass_sz = SE_ALIGN(subclass_sz);
1807 aligned_pub_sz = SE_ALIGN(pub_sz);
1810 * Calculate payload size. Consider the space needed for alignment
1811 * and subtract the size of the uint64_t placeholder variables of
1812 * sysevent_impl_t.
1814 payload_sz = (aligned_class_sz - sizeof (uint64_t)) +
1815 (aligned_subclass_sz - sizeof (uint64_t)) +
1816 (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) +
1817 atsz;
1820 * Allocate event buffer plus additional payload overhead
1822 if ((ev = evch_evq_evzalloc(sizeof (sysevent_impl_t) +
1823 payload_sz, flag)) == NULL) {
1824 return (NULL);
1827 /* Initialize the event buffer data */
1828 SE_VERSION(ev) = SYS_EVENT_VERSION;
1829 bcopy(class, SE_CLASS_NAME(ev), class_sz);
1831 SE_SUBCLASS_OFF(ev) = SE_ALIGN(offsetof(sysevent_impl_t,
1832 se_class_name)) + aligned_class_sz;
1833 bcopy(subclass, SE_SUBCLASS_NAME(ev), subclass_sz);
1835 SE_PUB_OFF(ev) = SE_SUBCLASS_OFF(ev) + aligned_subclass_sz;
1836 bcopy(pub, SE_PUB_NAME(ev), pub_sz);
1838 SE_ATTR_PTR(ev) = (uint64_t)0;
1839 SE_PAYLOAD_SZ(ev) = payload_sz;
1841 return (ev);
1845 * Initialize event channel handling queues.
1847 void
1848 sysevent_evc_init()
1850 evch_chinit();
1854 * Second initialization step: create threads, if event channels are already
1855 * created
1857 void
1858 sysevent_evc_thrinit()
1860 evch_chinitthr();
1864 sysevent_evc_bind(const char *ch_name, evchan_t **scpp, uint32_t flags)
1866 ASSERT(ch_name != NULL && scpp != NULL);
1867 ASSERT((flags & ~EVCH_B_FLAGS) == 0);
1868 return (evch_chbind(ch_name, (evch_bind_t **)scpp, flags));
1872 sysevent_evc_unbind(evchan_t *scp)
1874 evch_bind_t *bp = (evch_bind_t *)scp;
1876 ASSERT(scp != NULL);
1877 evch_chunsubscribe(bp, NULL, 0);
1878 evch_chunbind(bp);
1880 return (0);
1884 sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class,
1885 int (*callb)(sysevent_t *ev, void *cookie),
1886 void *cookie, uint32_t flags)
1888 ASSERT(scp != NULL && sid != NULL && class != NULL && callb != NULL);
1889 ASSERT(flags == 0);
1890 if (strlen(sid) > MAX_SUBID_LEN) {
1891 return (EINVAL);
1893 if (strcmp(class, EC_ALL) == 0) {
1894 class = NULL;
1896 return (evch_chsubscribe((evch_bind_t *)scp, EVCH_DELKERN, sid, class,
1897 (void *)callb, cookie, 0, 0));
1901 sysevent_evc_unsubscribe(evchan_t *scp, const char *sid)
1903 ASSERT(scp != NULL && sid != NULL);
1904 if (strcmp(sid, EVCH_ALLSUB) == 0) {
1905 sid = NULL;
1907 evch_chunsubscribe((evch_bind_t *)scp, sid, 0);
1909 return (0);
1913 * Publish kernel event. Returns 0 on success, error code else.
1914 * Optional attribute data is packed into the event structure.
1917 sysevent_evc_publish(evchan_t *scp, const char *class, const char *subclass,
1918 const char *vendor, const char *pubs, nvlist_t *attr, uint32_t flags)
1920 sysevent_impl_t *evp;
1921 char pub[MAX_PUB_LEN];
1922 int pub_sz; /* includes terminating 0 */
1923 int km_flags;
1924 size_t asz = 0;
1925 uint64_t attr_offset;
1926 caddr_t patt;
1927 int err;
1929 ASSERT(scp != NULL && class != NULL && subclass != NULL &&
1930 vendor != NULL && pubs != NULL);
1932 ASSERT((flags & ~(EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD |
1933 EVCH_QWAIT)) == 0);
1935 km_flags = flags & (EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD);
1936 ASSERT(km_flags == EVCH_SLEEP || km_flags == EVCH_NOSLEEP ||
1937 km_flags == EVCH_TRYHARD);
1939 pub_sz = snprintf(pub, MAX_PUB_LEN, "%s:kern:%s", vendor, pubs) + 1;
1940 if (pub_sz > MAX_PUB_LEN)
1941 return (EINVAL);
1943 if (attr != NULL) {
1944 if ((err = nvlist_size(attr, &asz, NV_ENCODE_NATIVE)) != 0) {
1945 return (err);
1948 evp = sysevent_evc_alloc(class, subclass, pub, pub_sz, asz, km_flags);
1949 if (evp == NULL) {
1950 return (ENOMEM);
1952 if (attr != NULL) {
1954 * Pack attributes into event buffer. Event buffer already
1955 * has enough room for the packed nvlist.
1957 attr_offset = SE_ATTR_OFF(evp);
1958 patt = (caddr_t)evp + attr_offset;
1960 err = nvlist_pack(attr, &patt, &asz, NV_ENCODE_NATIVE,
1961 km_flags & EVCH_SLEEP ? KM_SLEEP : KM_NOSLEEP);
1963 ASSERT(err != ENOMEM);
1965 if (err != 0) {
1966 return (EINVAL);
1969 evp->seh_attr_off = attr_offset;
1970 SE_FLAG(evp) = SE_PACKED_BUF;
1972 return (evch_chpublish((evch_bind_t *)scp, evp, flags));
1976 sysevent_evc_control(evchan_t *scp, int cmd, ...)
1978 va_list ap;
1979 evch_chan_t *chp = ((evch_bind_t *)scp)->bd_channel;
1980 uint32_t *chlenp;
1981 uint32_t chlen;
1982 uint32_t ochlen;
1983 int rc = 0;
1985 if (scp == NULL) {
1986 return (EINVAL);
1989 va_start(ap, cmd);
1990 mutex_enter(&chp->ch_mutex);
1991 switch (cmd) {
1992 case EVCH_GET_CHAN_LEN:
1993 chlenp = va_arg(ap, uint32_t *);
1994 *chlenp = chp->ch_maxev;
1995 break;
1996 case EVCH_SET_CHAN_LEN:
1997 chlen = va_arg(ap, uint32_t);
1998 ochlen = chp->ch_maxev;
1999 chp->ch_maxev = min(chlen, evch_events_max);
2000 if (ochlen < chp->ch_maxev) {
2001 cv_signal(&chp->ch_pubcv);
2003 break;
2004 case EVCH_GET_CHAN_LEN_MAX:
2005 *va_arg(ap, uint32_t *) = evch_events_max;
2006 break;
2007 default:
2008 rc = EINVAL;
2011 mutex_exit(&chp->ch_mutex);
2012 va_end(ap);
2013 return (rc);
2017 sysevent_evc_setpropnvl(evchan_t *scp, nvlist_t *nvl)
2019 nvlist_t *nvlcp = nvl;
2021 if (nvl != NULL && nvlist_dup(nvl, &nvlcp, 0) != 0)
2022 return (ENOMEM);
2024 evch_chsetpropnvl((evch_bind_t *)scp, nvlcp);
2026 return (0);
2030 sysevent_evc_getpropnvl(evchan_t *scp, nvlist_t **nvlp)
2032 return (evch_chgetpropnvl((evch_bind_t *)scp, nvlp, NULL));
2036 * Project private interface to take a snapshot of all events of the
2037 * specified event channel. Argument subscr may be a subscriber id, the empty
2038 * string "", or NULL. The empty string indicates that no subscriber is
2039 * selected, for example if a previous subscriber died. sysevent_evc_walk_next()
2040 * will deliver events from the main event queue in this case. If subscr is
2041 * NULL, the subscriber with the EVCH_SUB_DUMP flag set (subd->sd_dump != 0)
2042 * will be selected.
2044 * In panic case this function returns NULL. This is legal. The NULL has
2045 * to be delivered to sysevent_evc_walk_step() and sysevent_evc_walk_fini().
2047 evchanq_t *
2048 sysevent_evc_walk_init(evchan_t *scp, char *subscr)
2050 if (panicstr != NULL && scp == NULL)
2051 return (NULL);
2052 ASSERT(scp != NULL);
2053 return (evch_chrdevent_init(((evch_bind_t *)scp)->bd_channel, subscr));
2057 * Project private interface to read events from a previously taken
2058 * snapshot (with sysevent_evc_walk_init). In case of panic events
2059 * are retrieved directly from the channel data structures. No resources
2060 * are allocated and no mutexes are grabbed in panic context.
2062 sysevent_t *
2063 sysevent_evc_walk_step(evchanq_t *evcq)
2065 return ((sysevent_t *)evch_chgetnextev(evcq));
2069 * Project private interface to free a previously taken snapshot.
2071 void
2072 sysevent_evc_walk_fini(evchanq_t *evcq)
2074 evch_chrdevent_fini(evcq);
2078 * Get address and size of an event payload. Returns NULL when no
2079 * payload present.
2081 char *
2082 sysevent_evc_event_attr(sysevent_t *ev, size_t *plsize)
2084 char *attrp;
2085 size_t aoff;
2086 size_t asz;
2088 aoff = SE_ATTR_OFF(ev);
2089 attrp = (char *)ev + aoff;
2090 asz = *plsize = SE_SIZE(ev) - aoff;
2091 return (asz ? attrp : NULL);
2095 * sysevent_get_class_name - Get class name string
2097 char *
2098 sysevent_get_class_name(sysevent_t *ev)
2100 return (SE_CLASS_NAME(ev));
2104 * sysevent_get_subclass_name - Get subclass name string
2106 char *
2107 sysevent_get_subclass_name(sysevent_t *ev)
2109 return (SE_SUBCLASS_NAME(ev));
2113 * sysevent_get_seq - Get event sequence id
2115 uint64_t
2116 sysevent_get_seq(sysevent_t *ev)
2118 return (SE_SEQ(ev));
2122 * sysevent_get_time - Get event timestamp
2124 void
2125 sysevent_get_time(sysevent_t *ev, hrtime_t *etime)
2127 *etime = SE_TIME(ev);
2131 * sysevent_get_size - Get event buffer size
2133 size_t
2134 sysevent_get_size(sysevent_t *ev)
2136 return ((size_t)SE_SIZE(ev));
2140 * sysevent_get_pub - Get publisher name string
2142 char *
2143 sysevent_get_pub(sysevent_t *ev)
2145 return (SE_PUB_NAME(ev));
2149 * sysevent_get_attr_list - stores address of a copy of the attribute list
2150 * associated with the given sysevent buffer. The list must be freed by the
2151 * caller.
2154 sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist)
2156 int error;
2157 caddr_t attr;
2158 size_t attr_len;
2159 uint64_t attr_offset;
2161 *nvlist = NULL;
2162 if (SE_FLAG(ev) != SE_PACKED_BUF) {
2163 return (EINVAL);
2165 attr_offset = SE_ATTR_OFF(ev);
2166 if (SE_SIZE(ev) == attr_offset) {
2167 return (EINVAL);
2170 /* unpack nvlist */
2171 attr = (caddr_t)ev + attr_offset;
2172 attr_len = SE_SIZE(ev) - attr_offset;
2173 if ((error = nvlist_unpack(attr, attr_len, nvlist, 0)) != 0) {
2174 error = error != ENOMEM ? EINVAL : error;
2175 return (error);
2177 return (0);
2181 * Functions called by the sysevent driver for general purpose event channels
2183 * evch_usrchanopen - Create/Bind to an event channel
2184 * evch_usrchanclose - Unbind/Destroy event channel
2185 * evch_usrallocev - Allocate event data structure
2186 * evch_usrfreeev - Free event data structure
2187 * evch_usrpostevent - Publish event
2188 * evch_usrsubscribe - Subscribe (register callback function)
2189 * evch_usrunsubscribe - Unsubscribe
2190 * evch_usrcontrol_set - Set channel properties
2191 * evch_usrcontrol_get - Get channel properties
2192 * evch_usrgetchnames - Get list of channel names
2193 * evch_usrgetchdata - Get data of an event channel
2194 * evch_usrsetpropnvl - Set channel properties nvlist
2195 * evch_usrgetpropnvl - Get channel properties nvlist
2197 evchan_t *
2198 evch_usrchanopen(const char *name, uint32_t flags, int *err)
2200 evch_bind_t *bp = NULL;
2202 *err = evch_chbind(name, &bp, flags);
2203 return ((evchan_t *)bp);
2207 * Unbind from the channel.
2209 void
2210 evch_usrchanclose(evchan_t *cbp)
2212 evch_chunbind((evch_bind_t *)cbp);
2216 * Allocates log_evch_eventq_t structure but returns the pointer of the embedded
2217 * sysevent_impl_t structure as the opaque sysevent_t * data type
2219 sysevent_impl_t *
2220 evch_usrallocev(size_t evsize, uint32_t flags)
2222 return ((sysevent_impl_t *)evch_evq_evzalloc(evsize, flags));
2226 * Free evch_eventq_t structure
2228 void
2229 evch_usrfreeev(sysevent_impl_t *ev)
2231 evch_evq_evfree((void *)ev);
2235 * Posts an event to the given channel. The event structure has to be
2236 * allocated by evch_usrallocev(). Returns zero on success and an error
2237 * code else. Attributes have to be packed and included in the event structure.
2241 evch_usrpostevent(evchan_t *bp, sysevent_impl_t *ev, uint32_t flags)
2243 return (evch_chpublish((evch_bind_t *)bp, ev, flags));
2247 * Subscribe function for user land subscriptions
2250 evch_usrsubscribe(evchan_t *bp, const char *sid, const char *class,
2251 int d, uint32_t flags)
2253 door_handle_t dh = door_ki_lookup(d);
2254 int rv;
2256 if (dh == NULL) {
2257 return (EINVAL);
2259 if ((rv = evch_chsubscribe((evch_bind_t *)bp, EVCH_DELDOOR, sid, class,
2260 (void *)dh, NULL, flags, curproc->p_pid)) != 0) {
2261 door_ki_rele(dh);
2263 return (rv);
2267 * Flag can be EVCH_SUB_KEEP or 0. EVCH_SUB_KEEP preserves persistent
2268 * subscribers
2270 void
2271 evch_usrunsubscribe(evchan_t *bp, const char *subid, uint32_t flags)
2273 evch_chunsubscribe((evch_bind_t *)bp, subid, flags);
2276 /*ARGSUSED*/
2278 evch_usrcontrol_set(evchan_t *bp, int cmd, uint32_t value)
2280 evch_chan_t *chp = ((evch_bind_t *)bp)->bd_channel;
2281 uid_t uid = crgetuid(curthread->t_cred);
2282 int rc = 0;
2284 mutex_enter(&chp->ch_mutex);
2285 switch (cmd) {
2286 case EVCH_SET_CHAN_LEN:
2287 if (uid && uid != chp->ch_uid) {
2288 rc = EACCES;
2289 break;
2291 chp->ch_maxev = min(value, evch_events_max);
2292 break;
2293 default:
2294 rc = EINVAL;
2296 mutex_exit(&chp->ch_mutex);
2297 return (rc);
2300 /*ARGSUSED*/
2302 evch_usrcontrol_get(evchan_t *bp, int cmd, uint32_t *value)
2304 evch_chan_t *chp = ((evch_bind_t *)bp)->bd_channel;
2305 int rc = 0;
2307 mutex_enter(&chp->ch_mutex);
2308 switch (cmd) {
2309 case EVCH_GET_CHAN_LEN:
2310 *value = chp->ch_maxev;
2311 break;
2312 case EVCH_GET_CHAN_LEN_MAX:
2313 *value = evch_events_max;
2314 break;
2315 default:
2316 rc = EINVAL;
2318 mutex_exit(&chp->ch_mutex);
2319 return (rc);
2323 evch_usrgetchnames(char *buf, size_t size)
2325 return (evch_chgetnames(buf, size));
2329 evch_usrgetchdata(char *chname, void *buf, size_t size)
2331 return (evch_chgetchdata(chname, buf, size));
2334 void
2335 evch_usrsetpropnvl(evchan_t *bp, nvlist_t *nvl)
2337 evch_chsetpropnvl((evch_bind_t *)bp, nvl);
2341 evch_usrgetpropnvl(evchan_t *bp, nvlist_t **nvlp, int64_t *genp)
2343 return (evch_chgetpropnvl((evch_bind_t *)bp, nvlp, genp));