xref: /titanic_50/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
149b225e1SGavin Maltby /*
249b225e1SGavin Maltby  * CDDL HEADER START
349b225e1SGavin Maltby  *
449b225e1SGavin Maltby  * The contents of this file are subject to the terms of the
549b225e1SGavin Maltby  * Common Development and Distribution License (the "License").
649b225e1SGavin Maltby  * You may not use this file except in compliance with the License.
749b225e1SGavin Maltby  *
849b225e1SGavin Maltby  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
949b225e1SGavin Maltby  * or http://www.opensolaris.org/os/licensing.
1049b225e1SGavin Maltby  * See the License for the specific language governing permissions
1149b225e1SGavin Maltby  * and limitations under the License.
1249b225e1SGavin Maltby  *
1349b225e1SGavin Maltby  * When distributing Covered Code, include this CDDL HEADER in each
1449b225e1SGavin Maltby  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1549b225e1SGavin Maltby  * If applicable, add the following below this CDDL HEADER, with the
1649b225e1SGavin Maltby  * fields enclosed by brackets "[]" replaced with your own identifying
1749b225e1SGavin Maltby  * information: Portions Copyright [yyyy] [name of copyright owner]
1849b225e1SGavin Maltby  *
1949b225e1SGavin Maltby  * CDDL HEADER END
2049b225e1SGavin Maltby  */
2149b225e1SGavin Maltby 
2249b225e1SGavin Maltby /*
23*f6e214c7SGavin Maltby  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2449b225e1SGavin Maltby  */
2549b225e1SGavin Maltby 
2649b225e1SGavin Maltby /*
2749b225e1SGavin Maltby  * FMA event subscription interfaces - subscribe to FMA protocol
2849b225e1SGavin Maltby  * from outside the fault manager.
2949b225e1SGavin Maltby  */
3049b225e1SGavin Maltby 
3149b225e1SGavin Maltby #include <sys/types.h>
3249b225e1SGavin Maltby #include <atomic.h>
3349b225e1SGavin Maltby #include <libsysevent.h>
3449b225e1SGavin Maltby #include <libuutil.h>
3549b225e1SGavin Maltby #include <pthread.h>
3649b225e1SGavin Maltby #include <stdarg.h>
3749b225e1SGavin Maltby #include <stdlib.h>
3849b225e1SGavin Maltby #include <string.h>
3949b225e1SGavin Maltby #include <strings.h>
4049b225e1SGavin Maltby #include <unistd.h>
41*f6e214c7SGavin Maltby #include <fm/libtopo.h>
4249b225e1SGavin Maltby 
4349b225e1SGavin Maltby #include <fm/libfmevent.h>
4449b225e1SGavin Maltby 
4549b225e1SGavin Maltby #include "fmev_impl.h"
46*f6e214c7SGavin Maltby 
47*f6e214c7SGavin Maltby static topo_hdl_t *g_topohdl;
4849b225e1SGavin Maltby 
4949b225e1SGavin Maltby typedef struct {
5049b225e1SGavin Maltby 	struct fmev_hdl_cmn sh_cmn;
5149b225e1SGavin Maltby 	evchan_t *sh_binding;
5249b225e1SGavin Maltby 	uu_avl_pool_t *sh_pool;
5349b225e1SGavin Maltby 	uu_avl_t *sh_avl;
5449b225e1SGavin Maltby 	uint32_t sh_subcnt;
5549b225e1SGavin Maltby 	uint32_t sh_flags;
5649b225e1SGavin Maltby 	sysevent_subattr_t *sh_attr;
5749b225e1SGavin Maltby 	pthread_mutex_t sh_lock;
5849b225e1SGavin Maltby 	pthread_mutex_t sh_srlz_lock;
5949b225e1SGavin Maltby } fmev_shdl_impl_t;
6049b225e1SGavin Maltby 
6149b225e1SGavin Maltby #define	HDL2IHDL(hdl)	((fmev_shdl_impl_t *)(hdl))
6249b225e1SGavin Maltby #define	IHDL2HDL(ihdl)	((fmev_shdl_t)(ihdl))
6349b225e1SGavin Maltby 
6449b225e1SGavin Maltby #define	_FMEV_SHMAGIC	0x5368446c	/* ShDl */
6549b225e1SGavin Maltby #define	FMEV_SHDL_VALID(ihdl)	((ihdl)->sh_cmn.hc_magic == _FMEV_SHMAGIC)
6649b225e1SGavin Maltby 
6749b225e1SGavin Maltby #define	SHDL_FL_SERIALIZE	0x1
6849b225e1SGavin Maltby 
69*f6e214c7SGavin Maltby #define	FMEV_API_ENTER(hdl, v) \
70*f6e214c7SGavin Maltby 	fmev_api_enter(&HDL2IHDL(hdl)->sh_cmn, LIBFMEVENT_VERSION_##v)
7149b225e1SGavin Maltby 
7249b225e1SGavin Maltby /*
7349b225e1SGavin Maltby  * For each subscription on a handle we add a node to an avl tree
7449b225e1SGavin Maltby  * to track subscriptions.
7549b225e1SGavin Maltby  */
7649b225e1SGavin Maltby 
7749b225e1SGavin Maltby #define	FMEV_SID_SZ	(16 + 1)	/* Matches MAX_SUBID_LEN */
7849b225e1SGavin Maltby 
7949b225e1SGavin Maltby struct fmev_subinfo {
8049b225e1SGavin Maltby 	uu_avl_node_t si_node;
8149b225e1SGavin Maltby 	fmev_shdl_impl_t *si_ihdl;
8249b225e1SGavin Maltby 	char si_pat[FMEV_MAX_CLASS];
8349b225e1SGavin Maltby 	char si_sid[FMEV_SID_SZ];
8449b225e1SGavin Maltby 	fmev_cbfunc_t *si_cb;
8549b225e1SGavin Maltby 	void *si_cbarg;
8649b225e1SGavin Maltby };
8749b225e1SGavin Maltby 
8849b225e1SGavin Maltby struct fmev_hdl_cmn *
fmev_shdl_cmn(fmev_shdl_t hdl)8949b225e1SGavin Maltby fmev_shdl_cmn(fmev_shdl_t hdl)
9049b225e1SGavin Maltby {
9149b225e1SGavin Maltby 	return (&HDL2IHDL(hdl)->sh_cmn);
9249b225e1SGavin Maltby }
9349b225e1SGavin Maltby 
9449b225e1SGavin Maltby static int
shdlctl_start(fmev_shdl_impl_t * ihdl)9549b225e1SGavin Maltby shdlctl_start(fmev_shdl_impl_t *ihdl)
9649b225e1SGavin Maltby {
9749b225e1SGavin Maltby 	(void) pthread_mutex_lock(&ihdl->sh_lock);
9849b225e1SGavin Maltby 
9949b225e1SGavin Maltby 	if (ihdl->sh_subcnt == 0) {
10049b225e1SGavin Maltby 		return (1);	/* lock still held */
10149b225e1SGavin Maltby 	} else {
10249b225e1SGavin Maltby 		(void) pthread_mutex_unlock(&ihdl->sh_lock);
10349b225e1SGavin Maltby 		return (0);
10449b225e1SGavin Maltby 	}
10549b225e1SGavin Maltby }
10649b225e1SGavin Maltby 
10749b225e1SGavin Maltby static void
shdlctl_end(fmev_shdl_impl_t * ihdl)10849b225e1SGavin Maltby shdlctl_end(fmev_shdl_impl_t *ihdl)
10949b225e1SGavin Maltby {
11049b225e1SGavin Maltby 	(void) pthread_mutex_unlock(&ihdl->sh_lock);
11149b225e1SGavin Maltby }
11249b225e1SGavin Maltby 
11349b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_serialize(fmev_shdl_t hdl)11449b225e1SGavin Maltby fmev_shdlctl_serialize(fmev_shdl_t hdl)
11549b225e1SGavin Maltby {
11649b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
11749b225e1SGavin Maltby 
118*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
11949b225e1SGavin Maltby 		return (fmev_errno);
12049b225e1SGavin Maltby 
12149b225e1SGavin Maltby 	if (!shdlctl_start(ihdl))
12249b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_BUSY));
12349b225e1SGavin Maltby 
12449b225e1SGavin Maltby 	if (!(ihdl->sh_flags & SHDL_FL_SERIALIZE)) {
12549b225e1SGavin Maltby 		(void) pthread_mutex_init(&ihdl->sh_srlz_lock, NULL);
12649b225e1SGavin Maltby 		ihdl->sh_flags |= SHDL_FL_SERIALIZE;
12749b225e1SGavin Maltby 	}
12849b225e1SGavin Maltby 
12949b225e1SGavin Maltby 	shdlctl_end(ihdl);
13049b225e1SGavin Maltby 	return (fmev_seterr(FMEV_SUCCESS));
13149b225e1SGavin Maltby }
13249b225e1SGavin Maltby 
13349b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_thrattr(fmev_shdl_t hdl,pthread_attr_t * attr)13449b225e1SGavin Maltby fmev_shdlctl_thrattr(fmev_shdl_t hdl, pthread_attr_t *attr)
13549b225e1SGavin Maltby {
13649b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
13749b225e1SGavin Maltby 
138*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
13949b225e1SGavin Maltby 		return (fmev_errno);
14049b225e1SGavin Maltby 
14149b225e1SGavin Maltby 	if (!shdlctl_start(ihdl))
14249b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_BUSY));
14349b225e1SGavin Maltby 
14449b225e1SGavin Maltby 	sysevent_subattr_thrattr(ihdl->sh_attr, attr);
14549b225e1SGavin Maltby 
14649b225e1SGavin Maltby 	shdlctl_end(ihdl);
14749b225e1SGavin Maltby 	return (fmev_seterr(FMEV_SUCCESS));
14849b225e1SGavin Maltby }
14949b225e1SGavin Maltby 
15049b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_sigmask(fmev_shdl_t hdl,sigset_t * set)15149b225e1SGavin Maltby fmev_shdlctl_sigmask(fmev_shdl_t hdl, sigset_t *set)
15249b225e1SGavin Maltby {
15349b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
15449b225e1SGavin Maltby 
155*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
15649b225e1SGavin Maltby 		return (fmev_errno);
15749b225e1SGavin Maltby 
15849b225e1SGavin Maltby 	if (!shdlctl_start(ihdl))
15949b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_BUSY));
16049b225e1SGavin Maltby 
16149b225e1SGavin Maltby 	sysevent_subattr_sigmask(ihdl->sh_attr, set);
16249b225e1SGavin Maltby 
16349b225e1SGavin Maltby 	shdlctl_end(ihdl);
16449b225e1SGavin Maltby 	return (fmev_seterr(FMEV_SUCCESS));
16549b225e1SGavin Maltby }
16649b225e1SGavin Maltby 
16749b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_thrsetup(fmev_shdl_t hdl,door_xcreate_thrsetup_func_t * func,void * cookie)16849b225e1SGavin Maltby fmev_shdlctl_thrsetup(fmev_shdl_t hdl, door_xcreate_thrsetup_func_t *func,
16949b225e1SGavin Maltby     void *cookie)
17049b225e1SGavin Maltby {
17149b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
17249b225e1SGavin Maltby 
173*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
17449b225e1SGavin Maltby 		return (fmev_errno);
17549b225e1SGavin Maltby 
17649b225e1SGavin Maltby 	if (!shdlctl_start(ihdl))
17749b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_BUSY));
17849b225e1SGavin Maltby 
17949b225e1SGavin Maltby 	sysevent_subattr_thrsetup(ihdl->sh_attr, func, cookie);
18049b225e1SGavin Maltby 
18149b225e1SGavin Maltby 	shdlctl_end(ihdl);
18249b225e1SGavin Maltby 	return (fmev_seterr(FMEV_SUCCESS));
18349b225e1SGavin Maltby }
18449b225e1SGavin Maltby 
18549b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_thrcreate(fmev_shdl_t hdl,door_xcreate_server_func_t * func,void * cookie)18649b225e1SGavin Maltby fmev_shdlctl_thrcreate(fmev_shdl_t hdl, door_xcreate_server_func_t *func,
18749b225e1SGavin Maltby     void *cookie)
18849b225e1SGavin Maltby {
18949b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
19049b225e1SGavin Maltby 
191*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
19249b225e1SGavin Maltby 		return (fmev_errno);
19349b225e1SGavin Maltby 
19449b225e1SGavin Maltby 	if (!shdlctl_start(ihdl))
19549b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_BUSY));
19649b225e1SGavin Maltby 
19749b225e1SGavin Maltby 	sysevent_subattr_thrcreate(ihdl->sh_attr, func, cookie);
19849b225e1SGavin Maltby 
19949b225e1SGavin Maltby 	shdlctl_end(ihdl);
20049b225e1SGavin Maltby 	return (fmev_seterr(FMEV_SUCCESS));
20149b225e1SGavin Maltby }
20249b225e1SGavin Maltby 
20349b225e1SGavin Maltby /*
20449b225e1SGavin Maltby  * Our door service function.  We return 0 regardless so that the kernel
20549b225e1SGavin Maltby  * does not keep either retrying (EAGAIN) or bleat to cmn_err.
20649b225e1SGavin Maltby  */
20749b225e1SGavin Maltby 
20849b225e1SGavin Maltby uint64_t fmev_proxy_cb_inval;
20949b225e1SGavin Maltby uint64_t fmev_proxy_cb_enomem;
21049b225e1SGavin Maltby 
21149b225e1SGavin Maltby int
fmev_proxy_cb(sysevent_t * sep,void * arg)21249b225e1SGavin Maltby fmev_proxy_cb(sysevent_t *sep, void *arg)
21349b225e1SGavin Maltby {
21449b225e1SGavin Maltby 	struct fmev_subinfo *sip = arg;
21549b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = sip->si_ihdl;
21649b225e1SGavin Maltby 	nvlist_t *nvl;
21749b225e1SGavin Maltby 	char *class;
21849b225e1SGavin Maltby 	fmev_t ev;
21949b225e1SGavin Maltby 
22049b225e1SGavin Maltby 	if (sip == NULL || sip->si_cb == NULL) {
22149b225e1SGavin Maltby 		fmev_proxy_cb_inval++;
22249b225e1SGavin Maltby 		return (0);
22349b225e1SGavin Maltby 	}
22449b225e1SGavin Maltby 
22549b225e1SGavin Maltby 	if ((ev = fmev_sysev2fmev(IHDL2HDL(ihdl), sep, &class, &nvl)) == NULL) {
22649b225e1SGavin Maltby 		fmev_proxy_cb_enomem++;
22749b225e1SGavin Maltby 		return (0);
22849b225e1SGavin Maltby 	}
22949b225e1SGavin Maltby 
23049b225e1SGavin Maltby 	if (ihdl->sh_flags & SHDL_FL_SERIALIZE)
23149b225e1SGavin Maltby 		(void) pthread_mutex_lock(&ihdl->sh_srlz_lock);
23249b225e1SGavin Maltby 
23349b225e1SGavin Maltby 	sip->si_cb(ev, class, nvl, sip->si_cbarg);
23449b225e1SGavin Maltby 
23549b225e1SGavin Maltby 	if (ihdl->sh_flags & SHDL_FL_SERIALIZE)
23649b225e1SGavin Maltby 		(void) pthread_mutex_unlock(&ihdl->sh_srlz_lock);
23749b225e1SGavin Maltby 
23849b225e1SGavin Maltby 	fmev_rele(ev);	/* release hold obtained in fmev_sysev2fmev */
23949b225e1SGavin Maltby 
24049b225e1SGavin Maltby 	return (0);
24149b225e1SGavin Maltby }
24249b225e1SGavin Maltby 
24349b225e1SGavin Maltby static volatile uint32_t fmev_subid;
24449b225e1SGavin Maltby 
24549b225e1SGavin Maltby fmev_err_t
fmev_shdl_subscribe(fmev_shdl_t hdl,const char * pat,fmev_cbfunc_t func,void * funcarg)24649b225e1SGavin Maltby fmev_shdl_subscribe(fmev_shdl_t hdl, const char *pat, fmev_cbfunc_t func,
24749b225e1SGavin Maltby     void *funcarg)
24849b225e1SGavin Maltby {
24949b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
25049b225e1SGavin Maltby 	struct fmev_subinfo *sip;
25149b225e1SGavin Maltby 	uu_avl_index_t idx;
25249b225e1SGavin Maltby 	uint64_t nsid;
25349b225e1SGavin Maltby 	int serr;
25449b225e1SGavin Maltby 
255*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
25649b225e1SGavin Maltby 		return (fmev_errno);
25749b225e1SGavin Maltby 
25849b225e1SGavin Maltby 	if (pat == NULL || func == NULL)
25949b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_API));
26049b225e1SGavin Maltby 
26149b225e1SGavin Maltby 	/*
26249b225e1SGavin Maltby 	 * Empty class patterns are illegal, as is the sysevent magic for
26349b225e1SGavin Maltby 	 * all classes.  Also validate class length.
26449b225e1SGavin Maltby 	 */
26549b225e1SGavin Maltby 	if (*pat == '\0' || strncmp(pat, EC_ALL, sizeof (EC_ALL)) == 0 ||
26649b225e1SGavin Maltby 	    strncmp(pat, EC_SUB_ALL, sizeof (EC_SUB_ALL)) == 0 ||
26749b225e1SGavin Maltby 	    strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS)
26849b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_BADCLASS));
26949b225e1SGavin Maltby 
27049b225e1SGavin Maltby 	if ((sip = fmev_shdl_zalloc(hdl, sizeof (*sip))) == NULL)
27149b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_ALLOC));
27249b225e1SGavin Maltby 
27349b225e1SGavin Maltby 	(void) strncpy(sip->si_pat, pat, sizeof (sip->si_pat));
27449b225e1SGavin Maltby 
27549b225e1SGavin Maltby 	uu_avl_node_init(sip, &sip->si_node, ihdl->sh_pool);
27649b225e1SGavin Maltby 
27749b225e1SGavin Maltby 	(void) pthread_mutex_lock(&ihdl->sh_lock);
27849b225e1SGavin Maltby 
27949b225e1SGavin Maltby 	if (uu_avl_find(ihdl->sh_avl, sip, NULL, &idx) != NULL) {
28049b225e1SGavin Maltby 		(void) pthread_mutex_unlock(&ihdl->sh_lock);
28149b225e1SGavin Maltby 		fmev_shdl_free(hdl, sip, sizeof (*sip));
28249b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_DUPLICATE));
28349b225e1SGavin Maltby 	}
28449b225e1SGavin Maltby 
28549b225e1SGavin Maltby 	/*
28649b225e1SGavin Maltby 	 * Generate a subscriber id for GPEC that is unique to this
28749b225e1SGavin Maltby 	 * subscription.  There is no provision for persistent
28849b225e1SGavin Maltby 	 * subscribers.  The subscriber id must be unique within
28949b225e1SGavin Maltby 	 * this zone.
29049b225e1SGavin Maltby 	 */
29149b225e1SGavin Maltby 	nsid = (uint64_t)getpid() << 32 | atomic_inc_32_nv(&fmev_subid);
29249b225e1SGavin Maltby 	(void) snprintf(sip->si_sid, sizeof (sip->si_sid), "%llx", nsid);
29349b225e1SGavin Maltby 
29449b225e1SGavin Maltby 	sip->si_ihdl = ihdl;
29549b225e1SGavin Maltby 	sip->si_cb = func;
29649b225e1SGavin Maltby 	sip->si_cbarg = funcarg;
29749b225e1SGavin Maltby 
29849b225e1SGavin Maltby 	if ((serr = sysevent_evc_xsubscribe(ihdl->sh_binding, sip->si_sid,
29949b225e1SGavin Maltby 	    sip->si_pat, fmev_proxy_cb, sip, 0, ihdl->sh_attr)) != 0) {
30049b225e1SGavin Maltby 		fmev_err_t err;
30149b225e1SGavin Maltby 
30249b225e1SGavin Maltby 		(void) pthread_mutex_unlock(&ihdl->sh_lock);
30349b225e1SGavin Maltby 		fmev_shdl_free(hdl, sip, sizeof (*sip));
30449b225e1SGavin Maltby 
30549b225e1SGavin Maltby 		switch (serr) {
30649b225e1SGavin Maltby 		case ENOMEM:
30749b225e1SGavin Maltby 			err = FMEVERR_MAX_SUBSCRIBERS;
30849b225e1SGavin Maltby 			break;
30949b225e1SGavin Maltby 
31049b225e1SGavin Maltby 		default:
31149b225e1SGavin Maltby 			err = FMEVERR_INTERNAL;
31249b225e1SGavin Maltby 			break;
31349b225e1SGavin Maltby 		}
31449b225e1SGavin Maltby 
31549b225e1SGavin Maltby 		return (fmev_seterr(err));
31649b225e1SGavin Maltby 	}
31749b225e1SGavin Maltby 
31849b225e1SGavin Maltby 	uu_avl_insert(ihdl->sh_avl, sip, idx);
31949b225e1SGavin Maltby 	ihdl->sh_subcnt++;
32049b225e1SGavin Maltby 
32149b225e1SGavin Maltby 	(void) pthread_mutex_unlock(&ihdl->sh_lock);
32249b225e1SGavin Maltby 
32349b225e1SGavin Maltby 	return (fmev_seterr(FMEV_SUCCESS));
32449b225e1SGavin Maltby }
32549b225e1SGavin Maltby 
32649b225e1SGavin Maltby static int
fmev_subinfo_fini(fmev_shdl_impl_t * ihdl,struct fmev_subinfo * sip,boolean_t doavl)32749b225e1SGavin Maltby fmev_subinfo_fini(fmev_shdl_impl_t *ihdl, struct fmev_subinfo *sip,
32849b225e1SGavin Maltby     boolean_t doavl)
32949b225e1SGavin Maltby {
33049b225e1SGavin Maltby 	int err;
33149b225e1SGavin Maltby 
33249b225e1SGavin Maltby 	ASSERT(sip->si_ihdl == ihdl);
33349b225e1SGavin Maltby 
33449b225e1SGavin Maltby 	err = sysevent_evc_unsubscribe(ihdl->sh_binding, sip->si_sid);
33549b225e1SGavin Maltby 
33649b225e1SGavin Maltby 	if (err == 0) {
33749b225e1SGavin Maltby 		if (doavl) {
33849b225e1SGavin Maltby 			uu_avl_remove(ihdl->sh_avl, sip);
33949b225e1SGavin Maltby 			uu_avl_node_fini(sip, &sip->si_node, ihdl->sh_pool);
34049b225e1SGavin Maltby 		}
34149b225e1SGavin Maltby 		fmev_shdl_free(IHDL2HDL(ihdl), sip, sizeof (*sip));
34249b225e1SGavin Maltby 		ihdl->sh_subcnt--;
34349b225e1SGavin Maltby 	}
34449b225e1SGavin Maltby 
34549b225e1SGavin Maltby 	return (err);
34649b225e1SGavin Maltby }
34749b225e1SGavin Maltby 
34849b225e1SGavin Maltby fmev_err_t
fmev_shdl_unsubscribe(fmev_shdl_t hdl,const char * pat)34949b225e1SGavin Maltby fmev_shdl_unsubscribe(fmev_shdl_t hdl, const char *pat)
35049b225e1SGavin Maltby {
35149b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
35249b225e1SGavin Maltby 	fmev_err_t rv = FMEVERR_NOMATCH;
35349b225e1SGavin Maltby 	struct fmev_subinfo *sip;
35449b225e1SGavin Maltby 	struct fmev_subinfo si;
35549b225e1SGavin Maltby 	int err;
35649b225e1SGavin Maltby 
357*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
35849b225e1SGavin Maltby 		return (fmev_errno);
35949b225e1SGavin Maltby 
36049b225e1SGavin Maltby 	if (pat == NULL)
36149b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_API));
36249b225e1SGavin Maltby 
36349b225e1SGavin Maltby 	if (*pat == '\0' || strncmp(pat, EVCH_ALLSUB, sizeof (EC_ALL)) == 0 ||
36449b225e1SGavin Maltby 	    strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS)
36549b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_BADCLASS));
36649b225e1SGavin Maltby 
36749b225e1SGavin Maltby 	(void) strncpy(si.si_pat, pat, sizeof (si.si_pat));
36849b225e1SGavin Maltby 
36949b225e1SGavin Maltby 	(void) pthread_mutex_lock(&ihdl->sh_lock);
37049b225e1SGavin Maltby 
37149b225e1SGavin Maltby 	if ((sip = uu_avl_find(ihdl->sh_avl, &si, NULL, NULL)) != NULL) {
37249b225e1SGavin Maltby 		if ((err = fmev_subinfo_fini(ihdl, sip, B_TRUE)) == 0) {
37349b225e1SGavin Maltby 			rv = FMEV_SUCCESS;
37449b225e1SGavin Maltby 		} else {
37549b225e1SGavin Maltby 			/*
37649b225e1SGavin Maltby 			 * Return an API error if the unsubscribe was
37749b225e1SGavin Maltby 			 * attempted from within a door callback invocation;
37849b225e1SGavin Maltby 			 * other errors should not happen.
37949b225e1SGavin Maltby 			 */
38049b225e1SGavin Maltby 			rv = (err == EDEADLK) ? FMEVERR_API : FMEVERR_INTERNAL;
38149b225e1SGavin Maltby 		}
38249b225e1SGavin Maltby 	}
38349b225e1SGavin Maltby 
38449b225e1SGavin Maltby 	(void) pthread_mutex_unlock(&ihdl->sh_lock);
38549b225e1SGavin Maltby 
38649b225e1SGavin Maltby 	return (fmev_seterr(rv));
38749b225e1SGavin Maltby }
38849b225e1SGavin Maltby 
38949b225e1SGavin Maltby void *
fmev_shdl_alloc(fmev_shdl_t hdl,size_t sz)39049b225e1SGavin Maltby fmev_shdl_alloc(fmev_shdl_t hdl, size_t sz)
39149b225e1SGavin Maltby {
39249b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
39349b225e1SGavin Maltby 
394*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
395*f6e214c7SGavin Maltby 		return (NULL);
39649b225e1SGavin Maltby 
39749b225e1SGavin Maltby 	return (ihdl->sh_cmn.hc_alloc(sz));
39849b225e1SGavin Maltby }
39949b225e1SGavin Maltby 
40049b225e1SGavin Maltby void *
fmev_shdl_zalloc(fmev_shdl_t hdl,size_t sz)40149b225e1SGavin Maltby fmev_shdl_zalloc(fmev_shdl_t hdl, size_t sz)
40249b225e1SGavin Maltby {
40349b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
40449b225e1SGavin Maltby 
405*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
406*f6e214c7SGavin Maltby 		return (NULL);
40749b225e1SGavin Maltby 
40849b225e1SGavin Maltby 	return (ihdl->sh_cmn.hc_zalloc(sz));
40949b225e1SGavin Maltby }
41049b225e1SGavin Maltby 
41149b225e1SGavin Maltby void
fmev_shdl_free(fmev_shdl_t hdl,void * buf,size_t sz)41249b225e1SGavin Maltby fmev_shdl_free(fmev_shdl_t hdl, void *buf, size_t sz)
41349b225e1SGavin Maltby {
41449b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
41549b225e1SGavin Maltby 
416*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
417*f6e214c7SGavin Maltby 		return;
41849b225e1SGavin Maltby 
41949b225e1SGavin Maltby 	ihdl->sh_cmn.hc_free(buf, sz);
42049b225e1SGavin Maltby }
42149b225e1SGavin Maltby 
422*f6e214c7SGavin Maltby char *
fmev_shdl_strdup(fmev_shdl_t hdl,char * src)423*f6e214c7SGavin Maltby fmev_shdl_strdup(fmev_shdl_t hdl, char *src)
424*f6e214c7SGavin Maltby {
425*f6e214c7SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
426*f6e214c7SGavin Maltby 	size_t srclen;
427*f6e214c7SGavin Maltby 	char *dst;
428*f6e214c7SGavin Maltby 
429*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 2))
430*f6e214c7SGavin Maltby 		return (NULL);
431*f6e214c7SGavin Maltby 
432*f6e214c7SGavin Maltby 	srclen = strlen(src);
433*f6e214c7SGavin Maltby 
434*f6e214c7SGavin Maltby 	if ((dst = ihdl->sh_cmn.hc_alloc(srclen + 1)) == NULL) {
435*f6e214c7SGavin Maltby 		(void) fmev_seterr(FMEVERR_ALLOC);
436*f6e214c7SGavin Maltby 		return (NULL);
437*f6e214c7SGavin Maltby 	}
438*f6e214c7SGavin Maltby 
439*f6e214c7SGavin Maltby 	(void) strncpy(dst, src, srclen);
440*f6e214c7SGavin Maltby 	dst[srclen] = '\0';
441*f6e214c7SGavin Maltby 	return (dst);
442*f6e214c7SGavin Maltby }
443*f6e214c7SGavin Maltby 
444*f6e214c7SGavin Maltby void
fmev_shdl_strfree(fmev_shdl_t hdl,char * buf)445*f6e214c7SGavin Maltby fmev_shdl_strfree(fmev_shdl_t hdl, char *buf)
446*f6e214c7SGavin Maltby {
447*f6e214c7SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
448*f6e214c7SGavin Maltby 
449*f6e214c7SGavin Maltby 	(void) FMEV_API_ENTER(hdl, 2);
450*f6e214c7SGavin Maltby 
451*f6e214c7SGavin Maltby 	ihdl->sh_cmn.hc_free(buf, strlen(buf) + 1);
452*f6e214c7SGavin Maltby }
453*f6e214c7SGavin Maltby 
45449b225e1SGavin Maltby int
fmev_shdl_valid(fmev_shdl_t hdl)45549b225e1SGavin Maltby fmev_shdl_valid(fmev_shdl_t hdl)
45649b225e1SGavin Maltby {
45749b225e1SGavin Maltby 	return (FMEV_SHDL_VALID(HDL2IHDL(hdl)));
45849b225e1SGavin Maltby }
45949b225e1SGavin Maltby 
46049b225e1SGavin Maltby /*ARGSUSED*/
46149b225e1SGavin Maltby static int
fmev_keycmp(const void * l,const void * r,void * arg)46249b225e1SGavin Maltby fmev_keycmp(const void *l, const void *r, void *arg)
46349b225e1SGavin Maltby {
46449b225e1SGavin Maltby 	struct fmev_subinfo *left = (struct fmev_subinfo *)l;
46549b225e1SGavin Maltby 	struct fmev_subinfo *right = (struct fmev_subinfo *)r;
46649b225e1SGavin Maltby 
46749b225e1SGavin Maltby 	return (strncmp(left->si_pat, right->si_pat, FMEV_MAX_CLASS));
46849b225e1SGavin Maltby }
46949b225e1SGavin Maltby 
47049b225e1SGavin Maltby fmev_shdl_t
fmev_shdl_init(uint32_t caller_version,void * (* hdlalloc)(size_t),void * (* hdlzalloc)(size_t),void (* hdlfree)(void *,size_t))47149b225e1SGavin Maltby fmev_shdl_init(uint32_t caller_version, void *(*hdlalloc)(size_t),
47249b225e1SGavin Maltby     void *(*hdlzalloc)(size_t), void (*hdlfree)(void *, size_t))
47349b225e1SGavin Maltby {
47449b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl;
47549b225e1SGavin Maltby 	struct fmev_hdl_cmn hc;
47649b225e1SGavin Maltby 	const char *chan_name;
47749b225e1SGavin Maltby 	int err;
47849b225e1SGavin Maltby 
47949b225e1SGavin Maltby 	hc.hc_magic = _FMEV_SHMAGIC;
48049b225e1SGavin Maltby 	hc.hc_api_vers = caller_version;
48149b225e1SGavin Maltby 	hc.hc_alloc = hdlalloc ? hdlalloc : dflt_alloc;
48249b225e1SGavin Maltby 	hc.hc_zalloc = hdlzalloc ? hdlzalloc : dflt_zalloc;
48349b225e1SGavin Maltby 	hc.hc_free = hdlfree ? hdlfree : dflt_free;
48449b225e1SGavin Maltby 
48549b225e1SGavin Maltby 	if (!fmev_api_init(&hc))
48649b225e1SGavin Maltby 		return (NULL);	/* error type set */
48749b225e1SGavin Maltby 
48849b225e1SGavin Maltby 	if (!((hdlalloc == NULL && hdlzalloc == NULL && hdlfree == NULL) ||
48949b225e1SGavin Maltby 	    (hdlalloc != NULL && hdlzalloc != NULL && hdlfree != NULL))) {
49049b225e1SGavin Maltby 		(void) fmev_seterr(FMEVERR_API);
49149b225e1SGavin Maltby 		return (NULL);
49249b225e1SGavin Maltby 	}
49349b225e1SGavin Maltby 
49449b225e1SGavin Maltby 	if (hdlzalloc == NULL)
49549b225e1SGavin Maltby 		ihdl = dflt_zalloc(sizeof (*ihdl));
49649b225e1SGavin Maltby 	else
49749b225e1SGavin Maltby 		ihdl = hdlzalloc(sizeof (*ihdl));
49849b225e1SGavin Maltby 
49949b225e1SGavin Maltby 	if (ihdl == NULL) {
50049b225e1SGavin Maltby 		(void) fmev_seterr(FMEVERR_ALLOC);
50149b225e1SGavin Maltby 		return (NULL);
50249b225e1SGavin Maltby 	}
50349b225e1SGavin Maltby 
50449b225e1SGavin Maltby 	ihdl->sh_cmn = hc;
50549b225e1SGavin Maltby 
50649b225e1SGavin Maltby 	if ((ihdl->sh_attr = sysevent_subattr_alloc()) == NULL) {
50749b225e1SGavin Maltby 		err = FMEVERR_ALLOC;
50849b225e1SGavin Maltby 		goto error;
50949b225e1SGavin Maltby 	}
51049b225e1SGavin Maltby 
51149b225e1SGavin Maltby 	(void) pthread_mutex_init(&ihdl->sh_lock, NULL);
51249b225e1SGavin Maltby 
51349b225e1SGavin Maltby 	/*
51449b225e1SGavin Maltby 	 * For simulation purposes we allow an environment variable
51549b225e1SGavin Maltby 	 * to provide a different channel name.
51649b225e1SGavin Maltby 	 */
51749b225e1SGavin Maltby 	if ((chan_name = getenv("FMD_SNOOP_CHANNEL")) == NULL)
51849b225e1SGavin Maltby 		chan_name = FMD_SNOOP_CHANNEL;
51949b225e1SGavin Maltby 
52049b225e1SGavin Maltby 	/*
52149b225e1SGavin Maltby 	 * Try to bind to the event channel. If it's not already present,
52249b225e1SGavin Maltby 	 * attempt to create the channel so that we can startup before
52349b225e1SGavin Maltby 	 * the event producer (who will also apply choices such as
52449b225e1SGavin Maltby 	 * channel depth when they bind to the channel).
52549b225e1SGavin Maltby 	 */
52649b225e1SGavin Maltby 	if (sysevent_evc_bind(chan_name, &ihdl->sh_binding,
52749b225e1SGavin Maltby 	    EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0) {
52849b225e1SGavin Maltby 		switch (errno) {
52949b225e1SGavin Maltby 		case EINVAL:
53049b225e1SGavin Maltby 		default:
53149b225e1SGavin Maltby 			err = FMEVERR_INTERNAL;
53249b225e1SGavin Maltby 			break;
53349b225e1SGavin Maltby 		case ENOMEM:
53449b225e1SGavin Maltby 			err = FMEVERR_ALLOC;
53549b225e1SGavin Maltby 			break;
53649b225e1SGavin Maltby 		case EPERM:
53749b225e1SGavin Maltby 			err = FMEVERR_NOPRIV;
53849b225e1SGavin Maltby 			break;
53949b225e1SGavin Maltby 		}
54049b225e1SGavin Maltby 		goto error;
54149b225e1SGavin Maltby 	}
54249b225e1SGavin Maltby 
54349b225e1SGavin Maltby 	if ((ihdl->sh_pool = uu_avl_pool_create("subinfo_pool",
54449b225e1SGavin Maltby 	    sizeof (struct fmev_subinfo),
54549b225e1SGavin Maltby 	    offsetof(struct fmev_subinfo, si_node), fmev_keycmp,
54649b225e1SGavin Maltby 	    UU_AVL_POOL_DEBUG)) == NULL) {
54749b225e1SGavin Maltby 		err = FMEVERR_INTERNAL;
54849b225e1SGavin Maltby 		goto error;
54949b225e1SGavin Maltby 	}
55049b225e1SGavin Maltby 
55149b225e1SGavin Maltby 	if ((ihdl->sh_avl = uu_avl_create(ihdl->sh_pool, NULL,
55249b225e1SGavin Maltby 	    UU_DEFAULT)) == NULL) {
55349b225e1SGavin Maltby 		err = FMEVERR_INTERNAL;
55449b225e1SGavin Maltby 		goto error;
55549b225e1SGavin Maltby 	}
55649b225e1SGavin Maltby 
55749b225e1SGavin Maltby 	return (IHDL2HDL(ihdl));
55849b225e1SGavin Maltby 
55949b225e1SGavin Maltby error:
56049b225e1SGavin Maltby 	(void) fmev_shdl_fini(IHDL2HDL(ihdl));
56149b225e1SGavin Maltby 	(void) fmev_seterr(err);
56249b225e1SGavin Maltby 	return (NULL);
56349b225e1SGavin Maltby }
56449b225e1SGavin Maltby 
56549b225e1SGavin Maltby fmev_err_t
fmev_shdl_getauthority(fmev_shdl_t hdl,nvlist_t ** nvlp)566*f6e214c7SGavin Maltby fmev_shdl_getauthority(fmev_shdl_t hdl, nvlist_t **nvlp)
567*f6e214c7SGavin Maltby {
568*f6e214c7SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
569*f6e214c7SGavin Maltby 	nvlist_t *propnvl;
570*f6e214c7SGavin Maltby 	fmev_err_t rc;
571*f6e214c7SGavin Maltby 
572*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 2))
573*f6e214c7SGavin Maltby 		return (fmev_errno);
574*f6e214c7SGavin Maltby 
575*f6e214c7SGavin Maltby 	(void) pthread_mutex_lock(&ihdl->sh_lock);
576*f6e214c7SGavin Maltby 
577*f6e214c7SGavin Maltby 	if (sysevent_evc_getpropnvl(ihdl->sh_binding, &propnvl) != 0) {
578*f6e214c7SGavin Maltby 		*nvlp = NULL;
579*f6e214c7SGavin Maltby 		(void) pthread_mutex_unlock(&ihdl->sh_lock);
580*f6e214c7SGavin Maltby 		return (fmev_seterr(FMEVERR_UNKNOWN));
581*f6e214c7SGavin Maltby 	}
582*f6e214c7SGavin Maltby 
583*f6e214c7SGavin Maltby 	if (propnvl == NULL) {
584*f6e214c7SGavin Maltby 		rc = FMEVERR_BUSY;	/* Other end has not bound */
585*f6e214c7SGavin Maltby 	} else {
586*f6e214c7SGavin Maltby 		nvlist_t *auth;
587*f6e214c7SGavin Maltby 
588*f6e214c7SGavin Maltby 		if (nvlist_lookup_nvlist(propnvl, "fmdauth", &auth) == 0) {
589*f6e214c7SGavin Maltby 			rc = (nvlist_dup(auth, nvlp, 0) == 0) ? FMEV_SUCCESS :
590*f6e214c7SGavin Maltby 			    FMEVERR_ALLOC;
591*f6e214c7SGavin Maltby 		} else {
592*f6e214c7SGavin Maltby 			rc = FMEVERR_INTERNAL;
593*f6e214c7SGavin Maltby 		}
594*f6e214c7SGavin Maltby 		nvlist_free(propnvl);
595*f6e214c7SGavin Maltby 	}
596*f6e214c7SGavin Maltby 
597*f6e214c7SGavin Maltby 	(void) pthread_mutex_unlock(&ihdl->sh_lock);
598*f6e214c7SGavin Maltby 
599*f6e214c7SGavin Maltby 	if (rc != FMEV_SUCCESS) {
600*f6e214c7SGavin Maltby 		*nvlp = NULL;
601*f6e214c7SGavin Maltby 		(void) fmev_seterr(rc);
602*f6e214c7SGavin Maltby 	}
603*f6e214c7SGavin Maltby 
604*f6e214c7SGavin Maltby 	return (rc);
605*f6e214c7SGavin Maltby }
606*f6e214c7SGavin Maltby 
607*f6e214c7SGavin Maltby char *
fmev_shdl_nvl2str(fmev_shdl_t hdl,nvlist_t * nvl)608*f6e214c7SGavin Maltby fmev_shdl_nvl2str(fmev_shdl_t hdl, nvlist_t *nvl)
609*f6e214c7SGavin Maltby {
610*f6e214c7SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
611*f6e214c7SGavin Maltby 	char *fmri, *fmricp;
612*f6e214c7SGavin Maltby 	fmev_err_t err;
613*f6e214c7SGavin Maltby 	int topoerr;
614*f6e214c7SGavin Maltby 
615*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 2))
616*f6e214c7SGavin Maltby 		return (NULL);
617*f6e214c7SGavin Maltby 
618*f6e214c7SGavin Maltby 	if (g_topohdl == NULL) {
619*f6e214c7SGavin Maltby 		(void) pthread_mutex_lock(&ihdl->sh_lock);
620*f6e214c7SGavin Maltby 		if (g_topohdl == NULL)
621*f6e214c7SGavin Maltby 			g_topohdl = topo_open(TOPO_VERSION, NULL, &topoerr);
622*f6e214c7SGavin Maltby 		(void) pthread_mutex_unlock(&ihdl->sh_lock);
623*f6e214c7SGavin Maltby 
624*f6e214c7SGavin Maltby 		if (g_topohdl == NULL) {
625*f6e214c7SGavin Maltby 			(void) fmev_seterr(FMEVERR_INTERNAL);
626*f6e214c7SGavin Maltby 			return (NULL);
627*f6e214c7SGavin Maltby 		}
628*f6e214c7SGavin Maltby 	}
629*f6e214c7SGavin Maltby 
630*f6e214c7SGavin Maltby 	if (topo_fmri_nvl2str(g_topohdl, nvl, &fmri, &topoerr) == 0) {
631*f6e214c7SGavin Maltby 		fmricp = fmev_shdl_strdup(hdl, fmri);
632*f6e214c7SGavin Maltby 		topo_hdl_strfree(g_topohdl, fmri);
633*f6e214c7SGavin Maltby 		return (fmricp);	/* fmev_errno set if strdup failed */
634*f6e214c7SGavin Maltby 	}
635*f6e214c7SGavin Maltby 
636*f6e214c7SGavin Maltby 	switch (topoerr) {
637*f6e214c7SGavin Maltby 	case ETOPO_FMRI_NOMEM:
638*f6e214c7SGavin Maltby 		err = FMEVERR_ALLOC;
639*f6e214c7SGavin Maltby 		break;
640*f6e214c7SGavin Maltby 
641*f6e214c7SGavin Maltby 	case ETOPO_FMRI_MALFORM:
642*f6e214c7SGavin Maltby 	case ETOPO_METHOD_NOTSUP:
643*f6e214c7SGavin Maltby 	case ETOPO_METHOD_INVAL:
644*f6e214c7SGavin Maltby 	default:
645*f6e214c7SGavin Maltby 		err = FMEVERR_INVALIDARG;
646*f6e214c7SGavin Maltby 		break;
647*f6e214c7SGavin Maltby 	}
648*f6e214c7SGavin Maltby 
649*f6e214c7SGavin Maltby 	(void) fmev_seterr(err);
650*f6e214c7SGavin Maltby 	return (NULL);
651*f6e214c7SGavin Maltby }
652*f6e214c7SGavin Maltby 
653*f6e214c7SGavin Maltby fmev_err_t
fmev_shdl_fini(fmev_shdl_t hdl)65449b225e1SGavin Maltby fmev_shdl_fini(fmev_shdl_t hdl)
65549b225e1SGavin Maltby {
65649b225e1SGavin Maltby 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
65749b225e1SGavin Maltby 
658*f6e214c7SGavin Maltby 	if (!FMEV_API_ENTER(hdl, 1))
659*f6e214c7SGavin Maltby 		return (fmev_errno);
66049b225e1SGavin Maltby 
66149b225e1SGavin Maltby 	(void) pthread_mutex_lock(&ihdl->sh_lock);
66249b225e1SGavin Maltby 
66349b225e1SGavin Maltby 	/*
66449b225e1SGavin Maltby 	 * Verify that we are not in callback context - return an API
66549b225e1SGavin Maltby 	 * error if we are.
66649b225e1SGavin Maltby 	 */
66749b225e1SGavin Maltby 	if (sysevent_evc_unsubscribe(ihdl->sh_binding, "invalidsid") ==
66849b225e1SGavin Maltby 	    EDEADLK) {
66949b225e1SGavin Maltby 		(void) pthread_mutex_unlock(&ihdl->sh_lock);
67049b225e1SGavin Maltby 		return (fmev_seterr(FMEVERR_API));
67149b225e1SGavin Maltby 	}
67249b225e1SGavin Maltby 
67349b225e1SGavin Maltby 	if (ihdl->sh_avl) {
67449b225e1SGavin Maltby 		void *cookie = NULL;
67549b225e1SGavin Maltby 		struct fmev_subinfo *sip;
67649b225e1SGavin Maltby 
67749b225e1SGavin Maltby 		while ((sip = uu_avl_teardown(ihdl->sh_avl, &cookie)) != NULL)
67849b225e1SGavin Maltby 			(void) fmev_subinfo_fini(ihdl, sip, B_FALSE);
67949b225e1SGavin Maltby 
68049b225e1SGavin Maltby 		uu_avl_destroy(ihdl->sh_avl);
68149b225e1SGavin Maltby 		ihdl->sh_avl = NULL;
68249b225e1SGavin Maltby 	}
68349b225e1SGavin Maltby 
68449b225e1SGavin Maltby 	ASSERT(ihdl->sh_subcnt == 0);
68549b225e1SGavin Maltby 
68649b225e1SGavin Maltby 	if (ihdl->sh_binding) {
68749b225e1SGavin Maltby 		(void) sysevent_evc_unbind(ihdl->sh_binding);
68849b225e1SGavin Maltby 		ihdl->sh_binding = NULL;
68949b225e1SGavin Maltby 	}
69049b225e1SGavin Maltby 
69149b225e1SGavin Maltby 	if (ihdl->sh_pool) {
69249b225e1SGavin Maltby 		uu_avl_pool_destroy(ihdl->sh_pool);
69349b225e1SGavin Maltby 		ihdl->sh_pool = NULL;
69449b225e1SGavin Maltby 	}
69549b225e1SGavin Maltby 
69649b225e1SGavin Maltby 	if (ihdl->sh_attr) {
69749b225e1SGavin Maltby 		sysevent_subattr_free(ihdl->sh_attr);
69849b225e1SGavin Maltby 		ihdl->sh_attr = NULL;
69949b225e1SGavin Maltby 	}
70049b225e1SGavin Maltby 
70149b225e1SGavin Maltby 	ihdl->sh_cmn.hc_magic = 0;
70249b225e1SGavin Maltby 
703*f6e214c7SGavin Maltby 	if (g_topohdl) {
704*f6e214c7SGavin Maltby 		topo_close(g_topohdl);
705*f6e214c7SGavin Maltby 		g_topohdl = NULL;
706*f6e214c7SGavin Maltby 	}
707*f6e214c7SGavin Maltby 
70849b225e1SGavin Maltby 	(void) pthread_mutex_unlock(&ihdl->sh_lock);
70949b225e1SGavin Maltby 	(void) pthread_mutex_destroy(&ihdl->sh_lock);
71049b225e1SGavin Maltby 
71149b225e1SGavin Maltby 	fmev_shdl_free(hdl, hdl, sizeof (*ihdl));
71249b225e1SGavin Maltby 
71349b225e1SGavin Maltby 	fmev_api_freetsd();
71449b225e1SGavin Maltby 
71549b225e1SGavin Maltby 	return (fmev_seterr(FMEV_SUCCESS));
71649b225e1SGavin Maltby }
717