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]
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>
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>
55 #include <sys/atomic.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
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
105 evch_dl_init(evch_dlist_t
*hp
)
107 hp
->dh_head
.dl_prev
= hp
->dh_head
.dl_next
= &hp
->dh_head
;
112 * Assumes that list is empty.
115 evch_dl_fini(evch_dlist_t
*hp
)
117 hp
->dh_head
.dl_prev
= hp
->dh_head
.dl_next
= NULL
;
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.
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
;
143 * Remove arbitrary element out of dlist.
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
;
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
)
164 for (p
= hp
->dh_head
.dl_next
; p
!= &hp
->dh_head
; p
= p
->dl_next
) {
165 if (cmp(p
, s
) == 0) {
173 * Return number of elements in the list.
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.
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) {
194 return (hp
->dh_head
.dl_next
);
196 if ((ep
= ep
->dl_next
) == (evch_dlelem_t
*)hp
) {
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
211 evch_q_init(evch_squeue_t
*q
)
214 q
->sq_tail
= (evch_qelem_t
*)q
;
220 * Put element into the queue q
223 evch_q_in(evch_squeue_t
*q
, evch_qelem_t
*el
)
225 q
->sq_tail
->q_next
= el
;
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
)
242 if ((el
= q
->sq_head
) != NULL
) {
243 q
->sq_head
= el
->q_next
;
245 if (q
->sq_head
== NULL
) {
246 q
->sq_tail
= (evch_qelem_t
*)q
;
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
)
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
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
);
303 evch_zonefree(zoneid_t zoneid
, void *arg
)
305 struct evch_globals
*eg
= arg
;
309 mutex_enter(&eg
->evch_list_lock
);
312 * Keep picking the head element off the list until there are no
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.
378 evch_gevent_free(evch_gevent_t
*evp
)
382 refcnt
= (int32_t)atomic_dec_32_nv(&evp
->ge_refcount
);
384 if (evp
->ge_destruct
!= NULL
) {
385 evp
->ge_destruct((void *)&(evp
->ge_payload
),
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.
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
));
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.
419 evch_delivery_hold(evch_eventq_t
*eqp
, callb_cpr_t
*cpip
)
421 if (eqp
->eq_tabortflag
== 0) {
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()
438 evch_delivery_thr(evch_eventq_t
*eqp
)
448 (void) snprintf(thnam
, sizeof (thnam
), "sysevent_chan-%d",
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
) {
463 mutex_exit(&eqp
->eq_queuemx
);
464 res
= evch_deliver(sub
, qep
->q_objref
);
465 mutex_enter(&eqp
->eq_queuemx
);
467 cv_signal(&eqp
->eq_dactivecv
);
471 * Wait for subscriber to return.
473 eqp
->eq_holdmode
= 1;
474 evch_delivery_hold(eqp
, &cprinfo
);
475 if (eqp
->eq_tabortflag
) {
480 CALLB_CPR_SAFE_BEGIN(&cprinfo
);
481 mutex_exit(&eqp
->eq_queuemx
);
484 deltime
> EVCH_MAX_PAUSE
?
485 deltime
: deltime
<< 1;
486 mutex_enter(&eqp
->eq_queuemx
);
487 CALLB_CPR_SAFE_END(&cprinfo
,
489 if (repeatcount
-- > 0) {
494 if (eqp
->eq_tabortflag
) {
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 */
515 * Create the event delivery thread for an existing event queue.
518 evch_evq_thrcreate(evch_eventq_t
*eqp
)
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
*
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
);
552 * Destroy an event queue. All subscribers have to be unsubscribed prior to
556 evch_evq_destroy(evch_eventq_t
*eqp
)
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
);
612 * Unsubscribe from an event queue.
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.
632 evch_evq_pub(evch_eventq_t
*eqp
, void *ev
, int flags
)
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
);
642 qep
= kmem_alloc(size
, flags
& EVCH_NOSLEEP
?
643 KM_NOSLEEP
: KM_SLEEP
);
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
);
661 * Enter hold mode of an event queue. Event delivery thread stops event
662 * handling after delivery of current event (if any).
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.
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.
692 evch_evq_status(evch_eventq_t
*eqp
)
694 return (eqp
->eq_holdmode
);
698 * Add a destructor function to an event structure.
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.
717 evch_evq_evzalloc(size_t paylsize
, int flag
)
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
);
727 evp
= kmem_alloc(rsize
, flag
& EVCH_NOSLEEP
? KM_NOSLEEP
:
734 evp
->ge_size
= ge_size
;
735 return (&evp
->ge_payload
);
741 * Free event structure. Argument ev is address of payload offset.
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
758 * while ((ev = evch_evq_evnext(evp, ev)) != NULL) ...
761 evch_evq_evnext(evch_eventq_t
*evq
, void *ev
)
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
)
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
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.
809 evch_clsmatch(char *class, const char *pat
)
814 if ((c
= *pat
++) == '\0')
815 return (*class == '\0');
819 pat
++; /* consecutive *'s can be collapsed */
824 while (*class != '\0') {
825 if (evch_clsmatch(class++, pat
) != 0)
831 } while (c
== *class++);
837 * Sysevent filter callback routine. Enables event delivery only if it matches
838 * the event class pattern string given by parameter cookie.
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
);
852 * Callback routine to propagate the event into a per subscriber queue.
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
);
864 * Call kernel callback routine for sysevent kernel delivery.
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.
879 evch_door_deliver(void *evp
, void *cookie
)
883 sysevent_impl_t
*ev
= (sysevent_impl_t
*)evp
;
885 evch_subd_t
*sdp
= (evch_subd_t
*)cookie
;
886 int nticks
= EVCH_MIN_PAUSE
;
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
;
901 if ((error
= door_ki_upcall_limited(sdp
->sd_door
, &darg
,
902 NULL
, SIZE_MAX
, 0)) == 0) {
907 /* Cannot deliver event - process may be forking */
910 if (nticks
> EVCH_MAX_PAUSE
) {
911 nticks
= EVCH_MAX_PAUSE
;
914 cmn_err(CE_CONT
, "event delivery thread: "
915 "door_ki_upcall error EAGAIN\n");
925 "event delivery thread: door_ki_upcall error %d\n",
930 if (retval
== EAGAIN
) {
937 * Callback routine for evch_dl_search() to compare subscriber id's. Used by
938 * evch_subscribe() and evch_chrdevent_init().
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
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.
964 evch_destr_event(void *ev
, void *ch
)
966 evch_chan_t
*chp
= (evch_chan_t
*)ch
;
968 mutex_enter(&chp
->ch_pubmx
);
970 cv_signal(&chp
->ch_pubcv
);
971 mutex_exit(&chp
->ch_pubmx
);
975 * Integer square root according to Newton's iteration.
978 evch_isqrt(uint64_t n
)
982 static uint32_t lowval
[] = { 0, 1, 1, 2 };
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.
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.
1026 struct evch_globals
*eg
;
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
);
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.
1052 evch_chbind(const char *chnam
, evch_bind_t
**scpp
, uint32_t flags
)
1054 struct evch_globals
*eg
;
1061 eg
= zone_getspecific(evch_zone_key
, curproc
->p_zone
);
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
) {
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
);
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
);
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
;
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
);
1111 mutex_exit(&eg
->evch_list_lock
);
1116 /* Check for max binds and create binding */
1117 mutex_enter(&p
->ch_mutex
);
1118 if (p
->ch_bindings
>= p
->ch_maxbinds
) {
1121 * No need to destroy the channel because this call did not
1122 * create it. Other bindings will be present if ch_maxbinds
1127 bp
= kmem_alloc(sizeof (evch_bind_t
), KM_SLEEP
);
1129 bp
->bd_sublst
= NULL
;
1134 mutex_exit(&p
->ch_mutex
);
1135 mutex_exit(&eg
->evch_list_lock
);
1140 * Unbind: Free bind structure. Remove channel if last binding was freed.
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
);
1151 mutex_enter(&eg
->evch_list_lock
);
1152 mutex_enter(&chp
->ch_mutex
);
1153 ASSERT(chp
->ch_bindings
> 0);
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
));
1174 mutex_exit(&chp
->ch_mutex
);
1175 mutex_exit(&eg
->evch_list_lock
);
1179 wildcard_count(const char *class)
1187 while ((c
= *class++) != '\0') {
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.
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
;
1215 * Check if only known flags are set.
1217 if (flags
& ~(EVCH_SUB_KEEP
| EVCH_SUB_DUMP
))
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
)
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
) {
1235 if ((flags
& EVCH_SUB_KEEP
) && (esp
->sd_active
== 0)) {
1237 * Subscription with the name on hold, reconnect to
1240 ASSERT(dtype
== EVCH_DELDOOR
);
1241 esp
->sd_subnxt
= bp
->bd_sublst
;
1242 bp
->bd_sublst
= esp
;
1244 esp
->sd_door
= (door_handle_t
)dinfo
;
1246 evch_evq_continue(esp
->sd_queue
);
1248 /* Subscriber with given name already exists */
1251 mutex_exit(&chp
->ch_mutex
);
1255 if (evch_dl_getnum(&chp
->ch_subscr
) >= chp
->ch_maxsubscr
) {
1256 mutex_exit(&chp
->ch_mutex
);
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
1267 mutex_exit(&chp
->ch_mutex
);
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
;
1295 sdp
->sd_door
= (door_handle_t
)dinfo
;
1296 delivfkt
= evch_door_deliver
;
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
;
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
);
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
1331 evch_chunsubscribe(evch_bind_t
*bp
, const char *sid
, uint32_t flags
)
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 */
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
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
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
;
1366 prev
->sd_subnxt
= next
;
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
));
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
);
1382 ASSERT(sdp
->sd_active
== 0);
1383 next
= sdp
->sd_subnxt
;
1390 next
= sdp
->sd_subnxt
;
1394 if (!(chp
->ch_holdpend
&& evch_dl_getnum(&chp
->ch_subscr
) == 0)) {
1396 * Continue dispatch thread except if no subscribers are present
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.
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
);
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
);
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.
1453 evch_chgetnames(char *buf
, size_t size
)
1455 struct evch_globals
*eg
;
1461 eg
= zone_getspecific(evch_zone_key
, curproc
->p_zone
);
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
;
1469 mutex_exit(&eg
->evch_list_lock
);
1472 bcopy(chp
->ch_name
, addr
, chp
->ch_namelen
);
1473 addr
+= chp
->ch_namelen
;
1475 mutex_exit(&eg
->evch_list_lock
);
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.
1485 evch_chgetchdata(char *chname
, void *buf
, size_t size
)
1487 struct evch_globals
*eg
;
1492 sev_chinfo_t
*p
= (sev_chinfo_t
*)buf
;
1495 sev_subinfo_t
*subp
;
1499 eg
= zone_getspecific(evch_zone_key
, curproc
->p_zone
);
1502 mutex_enter(&eg
->evch_list_lock
);
1503 chp
= (evch_chan_t
*)evch_dl_search(&eg
->evch_list
, evch_namecmp
,
1506 mutex_exit(&eg
->evch_list_lock
);
1509 chdlen
= offsetof(sev_chinfo_t
, cd_subinfo
);
1510 if (size
< chdlen
) {
1511 mutex_exit(&eg
->evch_list_lock
);
1515 p
->cd_suboffs
= chdlen
;
1516 p
->cd_uid
= chp
->ch_uid
;
1517 p
->cd_gid
= chp
->ch_gid
;
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
;
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
+
1539 if (buflen
>= bufmax
) {
1540 mutex_exit(&eg
->evch_list_lock
);
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
,
1549 subp
->sb_clnamoff
= idlen
;
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
);
1563 mutex_exit(&eg
->evch_list_lock
);
1564 return (chdlen
+ buflen
);
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
);
1583 evch_chgetpropnvl(evch_bind_t
*bp
, nvlist_t
**nvlp
, int64_t *genp
)
1585 evch_chan_t
*chp
= bp
->bd_channel
;
1588 mutex_enter(&chp
->ch_mutex
);
1590 if (chp
->ch_propnvl
!= NULL
)
1591 rc
= (nvlist_dup(chp
->ch_propnvl
, nvlp
, 0) == 0) ? 0 : ENOMEM
;
1593 *nvlp
= NULL
; /* rc still 0 */
1596 *genp
= chp
->ch_propnvlgen
;
1598 mutex_exit(&chp
->ch_mutex
);
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
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
;
1625 evch_chrdevent_init(evch_chan_t
*chp
, char *subid
)
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 */
1634 compfunc
= subid
== NULL
? evch_dumpflgcmp
: evch_subidcmp
;
1635 if (panicstr
!= NULL
) {
1639 if ((sdp
= (evch_subd_t
*)evch_dl_search(&chp
->ch_subscr
,
1640 compfunc
, subid
)) != NULL
) {
1641 evch_subq
= sdp
->sd_queue
;
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
1651 pmqstat
= evch_evq_status(chp
->ch_queue
);
1653 evch_evq_stop(chp
->ch_queue
);
1655 psqstat
= evch_evq_status(sdp
->sd_queue
);
1657 evch_evq_stop(sdp
->sd_queue
);
1660 * Create event queue to make a snapshot of all events in the
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.
1671 while ((ev
= evch_evq_evnext(sdp
->sd_queue
, ev
)) != NULL
) {
1672 (void) evch_evq_pub(snp
->sn_queue
, ev
, EVCH_SLEEP
);
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
);
1686 evch_evq_continue(chp
->ch_queue
);
1687 mutex_exit(&chp
->ch_mutex
);
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.
1696 evch_chrdevent_fini(evchanq_t
*snp
)
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
)
1717 if (evch_subq
!= NULL
) {
1719 * We have a subscriber queue. Traverse this queue
1722 if ((evch_curev
= (sysevent_impl_t
*)
1723 evch_evq_evnext(evch_subq
, evch_curev
)) != NULL
) {
1724 return (evch_curev
);
1727 * All subscriber events traversed. evch_subq
1728 * == NULL indicates to take the main event
1735 * Traverse the main event queue.
1737 if ((evch_curev
= (sysevent_impl_t
*)
1738 evch_evq_evnext(evch_chan
->ch_queue
, evch_curev
)) ==
1742 return (evch_curev
);
1744 ASSERT(snp
!= NULL
);
1745 snp
->sn_nxtev
= (sysevent_impl_t
*)evch_evq_evnext(snp
->sn_queue
,
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
)
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
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) +
1820 * Allocate event buffer plus additional payload overhead
1822 if ((ev
= evch_evq_evzalloc(sizeof (sysevent_impl_t
) +
1823 payload_sz
, flag
)) == 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
;
1845 * Initialize event channel handling queues.
1854 * Second initialization step: create threads, if event channels are already
1858 sysevent_evc_thrinit()
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);
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
);
1890 if (strlen(sid
) > MAX_SUBID_LEN
) {
1893 if (strcmp(class, EC_ALL
) == 0) {
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) {
1907 evch_chunsubscribe((evch_bind_t
*)scp
, sid
, 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 */
1925 uint64_t attr_offset
;
1929 ASSERT(scp
!= NULL
&& class != NULL
&& subclass
!= NULL
&&
1930 vendor
!= NULL
&& pubs
!= NULL
);
1932 ASSERT((flags
& ~(EVCH_SLEEP
| EVCH_NOSLEEP
| EVCH_TRYHARD
|
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
)
1944 if ((err
= nvlist_size(attr
, &asz
, NV_ENCODE_NATIVE
)) != 0) {
1948 evp
= sysevent_evc_alloc(class, subclass
, pub
, pub_sz
, asz
, km_flags
);
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
);
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
, ...)
1979 evch_chan_t
*chp
= ((evch_bind_t
*)scp
)->bd_channel
;
1990 mutex_enter(&chp
->ch_mutex
);
1992 case EVCH_GET_CHAN_LEN
:
1993 chlenp
= va_arg(ap
, uint32_t *);
1994 *chlenp
= chp
->ch_maxev
;
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
);
2004 case EVCH_GET_CHAN_LEN_MAX
:
2005 *va_arg(ap
, uint32_t *) = evch_events_max
;
2011 mutex_exit(&chp
->ch_mutex
);
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)
2024 evch_chsetpropnvl((evch_bind_t
*)scp
, nvlcp
);
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)
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().
2048 sysevent_evc_walk_init(evchan_t
*scp
, char *subscr
)
2050 if (panicstr
!= NULL
&& scp
== 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.
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.
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
2082 sysevent_evc_event_attr(sysevent_t
*ev
, size_t *plsize
)
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
2098 sysevent_get_class_name(sysevent_t
*ev
)
2100 return (SE_CLASS_NAME(ev
));
2104 * sysevent_get_subclass_name - Get subclass name string
2107 sysevent_get_subclass_name(sysevent_t
*ev
)
2109 return (SE_SUBCLASS_NAME(ev
));
2113 * sysevent_get_seq - Get event sequence id
2116 sysevent_get_seq(sysevent_t
*ev
)
2118 return (SE_SEQ(ev
));
2122 * sysevent_get_time - Get event timestamp
2125 sysevent_get_time(sysevent_t
*ev
, hrtime_t
*etime
)
2127 *etime
= SE_TIME(ev
);
2131 * sysevent_get_size - Get event buffer size
2134 sysevent_get_size(sysevent_t
*ev
)
2136 return ((size_t)SE_SIZE(ev
));
2140 * sysevent_get_pub - Get publisher name string
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
2154 sysevent_get_attr_list(sysevent_t
*ev
, nvlist_t
**nvlist
)
2159 uint64_t attr_offset
;
2162 if (SE_FLAG(ev
) != SE_PACKED_BUF
) {
2165 attr_offset
= SE_ATTR_OFF(ev
);
2166 if (SE_SIZE(ev
) == attr_offset
) {
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
;
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
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.
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
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
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
);
2259 if ((rv
= evch_chsubscribe((evch_bind_t
*)bp
, EVCH_DELDOOR
, sid
, class,
2260 (void *)dh
, NULL
, flags
, curproc
->p_pid
)) != 0) {
2267 * Flag can be EVCH_SUB_KEEP or 0. EVCH_SUB_KEEP preserves persistent
2271 evch_usrunsubscribe(evchan_t
*bp
, const char *subid
, uint32_t flags
)
2273 evch_chunsubscribe((evch_bind_t
*)bp
, subid
, flags
);
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
);
2284 mutex_enter(&chp
->ch_mutex
);
2286 case EVCH_SET_CHAN_LEN
:
2287 if (uid
&& uid
!= chp
->ch_uid
) {
2291 chp
->ch_maxev
= min(value
, evch_events_max
);
2296 mutex_exit(&chp
->ch_mutex
);
2302 evch_usrcontrol_get(evchan_t
*bp
, int cmd
, uint32_t *value
)
2304 evch_chan_t
*chp
= ((evch_bind_t
*)bp
)->bd_channel
;
2307 mutex_enter(&chp
->ch_mutex
);
2309 case EVCH_GET_CHAN_LEN
:
2310 *value
= chp
->ch_maxev
;
2312 case EVCH_GET_CHAN_LEN_MAX
:
2313 *value
= evch_events_max
;
2318 mutex_exit(&chp
->ch_mutex
);
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
));
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
));