From 49b225e1cfa7bbf7738d4df0a03f18e3283426eb Mon Sep 17 00:00:00 2001 From: Gavin Maltby Date: Thu, 19 Nov 2009 15:28:11 +1100 Subject: PSARC/2009/554 door_xcreate - extended door creation interface for private doors PSARC/2009/573 libfmevent - external subscriptions to FMA protocol events PSARC/2009/574 GPEC interface changes and additions 6893144 add door_xcreate for creating private doors with per-door thread creation control 6896220 sysevent_evc_xsubscribe and other GPEC modifications 6900975 sysevent_evc_{unbind,unsubscribe} off-by-one in subscriber list traversal 6868087 facility to allow external processes to subscribe to FMA protocol events 6896205 fmd module to forward selected protocol events for external subscription --- usr/src/uts/common/os/evchannels.c | 101 ++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 14 deletions(-) (limited to 'usr/src/uts/common/os/evchannels.c') diff --git a/usr/src/uts/common/os/evchannels.c b/usr/src/uts/common/os/evchannels.c index a25ef4fa65..9ae0565244 100644 --- a/usr/src/uts/common/os/evchannels.c +++ b/usr/src/uts/common/os/evchannels.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * This file contains the source of the general purpose event channel extension * to the sysevent framework. This implementation is made up mainly of four @@ -70,6 +68,9 @@ #define EVCH_EVQ_EVCOUNT(x) ((&(x)->eq_eventq)->sq_count) #define EVCH_EVQ_HIGHWM(x) ((&(x)->eq_eventq)->sq_highwm) +#define CH_HOLD_PEND 1 +#define CH_HOLD_PEND_INDEF 2 + struct evch_globals { evch_dlist_t evch_list; kmutex_t evch_list_lock; @@ -320,7 +321,8 @@ evch_zonefree(zoneid_t zoneid, void *arg) */ mutex_enter(&chp->ch_mutex); ASSERT(chp->ch_bindings == 0); - ASSERT(evch_dl_getnum(&chp->ch_subscr) != 0); + ASSERT(evch_dl_getnum(&chp->ch_subscr) != 0 || + chp->ch_holdpend == CH_HOLD_PEND_INDEF); /* Forcibly unsubscribe each remaining subscription */ while ((sdp = evch_dl_next(&chp->ch_subscr, NULL)) != NULL) { @@ -799,18 +801,51 @@ evch_namecmp(evch_dlelem_t *ep, char *s) return (strcmp(((evch_chan_t *)ep)->ch_name, s)); } +/* + * Simple wildcarded match test of event class string 'class' to + * wildcarded subscription string 'pat'. Recursive only if + * 'pat' includes a wildcard, otherwise essentially just strcmp. + */ +static int +evch_clsmatch(char *class, const char *pat) +{ + char c; + + do { + if ((c = *pat++) == '\0') + return (*class == '\0'); + + if (c == '*') { + while (*pat == '*') + pat++; /* consecutive *'s can be collapsed */ + + if (*pat == '\0') + return (1); + + while (*class != '\0') { + if (evch_clsmatch(class++, pat) != 0) + return (1); + } + + return (0); + } + } while (c == *class++); + + return (0); +} + /* * Sysevent filter callback routine. Enables event delivery only if it matches - * the event class string given by parameter cookie. + * the event class pattern string given by parameter cookie. */ static int evch_class_filter(void *ev, void *cookie) { - char *class = (char *)cookie; + const char *pat = (const char *)cookie; - if (class == NULL || strcmp(SE_CLASS_NAME(ev), class) == 0) { + if (pat == NULL || evch_clsmatch(SE_CLASS_NAME(ev), pat)) return (EVQ_DELIVER); - } + return (EVQ_IGNORE); } @@ -1061,8 +1096,13 @@ evch_chbind(const char *chnam, evch_bind_t **scpp, uint32_t flags) p->ch_maxsubscr = EVCH_MAX_SUBSCRIPTIONS; p->ch_maxbinds = evch_bindings_max; p->ch_ctime = gethrestime_sec(); - if (flags & EVCH_HOLD_PEND) { - p->ch_holdpend = 1; + + if (flags & (EVCH_HOLD_PEND | EVCH_HOLD_PEND_INDEF)) { + if (flags & EVCH_HOLD_PEND_INDEF) + p->ch_holdpend = CH_HOLD_PEND_INDEF; + else + p->ch_holdpend = CH_HOLD_PEND; + evch_evq_stop(p->ch_queue); } @@ -1114,9 +1154,13 @@ evch_chunbind(evch_bind_t *bp) ASSERT(chp->ch_bindings > 0); chp->ch_bindings--; kmem_free(bp, sizeof (evch_bind_t)); - if (chp->ch_bindings == 0 && evch_dl_getnum(&chp->ch_subscr) == 0) { + if (chp->ch_bindings == 0 && evch_dl_getnum(&chp->ch_subscr) == 0 && + (chp->ch_nevents == 0 || chp->ch_holdpend != CH_HOLD_PEND_INDEF)) { /* - * No more bindings or persistent subscriber, destroy channel. + * No more bindings and no persistent subscriber(s). If there + * are no events in the channel then destroy the channel; + * otherwise destroy the channel only if we're not holding + * pending events indefinitely. */ mutex_exit(&chp->ch_mutex); evch_dl_del(&eg->evch_list, &chp->ch_link); @@ -1131,6 +1175,23 @@ evch_chunbind(evch_bind_t *bp) mutex_exit(&eg->evch_list_lock); } +static int +wildcard_count(const char *class) +{ + int count = 0; + char c; + + if (class == NULL) + return (0); + + while ((c = *class++) != '\0') { + if (c == '*') + count++; + } + + return (count); +} + /* * Subscribe to a channel. dtype is either EVCH_DELKERN for kernel callbacks * or EVCH_DELDOOR for door upcall delivery to user land. Depending on dtype @@ -1155,6 +1216,14 @@ evch_chsubscribe(evch_bind_t *bp, int dtype, const char *sid, const char *class, */ if (flags & ~(EVCH_SUB_KEEP | EVCH_SUB_DUMP)) return (EINVAL); + + /* + * Enforce a limit on the number of wildcards allowed in the class + * subscription string (limits recursion in pattern matching). + */ + if (wildcard_count(class) > EVCH_WILDCARD_MAX) + return (EINVAL); + /* * Check if we have already a subscription with that name and if we * have to reconnect the subscriber to a persistent subscription. @@ -1757,7 +1826,7 @@ sysevent_evc_bind(const char *ch_name, evchan_t **scpp, uint32_t flags) return (evch_chbind(ch_name, (evch_bind_t **)scpp, flags)); } -void +int sysevent_evc_unbind(evchan_t *scp) { evch_bind_t *bp = (evch_bind_t *)scp; @@ -1765,6 +1834,8 @@ sysevent_evc_unbind(evchan_t *scp) ASSERT(scp != NULL); evch_chunsubscribe(bp, NULL, 0); evch_chunbind(bp); + + return (0); } int @@ -1784,7 +1855,7 @@ sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class, (void *)callb, cookie, 0, 0)); } -void +int sysevent_evc_unsubscribe(evchan_t *scp, const char *sid) { ASSERT(scp != NULL && sid != NULL); @@ -1792,6 +1863,8 @@ sysevent_evc_unsubscribe(evchan_t *scp, const char *sid) sid = NULL; } evch_chunsubscribe((evch_bind_t *)scp, sid, 0); + + return (0); } /* -- cgit v1.2.3