diff options
author | Gavin Maltby <Gavin.Maltby@Sun.COM> | 2009-11-19 15:28:11 +1100 |
---|---|---|
committer | Gavin Maltby <Gavin.Maltby@Sun.COM> | 2009-11-19 15:28:11 +1100 |
commit | 49b225e1cfa7bbf7738d4df0a03f18e3283426eb (patch) | |
tree | fec8a2ac5f26eef93ef906b43e2642f06bcba3a6 /usr/src/lib | |
parent | 6dea387a9f9ddc2932a4c49a09384dfdcd5a703b (diff) | |
download | illumos-joyent-49b225e1cfa7bbf7738d4df0a03f18e3283426eb.tar.gz |
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
Diffstat (limited to 'usr/src/lib')
31 files changed, 2809 insertions, 133 deletions
diff --git a/usr/src/lib/fm/Makefile b/usr/src/lib/fm/Makefile index e5b3d8364c..775aba3fda 100644 --- a/usr/src/lib/fm/Makefile +++ b/usr/src/lib/fm/Makefile @@ -20,19 +20,13 @@ # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.lib -sparc_SUBDIRS = \ - libmdesc \ - libldom - -i386_SUBDIRS = - -SUBDIRS = \ +common_SUBDIRS = \ libfmd_agent \ libdiagcode \ libdiskstatus \ @@ -40,9 +34,17 @@ SUBDIRS = \ libfmd_log \ libfmd_msg \ libfmd_snmp \ - $($(MACH)_SUBDIRS) \ + libfmevent \ topo +sparc_SUBDIRS = \ + libmdesc \ + libldom + +i386_SUBDIRS = + +SUBDIRS = $(common_SUBDIRS) $($(MACH)_SUBDIRS) + libldom: libmdesc libfmd_agent libfmd_snmp: libfmd_adm topo diff --git a/usr/src/lib/fm/libfmevent/Makefile b/usr/src/lib/fm/libfmevent/Makefile new file mode 100644 index 0000000000..bcbca44b61 --- /dev/null +++ b/usr/src/lib/fm/libfmevent/Makefile @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../../Makefile.lib +include ../Makefile.lib + +FMHDRS = libfmevent.h +HDRDIR = common + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber lint: $(SUBDIRS) + +install: install_h .WAIT $(SUBDIRS) + +install_h: $(ROOTFMHDRS) + +check: $(CHECKHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../../Makefile.targ +include ../Makefile.targ diff --git a/usr/src/lib/fm/libfmevent/Makefile.com b/usr/src/lib/fm/libfmevent/Makefile.com new file mode 100644 index 0000000000..ed023a34d7 --- /dev/null +++ b/usr/src/lib/fm/libfmevent/Makefile.com @@ -0,0 +1,78 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +LIBRARY = libfmevent.a +VERS = .1 + +LIBSRCS = fmev_subscribe.c \ + fmev_evaccess.c \ + fmev_errstring.c \ + fmev_util.c + +OBJECTS = $(LIBSRCS:%.c=%.o) + +include ../../../Makefile.lib +include ../../Makefile.lib + +SRCS = $(LIBSRCS:%.c=../common/%.c) +LIBS = $(DYNLIB) $(LINTLIB) + +SRCDIR = ../common + +CPPFLAGS += -I../common -I. +$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG + +CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS) +CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS) +LDLIBS += -lumem -lnvpair -luutil -lsysevent -lc + +LINTFLAGS = -msux +LINTFLAGS64 = -msux -m64 + +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) +$(LINTLIB) := LINTFLAGS = -nsvx +$(LINTLIB) := LINTFLAGS64 = -nsvx -m64 + +CLEANFILES += ../common/fmev_errstring.c + +.KEEP_STATE: + +all: $(LIBS) + +lint: $(LINTLIB) lintcheck + +pics/%.o: ../$(MACH)/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +../common/fmev_errstring.c: ../common/mkerror.sh ../common/libfmevent.h + sh ../common/mkerror.sh ../common/libfmevent.h > $@ + +%.o: ../common/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +include ../../../Makefile.targ +include ../../Makefile.targ diff --git a/usr/src/lib/fm/libfmevent/amd64/Makefile b/usr/src/lib/fm/libfmevent/amd64/Makefile new file mode 100644 index 0000000000..e628bce86d --- /dev/null +++ b/usr/src/lib/fm/libfmevent/amd64/Makefile @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/fm/libfmevent/common/fmev_channels.h b/usr/src/lib/fm/libfmevent/common/fmev_channels.h new file mode 100644 index 0000000000..033e4f52b3 --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/fmev_channels.h @@ -0,0 +1,48 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _FMEV_CHANNELS_H +#define _FMEV_CHANNELS_H + +/* + * libfmevent - private GPEC channel names + * + * Note: The contents of this file are private to the implementation of + * libfmevent and are subject to change at any time without notice. + * This file is not delivered into /usr/include. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define FMD_SNOOP_CHANNEL "com.sun:fm:protocol_snoop" + +#ifdef __cplusplus +} +#endif + +#endif /* _FMEV_CHANNELS_H */ diff --git a/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c b/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c new file mode 100644 index 0000000000..442cf0bc01 --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c @@ -0,0 +1,277 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Subscription event access interfaces. + */ + +#include <sys/types.h> +#include <limits.h> +#include <atomic.h> +#include <libsysevent.h> +#include <umem.h> +#include <fm/libfmevent.h> +#include <sys/fm/protocol.h> + +#include "fmev_impl.h" + +#define API_ENTERV1(iep) \ + ((void) fmev_api_enter(fmev_shdl_cmn(((iep)->ei_hdl)), \ + LIBFMEVENT_VERSION_1)) + +typedef struct { + uint32_t ei_magic; /* _FMEVMAGIC */ + volatile uint32_t ei_refcnt; /* reference count */ + fmev_shdl_t ei_hdl; /* handle received on */ + nvlist_t *ei_nvl; /* (duped) sysevent attribute list */ + uint64_t ei_fmtime[2]; /* embedded protocol event time */ +} fmev_impl_t; + +#define FMEV2IMPL(ev) ((fmev_impl_t *)(ev)) +#define IMPL2FMEV(iep) ((fmev_t)(iep)) + +#define _FMEVMAGIC 0x466d4576 /* "FmEv" */ + +#define EVENT_VALID(iep) ((iep)->ei_magic == _FMEVMAGIC && \ + (iep)->ei_refcnt > 0 && fmev_shdl_valid((iep)->ei_hdl)) + +#define FM_TIME_SEC 0 +#define FM_TIME_NSEC 1 + +/* + * Transform a received sysevent_t into an fmev_t. + */ + +uint64_t fmev_bad_attr, fmev_bad_tod, fmev_bad_class; + +fmev_t +fmev_sysev2fmev(fmev_shdl_t hdl, sysevent_t *sep, char **clsp, nvlist_t **nvlp) +{ + fmev_impl_t *iep; + uint64_t *tod; + uint_t nelem; + + if ((iep = fmev_shdl_alloc(hdl, sizeof (*iep))) == NULL) + return (NULL); + + /* + * sysevent_get_attr_list duplicates the nvlist - we free it + * in fmev_free when the reference count hits zero. + */ + if (sysevent_get_attr_list(sep, &iep->ei_nvl) != 0) { + fmev_shdl_free(hdl, iep, sizeof (*iep)); + fmev_bad_attr++; + return (NULL); + } + + *nvlp = iep->ei_nvl; + + if (nvlist_lookup_string(iep->ei_nvl, FM_CLASS, clsp) != 0) { + nvlist_free(iep->ei_nvl); + fmev_shdl_free(hdl, iep, sizeof (*iep)); + fmev_bad_class++; + return (NULL); + } + + if (nvlist_lookup_uint64_array(iep->ei_nvl, "__tod", &tod, + &nelem) != 0 || nelem != 2) { + nvlist_free(iep->ei_nvl); + fmev_shdl_free(hdl, iep, sizeof (*iep)); + fmev_bad_tod++; + return (NULL); + } + + iep->ei_fmtime[FM_TIME_SEC] = tod[0]; + iep->ei_fmtime[FM_TIME_NSEC] = tod[1]; + + /* + * Now remove the fmd-private __tod and __ttl members. + */ + (void) nvlist_remove_all(iep->ei_nvl, "__tod"); + (void) nvlist_remove_all(iep->ei_nvl, "__ttl"); + + iep->ei_magic = _FMEVMAGIC; + iep->ei_hdl = hdl; + iep->ei_refcnt = 1; + ASSERT(EVENT_VALID(iep)); + + return (IMPL2FMEV(iep)); +} + +static void +fmev_free(fmev_impl_t *iep) +{ + ASSERT(iep->ei_refcnt == 0); + + nvlist_free(iep->ei_nvl); + fmev_shdl_free(iep->ei_hdl, iep, sizeof (*iep)); +} + +void +fmev_hold(fmev_t ev) +{ + fmev_impl_t *iep = FMEV2IMPL(ev); + + ASSERT(EVENT_VALID(iep)); + + API_ENTERV1(iep); + + atomic_inc_32(&iep->ei_refcnt); +} + +void +fmev_rele(fmev_t ev) +{ + fmev_impl_t *iep = FMEV2IMPL(ev); + + ASSERT(EVENT_VALID(iep)); + + API_ENTERV1(iep); + + if (atomic_dec_32_nv(&iep->ei_refcnt) == 0) + fmev_free(iep); +} + +fmev_t +fmev_dup(fmev_t ev) +{ + fmev_impl_t *iep = FMEV2IMPL(ev); + fmev_impl_t *cp; + + ASSERT(EVENT_VALID(iep)); + + API_ENTERV1(iep); + + if (ev == NULL) { + (void) fmev_seterr(FMEVERR_API); + return (NULL); + } + + if ((cp = fmev_shdl_alloc(iep->ei_hdl, sizeof (*iep))) == NULL) { + (void) fmev_seterr(FMEVERR_ALLOC); + return (NULL); + } + + if (nvlist_dup(iep->ei_nvl, &cp->ei_nvl, 0) != 0) { + fmev_shdl_free(iep->ei_hdl, cp, sizeof (*cp)); + (void) fmev_seterr(FMEVERR_ALLOC); + return (NULL); + } + + cp->ei_magic = _FMEVMAGIC; + cp->ei_hdl = iep->ei_hdl; + cp->ei_refcnt = 1; + return (IMPL2FMEV(cp)); +} + +nvlist_t * +fmev_attr_list(fmev_t ev) +{ + fmev_impl_t *iep = FMEV2IMPL(ev); + + ASSERT(EVENT_VALID(iep)); + + API_ENTERV1(iep); + + if (ev == NULL) { + (void) fmev_seterr(FMEVERR_API); + return (NULL); + } else if (iep->ei_nvl == NULL) { + (void) fmev_seterr(FMEVERR_MALFORMED_EVENT); + return (NULL); + } + + return (iep->ei_nvl); +} + +const char * +fmev_class(fmev_t ev) +{ + fmev_impl_t *iep = FMEV2IMPL(ev); + const char *class; + + ASSERT(EVENT_VALID(iep)); + + API_ENTERV1(iep); + + if (ev == NULL) { + (void) fmev_seterr(FMEVERR_API); + return (""); + } + + if (nvlist_lookup_string(iep->ei_nvl, FM_CLASS, (char **)&class) != 0 || + *class == '\0') { + (void) fmev_seterr(FMEVERR_MALFORMED_EVENT); + return (""); + } + + return (class); +} + +fmev_err_t +fmev_timespec(fmev_t ev, struct timespec *tp) +{ + fmev_impl_t *iep = FMEV2IMPL(ev); + uint64_t timetlimit; + + ASSERT(EVENT_VALID(iep)); + API_ENTERV1(iep); + +#ifdef _LP64 + timetlimit = INT64_MAX; +#else + timetlimit = INT32_MAX; +#endif + + if (iep->ei_fmtime[FM_TIME_SEC] > timetlimit) + return (FMEVERR_OVERFLOW); + + tp->tv_sec = (time_t)iep->ei_fmtime[FM_TIME_SEC]; + tp->tv_nsec = (long)iep->ei_fmtime[FM_TIME_NSEC]; + + return (FMEV_SUCCESS); +} + +uint64_t +fmev_time_sec(fmev_t ev) +{ + return (FMEV2IMPL(ev)->ei_fmtime[FM_TIME_SEC]); +} + +uint64_t +fmev_time_nsec(fmev_t ev) +{ + return (FMEV2IMPL(ev)->ei_fmtime[FM_TIME_NSEC]); +} + +struct tm * +fmev_localtime(fmev_t ev, struct tm *tm) +{ + time_t seconds; + + seconds = (time_t)fmev_time_sec(ev); + return (localtime_r(&seconds, tm)); +} diff --git a/usr/src/lib/fm/libfmevent/common/fmev_impl.h b/usr/src/lib/fm/libfmevent/common/fmev_impl.h new file mode 100644 index 0000000000..120660a27e --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/fmev_impl.h @@ -0,0 +1,76 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _FMEV_IMPL_H +#define _FMEV_IMPL_H + +/* + * libfmevent - private implementation + * + * Note: The contents of this file are private to the implementation of + * libfmevent and are subject to change at any time without notice. + * This file is not delivered into /usr/include. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <assert.h> +#include <errno.h> +#include <libuutil.h> +#include <libsysevent.h> +#include <fm/libfmevent.h> + +#ifdef DEBUG +#define ASSERT(x) (assert(x)) +#else +#define ASSERT(x) +#endif + +struct fmev_hdl_cmn { + uint32_t hc_magic; + uint32_t hc_api_vers; + void *(*hc_alloc)(size_t); + void *(*hc_zalloc)(size_t); + void (*hc_free)(void *, size_t); +}; + +struct fmev_hdl_cmn *fmev_shdl_cmn(fmev_shdl_t); + +extern int fmev_api_init(struct fmev_hdl_cmn *); +extern int fmev_api_enter(struct fmev_hdl_cmn *, uint32_t); +extern void fmev_api_freetsd(void); +extern fmev_err_t fmev_seterr(fmev_err_t); +extern int fmev_shdl_valid(fmev_shdl_t); +extern fmev_t fmev_sysev2fmev(fmev_shdl_t, sysevent_t *sep, char **, + nvlist_t **); + +#ifdef __cplusplus +} +#endif + +#endif /* _FMEV_IMPL_H */ diff --git a/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c b/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c new file mode 100644 index 0000000000..a1727f9e58 --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c @@ -0,0 +1,605 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * FMA event subscription interfaces - subscribe to FMA protocol + * from outside the fault manager. + */ + +#include <sys/types.h> +#include <atomic.h> +#include <libsysevent.h> +#include <libuutil.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <umem.h> +#include <unistd.h> + +#include <fm/libfmevent.h> + +#include "fmev_impl.h" +#include "fmev_channels.h" + +typedef struct { + struct fmev_hdl_cmn sh_cmn; + evchan_t *sh_binding; + uu_avl_pool_t *sh_pool; + uu_avl_t *sh_avl; + uint32_t sh_subcnt; + uint32_t sh_flags; + sysevent_subattr_t *sh_attr; + pthread_mutex_t sh_lock; + pthread_mutex_t sh_srlz_lock; +} fmev_shdl_impl_t; + +#define HDL2IHDL(hdl) ((fmev_shdl_impl_t *)(hdl)) +#define IHDL2HDL(ihdl) ((fmev_shdl_t)(ihdl)) + +#define _FMEV_SHMAGIC 0x5368446c /* ShDl */ +#define FMEV_SHDL_VALID(ihdl) ((ihdl)->sh_cmn.hc_magic == _FMEV_SHMAGIC) + +#define SHDL_FL_SERIALIZE 0x1 + +#define API_ENTERV1(hdl) \ + fmev_api_enter(&HDL2IHDL(hdl)->sh_cmn, LIBFMEVENT_VERSION_1) + +/* + * For each subscription on a handle we add a node to an avl tree + * to track subscriptions. + */ + +#define FMEV_SID_SZ (16 + 1) /* Matches MAX_SUBID_LEN */ + +struct fmev_subinfo { + uu_avl_node_t si_node; + fmev_shdl_impl_t *si_ihdl; + char si_pat[FMEV_MAX_CLASS]; + char si_sid[FMEV_SID_SZ]; + fmev_cbfunc_t *si_cb; + void *si_cbarg; +}; + +struct fmev_hdl_cmn * +fmev_shdl_cmn(fmev_shdl_t hdl) +{ + return (&HDL2IHDL(hdl)->sh_cmn); +} + +static int +shdlctl_start(fmev_shdl_impl_t *ihdl) +{ + (void) pthread_mutex_lock(&ihdl->sh_lock); + + if (ihdl->sh_subcnt == 0) { + return (1); /* lock still held */ + } else { + (void) pthread_mutex_unlock(&ihdl->sh_lock); + return (0); + } +} + +static void +shdlctl_end(fmev_shdl_impl_t *ihdl) +{ + (void) pthread_mutex_unlock(&ihdl->sh_lock); +} + +fmev_err_t +fmev_shdlctl_serialize(fmev_shdl_t hdl) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + + if (!API_ENTERV1(hdl)) + return (fmev_errno); + + if (!shdlctl_start(ihdl)) + return (fmev_seterr(FMEVERR_BUSY)); + + if (!(ihdl->sh_flags & SHDL_FL_SERIALIZE)) { + (void) pthread_mutex_init(&ihdl->sh_srlz_lock, NULL); + ihdl->sh_flags |= SHDL_FL_SERIALIZE; + } + + shdlctl_end(ihdl); + return (fmev_seterr(FMEV_SUCCESS)); +} + +fmev_err_t +fmev_shdlctl_thrattr(fmev_shdl_t hdl, pthread_attr_t *attr) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + + if (!API_ENTERV1(hdl)) + return (fmev_errno); + + if (!shdlctl_start(ihdl)) + return (fmev_seterr(FMEVERR_BUSY)); + + sysevent_subattr_thrattr(ihdl->sh_attr, attr); + + shdlctl_end(ihdl); + return (fmev_seterr(FMEV_SUCCESS)); +} + +fmev_err_t +fmev_shdlctl_sigmask(fmev_shdl_t hdl, sigset_t *set) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + + if (!API_ENTERV1(hdl)) + return (fmev_errno); + + if (!shdlctl_start(ihdl)) + return (fmev_seterr(FMEVERR_BUSY)); + + sysevent_subattr_sigmask(ihdl->sh_attr, set); + + shdlctl_end(ihdl); + return (fmev_seterr(FMEV_SUCCESS)); +} + +fmev_err_t +fmev_shdlctl_thrsetup(fmev_shdl_t hdl, door_xcreate_thrsetup_func_t *func, + void *cookie) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + + if (!API_ENTERV1(hdl)) + return (fmev_errno); + + if (!shdlctl_start(ihdl)) + return (fmev_seterr(FMEVERR_BUSY)); + + sysevent_subattr_thrsetup(ihdl->sh_attr, func, cookie); + + shdlctl_end(ihdl); + return (fmev_seterr(FMEV_SUCCESS)); +} + +fmev_err_t +fmev_shdlctl_thrcreate(fmev_shdl_t hdl, door_xcreate_server_func_t *func, + void *cookie) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + + if (!API_ENTERV1(hdl)) + return (fmev_errno); + + if (!shdlctl_start(ihdl)) + return (fmev_seterr(FMEVERR_BUSY)); + + sysevent_subattr_thrcreate(ihdl->sh_attr, func, cookie); + + shdlctl_end(ihdl); + return (fmev_seterr(FMEV_SUCCESS)); +} + +/* + * Our door service function. We return 0 regardless so that the kernel + * does not keep either retrying (EAGAIN) or bleat to cmn_err. + */ + +uint64_t fmev_proxy_cb_inval; +uint64_t fmev_proxy_cb_enomem; + +int +fmev_proxy_cb(sysevent_t *sep, void *arg) +{ + struct fmev_subinfo *sip = arg; + fmev_shdl_impl_t *ihdl = sip->si_ihdl; + nvlist_t *nvl; + char *class; + fmev_t ev; + + if (sip == NULL || sip->si_cb == NULL) { + fmev_proxy_cb_inval++; + return (0); + } + + if ((ev = fmev_sysev2fmev(IHDL2HDL(ihdl), sep, &class, &nvl)) == NULL) { + fmev_proxy_cb_enomem++; + return (0); + } + + if (ihdl->sh_flags & SHDL_FL_SERIALIZE) + (void) pthread_mutex_lock(&ihdl->sh_srlz_lock); + + sip->si_cb(ev, class, nvl, sip->si_cbarg); + + if (ihdl->sh_flags & SHDL_FL_SERIALIZE) + (void) pthread_mutex_unlock(&ihdl->sh_srlz_lock); + + fmev_rele(ev); /* release hold obtained in fmev_sysev2fmev */ + + return (0); +} + +static volatile uint32_t fmev_subid; + +fmev_err_t +fmev_shdl_subscribe(fmev_shdl_t hdl, const char *pat, fmev_cbfunc_t func, + void *funcarg) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + struct fmev_subinfo *sip; + uu_avl_index_t idx; + uint64_t nsid; + int serr; + + if (!API_ENTERV1(hdl)) + return (fmev_errno); + + if (pat == NULL || func == NULL) + return (fmev_seterr(FMEVERR_API)); + + /* + * Empty class patterns are illegal, as is the sysevent magic for + * all classes. Also validate class length. + */ + if (*pat == '\0' || strncmp(pat, EC_ALL, sizeof (EC_ALL)) == 0 || + strncmp(pat, EC_SUB_ALL, sizeof (EC_SUB_ALL)) == 0 || + strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS) + return (fmev_seterr(FMEVERR_BADCLASS)); + + if ((sip = fmev_shdl_zalloc(hdl, sizeof (*sip))) == NULL) + return (fmev_seterr(FMEVERR_ALLOC)); + + (void) strncpy(sip->si_pat, pat, sizeof (sip->si_pat)); + + uu_avl_node_init(sip, &sip->si_node, ihdl->sh_pool); + + (void) pthread_mutex_lock(&ihdl->sh_lock); + + if (uu_avl_find(ihdl->sh_avl, sip, NULL, &idx) != NULL) { + (void) pthread_mutex_unlock(&ihdl->sh_lock); + fmev_shdl_free(hdl, sip, sizeof (*sip)); + return (fmev_seterr(FMEVERR_DUPLICATE)); + } + + /* + * Generate a subscriber id for GPEC that is unique to this + * subscription. There is no provision for persistent + * subscribers. The subscriber id must be unique within + * this zone. + */ + nsid = (uint64_t)getpid() << 32 | atomic_inc_32_nv(&fmev_subid); + (void) snprintf(sip->si_sid, sizeof (sip->si_sid), "%llx", nsid); + + sip->si_ihdl = ihdl; + sip->si_cb = func; + sip->si_cbarg = funcarg; + + if ((serr = sysevent_evc_xsubscribe(ihdl->sh_binding, sip->si_sid, + sip->si_pat, fmev_proxy_cb, sip, 0, ihdl->sh_attr)) != 0) { + fmev_err_t err; + + (void) pthread_mutex_unlock(&ihdl->sh_lock); + fmev_shdl_free(hdl, sip, sizeof (*sip)); + + switch (serr) { + case ENOMEM: + err = FMEVERR_MAX_SUBSCRIBERS; + break; + + default: + err = FMEVERR_INTERNAL; + break; + } + + return (fmev_seterr(err)); + } + + uu_avl_insert(ihdl->sh_avl, sip, idx); + ihdl->sh_subcnt++; + + (void) pthread_mutex_unlock(&ihdl->sh_lock); + + return (fmev_seterr(FMEV_SUCCESS)); +} + +static int +fmev_subinfo_fini(fmev_shdl_impl_t *ihdl, struct fmev_subinfo *sip, + boolean_t doavl) +{ + int err; + + ASSERT(sip->si_ihdl == ihdl); + + err = sysevent_evc_unsubscribe(ihdl->sh_binding, sip->si_sid); + + if (err == 0) { + if (doavl) { + uu_avl_remove(ihdl->sh_avl, sip); + uu_avl_node_fini(sip, &sip->si_node, ihdl->sh_pool); + } + fmev_shdl_free(IHDL2HDL(ihdl), sip, sizeof (*sip)); + ihdl->sh_subcnt--; + } + + return (err); +} + +fmev_err_t +fmev_shdl_unsubscribe(fmev_shdl_t hdl, const char *pat) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + fmev_err_t rv = FMEVERR_NOMATCH; + struct fmev_subinfo *sip; + struct fmev_subinfo si; + int err; + + if (!API_ENTERV1(hdl)) + return (fmev_errno); + + if (pat == NULL) + return (fmev_seterr(FMEVERR_API)); + + if (*pat == '\0' || strncmp(pat, EVCH_ALLSUB, sizeof (EC_ALL)) == 0 || + strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS) + return (fmev_seterr(FMEVERR_BADCLASS)); + + (void) strncpy(si.si_pat, pat, sizeof (si.si_pat)); + + (void) pthread_mutex_lock(&ihdl->sh_lock); + + if ((sip = uu_avl_find(ihdl->sh_avl, &si, NULL, NULL)) != NULL) { + if ((err = fmev_subinfo_fini(ihdl, sip, B_TRUE)) == 0) { + rv = FMEV_SUCCESS; + } else { + /* + * Return an API error if the unsubscribe was + * attempted from within a door callback invocation; + * other errors should not happen. + */ + rv = (err == EDEADLK) ? FMEVERR_API : FMEVERR_INTERNAL; + } + } + + (void) pthread_mutex_unlock(&ihdl->sh_lock); + + return (fmev_seterr(rv)); +} + +static void * +dflt_alloc(size_t sz) +{ + return (umem_alloc(sz, UMEM_DEFAULT)); +} + +static void * +dflt_zalloc(size_t sz) +{ + return (umem_zalloc(sz, UMEM_DEFAULT)); +} + +static void +dflt_free(void *buf, size_t sz) +{ + umem_free(buf, sz); +} + +void * +fmev_shdl_alloc(fmev_shdl_t hdl, size_t sz) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + + (void) API_ENTERV1(hdl); + + return (ihdl->sh_cmn.hc_alloc(sz)); +} + +void * +fmev_shdl_zalloc(fmev_shdl_t hdl, size_t sz) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + + (void) API_ENTERV1(hdl); + + return (ihdl->sh_cmn.hc_zalloc(sz)); +} + +void +fmev_shdl_free(fmev_shdl_t hdl, void *buf, size_t sz) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + + (void) API_ENTERV1(hdl); + + ihdl->sh_cmn.hc_free(buf, sz); +} + +int +fmev_shdl_valid(fmev_shdl_t hdl) +{ + return (FMEV_SHDL_VALID(HDL2IHDL(hdl))); +} + +/*ARGSUSED*/ +static int +fmev_keycmp(const void *l, const void *r, void *arg) +{ + struct fmev_subinfo *left = (struct fmev_subinfo *)l; + struct fmev_subinfo *right = (struct fmev_subinfo *)r; + + return (strncmp(left->si_pat, right->si_pat, FMEV_MAX_CLASS)); +} + +fmev_shdl_t +fmev_shdl_init(uint32_t caller_version, void *(*hdlalloc)(size_t), + void *(*hdlzalloc)(size_t), void (*hdlfree)(void *, size_t)) +{ + fmev_shdl_impl_t *ihdl; + struct fmev_hdl_cmn hc; + const char *chan_name; + int err; + + hc.hc_magic = _FMEV_SHMAGIC; + hc.hc_api_vers = caller_version; + hc.hc_alloc = hdlalloc ? hdlalloc : dflt_alloc; + hc.hc_zalloc = hdlzalloc ? hdlzalloc : dflt_zalloc; + hc.hc_free = hdlfree ? hdlfree : dflt_free; + + if (!fmev_api_init(&hc)) + return (NULL); /* error type set */ + + if (!((hdlalloc == NULL && hdlzalloc == NULL && hdlfree == NULL) || + (hdlalloc != NULL && hdlzalloc != NULL && hdlfree != NULL))) { + (void) fmev_seterr(FMEVERR_API); + return (NULL); + } + + if (hdlzalloc == NULL) + ihdl = dflt_zalloc(sizeof (*ihdl)); + else + ihdl = hdlzalloc(sizeof (*ihdl)); + + if (ihdl == NULL) { + (void) fmev_seterr(FMEVERR_ALLOC); + return (NULL); + } + + ihdl->sh_cmn = hc; + + if ((ihdl->sh_attr = sysevent_subattr_alloc()) == NULL) { + err = FMEVERR_ALLOC; + goto error; + } + + (void) pthread_mutex_init(&ihdl->sh_lock, NULL); + + /* + * For simulation purposes we allow an environment variable + * to provide a different channel name. + */ + if ((chan_name = getenv("FMD_SNOOP_CHANNEL")) == NULL) + chan_name = FMD_SNOOP_CHANNEL; + + /* + * Try to bind to the event channel. If it's not already present, + * attempt to create the channel so that we can startup before + * the event producer (who will also apply choices such as + * channel depth when they bind to the channel). + */ + if (sysevent_evc_bind(chan_name, &ihdl->sh_binding, + EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0) { + switch (errno) { + case EINVAL: + default: + err = FMEVERR_INTERNAL; + break; + case ENOMEM: + err = FMEVERR_ALLOC; + break; + case EPERM: + err = FMEVERR_NOPRIV; + break; + } + goto error; + } + + if ((ihdl->sh_pool = uu_avl_pool_create("subinfo_pool", + sizeof (struct fmev_subinfo), + offsetof(struct fmev_subinfo, si_node), fmev_keycmp, + UU_AVL_POOL_DEBUG)) == NULL) { + err = FMEVERR_INTERNAL; + goto error; + } + + if ((ihdl->sh_avl = uu_avl_create(ihdl->sh_pool, NULL, + UU_DEFAULT)) == NULL) { + err = FMEVERR_INTERNAL; + goto error; + } + + return (IHDL2HDL(ihdl)); + +error: + (void) fmev_shdl_fini(IHDL2HDL(ihdl)); + (void) fmev_seterr(err); + return (NULL); +} + +fmev_err_t +fmev_shdl_fini(fmev_shdl_t hdl) +{ + fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); + + (void) API_ENTERV1(hdl); + + (void) pthread_mutex_lock(&ihdl->sh_lock); + + /* + * Verify that we are not in callback context - return an API + * error if we are. + */ + if (sysevent_evc_unsubscribe(ihdl->sh_binding, "invalidsid") == + EDEADLK) { + (void) pthread_mutex_unlock(&ihdl->sh_lock); + return (fmev_seterr(FMEVERR_API)); + } + + if (ihdl->sh_avl) { + void *cookie = NULL; + struct fmev_subinfo *sip; + + while ((sip = uu_avl_teardown(ihdl->sh_avl, &cookie)) != NULL) + (void) fmev_subinfo_fini(ihdl, sip, B_FALSE); + + uu_avl_destroy(ihdl->sh_avl); + ihdl->sh_avl = NULL; + } + + ASSERT(ihdl->sh_subcnt == 0); + + if (ihdl->sh_binding) { + (void) sysevent_evc_unbind(ihdl->sh_binding); + ihdl->sh_binding = NULL; + } + + if (ihdl->sh_pool) { + uu_avl_pool_destroy(ihdl->sh_pool); + ihdl->sh_pool = NULL; + } + + if (ihdl->sh_attr) { + sysevent_subattr_free(ihdl->sh_attr); + ihdl->sh_attr = NULL; + } + + ihdl->sh_cmn.hc_magic = 0; + + (void) pthread_mutex_unlock(&ihdl->sh_lock); + (void) pthread_mutex_destroy(&ihdl->sh_lock); + + fmev_shdl_free(hdl, hdl, sizeof (*ihdl)); + + fmev_api_freetsd(); + + return (fmev_seterr(FMEV_SUCCESS)); +} diff --git a/usr/src/lib/fm/libfmevent/common/fmev_util.c b/usr/src/lib/fm/libfmevent/common/fmev_util.c new file mode 100644 index 0000000000..775cc14bf4 --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/fmev_util.c @@ -0,0 +1,180 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Subscription event access interfaces. + */ + +#include <sys/types.h> +#include <pthread.h> +#include <umem.h> +#include <fm/libfmevent.h> + +#include "fmev_impl.h" + +static pthread_key_t fmev_tsdkey = PTHREAD_ONCE_KEY_NP; +static int key_inited; + +/* + * Thread and handle specific data. + */ +struct fmev_tsd { + fmev_err_t ts_lasterr; +}; + +static void +fmev_tsd_destructor(void *data) +{ + umem_free(data, sizeof (struct fmev_tsd)); +} + +/* + * Called only from fmev_shdl_init. Check we are opening a valid version + * of the ABI. + */ +int +fmev_api_init(struct fmev_hdl_cmn *hc) +{ + if (!fmev_api_enter(NULL, 0)) + return (0); + /* + * We implement only version 1 of the ABI at this point. + */ + if (hc->hc_api_vers != LIBFMEVENT_VERSION_1) { + if (key_inited) + (void) fmev_seterr(FMEVERR_VERSION_MISMATCH); + return (0); + } + + return (1); +} + +/* + * On entry to other libfmevent API members we call fmev_api_enter. + * Some thread-specific data is used to keep a per-thread error value. + * The version opened must be no greater than the latest version but can + * be older. The ver_intro is the api version at which the interface + * was added - the caller must have opened at least this version. + */ +int +fmev_api_enter(struct fmev_hdl_cmn *hc, uint32_t ver_intro) +{ + struct fmev_tsd *tsd; + + /* Initialize key on first visit */ + if (!key_inited) { + (void) pthread_key_create_once_np(&fmev_tsdkey, + fmev_tsd_destructor); + key_inited = 1; + } + + /* + * Allocate TSD for error value for this thread. It is only + * freed if/when the thread exits. + */ + if ((tsd = pthread_getspecific(fmev_tsdkey)) == NULL) { + if ((tsd = umem_alloc(sizeof (*tsd), UMEM_DEFAULT)) == NULL || + pthread_setspecific(fmev_tsdkey, (const void *)tsd) != 0) { + if (tsd) + umem_free(tsd, sizeof (*tsd)); + return (0); /* no error set, but what can we do */ + } + } + + tsd->ts_lasterr = 0; + + if (hc == NULL) { + return (1); + } + + /* Enforce version adherence. */ + if (ver_intro > hc->hc_api_vers || + hc->hc_api_vers > LIBFMEVENT_VERSION_LATEST || + ver_intro > LIBFMEVENT_VERSION_LATEST) { + tsd->ts_lasterr = FMEVERR_VERSION_MISMATCH; + return (0); + } + + return (1); +} + +/* + * Called on any fmev_shdl_fini. Free the TSD for this thread. If this + * thread makes other API calls for other open handles, or opens a new + * handle, then TSD will be allocated again in fmev_api_enter. + */ +void +fmev_api_freetsd(void) +{ + struct fmev_tsd *tsd; + + if ((tsd = pthread_getspecific(fmev_tsdkey)) != NULL) { + (void) pthread_setspecific(fmev_tsdkey, NULL); + fmev_tsd_destructor((void *)tsd); + } +} + +/* + * To return an error condition an API member first sets the error type + * with a call to fmev_seterr and then returns NULL or whatever it wants. + * The caller can then retrieve the per-thread error type using fmev_errno + * or format it with fmev_strerr. + */ +fmev_err_t +fmev_seterr(fmev_err_t error) +{ + struct fmev_tsd *tsd; + + ASSERT(key_inited); + + if ((tsd = pthread_getspecific(fmev_tsdkey)) != NULL) + tsd->ts_lasterr = error; + + return (error); +} + +/* + * fmev_errno is a macro defined in terms of the following function. It + * can be used to dereference the last error value on the current thread; + * it must not be used to assign to fmev_errno. + */ + +const fmev_err_t apierr = FMEVERR_API; +const fmev_err_t unknownerr = FMEVERR_UNKNOWN; + +const fmev_err_t * +__fmev_errno(void) +{ + struct fmev_tsd *tsd; + + if (!key_inited) + return (&apierr); + + if ((tsd = pthread_getspecific(fmev_tsdkey)) == NULL) + return (&unknownerr); + + return ((const fmev_err_t *)&tsd->ts_lasterr); +} diff --git a/usr/src/lib/fm/libfmevent/common/libfmevent.h b/usr/src/lib/fm/libfmevent/common/libfmevent.h new file mode 100644 index 0000000000..8cc06595bc --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/libfmevent.h @@ -0,0 +1,279 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBFMEVENT_H +#define _LIBFMEVENT_H + +/* + * FMA event library. + * + * A. Protocol event subscription interfaces (Committed). + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <libnvpair.h> +#include <stdlib.h> +#include <door.h> +#include <sys/time.h> +#include <sys/fm/protocol.h> + +/* + * Library ABI interface version. Quote the version you are using + * to fmev_shdl_init. Only interfaces introduced in or prior to the + * quoted version will be available. Once introduced an interface + * only ever changes compatibly. + */ +#define LIBFMEVENT_VERSION_1 1 + +#define LIBFMEVENT_VERSION_LATEST LIBFMEVENT_VERSION_1 + +/* + * Success and error return values. The descriptive comment for each + * FMEVERR_* becomes the string that is returned by fmev_strerror for that + * error type. + */ +typedef enum { + FMEV_SUCCESS = 0, + FMEV_OK = FMEV_SUCCESS, /* alias for FMEV_SUCCESS */ + FMEVERR_UNKNOWN = 0xe000, /* Error details unknown */ + FMEVERR_VERSION_MISMATCH, /* Library ABI version incompatible with caller */ + FMEVERR_API, /* Library API usage violation */ + FMEVERR_ALLOC, /* Failed to allocate additional resources */ + FMEVERR_MALFORMED_EVENT, /* Event contents are inconsistent or corrupt */ + FMEVERR_OVERFLOW, /* Operation would overflow result type */ + FMEVERR_INTERNAL, /* Internal library error */ + FMEVERR_NOPRIV, /* Insufficient permissions or privilege */ + FMEVERR_BUSY, /* Resource is busy */ + FMEVERR_DUPLICATE, /* Duplicate request */ + FMEVERR_BADCLASS, /* Bad event class or class pattern */ + FMEVERR_NOMATCH, /* No match to criteria provided */ + FMEVERR_MAX_SUBSCRIBERS, /* Exceeds maximum subscribers per handle */ + FMEVERR_INVALIDARG /* Argument is invalid */ +} fmev_err_t; + +/* + * Some interfaces return an fmev_err_t - FMEV_SUCCESS on success, otherwise + * failure of the indicated type. You can use fmev_strerror to render an + * fmev_err_t into a string. + * + * Other interfaces do not return an fmev_err_t directly. For example + * where we return a pointer an error is indicated by a NULL return. + * In these cases you can retrieve the fmev_err_t describing the reason + * for the failure using fmev_errno or get a string with + * fmev_strerr(fmev_errno). Note that fmev_errno is per-thread and holds + * the error value for any error that occured during the last libfmevent + * API call made by the current thread. Use fmev_errno as you would + * regular errno, but you should not assign to fmev_errno. + */ +extern const fmev_err_t *__fmev_errno(void); /* do not use this directly */ +#define fmev_errno (*(__fmev_errno())) +extern const char *fmev_strerror(fmev_err_t); + +/* + * Part A - Protocol Event Subscription + * ====== + * + * Subscribe to FMA protocol events published by the fault management + * daemon, receiving a callback for each matching event. + * + * This is a Committed interface (see attributes(5) for a definition). + */ + +/* + * Opaque subscription handle and event types. + */ +typedef struct fmev_shdl *fmev_shdl_t; +typedef struct fmev *fmev_t; + +/* + * Subscription callback function type for fmev_shdl_subscribe. + */ +typedef void fmev_cbfunc_t(fmev_t, const char *, nvlist_t *, void *); + +/* + * Initialize a new handle using fmev_shdl_init and quoting interface + * version number along with alloc, zalloc and free function pointers (all + * NULL to use the defaults. + * + * Close the handle and release resources with fmev_shdl_fini. + */ + +extern fmev_shdl_t fmev_shdl_init(uint32_t, + void *(*)(size_t), /* alloc */ + void *(*)(size_t), /* zalloc */ + void (*)(void *, size_t)); /* free */ + +extern fmev_err_t fmev_shdl_fini(fmev_shdl_t); + +/* + * Having created a handle you may optionally configure various properties + * for this handle using fmev_shdlctl_*. In most cases accepting the defaults + * (that are obtained through fmev_shdl_init alone) will provide adequate + * semantics - the controls below are provided for applications + * that require fine-grained control over event delivery semantics and, in + * particular, the service threads used to perform delivery callbacks. + * + * These controls may only be applied to a subscription handle + * that has no current subscriptions in place. You therefore cannot + * change the properties once subscriptions are established, and the + * handle properties apply uniformly to all subscriptions on that handle. + * If you require different properties per subscription then use multiple + * handles. + * + * fmev_shdlctl_serialize() will serialize all callbacks arising from all + * subscriptions on a handle. Event deliveries are normally single-threaded + * on a per-subscribtion bases, that is a call to fmev_shdl_subscribe + * will have deliveries arising from that subscription delivered + * in a serialized fashion on a single thread dedicated to the subscription. + * If multiple subscriptions are established then each has a dedicated + * delivery thread - fmev_shdlctl_serialize arranges that only one of these + * threads services a callback at any one time. + * + * fmev_shdlctl_thrattr() allows you to provide thread attributes for use + * in pthread_create() when server threads are created. The attributes + * are not copied - the pthread_attr_t object passed must exist for + * the duration of all subscriptions on the handle. These attributes only + * apply if fmev_shdlctl_thrcreate() is not in use on this handle. + * + * fmev_shdlctl_sigmask() allows you to provide a sigset_t signal mask + * of signals to block in server threads. The pthread_sigmask is set + * to this immediately before pthread_create, and restored immediately + * after pthread_create. This mask only applies if fmev_shdlctl_thrcreate() + * is not in use on this handle. + * + * fmev_shdlctl_thrsetup() allows you to install a custom door server thread + * setup function - see door_xcreate(3C). This will be used with the + * default thread creation semantics or with any custom thread creation + * function appointed with fmev_shdlctl_thrcreate(). + * + * fmev_shdlctl_thrcreate() allows you to install a custom door server thread + * creation function - see door_xcreate(3C). This option excludes + * fmev_shdlctl_{thrattr,sigmask} but the remaining options + * of fmev_shdlctl_{serialize,thrsetup} are still available. + */ + +extern fmev_err_t fmev_shdlctl_serialize(fmev_shdl_t); +extern fmev_err_t fmev_shdlctl_thrattr(fmev_shdl_t, pthread_attr_t *); +extern fmev_err_t fmev_shdlctl_sigmask(fmev_shdl_t, sigset_t *); +extern fmev_err_t fmev_shdlctl_thrsetup(fmev_shdl_t, + door_xcreate_thrsetup_func_t *, void *); +extern fmev_err_t fmev_shdlctl_thrcreate(fmev_shdl_t, + door_xcreate_server_func_t *, void *); + +/* + * Specify subscription choices on a handle using fmev_shdl_subscribe as + * many times as needed to describe the full event set. The event class + * pattern can be wildcarded using simple '*' wildcarding. When an event + * matching a subscription is received a callback is performed to the + * nominated function passing a fmev_t handle on the event and the + * requested cookie argument. + * + * See the fault management event protocol specification for a description + * of event classes. + * + * Drop a subscription using fmev_shdl_unsubscribe (which must match an + * earlier subscription). + */ + +#define FMEV_MAX_CLASS 64 /* Longest class string for subscription */ + +extern fmev_err_t fmev_shdl_subscribe(fmev_shdl_t, const char *, fmev_cbfunc_t, + void *); +extern fmev_err_t fmev_shdl_unsubscribe(fmev_shdl_t, const char *); + +/* + * Event access. In the common case that the event is processed to + * completion in the context of the event callback you need only + * use fmev_attr_list to access the nvlist of event attributes, + * with no responsibility for freeing the event or the nvlist; for + * convenience, fmev_class and fmev_timestamp can both be used to + * look inside an event without having to work with the attribute list (and + * the callback receives the class as an argument). + * + * See libnvpair(3LIB) for interfaces to access an nvlist_t. + * + * The remaining interfaces apply in the case that event handling will + * continue beyond the context of the event callback in which it is received. + * + * The fmev_t handle received in a callback is reference-counted; + * the initial reference count on entry to the callback is 1, and the + * count is always decremented when the callback completes. To continue + * to operate on a received event outside of the context of the callback + * in which it is first received, take an fmev_hold during the callback + * and later fmev_rele to release your hold (and free the event if the count + * drops to 0). + * + * To access attributes of an event use fmev_attr_list to receive + * an nvlist_t pointer valid for the same lifetime as the event itself (i.e., + * until its reference count drops to zero). + * + * If changes are made to a received fmev_t (discouraged) then all who + * have a hold on the event share the change. To obtain an independent + * copy of an fmev_t, with a reference count of 1, use fmev_dup. When + * finished with the copy decrement the reference count + * using fmev_rele - the event will be freed if the count reaches 0. + * + * For convenience you can retrieve the class of an event using fmev_class + * (it's also available as an argument to a callback, and within the + * event attribute list). The string returned by fmev_class is valid for + * the same lifetime as the event itself. + * + * The time at which a protocol event was generated is available via + * fmev_timespec; tv_sec has seconds since the epoch, and tv_nsec nanoseconds + * past that second. This can fail with FMEVERR_OVERFLOW if the seconds + * value does not fit within a time_t; you can retrieve the 64-bit second + * and nanosecond values with fmev_time_sec and fmev_time_nsec. + */ + +extern nvlist_t *fmev_attr_list(fmev_t); +extern const char *fmev_class(fmev_t); + +extern fmev_err_t fmev_timespec(fmev_t, struct timespec *); +extern uint64_t fmev_time_sec(fmev_t); +extern uint64_t fmev_time_nsec(fmev_t); +extern struct tm *fmev_localtime(fmev_t, struct tm *); + +extern void fmev_hold(fmev_t); +extern void fmev_rele(fmev_t); +extern fmev_t fmev_dup(fmev_t); + +/* + * The following will allocate and free memory based on the choices made + * at fmev_shdl_init. + */ +void *fmev_shdl_alloc(fmev_shdl_t, size_t); +void *fmev_shdl_zalloc(fmev_shdl_t, size_t); +void fmev_shdl_free(fmev_shdl_t, void *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBFMEVENT_H */ diff --git a/usr/src/lib/fm/libfmevent/common/llib-lfmevent b/usr/src/lib/fm/libfmevent/common/llib-lfmevent new file mode 100644 index 0000000000..44c20c3d72 --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/llib-lfmevent @@ -0,0 +1,30 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include <fm/libfmevent.h> diff --git a/usr/src/lib/fm/libfmevent/common/mapfile-vers b/usr/src/lib/fm/libfmevent/common/mapfile-vers new file mode 100644 index 0000000000..759c7ae124 --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/mapfile-vers @@ -0,0 +1,73 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +SUNW_1.1 { + global: + fmev_attr_list; + fmev_class; + fmev_dup; + fmev_hold; + fmev_localtime; + fmev_rele; + fmev_shdl_alloc; + fmev_shdl_init; + fmev_shdl_fini; + fmev_shdl_free; + fmev_shdl_subscribe; + fmev_shdl_unsubscribe; + fmev_shdl_zalloc; + fmev_shdlctl_serialize; + fmev_shdlctl_sigmask; + fmev_shdlctl_thrattr; + fmev_shdlctl_thrcreate; + fmev_shdlctl_thrsetup; + fmev_strerror; + fmev_timespec; + fmev_time_nsec; + fmev_time_sec; + local: + *; +}; + +SUNWprivate { + global: + __fmev_errno; + local: + *; +}; diff --git a/usr/src/lib/fm/libfmevent/common/mkerror.sh b/usr/src/lib/fm/libfmevent/common/mkerror.sh new file mode 100644 index 0000000000..6a7467cb1e --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/mkerror.sh @@ -0,0 +1,65 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +cat <<EOM +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * This file was generated during make. + */ + +#include <fm/libfmevent.h> + +static const char *_fmev_errstrs[] = { +EOM + +pattern='^ \(FMEVERR_[A-Z0-9_]*\).*\/\* *\(.*\) *\*\/.*' +replace=' "\2" \/\* \1 \*\/,' + +sed -n "s/$pattern/$replace/p" $1 || exit 1 + +cat <<EOM +}; + +static const int _fmev_nerrs = + sizeof (_fmev_errstrs) / sizeof (_fmev_errstrs[0]); + +const char * +fmev_strerror(fmev_err_t err) +{ + const char *s; + + if (err >= FMEVERR_UNKNOWN && (err - FMEVERR_UNKNOWN < _fmev_nerrs)) + s = _fmev_errstrs[err - FMEVERR_UNKNOWN]; + else + s = _fmev_errstrs[0]; + + return (s); +} +EOM diff --git a/usr/src/lib/fm/libfmevent/i386/Makefile b/usr/src/lib/fm/libfmevent/i386/Makefile new file mode 100644 index 0000000000..c86be4377c --- /dev/null +++ b/usr/src/lib/fm/libfmevent/i386/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/fm/libfmevent/sparc/Makefile b/usr/src/lib/fm/libfmevent/sparc/Makefile new file mode 100644 index 0000000000..c86be4377c --- /dev/null +++ b/usr/src/lib/fm/libfmevent/sparc/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/fm/libfmevent/sparcv9/Makefile b/usr/src/lib/fm/libfmevent/sparcv9/Makefile new file mode 100644 index 0000000000..e628bce86d --- /dev/null +++ b/usr/src/lib/fm/libfmevent/sparcv9/Makefile @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libc/amd64/sys/door.s b/usr/src/lib/libc/amd64/sys/door.s index f5fd6ec515..15061adf95 100644 --- a/usr/src/lib/libc/amd64/sys/door.s +++ b/usr/src/lib/libc/amd64/sys/door.s @@ -37,7 +37,6 @@ ANSI_PRAGMA_WEAK2(door_info,__door_info,function) ANSI_PRAGMA_WEAK2(door_revoke,__door_revoke,function) ANSI_PRAGMA_WEAK2(door_setparam,__door_setparam,function) - ANSI_PRAGMA_WEAK2(door_unbind,__door_unbind,function) /* * Offsets within struct door_results @@ -110,10 +109,8 @@ door_restart: /* * this is the last server thread - call creation func for more */ - movq _daref_(door_server_func), %rax - movq 0(%rax), %rax movq DOOR_INFO_PTR(%rsp), %rdi - call *%rax /* call create function */ + call door_depletion_cb@PLT 1: /* Call the door server function now */ movq DOOR_COOKIE(%rsp), %rdi diff --git a/usr/src/lib/libc/i386/sys/door.s b/usr/src/lib/libc/i386/sys/door.s index 2d348ebba8..1e5561c387 100644 --- a/usr/src/lib/libc/i386/sys/door.s +++ b/usr/src/lib/libc/i386/sys/door.s @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,7 +37,6 @@ ANSI_PRAGMA_WEAK2(door_info,__door_info,function) ANSI_PRAGMA_WEAK2(door_revoke,__door_revoke,function) ANSI_PRAGMA_WEAK2(door_setparam,__door_setparam,function) - ANSI_PRAGMA_WEAK2(door_unbind,__door_unbind,function) /* * Offsets within struct door_results @@ -153,13 +152,9 @@ door_restart: * this is the last server thread - call creation func for more */ movl DOOR_INFO_PTR(%esp), %eax - _prologue_ pushl %eax /* door_info_t * */ - movl _daref_(door_server_func), %eax - movl 0(%eax), %eax - call *%eax /* call create function */ + call door_depletion_cb@PLT addl $4, %esp - _epilogue_ 1: /* Call the door server function now */ movl DOOR_PC(%esp), %eax @@ -192,7 +187,6 @@ door_restart: cmpl %eax, %edx /* same process? */ movl $EINTR, %eax /* if no, return EINTR (child of forkall) */ jne 4f - movl $0, 4(%esp) /* clear arguments and restart */ movl $0, 8(%esp) movl $0, 12(%esp) diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers index d46bcae8e2..e788fca0aa 100644 --- a/usr/src/lib/libc/port/mapfile-vers +++ b/usr/src/lib/libc/port/mapfile-vers @@ -100,6 +100,7 @@ SUNW_1.23 { # SunOS 5.11 (Solaris 11) door_setparam; door_ucred; door_unbind; + door_xcreate; err; errx; faccessat; diff --git a/usr/src/lib/libc/port/threads/door_calls.c b/usr/src/lib/libc/port/threads/door_calls.c index 3dae1445d8..baa1f573aa 100644 --- a/usr/src/lib/libc/port/threads/door_calls.c +++ b/usr/src/lib/libc/port/threads/door_calls.c @@ -20,12 +20,10 @@ */ /* - * 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" - #include "lint.h" #include "thr_uberdata.h" #include "libc.h" @@ -39,7 +37,10 @@ #include <door.h> #include <signal.h> #include <ucred.h> +#include <strings.h> +#include <ucontext.h> #include <sys/ucred.h> +#include <atomic.h> static door_server_func_t door_create_server; @@ -62,6 +63,61 @@ extern int __door_return(caddr_t, size_t, door_return_desc_t *, caddr_t, size_t); extern int __door_ucred(ucred_t *); extern int __door_unref(void); +extern int __door_unbind(void); + +/* + * Key for per-door data for doors created with door_xcreate. + */ +static pthread_key_t privdoor_key = PTHREAD_ONCE_KEY_NP; + +/* + * Each door_xcreate'd door has a struct privdoor_data allocated for it, + * and each of the initial pool of service threads for the door + * has TSD for the privdoor_key set to point to this structure. + * When a thread in door_return decides it is time to perform a + * thread depletion callback we can retrieve this door information + * via a TSD lookup on the privdoor key. + */ +struct privdoor_data { + int pd_dfd; + door_id_t pd_uniqid; + volatile uint32_t pd_refcnt; + door_xcreate_server_func_t *pd_crf; + void *pd_crcookie; + door_xcreate_thrsetup_func_t *pd_setupf; +}; + +static int door_xcreate_n(door_info_t *, struct privdoor_data *, int); + +/* + * door_create_cmn holds the privdoor data before kicking off server + * thread creation, all of which must succeed; if they don't then + * they return leaving the refcnt unchanged overall, and door_create_cmn + * releases its hold after revoking the door and we're done. Otherwise + * all n threads created add one each to the refcnt, and door_create_cmn + * drops its hold. If and when a server thread exits the key destructor + * function will be called, and we use that to decrement the reference + * count. We also decrement the reference count on door_unbind(). + * If ever we get the reference count to 0 then we will free that data. + */ +static void +privdoor_data_hold(struct privdoor_data *pdd) +{ + atomic_inc_32(&pdd->pd_refcnt); +} + +static void +privdoor_data_rele(struct privdoor_data *pdd) +{ + if (atomic_dec_32_nv(&pdd->pd_refcnt) == 0) + free(pdd); +} + +void +privdoor_destructor(void *data) +{ + privdoor_data_rele((struct privdoor_data *)data); +} /* * We park the ourselves in the kernel to serve as the "caller" for @@ -87,9 +143,10 @@ door_unref_func(void *arg) return (NULL); } -int -door_create(void (*f)(void *, char *, size_t, door_desc_t *, uint_t), - void *cookie, uint_t flags) +static int +door_create_cmn(door_server_procedure_t *f, void *cookie, uint_t flags, + door_xcreate_server_func_t *crf, door_xcreate_thrsetup_func_t *setupf, + void *crcookie, int nthread) { int d; @@ -107,6 +164,9 @@ door_create(void (*f)(void *, char *, size_t, door_desc_t *, uint_t), return (-1); } + if (crf) + flags |= DOOR_PRIVCREATE; + /* * Doors are associated with the processes which created them. In * the face of forkall(), this gets quite complicated. To simplify @@ -117,7 +177,7 @@ door_create(void (*f)(void *, char *, size_t, door_desc_t *, uint_t), enter_critical(self); if ((d = __door_create(f, cookie, flags)) < 0) { exit_critical(self); - return (-1); + return (-1); /* errno is set */ } mypid = getpid(); if (mypid != door_create_pid || @@ -150,25 +210,86 @@ door_create(void (*f)(void *, char *, size_t, door_desc_t *, uint_t), (void *)(uintptr_t)mypid, THR_DAEMON, NULL); } - /* - * If this is the first door created in the process, or the door - * has a private pool, we need to kick off the thread pool now. - */ - if (do_create_first) - (*door_server_func)(NULL); - if (is_private) { door_info_t di; + /* + * Create the first thread(s) for this private door. + */ if (__door_info(d, &di) < 0) - return (-1); - (*door_server_func)(&di); + return (-1); /* errno is set */ + + /* + * This key must be available for lookup for all private + * door threads, whether associated with a door created via + * door_create or door_xcreate. + */ + (void) pthread_key_create_once_np(&privdoor_key, + privdoor_destructor); + + if (crf == NULL) { + (*door_server_func)(&di); + } else { + struct privdoor_data *pdd = malloc(sizeof (*pdd)); + + if (pdd == NULL) { + (void) door_revoke(d); + errno = ENOMEM; + return (-1); + } + + pdd->pd_dfd = d; + pdd->pd_uniqid = di.di_uniquifier; + pdd->pd_refcnt = 1; /* prevent free during xcreate_n */ + pdd->pd_crf = crf; + pdd->pd_crcookie = crcookie; + pdd->pd_setupf = setupf; + + if (!door_xcreate_n(&di, pdd, nthread)) { + int errnocp = errno; + + (void) door_revoke(d); + privdoor_data_rele(pdd); + errno = errnocp; + return (-1); + } else { + privdoor_data_rele(pdd); + } + } + } else if (do_create_first) { + /* First non-private door created in the process */ + (*door_server_func)(NULL); } return (d); } int +door_create(door_server_procedure_t *f, void *cookie, uint_t flags) +{ + if (flags & (DOOR_NO_DEPLETION_CB | DOOR_PRIVCREATE)) { + errno = EINVAL; + return (-1); + } + + return (door_create_cmn(f, cookie, flags, NULL, NULL, NULL, 1)); +} + +int +door_xcreate(door_server_procedure_t *f, void *cookie, uint_t flags, + door_xcreate_server_func_t *crf, door_xcreate_thrsetup_func_t *setupf, + void *crcookie, int nthread) +{ + if (flags & DOOR_PRIVCREATE || nthread < 1 || crf == NULL) { + errno = EINVAL; + return (-1); + } + + return (door_create_cmn(f, cookie, flags | DOOR_PRIVATE, + crf, setupf, crcookie, nthread)); +} + +int door_ucred(ucred_t **uc) { ucred_t *ucp = *uc; @@ -211,6 +332,26 @@ door_cred(door_cred_t *dc) } int +door_unbind(void) +{ + struct privdoor_data *pdd; + int rv = __door_unbind(); + + /* + * If we were indeed bound to the door then check to see whether + * we are part of a door_xcreate'd door by checking for our TSD. + * If so, then clear the TSD for this key to avoid destructor + * callback on future thread exit, and release the private door data. + */ + if (rv == 0 && (pdd = pthread_getspecific(privdoor_key)) != NULL) { + (void) pthread_setspecific(privdoor_key, NULL); + privdoor_data_rele(pdd); + } + + return (rv); +} + +int door_return(char *data_ptr, size_t data_size, door_desc_t *desc_ptr, uint_t num_desc) { @@ -321,7 +462,337 @@ door_return(char *data_ptr, size_t data_size, } /* - * Install a new server creation function. + * To start and synchronize a number of door service threads at once + * we use a struct door_xsync_shared shared by all threads, and + * a struct door_xsync for each thread. While each thread + * has its own startup state, all such state are protected by the same + * shared lock. This could cause a little contention but it is a one-off + * cost at door creation. + */ +enum door_xsync_state { + DOOR_XSYNC_CREATEWAIT = 0x1c8c8c80, /* awaits creation handshake */ + DOOR_XSYNC_ABORT, /* aborting door_xcreate */ + DOOR_XSYNC_ABORTED, /* thread heeded abort request */ + DOOR_XSYNC_MAXCONCUR, /* create func decided no more */ + DOOR_XSYNC_CREATEFAIL, /* thr_create/pthread_create failure */ + DOOR_XSYNC_SETSPEC_FAIL, /* setspecific failed */ + DOOR_XSYNC_BINDFAIL, /* door_bind failed */ + DOOR_XSYNC_BOUND, /* door_bind succeeded */ + DOOR_XSYNC_ENTER_SERVICE /* Go on to door_return */ +}; + +/* These stats are incremented non-atomically - indicative only */ +uint64_t door_xcreate_n_stats[DOOR_XSYNC_ENTER_SERVICE - + DOOR_XSYNC_CREATEWAIT + 1]; + +struct door_xsync_shared { + pthread_mutex_t lock; + pthread_cond_t cv_m2s; + pthread_cond_t cv_s2m; + struct privdoor_data *pdd; + volatile uint32_t waiting; +}; + +struct door_xsync { + volatile enum door_xsync_state state; + struct door_xsync_shared *sharedp; +}; + +/* + * Thread start function that xcreated private doors must use in + * thr_create or pthread_create. They must also use the argument we + * provide. We: + * + * o call a thread setup function if supplied, or apply sensible defaults + * o bind the newly-created thread to the door it will service + * o synchronize with door_xcreate to indicate that we have successfully + * bound to the door; door_xcreate will not return until all + * requested threads have at least bound + * o enter service with door_return quoting magic sentinel args + */ +void * +door_xcreate_startf(void *arg) +{ + struct door_xsync *xsp = (struct door_xsync *)arg; + struct door_xsync_shared *xssp = xsp->sharedp; + struct privdoor_data *pdd = xssp->pdd; + enum door_xsync_state next_state; + + privdoor_data_hold(pdd); + if (pthread_setspecific(privdoor_key, (const void *)pdd) != 0) { + next_state = DOOR_XSYNC_SETSPEC_FAIL; + privdoor_data_rele(pdd); + goto handshake; + } + + if (pdd->pd_setupf != NULL) { + (pdd->pd_setupf)(pdd->pd_crcookie); + } else { + (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + } + + if (door_bind(pdd->pd_dfd) == 0) + next_state = DOOR_XSYNC_BOUND; + else + next_state = DOOR_XSYNC_BINDFAIL; + +handshake: + (void) pthread_mutex_lock(&xssp->lock); + + ASSERT(xsp->state == DOOR_XSYNC_CREATEWAIT || + xsp->state == DOOR_XSYNC_ABORT); + + if (xsp->state == DOOR_XSYNC_ABORT) + next_state = DOOR_XSYNC_ABORTED; + + xsp->state = next_state; + + if (--xssp->waiting == 0) + (void) pthread_cond_signal(&xssp->cv_s2m); + + if (next_state != DOOR_XSYNC_BOUND) { + (void) pthread_mutex_unlock(&xssp->lock); + return (NULL); /* thread exits, key destructor called */ + } + + while (xsp->state == DOOR_XSYNC_BOUND) + (void) pthread_cond_wait(&xssp->cv_m2s, &xssp->lock); + + next_state = xsp->state; + ASSERT(next_state == DOOR_XSYNC_ENTER_SERVICE || + next_state == DOOR_XSYNC_ABORT); + + if (--xssp->waiting == 0) + (void) pthread_cond_signal(&xssp->cv_s2m); + + (void) pthread_mutex_unlock(&xssp->lock); /* xssp/xsp can be freed */ + + if (next_state == DOOR_XSYNC_ABORT) + return (NULL); /* thread exits, key destructor called */ + + (void) door_return(NULL, 0, NULL, 0); + return (NULL); +} + +static int +door_xcreate_n(door_info_t *dip, struct privdoor_data *pdd, int n) +{ + struct door_xsync_shared *xssp; + struct door_xsync *xsp; + int i, failidx = -1; + int isdepcb = 0; + int failerrno; + int bound = 0; +#ifdef _STACK_GROWS_DOWNWARD + int stkdir = -1; +#else + int stkdir = 1; +#endif + int rv = 0; + + /* + * If we're called during door creation then we have the + * privdoor_data. If we're called as part of a depletion callback + * then the current thread has the privdoor_data as TSD. + */ + if (pdd == NULL) { + isdepcb = 1; + if ((pdd = pthread_getspecific(privdoor_key)) == NULL) + thr_panic("door_xcreate_n - no privdoor_data " + "on existing server thread"); + } + + /* + * Allocate on our stack. We'll pass pointers to this to the + * newly-created threads, therefore this function must not return until + * we have synced with server threads that are created. + * We do not limit the number of threads so begin by checking + * that we have space on the stack for this. + */ + { + size_t sz = sizeof (*xssp) + n * sizeof (*xsp) + 32; + char dummy; + + if (!stack_inbounds(&dummy + stkdir * sz)) { + errno = E2BIG; + return (0); + } + } + + if ((xssp = alloca(sizeof (*xssp))) == NULL || + (xsp = alloca(n * sizeof (*xsp))) == NULL) { + errno = E2BIG; + return (0); + } + + (void) pthread_mutex_init(&xssp->lock, NULL); + (void) pthread_cond_init(&xssp->cv_m2s, NULL); + (void) pthread_cond_init(&xssp->cv_s2m, NULL); + xssp->pdd = pdd; + xssp->waiting = 0; + + (void) pthread_mutex_lock(&xssp->lock); + + for (i = 0; failidx == -1 && i < n; i++) { + xsp[i].sharedp = xssp; + membar_producer(); /* xssp and xsp[i] for new thread */ + + switch ((pdd->pd_crf)(dip, door_xcreate_startf, + (void *)&xsp[i], pdd->pd_crcookie)) { + case 1: + /* + * Thread successfully created. Set mailbox + * state and increment the number we have to + * sync with. + */ + xsp[i].state = DOOR_XSYNC_CREATEWAIT; + xssp->waiting++; + break; + case 0: + /* + * Elected to create no further threads. OK for + * a depletion callback, but not during door_xcreate. + */ + xsp[i].state = DOOR_XSYNC_MAXCONCUR; + if (!isdepcb) { + failidx = i; + failerrno = EINVAL; + } + break; + case -1: + /* + * Thread creation was attempted but failed. + */ + xsp[i].state = DOOR_XSYNC_CREATEFAIL; + failidx = i; + failerrno = EPIPE; + break; + default: + /* + * The application-supplied function did not return + * -1/0/1 - best we can do is panic because anything + * else is harder to debug. + */ + thr_panic("door server create function illegal return"); + /*NOTREACHED*/ + } + } + + /* + * On initial creation all must succeed; if not then abort + */ + if (!isdepcb && failidx != -1) { + for (i = 0; i < failidx; i++) + if (xsp[i].state == DOOR_XSYNC_CREATEWAIT) + xsp[i].state = DOOR_XSYNC_ABORT; + } + + /* + * Wait for thread startup handshake to complete for all threads + */ + while (xssp->waiting) + (void) pthread_cond_wait(&xssp->cv_s2m, &xssp->lock); + + /* + * If we are aborting for a failed thread create in door_xcreate + * then we're done. + */ + if (!isdepcb && failidx != -1) { + rv = 0; + goto out; /* lock held, failerrno is set */ + } + + /* + * Did we all succeed in binding? + */ + for (i = 0; i < n; i++) { + int statidx = xsp[i].state - DOOR_XSYNC_CREATEWAIT; + + door_xcreate_n_stats[statidx]++; + if (xsp[i].state == DOOR_XSYNC_BOUND) + bound++; + } + + if (bound == n) { + rv = 1; + } else { + failerrno = EBADF; + rv = 0; + } + + /* + * During door_xcreate all must succeed in binding - if not then + * we command even those that did bind to abort. Threads that + * did not get as far as binding have already exited. + */ + for (i = 0; i < n; i++) { + if (xsp[i].state == DOOR_XSYNC_BOUND) { + xsp[i].state = (rv == 1 || isdepcb) ? + DOOR_XSYNC_ENTER_SERVICE : DOOR_XSYNC_ABORT; + xssp->waiting++; + } + } + + (void) pthread_cond_broadcast(&xssp->cv_m2s); + + while (xssp->waiting) + (void) pthread_cond_wait(&xssp->cv_s2m, &xssp->lock); + +out: + (void) pthread_mutex_unlock(&xssp->lock); + (void) pthread_mutex_destroy(&xssp->lock); + (void) pthread_cond_destroy(&xssp->cv_m2s); + (void) pthread_cond_destroy(&xssp->cv_s2m); + + if (rv == 0) + errno = failerrno; + + return (rv); +} + +/* + * Call the server creation function to give it the opportunity to + * create more threads. Called during a door invocation when we + * return from door_return(NULL,0, NULL, 0) and notice that we're + * running on the last available thread. + */ +void +door_depletion_cb(door_info_t *dip) +{ + if (dip == NULL) { + /* + * Non-private doors always use door_server_func. + */ + (*door_server_func)(NULL); + return; + } + + if (dip->di_attributes & DOOR_NO_DEPLETION_CB) { + /* + * Private, door_xcreate'd door specified no callbacks. + */ + return; + } else if (!(dip->di_attributes & DOOR_PRIVCREATE)) { + /* + * Private door with standard/legacy creation semantics. + */ + dip->di_attributes |= DOOR_DEPLETION_CB; + (*door_server_func)(dip); + return; + } else { + /* + * Private, door_xcreate'd door. + */ + dip->di_attributes |= DOOR_DEPLETION_CB; + (void) door_xcreate_n(dip, NULL, 1); + } +} + +/* + * Install a new server creation function. The appointed function + * will receieve depletion callbacks for non-private doors and private + * doors created with door_create(..., DOOR_PRIVATE). */ door_server_func_t * door_server_create(door_server_func_t *create_func) @@ -337,6 +808,7 @@ door_server_create(door_server_func_t *create_func) } /* + * Thread start function for door_create_server() below. * Create door server threads with cancellation(5) disabled. */ static void * @@ -349,7 +821,7 @@ door_create_func(void *arg) } /* - * The default server thread creation routine. + * The default door_server_func_t. */ /* ARGSUSED */ static void diff --git a/usr/src/lib/libc/sparc/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com index 9ee0e063c5..bda105c159 100644 --- a/usr/src/lib/libc/sparc/Makefile.com +++ b/usr/src/lib/libc/sparc/Makefile.com @@ -1134,7 +1134,8 @@ TIL= \ $(TIL:%=pics/%) := CFLAGS += $(LIBCBASE)/threads/sparc.il # special kludge for inlines with 'cas': -pics/rwlock.o pics/synch.o pics/lwp.o := sparc_CFLAGS += -_gcc=-Wa,-xarch=v8plus +pics/rwlock.o pics/synch.o pics/lwp.o pics/door_calls.o := \ + sparc_CFLAGS += -_gcc=-Wa,-xarch=v8plus # Files in port/fp subdirectory that need base.il inline template IL= \ diff --git a/usr/src/lib/libc/sparc/sys/door.s b/usr/src/lib/libc/sparc/sys/door.s index ce788bdbe8..8a45879130 100644 --- a/usr/src/lib/libc/sparc/sys/door.s +++ b/usr/src/lib/libc/sparc/sys/door.s @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,7 +37,6 @@ ANSI_PRAGMA_WEAK2(door_info,__door_info,function) ANSI_PRAGMA_WEAK2(door_revoke,__door_revoke,function) ANSI_PRAGMA_WEAK2(door_setparam,__door_setparam,function) - ANSI_PRAGMA_WEAK2(door_unbind,__door_unbind,function) /* * Offsets within struct door_results @@ -105,16 +104,7 @@ door_restart: * this is the last server thread - call creation func for more */ save %sp, -SA(MINFRAME), %sp - PIC_SETUP(g1) -#ifdef __sparcv9 - sethi %hi(door_server_func), %g5 - or %g5, %lo(door_server_func), %g5 - ldn [%g5 + %g1], %g1 -#else - ldn [%g1 + door_server_func], %g1 -#endif - ldn [%g1], %g1 - jmpl %g1, %o7 /* call create function */ + call door_depletion_cb ldn [%fp + DOOR_INFO_PTR], %o0 /* (delay) load door_info ptr */ restore 1: diff --git a/usr/src/lib/libdoor/common/llib-ldoor b/usr/src/lib/libdoor/common/llib-ldoor index eaaab62800..fb6075ca86 100644 --- a/usr/src/lib/libdoor/common/llib-ldoor +++ b/usr/src/lib/libdoor/common/llib-ldoor @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -23,16 +22,18 @@ /* PROTOLIB1 */ /* - * Copyright (c) 1999 by 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" #include <door.h> /* door_calls.c */ int door_create(void (*)(void *, char *, size_t, door_desc_t *, uint_t), void *, uint_t); +int door_xcreate(door_server_procedure_t *, void *, uint_t, + door_xcreate_server_func_t *, door_xcreate_thrsetup_func_t *, + void *, int); int door_revoke(int); int door_info(int, door_info_t *); int door_call(int, door_arg_t *); diff --git a/usr/src/lib/libdoor/common/mapfile-vers b/usr/src/lib/libdoor/common/mapfile-vers index efebf1514f..6f767b676a 100644 --- a/usr/src/lib/libdoor/common/mapfile-vers +++ b/usr/src/lib/libdoor/common/mapfile-vers @@ -48,6 +48,7 @@ SUNWprivate_1.1 { door_server_create = FUNCTION; door_ucred = FUNCTION; door_unbind = FUNCTION; + door_xcreate = FUNCTION; local: *; }; diff --git a/usr/src/lib/libds/common/libds.c b/usr/src/lib/libds/common/libds.c index da4b95f524..274b90132a 100644 --- a/usr/src/lib/libds/common/libds.c +++ b/usr/src/lib/libds/common/libds.c @@ -19,7 +19,7 @@ * 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. */ @@ -316,7 +316,7 @@ ds_init_sysev(void) } if (sysevent_evc_subscribe(ds_evchan, ds_sid_name, EC_VLDS, ds_recv, NULL, 0) != 0) { - sysevent_evc_unbind(ds_evchan); + (void) sysevent_evc_unbind(ds_evchan); ds_evchan = NULL; return (errno); } diff --git a/usr/src/lib/libsysevent/libevchannel.c b/usr/src/lib/libsysevent/libevchannel.c index a18a62b0dd..52f9b4641c 100644 --- a/usr/src/lib/libsysevent/libevchannel.c +++ b/usr/src/lib/libsysevent/libevchannel.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2005 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" - #include <stdio.h> #include <ctype.h> #include <fcntl.h> @@ -35,6 +32,9 @@ #include <stddef.h> #include <stdlib.h> #include <strings.h> +#include <pthread.h> +#include <atomic.h> +#include <signal.h> #include <sys/types.h> #include <sys/varargs.h> #include <sys/sysevent.h> @@ -54,8 +54,34 @@ * sysevent_evc_control - various channel based control operation */ +static void kill_door_servers(evchan_subscr_t *); + #define misaligned(p) ((uintptr_t)(p) & 3) /* 4-byte alignment required */ +static pthread_key_t nrkey = PTHREAD_ONCE_KEY_NP; + +/* + * If the current thread is a door server thread servicing a door created + * for us in sysevent_evc_xsubscribe, then an attempt to unsubscribe from + * within door invocation context on the same channel will deadlock in the + * kernel waiting for our own invocation to complete. Such calls are + * forbidden, and we abort if they are encountered (better than hanging + * unkillably). + * + * We'd like to offer this detection to subscriptions established with + * sysevent_evc_subscribe, but we don't have control over the door service + * threads in that case. Perhaps the fix is to always use door_xcreate + * even for sysevent_evc_subscribe? + */ +static boolean_t +will_deadlock(evchan_t *scp) +{ + evchan_subscr_t *subp = pthread_getspecific(nrkey); + evchan_impl_hdl_t *hdl = EVCHAN_IMPL_HNDL(scp); + + return (subp != NULL && subp->ev_subhead == hdl ? B_TRUE : B_FALSE); +} + /* * Check syntax of a channel name */ @@ -173,14 +199,18 @@ sysevent_evc_bind(const char *channel, evchan_t **scpp, uint32_t flags) /* * sysevent_evc_unbind - Unbind from previously bound/created channel */ -void +int sysevent_evc_unbind(evchan_t *scp) { sev_unsubscribe_args_t uargs; - evchan_subscr_t *subp, *tofree; + evchan_subscr_t *subp; + int errcp; if (scp == NULL || misaligned(scp)) - return; + return (errno = EINVAL); + + if (will_deadlock(scp)) + return (errno = EDEADLK); (void) mutex_lock(EV_LOCK(scp)); @@ -195,19 +225,24 @@ sysevent_evc_unbind(evchan_t *scp) * drained. */ if (ioctl(EV_FD(scp), SEV_UNSUBSCRIBE, (intptr_t)&uargs) != 0) { + errcp = errno; (void) mutex_unlock(EV_LOCK(scp)); - return; + return (errno = errcp); } } - subp = (evchan_subscr_t *)(void*)EV_SUB(scp); - while (subp->evsub_next != NULL) { - tofree = subp->evsub_next; - subp->evsub_next = tofree->evsub_next; - if (door_revoke(tofree->evsub_door_desc) != 0 && errno == EPERM) - (void) close(tofree->evsub_door_desc); - free(tofree->evsub_sid); - free(tofree); + while ((subp = EV_SUB_NEXT(scp)) != NULL) { + EV_SUB_NEXT(scp) = subp->evsub_next; + + /* If door_xcreate was applied we can clean up */ + if (subp->evsub_attr) + kill_door_servers(subp); + + if (door_revoke(subp->evsub_door_desc) != 0 && errno == EPERM) + (void) close(subp->evsub_door_desc); + + free(subp->evsub_sid); + free(subp); } (void) mutex_unlock(EV_LOCK(scp)); @@ -219,6 +254,8 @@ sysevent_evc_unbind(evchan_t *scp) (void) close(EV_FD(scp)); (void) mutex_destroy(EV_LOCK(scp)); free(scp); + + return (0); } /* @@ -287,6 +324,13 @@ door_upcall(void *cookie, char *args, size_t alen, evchan_subscr_t *subp = EVCHAN_SUBSCR(cookie); int rval = 0; + /* + * If we've been invoked simply to kill the thread then + * exit now. + */ + if (subp->evsub_state == EVCHAN_SUB_STATE_CLOSING) + pthread_exit(NULL); + if (args == NULL || alen <= (size_t)0) { /* Skip callback execution */ rval = EINVAL; @@ -304,13 +348,106 @@ door_upcall(void *cookie, char *args, size_t alen, (void) door_return(args, alen, NULL, 0); } +static pthread_once_t xsub_thrattr_once = PTHREAD_ONCE_INIT; +static pthread_attr_t xsub_thrattr; + +static void +xsub_thrattr_init(void) +{ + (void) pthread_attr_init(&xsub_thrattr); + (void) pthread_attr_setdetachstate(&xsub_thrattr, + PTHREAD_CREATE_DETACHED); + (void) pthread_attr_setscope(&xsub_thrattr, PTHREAD_SCOPE_SYSTEM); +} + /* - * sysevent_evc_subscribe - Subscribe to an existing event channel + * Our door server create function is only called during initial + * door_xcreate since we specify DOOR_NO_DEPLETION_CB. */ int -sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class, +xsub_door_server_create(door_info_t *dip, void *(*startf)(void *), + void *startfarg, void *cookie) +{ + evchan_subscr_t *subp = EVCHAN_SUBSCR(cookie); + struct sysevent_subattr_impl *xsa = subp->evsub_attr; + pthread_attr_t *thrattr; + sigset_t oset; + int err; + + if (subp->evsub_state == EVCHAN_SUB_STATE_CLOSING) + return (0); /* shouldn't happen, but just in case */ + + /* + * If sysevent_evc_xsubscribe was called electing to use a + * different door server create function then let it take it + * from here. + */ + if (xsa->xs_thrcreate) { + return (xsa->xs_thrcreate(dip, startf, startfarg, + xsa->xs_thrcreate_cookie)); + } + + if (xsa->xs_thrattr == NULL) { + (void) pthread_once(&xsub_thrattr_once, xsub_thrattr_init); + thrattr = &xsub_thrattr; + } else { + thrattr = xsa->xs_thrattr; + } + + (void) pthread_sigmask(SIG_SETMASK, &xsa->xs_sigmask, &oset); + err = pthread_create(NULL, thrattr, startf, startfarg); + (void) pthread_sigmask(SIG_SETMASK, &oset, NULL); + + return (err == 0 ? 1 : -1); +} + +void +xsub_door_server_setup(void *cookie) +{ + evchan_subscr_t *subp = EVCHAN_SUBSCR(cookie); + struct sysevent_subattr_impl *xsa = subp->evsub_attr; + + if (xsa->xs_thrsetup == NULL) { + (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + } + + (void) pthread_setspecific(nrkey, (void *)subp); + + if (xsa->xs_thrsetup) + xsa->xs_thrsetup(xsa->xs_thrsetup_cookie); +} + +/* + * Cause private door server threads to exit. We have already performed the + * unsubscribe ioctl which stops new invocations and waits until all + * existing invocations are complete. So all server threads should be + * blocked in door_return. The door has not yet been revoked. We will + * invoke repeatedly after setting the evsub_state to be noticed on + * wakeup; each invocation will result in the death of one server thread. + * + * You'd think it would be easier to kill these threads, such as through + * pthread_cancel. Unfortunately door_return is not a cancellation point, + * and if you do cancel a thread blocked in door_return the EINTR check in + * the door_return assembly logic causes us to loop with EINTR forever! + */ +static void +kill_door_servers(evchan_subscr_t *subp) +{ + door_arg_t da; + int i; + + bzero(&da, sizeof (da)); + subp->evsub_state = EVCHAN_SUB_STATE_CLOSING; + membar_producer(); + + (void) door_call(subp->evsub_door_desc, &da); +} + +static int +sysevent_evc_subscribe_cmn(evchan_t *scp, const char *sid, const char *class, int (*event_handler)(sysevent_t *ev, void *cookie), - void *cookie, uint32_t flags) + void *cookie, uint32_t flags, struct sysevent_subattr_impl *xsa) { evchan_subscr_t *subp; int upcall_door; @@ -342,6 +479,9 @@ sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class, return (errno = EINVAL); } + if (pthread_key_create_once_np(&nrkey, NULL) != 0) + return (errno); /* ENOMEM or EAGAIN */ + /* Create subscriber data */ if ((subp = calloc(1, sizeof (evchan_subscr_t))) == NULL) { return (errno); @@ -361,8 +501,29 @@ sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class, class_len = 0; } - upcall_door = door_create(door_upcall, (void *)subp, - DOOR_REFUSE_DESC | DOOR_NO_CANCEL); + /* + * Fill this in now for the xsub_door_server_setup dance + */ + subp->ev_subhead = EVCHAN_IMPL_HNDL(scp); + subp->evsub_state = EVCHAN_SUB_STATE_ACTIVE; + + if (xsa == NULL) { + upcall_door = door_create(door_upcall, (void *)subp, + DOOR_REFUSE_DESC | DOOR_NO_CANCEL); + } else { + subp->evsub_attr = xsa; + + /* + * Create a private door with exactly one thread to + * service the callbacks (the GPEC kernel implementation + * serializes deliveries for each subscriber id). + */ + upcall_door = door_xcreate(door_upcall, (void *)subp, + DOOR_REFUSE_DESC | DOOR_NO_CANCEL | DOOR_NO_DEPLETION_CB, + xsub_door_server_create, xsub_door_server_setup, + (void *)subp, 1); + } + if (upcall_door == -1) { ec = errno; free(subp->evsub_sid); @@ -377,8 +538,6 @@ sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class, (void) mutex_lock(EV_LOCK(scp)); - subp->ev_subhead = EVCHAN_IMPL_HNDL(scp); - uargs.sid.name = (uintptr_t)sid; uargs.sid.len = sid_len; uargs.class_info.name = (uintptr_t)class; @@ -388,6 +547,8 @@ sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class, if (ioctl(EV_FD(scp), SEV_SUBSCRIBE, (intptr_t)&uargs) != 0) { ec = errno; (void) mutex_unlock(EV_LOCK(scp)); + if (xsa) + kill_door_servers(subp); (void) door_revoke(upcall_door); free(subp->evsub_sid); free(subp); @@ -404,27 +565,145 @@ sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class, } /* - * sysevent_evc_unsubscribe - Unsubscribe from an existing event channel + * sysevent_evc_subscribe - subscribe to an existing event channel + * using a non-private door (which will create as many server threads + * as the apparent maximum concurrency requirements suggest). */ +int +sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class, + int (*event_handler)(sysevent_t *ev, void *cookie), + void *cookie, uint32_t flags) +{ + return (sysevent_evc_subscribe_cmn(scp, sid, class, event_handler, + cookie, flags, NULL)); +} + +static void +subattr_dfltinit(struct sysevent_subattr_impl *xsa) +{ + (void) sigfillset(&xsa->xs_sigmask); + (void) sigdelset(&xsa->xs_sigmask, SIGABRT); +} + +static struct sysevent_subattr_impl dfltsa; +pthread_once_t dfltsa_inited = PTHREAD_ONCE_INIT; + +static void +init_dfltsa(void) +{ + subattr_dfltinit(&dfltsa); +} + +/* + * sysevent_evc_subscribe - subscribe to an existing event channel + * using a private door with control over thread creation. + */ +int +sysevent_evc_xsubscribe(evchan_t *scp, const char *sid, const char *class, + int (*event_handler)(sysevent_t *ev, void *cookie), + void *cookie, uint32_t flags, sysevent_subattr_t *attr) +{ + struct sysevent_subattr_impl sa; + struct sysevent_subattr_impl *xsa; + + if (attr != NULL) { + xsa = (struct sysevent_subattr_impl *)attr; + } else { + xsa = &dfltsa; + (void) pthread_once(&dfltsa_inited, init_dfltsa); + } + + return (sysevent_evc_subscribe_cmn(scp, sid, class, event_handler, + cookie, flags, xsa)); +} + +sysevent_subattr_t * +sysevent_subattr_alloc(void) +{ + struct sysevent_subattr_impl *xsa = calloc(1, sizeof (*xsa)); + + if (xsa != NULL) + subattr_dfltinit(xsa); + + return (xsa != NULL ? (sysevent_subattr_t *)xsa : NULL); +} + +void +sysevent_subattr_free(sysevent_subattr_t *attr) +{ + struct sysevent_subattr_impl *xsa = + (struct sysevent_subattr_impl *)attr; + + free(xsa); +} + void +sysevent_subattr_thrcreate(sysevent_subattr_t *attr, + door_xcreate_server_func_t *thrcreate, void *cookie) +{ + struct sysevent_subattr_impl *xsa = + (struct sysevent_subattr_impl *)attr; + + xsa->xs_thrcreate = thrcreate; + xsa->xs_thrcreate_cookie = cookie; +} + +void +sysevent_subattr_thrsetup(sysevent_subattr_t *attr, + door_xcreate_thrsetup_func_t *thrsetup, void *cookie) +{ + struct sysevent_subattr_impl *xsa = + (struct sysevent_subattr_impl *)attr; + + xsa->xs_thrsetup = thrsetup; + xsa->xs_thrsetup_cookie = cookie; +} + +void +sysevent_subattr_sigmask(sysevent_subattr_t *attr, sigset_t *set) +{ + struct sysevent_subattr_impl *xsa = + (struct sysevent_subattr_impl *)attr; + + if (set) { + xsa->xs_sigmask = *set; + } else { + (void) sigfillset(&xsa->xs_sigmask); + (void) sigdelset(&xsa->xs_sigmask, SIGABRT); + } +} + +void +sysevent_subattr_thrattr(sysevent_subattr_t *attr, pthread_attr_t *thrattr) +{ + struct sysevent_subattr_impl *xsa = + (struct sysevent_subattr_impl *)attr; + + xsa->xs_thrattr = thrattr; +} + +/* + * sysevent_evc_unsubscribe - Unsubscribe from an existing event channel + */ +int sysevent_evc_unsubscribe(evchan_t *scp, const char *sid) { int all_subscribers = 0; sev_unsubscribe_args_t uargs; - evchan_subscr_t *subp, *tofree; + evchan_subscr_t *subp, *prevsubp, *tofree; + int errcp; int rc; if (scp == NULL || misaligned(scp)) - return; + return (errno = EINVAL); if (sid == NULL || strlen(sid) == 0 || (strlen(sid) >= MAX_SUBID_LEN)) - return; + return (errno = EINVAL); /* No inheritance of binding handles via fork() */ - if (EV_PID(scp) != getpid()) { - return; - } + if (EV_PID(scp) != getpid()) + return (errno = EINVAL); if (strcmp(sid, EVCH_ALLSUB) == 0) { all_subscribers++; @@ -436,6 +715,9 @@ sysevent_evc_unsubscribe(evchan_t *scp, const char *sid) uargs.sid.len = strlen(sid) + 1; } + if (will_deadlock(scp)) + return (errno = EDEADLK); + (void) mutex_lock(EV_LOCK(scp)); /* @@ -444,31 +726,50 @@ sysevent_evc_unsubscribe(evchan_t *scp, const char *sid) rc = ioctl(EV_FD(scp), SEV_UNSUBSCRIBE, (intptr_t)&uargs); if (rc != 0) { + errcp = errno; (void) mutex_unlock(EV_LOCK(scp)); - return; + return (errno = errcp); /* EFAULT, ENXIO, EINVAL possible */ } - /* Search for the matching subscriber */ - subp = (evchan_subscr_t *)(void*)EV_SUB(scp); - while (subp->evsub_next != NULL) { - if (all_subscribers || - (strcmp(subp->evsub_next->evsub_sid, sid) == 0)) { + /* + * Search for the matching subscriber. If EVCH_ALLSUB was specified + * then the ioctl above will have returned 0 even if there are + * no subscriptions, so the initial EV_SUB_NEXT can be NULL. + */ + prevsubp = NULL; + subp = EV_SUB_NEXT(scp); + while (subp != NULL) { + if (all_subscribers || strcmp(subp->evsub_sid, sid) == 0) { + if (prevsubp == NULL) { + EV_SUB_NEXT(scp) = subp->evsub_next; + } else { + prevsubp->evsub_next = subp->evsub_next; + } + + tofree = subp; + subp = subp->evsub_next; + + /* If door_xcreate was applied we can clean up */ + if (tofree->evsub_attr) + kill_door_servers(tofree); - tofree = subp->evsub_next; - subp->evsub_next = tofree->evsub_next; (void) door_revoke(tofree->evsub_door_desc); free(tofree->evsub_sid); free(tofree); - /* Freed single subscriber already */ - if (all_subscribers == 0) { + + /* Freed single subscriber already? */ + if (all_subscribers == 0) break; - } - } else + } else { + prevsubp = subp; subp = subp->evsub_next; + } } (void) mutex_unlock(EV_LOCK(scp)); + + return (0); } /* diff --git a/usr/src/lib/libsysevent/libsysevent.c b/usr/src/lib/libsysevent/libsysevent.c index dc2ebe9858..23cecfd7ad 100644 --- a/usr/src/lib/libsysevent/libsysevent.c +++ b/usr/src/lib/libsysevent/libsysevent.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2006 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" - #include <stdio.h> #include <fcntl.h> #include <errno.h> @@ -103,9 +101,9 @@ sysevent_alloc(char *class, int class_sz, char *subclass, int subclass_sz, aligned_pub_sz = SE_ALIGN(pub_sz); payload_sz = (aligned_class_sz - sizeof (uint64_t)) + - (aligned_subclass_sz - sizeof (uint64_t)) + - (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) + - nvlist_sz; + (aligned_subclass_sz - sizeof (uint64_t)) + + (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) + + nvlist_sz; /* * Allocate event buffer plus additional payload overhead. @@ -163,8 +161,7 @@ sysevent_post_event(char *class, char *subclass, char *vendor, char *pub_name, } error = modctl(MODEVENTS, (uintptr_t)MODEVENTS_POST_EVENT, - (uintptr_t)ev, (uintptr_t)SE_SIZE(ev), - (uintptr_t)eid, 0); + (uintptr_t)ev, (uintptr_t)SE_SIZE(ev), (uintptr_t)eid, 0); sysevent_free(ev); @@ -692,7 +689,7 @@ se_print(FILE *fp, sysevent_t *ev) (void) sysevent_get_time(ev, &hrt); (void) fprintf(fp, "received sysevent id = 0X%llx:%llx\n", - hrt, (longlong_t)sysevent_get_seq(ev)); + hrt, (longlong_t)sysevent_get_seq(ev)); (void) fprintf(fp, "\tclass = %s\n", sysevent_get_class_name(ev)); (void) fprintf(fp, "\tsubclass = %s\n", sysevent_get_subclass_name(ev)); if ((vendor = sysevent_get_vendor_name(ev)) != NULL) { @@ -807,6 +804,7 @@ struct reg_args { * is called in response to a door call to post an event. * */ +/*ARGSUSED*/ static void event_deliver_service(void *cookie, char *args, size_t alen, door_desc_t *ddp, uint_t ndid) @@ -1087,6 +1085,7 @@ dealloc_subscribers(sysevent_handle_t *shp) } } +/*ARGSUSED*/ static int alloc_subscriber(sysevent_handle_t *shp, uint32_t sub_id, int oflag) { @@ -1470,6 +1469,7 @@ create_failed: * is called in response to a registration cache update. * */ +/*ARGSUSED*/ static void cache_update_service(void *cookie, char *args, size_t alen, door_desc_t *ddp, uint_t ndid) @@ -1490,6 +1490,7 @@ cache_update_service(void *cookie, char *args, size_t alen, goto return_from_door; } + /* LINTED: E_BAD_PTR_CAST_ALIGN */ rargs = (struct reg_args *)args; shp = (sysevent_handle_t *)cookie; @@ -1833,8 +1834,8 @@ sysevent_open_channel(const char *channel) while (getextmntent(fp, &m, sizeof (struct extmnttab)) == 0) { if (strcmp(m.mnt_mountp, "/var/run") == 0 && - strcmp(m.mnt_fstype, "tmpfs") == 0) { - (void) fclose(fp); + strcmp(m.mnt_fstype, "tmpfs") == 0) { + (void) fclose(fp); var_run_mounted = 1; break; } @@ -2127,7 +2128,7 @@ sysevent_bind_subscriber(sysevent_handle_t *shp, if ((SH_DOOR_DESC(shp) = door_create(event_deliver_service, (void *)shp, DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { dprint("sysevent_bind_subscriber: door create failed: " - "%s\n", strerror(errno)); + "%s\n", strerror(errno)); error = EFAULT; goto fail; } @@ -2154,7 +2155,7 @@ sysevent_bind_subscriber(sysevent_handle_t *shp, /* Create an event handler thread */ if (thr_create(NULL, NULL, (void *(*)(void *))subscriber_event_handler, - shp, THR_BOUND, &sub_info->sp_handler_tid) != 0) { + shp, THR_BOUND, &sub_info->sp_handler_tid) != 0) { error = EFAULT; goto fail; } diff --git a/usr/src/lib/libsysevent/libsysevent_impl.h b/usr/src/lib/libsysevent/libsysevent_impl.h index 7d02359212..edd6988169 100644 --- a/usr/src/lib/libsysevent/libsysevent_impl.h +++ b/usr/src/lib/libsysevent/libsysevent_impl.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,16 +18,15 @@ * * CDDL HEADER END */ + /* - * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _LIBSYSEVENT_IMPL_H #define _LIBSYSEVENT_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -137,6 +135,15 @@ typedef struct evchan_impl_handle { #define EV_SUB(evcp) (&(EVCHAN_IMPL_HNDL(evcp)->ev_sub)) #define EV_SUB_NEXT(evcp) (EVCHAN_IMPL_HNDL(evcp)->ev_sub.evchan_sub_next) +struct sysevent_subattr_impl { + door_xcreate_server_func_t *xs_thrcreate; + void *xs_thrcreate_cookie; + door_xcreate_thrsetup_func_t *xs_thrsetup; + void *xs_thrsetup_cookie; + pthread_attr_t *xs_thrattr; + sigset_t xs_sigmask; +}; + /* * Subscriber private data */ @@ -147,8 +154,13 @@ struct evchan_subscriber { char *evsub_sid; /* identifier of subscriber */ void *evsub_cookie; /* subscriber cookie */ int (*evsub_func)(sysevent_t *, void *); /* subscriber event handler */ + struct sysevent_subattr_impl *evsub_attr; + uint32_t evsub_state; }; +#define EVCHAN_SUB_STATE_ACTIVE 1 +#define EVCHAN_SUB_STATE_CLOSING 2 + /* Access to subscriber data */ #define EVCHAN_SUBSCR(subp) ((evchan_subscr_t *)(subp)) diff --git a/usr/src/lib/libsysevent/llib-lsysevent b/usr/src/lib/libsysevent/llib-lsysevent index 848135ec67..a0cc48b012 100644 --- a/usr/src/lib/libsysevent/llib-lsysevent +++ b/usr/src/lib/libsysevent/llib-lsysevent @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -22,14 +21,12 @@ /*LINTLIBRARY*/ /*PROTOLIB1*/ /* - * Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * usr/src/lib/libsysevent/llib-lsysevent */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <synch.h> #include <thread.h> #include "libsysevent.h" @@ -118,7 +115,7 @@ void se_print(FILE *fp, sysevent_t *ev); int sysevent_evc_bind(const char *channel_path, evchan_t **scpp, uint32_t flags); -void sysevent_evc_unbind(evchan_t *scp); +int sysevent_evc_unbind(evchan_t *scp); int sysevent_evc_publish(evchan_t *scp, const char *event_class, const char *event_subclass, const char *vendor, const char *pub_name, @@ -128,7 +125,24 @@ int sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *event_class, int (*event_handler)(sysevent_t *ev, void *cookie), void *cookie, uint32_t flags); +int sysevent_evc_xsubscribe(evchan_t *scp, const char *sid, + const char *event_class, + int (*event_handler)(sysevent_t *ev, void *cookie), + void *cookie, uint32_t flags, + sysevent_subattr_t *xs); -void sysevent_evc_unsubscribe(evchan_t *scp, const char *sid); +int sysevent_evc_unsubscribe(evchan_t *scp, const char *sid); int sysevent_evc_control(evchan_t *scp, int cmd, ...); + +sysevent_subattr_t *sysevent_subattr_alloc(void); +void sysevent_subattr_free(sysevent_subattr_t *attr); + +void sysevent_subattr_thrcreate(sysevent_subattr_t *attr, + door_xcreate_server_func_t *func, void *cookie); +void sysevent_subattr_thrsetup(sysevent_subattr_t *attr, + door_xcreate_thrsetup_func_t *func, void *cookie); + +void sysevent_subattr_thrattr(sysevent_subattr_t *attr, + pthread_attr_t *thrattr); + diff --git a/usr/src/lib/libsysevent/mapfile-vers b/usr/src/lib/libsysevent/mapfile-vers index e9fb12a36d..7e4107db31 100644 --- a/usr/src/lib/libsysevent/mapfile-vers +++ b/usr/src/lib/libsysevent/mapfile-vers @@ -84,6 +84,7 @@ SUNWprivate_1.1 { sysevent_evc_subscribe; sysevent_evc_unbind; sysevent_evc_unsubscribe; + sysevent_evc_xsubscribe; sysevent_get_class; sysevent_get_pub; sysevent_get_subclass; @@ -95,6 +96,12 @@ SUNWprivate_1.1 { sysevent_unbind_publisher; sysevent_unbind_subscriber; sysevent_unregister_event; + sysevent_subattr_alloc; + sysevent_subattr_free; + sysevent_subattr_sigmask; + sysevent_subattr_thrattr; + sysevent_subattr_thrcreate; + sysevent_subattr_thrsetup; local: *; }; diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c index 88c2c2c5a1..d483088ccd 100644 --- a/usr/src/lib/libzonecfg/common/libzonecfg.c +++ b/usr/src/lib/libzonecfg/common/libzonecfg.c @@ -6060,7 +6060,7 @@ zonecfg_notify_bind(int(*func)(const char *zonename, zoneid_t zid, return (zevtchan); out1: - sysevent_evc_unbind(zevtchan->zn_eventchan); + (void) sysevent_evc_unbind(zevtchan->zn_eventchan); out2: (void) pthread_mutex_destroy(&zevtchan->zn_mutex); (void) pthread_cond_destroy(&zevtchan->zn_cond); @@ -6077,7 +6077,7 @@ zonecfg_notify_unbind(void *handle) int ret; - sysevent_evc_unbind(((struct znotify *)handle)->zn_eventchan); + (void) sysevent_evc_unbind(((struct znotify *)handle)->zn_eventchan); /* * Check that all evc threads have gone away. This should be * enforced by sysevent_evc_unbind. |