changeset 11102:b91faef0c984

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
author Gavin Maltby <Gavin.Maltby@Sun.COM>
date Thu, 19 Nov 2009 15:28:11 +1100
parents 69561cde8165
children 3b3f33a37cab
files usr/src/cmd/dtrace/test/tst/common/sysevent/tst.post_chan.c usr/src/cmd/fm/fmd/common/fmd_sysevent.c usr/src/cmd/fm/fminject/common/inj_main.c usr/src/cmd/fm/modules/common/Makefile usr/src/cmd/fm/modules/common/ext-event-transport/Makefile usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c usr/src/cmd/fps/fptest/fps_ereport_mod.c usr/src/head/door.h usr/src/lib/fm/Makefile usr/src/lib/fm/libfmevent/Makefile usr/src/lib/fm/libfmevent/Makefile.com usr/src/lib/fm/libfmevent/amd64/Makefile usr/src/lib/fm/libfmevent/common/fmev_channels.h usr/src/lib/fm/libfmevent/common/fmev_evaccess.c usr/src/lib/fm/libfmevent/common/fmev_impl.h usr/src/lib/fm/libfmevent/common/fmev_subscribe.c usr/src/lib/fm/libfmevent/common/fmev_util.c usr/src/lib/fm/libfmevent/common/libfmevent.h usr/src/lib/fm/libfmevent/common/llib-lfmevent usr/src/lib/fm/libfmevent/common/mapfile-vers usr/src/lib/fm/libfmevent/common/mkerror.sh usr/src/lib/fm/libfmevent/i386/Makefile usr/src/lib/fm/libfmevent/sparc/Makefile usr/src/lib/fm/libfmevent/sparcv9/Makefile usr/src/lib/libc/amd64/sys/door.s usr/src/lib/libc/i386/sys/door.s usr/src/lib/libc/port/mapfile-vers usr/src/lib/libc/port/threads/door_calls.c usr/src/lib/libc/sparc/Makefile.com usr/src/lib/libc/sparc/sys/door.s usr/src/lib/libdoor/common/llib-ldoor usr/src/lib/libdoor/common/mapfile-vers usr/src/lib/libds/common/libds.c usr/src/lib/libsysevent/libevchannel.c usr/src/lib/libsysevent/libsysevent.c usr/src/lib/libsysevent/libsysevent_impl.h usr/src/lib/libsysevent/llib-lsysevent usr/src/lib/libsysevent/mapfile-vers usr/src/lib/libzonecfg/common/libzonecfg.c usr/src/pkgdefs/SUNWfmd/prototype_com usr/src/pkgdefs/SUNWfmd/prototype_i386 usr/src/pkgdefs/SUNWfmd/prototype_sparc usr/src/uts/common/io/bofi.c usr/src/uts/common/os/evchannels.c usr/src/uts/common/os/fm.c usr/src/uts/common/sys/door.h usr/src/uts/common/sys/sysevent.h usr/src/uts/common/sys/sysevent_impl.h usr/src/uts/sun4v/io/vlds.c
diffstat 53 files changed, 3412 insertions(+), 240 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/dtrace/test/tst/common/sysevent/tst.post_chan.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/cmd/dtrace/test/tst/common/sysevent/tst.post_chan.c	Thu Nov 19 15:28:11 2009 +1100
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 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 <libsysevent.h>
 #include <stdio.h>
 
@@ -42,7 +40,7 @@
 	for (;;) {
 		if (sysevent_evc_publish(ch, "class_dtest", "subclass_dtest",
 		    "vendor_dtest", "publisher_dtest", NULL, EVCH_SLEEP) != 0) {
-			sysevent_evc_unbind(ch);
+			(void) sysevent_evc_unbind(ch);
 			(void) fprintf(stderr, "failed to publisth sysevent\n");
 			return (1);
 		}
--- a/usr/src/cmd/fm/fmd/common/fmd_sysevent.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/cmd/fm/fmd/common/fmd_sysevent.c	Thu Nov 19 15:28:11 2009 +1100
@@ -533,8 +533,8 @@
 sysev_fini(fmd_hdl_t *hdl)
 {
 	if (strcmp(sysev_channel, FM_ERROR_CHAN) != 0) {
-		sysevent_evc_unsubscribe(sysev_evc, sysev_sid);
-		sysevent_evc_unbind(sysev_evc);
+		(void) sysevent_evc_unsubscribe(sysev_evc, sysev_sid);
+		(void) sysevent_evc_unbind(sysev_evc);
 	}
 
 	if (fmd.d_sysev_hdl != NULL)
--- a/usr/src/cmd/fm/fminject/common/inj_main.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/cmd/fm/fminject/common/inj_main.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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 2004 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 <sys/sysevent/eventdefs.h>
 #include <sys/fm/util.h>
 #include <fm/fmd_log.h>
@@ -80,7 +77,7 @@
 static void
 sev_close(void *arg)
 {
-	sysevent_evc_unbind(arg);
+	(void) sysevent_evc_unbind(arg);
 }
 
 static inj_mode_ops_t sysevent_ops = {
--- a/usr/src/cmd/fm/modules/common/Makefile	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/cmd/fm/modules/common/Makefile	Thu Nov 19 15:28:11 2009 +1100
@@ -27,6 +27,9 @@
 	disk-monitor	\
 	disk-transport	\
 	eversholt 	\
+	ext-event-transport \
+	fabric-xlate \
+	fdd-msg \
 	io-retire	\
 	ip-transport	\
 	sensor-transport \
@@ -34,8 +37,6 @@
 	sp-monitor	\
 	syslog-msgs	\
 	zfs-diagnosis	\
-	zfs-retire	\
-	fdd-msg		\
-	fabric-xlate
+	zfs-retire
 
 include ../../Makefile.subdirs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/Makefile	Thu Nov 19 15:28:11 2009 +1100
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+MODULE = ext-event-transport
+CLASS = common
+SRCS = fmevt_main.c fmevt_outbound.c 
+
+include ../../Makefile.plugin
+
+CFLAGS += $(INCS)
+LINTFLAGS += $(INCS)
+LDLIBS += -lsysevent
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf	Thu Nov 19 15:28:11 2009 +1100
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+
+#
+# Configuration for the ext-event-transport transport module.  This module
+# forwards protocol events out of fmd for subscription by other entities
+# using other libfmevent interfaces.
+#
+
+#
+# You can disable the forwarding functionality by setting this true
+#
+setprop protocol_forward_disable false
+
+#
+# The channel we forward events on can be changed for simulation environments
+# by changing the "outbound_channel" string property from its default.
+# The maximum number of events that can queue in the channel (each
+# consuming a little kernel memory) is controlled by "outbound_channel_depth".
+#
+# setprop outbound_channel ...
+# setprop outbound_channel_depth 256
+
+#
+# Protocol event classes that will be forwarded.
+# Changing this list may lead to breakage and/or excessive event forwarding.
+#
+subscribe	list.*
+subscribe	swevent.*
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h	Thu Nov 19 15:28:11 2009 +1100
@@ -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.
+ */
+
+#ifndef _FMEVT_H
+#define	_FMEVT_H
+
+/*
+ * ext-event-transport module - implementation detail.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/fm/protocol.h>
+#include <fm/fmd_api.h>
+#include <fm/libfmevent.h>
+#include <libnvpair.h>
+
+#include "../../../../../lib/fm/libfmevent/common/fmev_channels.h"
+
+extern fmd_hdl_t *fmevt_hdl;
+extern const fmd_prop_t fmevt_props[];
+
+extern void fmevt_init_outbound(fmd_hdl_t *);
+extern void fmevt_fini_outbound(fmd_hdl_t *);
+
+extern void fmevt_recv(fmd_hdl_t *, fmd_event_t *, nvlist_t *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FMEVT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c	Thu Nov 19 15:28:11 2009 +1100
@@ -0,0 +1,70 @@
+/*
+ * 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 <sys/types.h>
+
+#include "fmevt.h"
+
+const fmd_prop_t fmevt_props[] = {
+	{ "protocol_forward_disable", FMD_TYPE_BOOL, "false" },
+	{ "outbound_channel", FMD_TYPE_STRING, FMD_SNOOP_CHANNEL },
+	{ "outbound_channel_depth", FMD_TYPE_INT32, "256" },
+	{ NULL, 0, NULL },
+};
+
+static const fmd_hdl_ops_t fmd_ops = {
+	fmevt_recv,	/* fmdo_recv */
+	NULL,		/* fmdo_timeout */
+	NULL,		/* fmdo_close */
+	NULL,		/* fmdo_stats */
+	NULL,		/* fmdo_gc */
+	NULL,		/* fmdo_send */
+	NULL		/* fmdo_topo */
+};
+
+static const fmd_hdl_info_t fmd_info = {
+	"External FM event transport", "0.1", &fmd_ops, fmevt_props
+};
+
+void
+_fmd_init(fmd_hdl_t *hdl)
+{
+	/*
+	 * Register the handle, pulling in configuration from our
+	 * conf file.  This includes our event class subscriptions
+	 * for those events that we will forward out of fmd.
+	 */
+	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
+		return;
+
+	fmevt_init_outbound(hdl);
+}
+
+void
+_fmd_fini(fmd_hdl_t *hdl)
+{
+	fmevt_fini_outbound(hdl);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c	Thu Nov 19 15:28:11 2009 +1100
@@ -0,0 +1,137 @@
+/*
+ * 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 <sys/types.h>
+#include <strings.h>
+#include <fm/fmd_api.h>
+#include <sys/fm/protocol.h>
+#include <sys/fm/util.h>
+#include <sys/sysevent.h>
+
+#include "fmevt.h"
+
+static evchan_t *fmevt_outbound_chan;
+
+static struct fmevt_outbound_stats {
+	fmd_stat_t recv_calls;
+	fmd_stat_t recv_list;
+	fmd_stat_t recv_swevent;
+	fmd_stat_t recv_other;
+	fmd_stat_t fwd_success;
+	fmd_stat_t fwd_failure;
+} outbound_stats = {
+	{ "outbound_recv_calls", FMD_TYPE_UINT64,
+	    "total events received for forwarding" },
+	{ "outbound_cat1class_list", FMD_TYPE_UINT64,
+	    "events received matching list.*" },
+	{ "outbound_cat1class_swevent", FMD_TYPE_UINT64,
+	    "events received matching swevent.*" },
+	{ "outbound_cat1class_other", FMD_TYPE_UINT64,
+	    "events of other classes" },
+	{ "outbound_fwd_success", FMD_TYPE_UINT64,
+	    "events forwarded successfully" },
+	{ "outbound_fwd_failure", FMD_TYPE_UINT64,
+	    "events we failed to forward" }
+};
+
+#define	BUMPSTAT(stat)	outbound_stats.stat.fmds_value.ui64++
+
+/*
+ * In the .conf file we subscribe to list.* and swevent.* event classes.
+ * Any additions to that set could cause some unexpected behaviour.
+ * For example adding fault.foo won't work (since we don't publish
+ * faults directly but only within a list.suspect) but we will get
+ * any list.* including fault.foo as a suspect.
+ */
+/*ARGSUSED*/
+void
+fmevt_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
+{
+	BUMPSTAT(recv_calls);
+
+	if (strncmp(class, "list.", 5) == 0)
+		BUMPSTAT(recv_list);
+	else if (strncmp(class, "swevent.", 8) == 0)
+		BUMPSTAT(recv_swevent);
+	else
+		BUMPSTAT(recv_other);
+
+	if (sysevent_evc_publish(fmevt_outbound_chan, class, "",
+	    SUNW_VENDOR, FM_PUB, nvl, EVCH_SLEEP) == 0) {
+		BUMPSTAT(fwd_success);
+	} else {
+		BUMPSTAT(fwd_failure);
+		fmd_hdl_debug(hdl, "sysevent_evc_publish failed:");
+	}
+}
+
+void
+fmevt_init_outbound(fmd_hdl_t *hdl)
+{
+	int32_t channel_depth;
+	char *channel_name;
+
+	if (fmd_prop_get_int32(hdl, "protocol_forward_disable") == B_TRUE) {
+		fmd_hdl_debug(hdl, "protocol forwarding disabled "
+		    "through .conf file setting\n");
+		return;
+	}
+
+	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (outbound_stats) /
+	    sizeof (fmd_stat_t), (fmd_stat_t *)&outbound_stats);
+
+	/*
+	 * Allow simulation environment to change outbound channel name.
+	 */
+	channel_name = fmd_prop_get_string(hdl, "outbound_channel");
+
+	if (sysevent_evc_bind(channel_name, &fmevt_outbound_chan,
+	    EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0) {
+		fmd_hdl_abort(hdl, "Unable to bind channel %s",
+		    channel_name);
+		return;
+	}
+
+	channel_depth = fmd_prop_get_int32(hdl, "outbound_channel_depth");
+
+	if (sysevent_evc_control(fmevt_outbound_chan, EVCH_SET_CHAN_LEN,
+	    (uint32_t)channel_depth) != 0) {
+		fmd_hdl_abort(hdl, "Unable to set depth of channel %s to %d",
+		    channel_name, channel_depth);
+	}
+
+	fmd_prop_free_string(hdl, channel_name);
+}
+
+/*ARGSUSED*/
+void
+fmevt_fini_outbound(fmd_hdl_t *hdl)
+{
+	if (fmevt_outbound_chan != NULL) {
+		(void) sysevent_evc_unbind(fmevt_outbound_chan);
+		fmevt_outbound_chan = NULL;
+	}
+}
--- a/usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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 <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -122,8 +120,8 @@
 _fmd_fini(fmd_hdl_t *handle)
 {
 	if (h_event != NULL) {
-		sysevent_evc_unsubscribe(h_event, SUBSCRIBE_ID);
-		sysevent_evc_unbind(h_event);
+		(void) sysevent_evc_unsubscribe(h_event, SUBSCRIBE_ID);
+		(void) sysevent_evc_unbind(h_event);
 	}
 
 	if (h_fmd != NULL && h_xprt != NULL)
--- a/usr/src/cmd/fps/fptest/fps_ereport_mod.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/cmd/fps/fptest/fps_ereport_mod.c	Thu Nov 19 15:28:11 2009 +1100
@@ -197,7 +197,7 @@
 	(void) sleep(1);
 
 	(void) fflush(NULL);
-	sysevent_evc_unbind(scp);
+	(void) sysevent_evc_unbind(scp);
 
 	return (0);
 }
--- a/usr/src/head/door.h	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/head/door.h	Thu Nov 19 15:28:11 2009 +1100
@@ -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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_DOOR_H
 #define	_DOOR_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/door.h>
 #include <ucred.h>
@@ -42,8 +40,11 @@
 /*
  * Doors API
  */
-int	door_create(void (*)(void *, char *, size_t, door_desc_t *, uint_t),
-    void *, uint_t);
+
+typedef void door_server_procedure_t(void *, char *, size_t, door_desc_t *,
+    uint_t);
+
+int	door_create(door_server_procedure_t *, void *, uint_t);
 int	door_revoke(int);
 int	door_info(int, door_info_t *);
 int	door_call(int, door_arg_t *);
@@ -58,6 +59,14 @@
 typedef void door_server_func_t(door_info_t *);
 door_server_func_t *door_server_create(door_server_func_t *);
 
+typedef int door_xcreate_server_func_t(door_info_t *,
+    void *(*)(void *), void *, void *);
+typedef void door_xcreate_thrsetup_func_t(void *);
+
+int	door_xcreate(door_server_procedure_t *, void *, uint_t,
+	    door_xcreate_server_func_t *, door_xcreate_thrsetup_func_t *,
+	    void *, int);
+
 #endif /* _ASM */
 
 #ifdef __cplusplus
--- a/usr/src/lib/fm/Makefile	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/fm/Makefile	Thu Nov 19 15:28:11 2009 +1100
@@ -20,28 +20,30 @@
 #
 
 #
-# 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
 
+common_SUBDIRS = \
+	libfmd_agent \
+	libdiagcode \
+	libdiskstatus \
+	libfmd_adm \
+	libfmd_log \
+	libfmd_msg \
+	libfmd_snmp \
+	libfmevent \
+	topo
+
 sparc_SUBDIRS = \
 	libmdesc \
 	libldom
 
 i386_SUBDIRS =
 
-SUBDIRS = \
-	libfmd_agent \
-	libdiagcode \
-	libdiskstatus \
-	libfmd_adm \
-	libfmd_log \
-	libfmd_msg \
-	libfmd_snmp \
-	$($(MACH)_SUBDIRS) \
-	topo
+SUBDIRS = $(common_SUBDIRS) $($(MACH)_SUBDIRS)
 
 libldom: libmdesc libfmd_agent
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/Makefile	Thu Nov 19 15:28:11 2009 +1100
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/Makefile.com	Thu Nov 19 15:28:11 2009 +1100
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/amd64/Makefile	Thu Nov 19 15:28:11 2009 +1100
@@ -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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/fmev_channels.h	Thu Nov 19 15:28:11 2009 +1100
@@ -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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/fmev_impl.h	Thu Nov 19 15:28:11 2009 +1100
@@ -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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/fmev_util.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/libfmevent.h	Thu Nov 19 15:28:11 2009 +1100
@@ -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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/llib-lfmevent	Thu Nov 19 15:28:11 2009 +1100
@@ -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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/mapfile-vers	Thu Nov 19 15:28:11 2009 +1100
@@ -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:
+	*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/mkerror.sh	Thu Nov 19 15:28:11 2009 +1100
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/i386/Makefile	Thu Nov 19 15:28:11 2009 +1100
@@ -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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/sparc/Makefile	Thu Nov 19 15:28:11 2009 +1100
@@ -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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/sparcv9/Makefile	Thu Nov 19 15:28:11 2009 +1100
@@ -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)
--- a/usr/src/lib/libc/amd64/sys/door.s	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libc/amd64/sys/door.s	Thu Nov 19 15:28:11 2009 +1100
@@ -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 @@
 	/*
 	 * 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
--- a/usr/src/lib/libc/i386/sys/door.s	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libc/i386/sys/door.s	Thu Nov 19 15:28:11 2009 +1100
@@ -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 @@
 	 * 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 @@
 	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)
--- a/usr/src/lib/libc/port/mapfile-vers	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libc/port/mapfile-vers	Thu Nov 19 15:28:11 2009 +1100
@@ -100,6 +100,7 @@
 	door_setparam;
 	door_ucred;
 	door_unbind;
+	door_xcreate;
 	err;
 	errx;
 	faccessat;
--- a/usr/src/lib/libc/port/threads/door_calls.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libc/port/threads/door_calls.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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 @@
     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 @@
 	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 @@
 		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 @@
 	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 @@
 		    (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 @@
 }
 
 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 @@
 }
 
 /*
- * 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 @@
 }
 
 /*
+ * Thread start function for door_create_server() below.
  * Create door server threads with cancellation(5) disabled.
  */
 static void *
@@ -349,7 +821,7 @@
 }
 
 /*
- * The default server thread creation routine.
+ * The default door_server_func_t.
  */
 /* ARGSUSED */
 static void
--- a/usr/src/lib/libc/sparc/Makefile.com	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libc/sparc/Makefile.com	Thu Nov 19 15:28:11 2009 +1100
@@ -1134,7 +1134,8 @@
 $(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=				\
--- a/usr/src/lib/libc/sparc/sys/door.s	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libc/sparc/sys/door.s	Thu Nov 19 15:28:11 2009 +1100
@@ -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 @@
 	 * 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:
--- a/usr/src/lib/libdoor/common/llib-ldoor	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libdoor/common/llib-ldoor	Thu Nov 19 15:28:11 2009 +1100
@@ -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 *);
--- a/usr/src/lib/libdoor/common/mapfile-vers	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libdoor/common/mapfile-vers	Thu Nov 19 15:28:11 2009 +1100
@@ -48,6 +48,7 @@
 	door_server_create = FUNCTION;
 	door_ucred = FUNCTION;
 	door_unbind = FUNCTION;
+	door_xcreate = FUNCTION;
     local:
 	*;
 };
--- a/usr/src/lib/libds/common/libds.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libds/common/libds.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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 @@
 	}
 	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);
 	}
--- a/usr/src/lib/libsysevent/libevchannel.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libsysevent/libevchannel.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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_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 @@
 		 * 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 @@
 	(void) close(EV_FD(scp));
 	(void) mutex_destroy(EV_LOCK(scp));
 	free(scp);
+
+	return (0);
 }
 
 /*
@@ -287,6 +324,13 @@
 	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 @@
 	(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 @@
 		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 @@
 		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 @@
 
 	(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 @@
 	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 - 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
  */
-void
+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 @@
 		uargs.sid.len = strlen(sid) + 1;
 	}
 
+	if (will_deadlock(scp))
+		return (errno = EDEADLK);
+
 	(void) mutex_lock(EV_LOCK(scp));
 
 	/*
@@ -444,31 +726,50 @@
 	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->evsub_next;
-			subp->evsub_next = tofree->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);
+
 			(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);
 }
 
 /*
--- a/usr/src/lib/libsysevent/libsysevent.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libsysevent/libsysevent.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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 @@
 	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 @@
 	}
 
 	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 @@
 
 	(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 @@
  *		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 @@
 	}
 }
 
+/*ARGSUSED*/
 static int
 alloc_subscriber(sysevent_handle_t *shp, uint32_t sub_id, int oflag)
 {
@@ -1470,6 +1469,7 @@
  *		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 @@
 		goto return_from_door;
 	}
 
+	/* LINTED: E_BAD_PTR_CAST_ALIGN */
 	rargs = (struct reg_args *)args;
 	shp = (sysevent_handle_t *)cookie;
 
@@ -1833,8 +1834,8 @@
 
 	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 @@
 	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 @@
 
 	/* 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;
 	}
--- a/usr/src/lib/libsysevent/libsysevent_impl.h	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libsysevent/libsysevent_impl.h	Thu Nov 19 15:28:11 2009 +1100
@@ -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 @@
 #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 @@
 	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))
 
--- a/usr/src/lib/libsysevent/llib-lsysevent	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libsysevent/llib-lsysevent	Thu Nov 19 15:28:11 2009 +1100
@@ -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 @@
 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 @@
     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);
+
--- a/usr/src/lib/libsysevent/mapfile-vers	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libsysevent/mapfile-vers	Thu Nov 19 15:28:11 2009 +1100
@@ -84,6 +84,7 @@
 	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 @@
 	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:
 	*;
 };
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c	Thu Nov 19 15:28:11 2009 +1100
@@ -6060,7 +6060,7 @@
 
 	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 @@
 
 	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.
--- a/usr/src/pkgdefs/SUNWfmd/prototype_com	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/pkgdefs/SUNWfmd/prototype_com	Thu Nov 19 15:28:11 2009 +1100
@@ -34,6 +34,7 @@
 f none usr/include/fm/fmd_agent.h 644 root bin
 f none usr/include/fm/diagcode.h 644 root bin
 f none usr/include/fm/libdiskstatus.h 644 root bin
+f none usr/include/fm/libfmevent.h 644 root bin
 f none usr/include/fm/fmd_adm.h 644 root bin
 f none usr/include/fm/fmd_api.h 644 root bin
 f none usr/include/fm/fmd_fmri.h 644 root bin
@@ -78,6 +79,8 @@
 f none usr/lib/fm/fmd/plugins/disk-monitor.so 555 root bin
 f none usr/lib/fm/fmd/plugins/eft.conf 644 root bin
 f none usr/lib/fm/fmd/plugins/eft.so 555 root bin
+f none usr/lib/fm/fmd/plugins/ext-event-transport.conf 644 root bin
+f none usr/lib/fm/fmd/plugins/ext-event-transport.so 555 root bin
 f none usr/lib/fm/fmd/plugins/fabric-xlate.conf 644 root bin
 f none usr/lib/fm/fmd/plugins/fabric-xlate.so 555 root bin
 f none usr/lib/fm/fmd/plugins/fdd-msg.conf 644 root bin
@@ -137,6 +140,10 @@
 s none usr/lib/fm/libfmd_snmp.so=libfmd_snmp.so.1
 f none usr/lib/fm/llib-lfmd_snmp 644 root bin
 f none usr/lib/fm/llib-lfmd_snmp.ln 644 root bin
+f none usr/lib/fm/libfmevent.so.1 755 root bin
+s none usr/lib/fm/libfmevent.so=libfmevent.so.1
+f none usr/lib/fm/llib-lfmevent 644 root bin
+f none usr/lib/fm/llib-lfmevent.ln 644 root bin
 f none usr/lib/fm/libtopo.so.1 755 root bin
 s none usr/lib/fm/libtopo.so=libtopo.so.1
 f none usr/lib/fm/llib-ltopo 644 root bin
--- a/usr/src/pkgdefs/SUNWfmd/prototype_i386	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/pkgdefs/SUNWfmd/prototype_i386	Thu Nov 19 15:28:11 2009 +1100
@@ -33,6 +33,8 @@
 s none usr/lib/fm/amd64/libdiagcode.so=./libdiagcode.so.1
 f none usr/lib/fm/amd64/libdiskstatus.so.1 755 root bin
 s none usr/lib/fm/amd64/libdiskstatus.so=./libdiskstatus.so.1
+f none usr/lib/fm/amd64/libfmevent.so.1 755 root bin
+s none usr/lib/fm/amd64/libfmevent.so=./libfmevent.so.1
 f none usr/lib/fm/amd64/libfmd_adm.so.1 755 root bin
 s none usr/lib/fm/amd64/libfmd_adm.so=./libfmd_adm.so.1
 f none usr/lib/fm/amd64/libfmd_log.so.1 755 root bin
@@ -50,6 +52,7 @@
 f none usr/lib/fm/amd64/llib-lfmd_log.ln 644 root bin
 f none usr/lib/fm/amd64/llib-lfmd_msg.ln 644 root bin
 f none usr/lib/fm/amd64/llib-lfmd_snmp.ln 644 root bin
+f none usr/lib/fm/amd64/llib-lfmevent.ln 644 root bin
 f none usr/lib/fm/amd64/llib-ltopo.ln 644 root bin
 d none usr/lib/fm/fmd/schemes/amd64 755 root bin
 f none usr/lib/fm/fmd/schemes/amd64/cpu.so 555 root bin
--- a/usr/src/pkgdefs/SUNWfmd/prototype_sparc	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/pkgdefs/SUNWfmd/prototype_sparc	Thu Nov 19 15:28:11 2009 +1100
@@ -56,6 +56,8 @@
 s none usr/lib/fm/sparcv9/libdiagcode.so=libdiagcode.so.1
 f none usr/lib/fm/sparcv9/libdiskstatus.so.1 755 root bin
 s none usr/lib/fm/sparcv9/libdiskstatus.so=libdiskstatus.so.1
+f none usr/lib/fm/sparcv9/libfmevent.so.1 755 root bin
+s none usr/lib/fm/sparcv9/libfmevent.so=libfmevent.so.1
 f none usr/lib/fm/sparcv9/llib-lfmd_agent.ln 644 root bin
 f none usr/lib/fm/sparcv9/llib-ldiagcode.ln 644 root bin
 f none usr/lib/fm/sparcv9/llib-ldiskstatus.ln 644 root bin
@@ -77,6 +79,7 @@
 f none usr/lib/fm/sparcv9/libfmd_snmp.so.1 755 root bin
 s none usr/lib/fm/sparcv9/libfmd_snmp.so=libfmd_snmp.so.1
 f none usr/lib/fm/sparcv9/llib-lfmd_snmp.ln 644 root bin
+f none usr/lib/fm/sparcv9/llib-lfmevent.ln 644 root bin
 f none usr/lib/fm/sparcv9/libtopo.so.1 755 root bin
 s none usr/lib/fm/sparcv9/libtopo.so=libtopo.so.1
 f none usr/lib/fm/sparcv9/llib-ltopo.ln 644 root bin
--- a/usr/src/uts/common/io/bofi.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/uts/common/io/bofi.c	Thu Nov 19 15:28:11 2009 +1100
@@ -784,7 +784,7 @@
 	if (reset_bus_ops(nexus_name, &save_bus_ops) == 0)
 		return (DDI_FAILURE);
 
-	sysevent_evc_unbind(bofi_error_chan);
+	(void) sysevent_evc_unbind(bofi_error_chan);
 
 	mutex_destroy(&clone_tab_mutex);
 	mutex_destroy(&bofi_mutex);
--- a/usr/src/uts/common/os/evchannels.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/uts/common/os/evchannels.c	Thu Nov 19 15:28:11 2009 +1100
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * This file contains the source of the general purpose event channel extension
  * to the sysevent framework. This implementation is made up mainly of four
@@ -70,6 +68,9 @@
 #define	EVCH_EVQ_EVCOUNT(x)	((&(x)->eq_eventq)->sq_count)
 #define	EVCH_EVQ_HIGHWM(x)	((&(x)->eq_eventq)->sq_highwm)
 
+#define	CH_HOLD_PEND		1
+#define	CH_HOLD_PEND_INDEF	2
+
 struct evch_globals {
 	evch_dlist_t evch_list;
 	kmutex_t evch_list_lock;
@@ -320,7 +321,8 @@
 		 */
 		mutex_enter(&chp->ch_mutex);
 		ASSERT(chp->ch_bindings == 0);
-		ASSERT(evch_dl_getnum(&chp->ch_subscr) != 0);
+		ASSERT(evch_dl_getnum(&chp->ch_subscr) != 0 ||
+		    chp->ch_holdpend == CH_HOLD_PEND_INDEF);
 
 		/* Forcibly unsubscribe each remaining subscription */
 		while ((sdp = evch_dl_next(&chp->ch_subscr, NULL)) != NULL) {
@@ -800,17 +802,50 @@
 }
 
 /*
+ * Simple wildcarded match test of event class string 'class' to
+ * wildcarded subscription string 'pat'.  Recursive only if
+ * 'pat' includes a wildcard, otherwise essentially just strcmp.
+ */
+static int
+evch_clsmatch(char *class, const char *pat)
+{
+	char c;
+
+	do {
+		if ((c = *pat++) == '\0')
+			return (*class == '\0');
+
+		if (c == '*') {
+			while (*pat == '*')
+				pat++; /* consecutive *'s can be collapsed */
+
+			if (*pat == '\0')
+				return (1);
+
+			while (*class != '\0') {
+				if (evch_clsmatch(class++, pat) != 0)
+					return (1);
+			}
+
+			return (0);
+		}
+	} while (c == *class++);
+
+	return (0);
+}
+
+/*
  * Sysevent filter callback routine. Enables event delivery only if it matches
- * the event class string given by parameter cookie.
+ * the event class pattern string given by parameter cookie.
  */
 static int
 evch_class_filter(void *ev, void *cookie)
 {
-	char *class = (char *)cookie;
+	const char *pat = (const char *)cookie;
 
-	if (class == NULL || strcmp(SE_CLASS_NAME(ev), class) == 0) {
+	if (pat == NULL || evch_clsmatch(SE_CLASS_NAME(ev), pat))
 		return (EVQ_DELIVER);
-	}
+
 	return (EVQ_IGNORE);
 }
 
@@ -1061,8 +1096,13 @@
 			p->ch_maxsubscr = EVCH_MAX_SUBSCRIPTIONS;
 			p->ch_maxbinds = evch_bindings_max;
 			p->ch_ctime = gethrestime_sec();
-			if (flags & EVCH_HOLD_PEND) {
-				p->ch_holdpend = 1;
+
+			if (flags & (EVCH_HOLD_PEND | EVCH_HOLD_PEND_INDEF)) {
+				if (flags & EVCH_HOLD_PEND_INDEF)
+					p->ch_holdpend = CH_HOLD_PEND_INDEF;
+				else
+					p->ch_holdpend = CH_HOLD_PEND;
+
 				evch_evq_stop(p->ch_queue);
 			}
 
@@ -1114,9 +1154,13 @@
 	ASSERT(chp->ch_bindings > 0);
 	chp->ch_bindings--;
 	kmem_free(bp, sizeof (evch_bind_t));
-	if (chp->ch_bindings == 0 && evch_dl_getnum(&chp->ch_subscr) == 0) {
+	if (chp->ch_bindings == 0 && evch_dl_getnum(&chp->ch_subscr) == 0 &&
+	    (chp->ch_nevents == 0 || chp->ch_holdpend != CH_HOLD_PEND_INDEF)) {
 		/*
-		 * No more bindings or persistent subscriber, destroy channel.
+		 * No more bindings and no persistent subscriber(s).  If there
+		 * are no events in the channel then destroy the channel;
+		 * otherwise destroy the channel only if we're not holding
+		 * pending events indefinitely.
 		 */
 		mutex_exit(&chp->ch_mutex);
 		evch_dl_del(&eg->evch_list, &chp->ch_link);
@@ -1131,6 +1175,23 @@
 	mutex_exit(&eg->evch_list_lock);
 }
 
+static int
+wildcard_count(const char *class)
+{
+	int count = 0;
+	char c;
+
+	if (class == NULL)
+		return (0);
+
+	while ((c = *class++) != '\0') {
+		if (c == '*')
+			count++;
+	}
+
+	return (count);
+}
+
 /*
  * Subscribe to a channel. dtype is either EVCH_DELKERN for kernel callbacks
  * or EVCH_DELDOOR for door upcall delivery to user land. Depending on dtype
@@ -1155,6 +1216,14 @@
 	 */
 	if (flags & ~(EVCH_SUB_KEEP | EVCH_SUB_DUMP))
 		return (EINVAL);
+
+	/*
+	 * Enforce a limit on the number of wildcards allowed in the class
+	 * subscription string (limits recursion in pattern matching).
+	 */
+	if (wildcard_count(class) > EVCH_WILDCARD_MAX)
+		return (EINVAL);
+
 	/*
 	 * Check if we have already a subscription with that name and if we
 	 * have to reconnect the subscriber to a persistent subscription.
@@ -1757,7 +1826,7 @@
 	return (evch_chbind(ch_name, (evch_bind_t **)scpp, flags));
 }
 
-void
+int
 sysevent_evc_unbind(evchan_t *scp)
 {
 	evch_bind_t *bp = (evch_bind_t *)scp;
@@ -1765,6 +1834,8 @@
 	ASSERT(scp != NULL);
 	evch_chunsubscribe(bp, NULL, 0);
 	evch_chunbind(bp);
+
+	return (0);
 }
 
 int
@@ -1784,7 +1855,7 @@
 	    (void *)callb, cookie, 0, 0));
 }
 
-void
+int
 sysevent_evc_unsubscribe(evchan_t *scp, const char *sid)
 {
 	ASSERT(scp != NULL && sid != NULL);
@@ -1792,6 +1863,8 @@
 		sid = NULL;
 	}
 	evch_chunsubscribe((evch_bind_t *)scp, sid, 0);
+
+	return (0);
 }
 
 /*
--- a/usr/src/uts/common/os/fm.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/uts/common/os/fm.c	Thu Nov 19 15:28:11 2009 +1100
@@ -517,10 +517,10 @@
 	if (sysevent_evc_publish(error_chan, EC_FM, ESC_FM_ERROR,
 	    SUNW_VENDOR, FM_PUB, ereport, evc_flag) != 0) {
 		atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1);
-		sysevent_evc_unbind(error_chan);
+		(void) sysevent_evc_unbind(error_chan);
 		return;
 	}
-	sysevent_evc_unbind(error_chan);
+	(void) sysevent_evc_unbind(error_chan);
 }
 
 /*
--- a/usr/src/uts/common/sys/door.h	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/uts/common/sys/door.h	Thu Nov 19 15:28:11 2009 +1100
@@ -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.
  */
 
@@ -30,12 +30,29 @@
 #ifndef	_SYS_DOOR_H
 #define	_SYS_DOOR_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
 
+/*
+ * Attributes associated with doors.
+ */
+
+/* Attributes originally obtained from door_create operation */
+#define	DOOR_UNREF	0x01	/* Deliver an unref notification with door */
+#define	DOOR_PRIVATE	0x02	/* Use a private pool of server threads */
+#define	DOOR_UNREF_MULTI 0x10	/* Deliver unref notification more than once */
+#define	DOOR_REFUSE_DESC 0x40	/* Do not accept descriptors from callers */
+#define	DOOR_NO_CANCEL	0x80	/* No server thread cancel on client abort */
+#define	DOOR_NO_DEPLETION_CB 0x100 /* No thread create callbacks on depletion */
+
+/* Attributes (additional) returned with door_info and door_desc_t data */
+#define	DOOR_LOCAL	0x04	/* Descriptor is local to current process */
+#define	DOOR_REVOKED	0x08	/* Door has been revoked */
+#define	DOOR_IS_UNREF	0x20	/* Door is currently unreferenced */
+#define	DOOR_PRIVCREATE	0x200	/* Door has a private thread creation func */
+#define	DOOR_DEPLETION_CB 0x400	/* Set only during depletion callbacks */
+
 #if !defined(_ASM)
 
 #include <sys/types.h>
@@ -62,25 +79,10 @@
 /* Door descriptor passed to door_info to get current thread's binding */
 #define	DOOR_QUERY -2
 
-/*
- * Attributes associated with doors.
- */
-
-/* Attributes originally obtained from door_create operation */
-#define	DOOR_UNREF	0x01	/* Deliver an unref notification with door */
-#define	DOOR_PRIVATE	0x02	/* Use a private pool of server threads */
-#define	DOOR_UNREF_MULTI 0x10	/* Deliver unref notification more than once */
-#define	DOOR_REFUSE_DESC 0x40	/* Do not accept descriptors from callers */
-#define	DOOR_NO_CANCEL	0x80	/* No server thread cancel on client abort */
-
-/* Attributes (additional) returned with door_info and door_desc_t data */
-#define	DOOR_LOCAL	0x04	/* Descriptor is local to current process */
-#define	DOOR_REVOKED	0x08	/* Door has been revoked */
-#define	DOOR_IS_UNREF	0x20	/* Door is currently unreferenced */
-
 /* Masks of applicable flags */
 #define	DOOR_CREATE_MASK	(DOOR_UNREF | DOOR_PRIVATE | \
-	    DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | DOOR_NO_CANCEL)
+	    DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | DOOR_NO_CANCEL | \
+	    DOOR_NO_DEPLETION_CB | DOOR_PRIVCREATE)
 #define	DOOR_KI_CREATE_MASK	(DOOR_UNREF | DOOR_UNREF_MULTI)
 
 /* Mask of above attributes */
--- a/usr/src/uts/common/sys/sysevent.h	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/uts/common/sys/sysevent.h	Thu Nov 19 15:28:11 2009 +1100
@@ -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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_SYS_SYSEVENT_H
 #define	_SYS_SYSEVENT_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/nvpair.h>
 
 #ifdef	__cplusplus
@@ -164,18 +162,50 @@
 #define	EVCH_QWAIT	0x0008	/* Wait for slot in event queue */
 
 /*
- * Meaning of flags for subscribe/unsubscribe. Bits 0 to 7 are dedicated to
- * the consolidation private interface.
+ * Meaning of flags for subscribe. Bits 8 to 15 are dedicated to
+ * the consolidation private interface, so flags defined here are restricted
+ * to the LSB.
+ *
+ * EVCH_SUB_KEEP indicates that this subscription should persist even if
+ * this subscriber id should die unexpectedly; matching events will be
+ * queued (up to a limit) and will be delivered if/when we restart again
+ * with the same subscriber id.
  */
-#define	EVCH_SUB_KEEP		0x0001
+#define	EVCH_SUB_KEEP		0x01
+
+/*
+ * Subscriptions may be wildcarded, but we limit the number of
+ * wildcards permitted.
+ */
+#define	EVCH_WILDCARD_MAX	10
+
+/*
+ * Used in unsubscribe to indicate all subscriber ids for a channel.
+ */
 #define	EVCH_ALLSUB		"all_subs"
 
 /*
  * Meaning of flags parameter of channel bind function
+ *
+ * EVCH_CREAT indicates to create a channel if not already present.
+ *
+ * EVCH_HOLD_PEND indicates that events should be published to this
+ * channel even if there are no matching subscribers present; when
+ * a subscriber belatedly binds to the channel and registers their
+ * subscriptions they will receive events that predate their bind.
+ * If the channel is closed, however, with no remaining bindings then
+ * the channel is destroyed.
+ *
+ * EVCH_HOLD_PEND_INDEF is a stronger version of EVCH_HOLD_PEND -
+ * even if the channel has no remaining bindings it will not be
+ * destroyed so long as events remain unconsumed.  This is suitable for
+ * use with short-lived event producers that may bind to (create) the
+ * channel and exit before the intended consumer has started.
  */
-#define	EVCH_CREAT		0x0001	/* Create a channel if not present */
+#define	EVCH_CREAT		0x0001
 #define	EVCH_HOLD_PEND		0x0002
-#define	EVCH_B_FLAGS		0x0003	/* All valid bits */
+#define	EVCH_HOLD_PEND_INDEF	0x0004
+#define	EVCH_B_FLAGS		0x0007	/* All valid bits */
 
 /*
  * Meaning of commands of evc_control function
@@ -186,37 +216,62 @@
 #define	EVCH_CMD_LAST		 EVCH_SET_CHAN_LEN	/* Last command */
 
 /*
- * Event channel interface definitions
+ * Shared user/kernel event channel interface definitions
  */
-int sysevent_evc_bind(const char *, evchan_t **, uint32_t);
-void sysevent_evc_unbind(evchan_t *);
-int sysevent_evc_subscribe(evchan_t *, const char *, const char *,
+extern int sysevent_evc_bind(const char *, evchan_t **, uint32_t);
+extern int sysevent_evc_unbind(evchan_t *);
+extern int sysevent_evc_subscribe(evchan_t *, const char *, const char *,
     int (*)(sysevent_t *, void *), void *, uint32_t);
-void sysevent_evc_unsubscribe(evchan_t *, const char *);
-int sysevent_evc_publish(evchan_t *, const char *, const char *,
+extern int sysevent_evc_unsubscribe(evchan_t *, const char *);
+extern int sysevent_evc_publish(evchan_t *, const char *, const char *,
     const char *, const char *, nvlist_t *, uint32_t);
-int sysevent_evc_control(evchan_t *, int, ...);
+extern int sysevent_evc_control(evchan_t *, int, ...);
+
+#ifndef	_KERNEL
+
+/*
+ * Userland-only event channel interfaces
+ */
+
+#include <door.h>
+
+typedef struct sysevent_subattr sysevent_subattr_t;
 
-#ifdef	_KERNEL
+extern sysevent_subattr_t *sysevent_subattr_alloc(void);
+extern void sysevent_subattr_free(sysevent_subattr_t *);
+
+extern void sysevent_subattr_thrattr(sysevent_subattr_t *, pthread_attr_t *);
+extern void sysevent_subattr_sigmask(sysevent_subattr_t *, sigset_t *);
+
+extern void sysevent_subattr_thrcreate(sysevent_subattr_t *,
+    door_xcreate_server_func_t *, void *);
+extern void sysevent_subattr_thrsetup(sysevent_subattr_t *,
+    door_xcreate_thrsetup_func_t *, void *);
+
+extern int sysevent_evc_xsubscribe(evchan_t *, const char *, const char *,
+    int (*)(sysevent_t *, void *), void *, uint32_t, sysevent_subattr_t *);
+
+#else
 
 /*
  * Kernel log_event interfaces.
  */
-int log_sysevent(sysevent_t *, int, sysevent_id_t *);
+extern int log_sysevent(sysevent_t *, int, sysevent_id_t *);
 
-sysevent_t *sysevent_alloc(char *, char *, char *, int);
-void sysevent_free(sysevent_t *);
-int sysevent_add_attr(sysevent_attr_list_t **, char *, sysevent_value_t *, int);
-void sysevent_free_attr(sysevent_attr_list_t *);
-int sysevent_attach_attributes(sysevent_t *, sysevent_attr_list_t *);
-void sysevent_detach_attributes(sysevent_t *);
-char *sysevent_get_class_name(sysevent_t *);
-char *sysevent_get_subclass_name(sysevent_t *);
-uint64_t sysevent_get_seq(sysevent_t *);
-void sysevent_get_time(sysevent_t *, hrtime_t *);
-size_t sysevent_get_size(sysevent_t *);
-char *sysevent_get_pub(sysevent_t *);
-int sysevent_get_attr_list(sysevent_t *, nvlist_t **);
+extern sysevent_t *sysevent_alloc(char *, char *, char *, int);
+extern void sysevent_free(sysevent_t *);
+extern int sysevent_add_attr(sysevent_attr_list_t **, char *,
+    sysevent_value_t *, int);
+extern void sysevent_free_attr(sysevent_attr_list_t *);
+extern int sysevent_attach_attributes(sysevent_t *, sysevent_attr_list_t *);
+extern void sysevent_detach_attributes(sysevent_t *);
+extern char *sysevent_get_class_name(sysevent_t *);
+extern char *sysevent_get_subclass_name(sysevent_t *);
+extern uint64_t sysevent_get_seq(sysevent_t *);
+extern void sysevent_get_time(sysevent_t *, hrtime_t *);
+extern size_t sysevent_get_size(sysevent_t *);
+extern char *sysevent_get_pub(sysevent_t *);
+extern int sysevent_get_attr_list(sysevent_t *, nvlist_t **);
 
 #endif	/* _KERNEL */
 
--- a/usr/src/uts/common/sys/sysevent_impl.h	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/uts/common/sys/sysevent_impl.h	Thu Nov 19 15:28:11 2009 +1100
@@ -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.
  */
 
@@ -476,9 +476,9 @@
 
 /*
  * Project private flags for sysevent_evc_subscribe. Bits 0 to 7 are reserved
- * for the consolidation private interface.
+ * for the consolidation private interface, so we must use bits 8-15 here.
  */
-#define	EVCH_SUB_DUMP		0x0100
+#define	EVCH_SUB_DUMP		(0x01 << 8)
 
 /*
  * Permission flags
--- a/usr/src/uts/sun4v/io/vlds.c	Thu Nov 19 11:57:44 2009 +0800
+++ b/usr/src/uts/sun4v/io/vlds.c	Thu Nov 19 15:28:11 2009 +1100
@@ -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.
  */
 
@@ -407,7 +407,7 @@
 	}
 
 	if (sp->evchan) {
-		sysevent_evc_unbind(sp->evchan);
+		(void) sysevent_evc_unbind(sp->evchan);
 		sp->evchan = NULL;
 	}