xref: /titanic_51/usr/src/cmd/fm/fmd/common/fmd_scheme.c (revision 35f59e50e9ef37e6f1bd3b018289d678d5551cab)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
519e1255fScy152378  * Common Development and Distribution License (the "License").
619e1255fScy152378  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21d9638e54Smws 
227c478bd9Sstevel@tonic-gate /*
23e58a33b6SStephen Hanson  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <limits.h>
277c478bd9Sstevel@tonic-gate #include <stddef.h>
287c478bd9Sstevel@tonic-gate #include <unistd.h>
297c478bd9Sstevel@tonic-gate #include <dlfcn.h>
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <fmd_alloc.h>
327c478bd9Sstevel@tonic-gate #include <fmd_error.h>
337c478bd9Sstevel@tonic-gate #include <fmd_subr.h>
347c478bd9Sstevel@tonic-gate #include <fmd_string.h>
357c478bd9Sstevel@tonic-gate #include <fmd_scheme.h>
367c478bd9Sstevel@tonic-gate #include <fmd_fmri.h>
377c478bd9Sstevel@tonic-gate #include <fmd_module.h>
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #include <fmd.h>
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate /*
427c478bd9Sstevel@tonic-gate  * The fmd resource scheme, used for fmd modules, must be implemented here for
437c478bd9Sstevel@tonic-gate  * the benefit of fmd-self-diagnosis and also in schemes/fmd for fmdump(1M).
447c478bd9Sstevel@tonic-gate  */
457c478bd9Sstevel@tonic-gate ssize_t
467c478bd9Sstevel@tonic-gate fmd_scheme_fmd_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
477c478bd9Sstevel@tonic-gate {
487c478bd9Sstevel@tonic-gate 	char *name;
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0)
517c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate 	return (snprintf(buf, buflen,
547c478bd9Sstevel@tonic-gate 	    "%s:///module/%s", FM_FMRI_SCHEME_FMD, name));
557c478bd9Sstevel@tonic-gate }
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate static int
587c478bd9Sstevel@tonic-gate fmd_scheme_fmd_present(nvlist_t *nvl)
597c478bd9Sstevel@tonic-gate {
607c478bd9Sstevel@tonic-gate 	char *name, *version;
617c478bd9Sstevel@tonic-gate 	fmd_module_t *mp;
62e58a33b6SStephen Hanson 	int rv = 1;
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0 ||
657c478bd9Sstevel@tonic-gate 	    nvlist_lookup_string(nvl, FM_FMRI_FMD_VERSION, &version) != 0)
667c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
677c478bd9Sstevel@tonic-gate 
68*35f59e50SStephen Hanson 	if (!fmd.d_loaded)
69*35f59e50SStephen Hanson 		return (1);
70*35f59e50SStephen Hanson 
717c478bd9Sstevel@tonic-gate 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
7219e1255fScy152378 		rv = mp->mod_vers != NULL &&
7319e1255fScy152378 		    strcmp(mp->mod_vers, version) == 0;
747c478bd9Sstevel@tonic-gate 		fmd_module_rele(mp);
757c478bd9Sstevel@tonic-gate 	}
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate 	return (rv);
787c478bd9Sstevel@tonic-gate }
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate static int
8125c6ff4bSstephh fmd_scheme_fmd_replaced(nvlist_t *nvl)
8225c6ff4bSstephh {
8325c6ff4bSstephh 	char *name, *version;
8425c6ff4bSstephh 	fmd_module_t *mp;
85e58a33b6SStephen Hanson 	int rv = 1;
8625c6ff4bSstephh 
8725c6ff4bSstephh 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0 ||
8825c6ff4bSstephh 	    nvlist_lookup_string(nvl, FM_FMRI_FMD_VERSION, &version) != 0)
8925c6ff4bSstephh 		return (fmd_fmri_set_errno(EINVAL));
9025c6ff4bSstephh 
91*35f59e50SStephen Hanson 	if (!fmd.d_loaded)
92*35f59e50SStephen Hanson 		return (FMD_OBJ_STATE_UNKNOWN);
93*35f59e50SStephen Hanson 
9425c6ff4bSstephh 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
9525c6ff4bSstephh 		rv = mp->mod_vers != NULL &&
9625c6ff4bSstephh 		    strcmp(mp->mod_vers, version) == 0;
9725c6ff4bSstephh 		fmd_module_rele(mp);
9825c6ff4bSstephh 	}
9925c6ff4bSstephh 
100e58a33b6SStephen Hanson 	return (rv ? FMD_OBJ_STATE_STILL_PRESENT : FMD_OBJ_STATE_REPLACED);
10125c6ff4bSstephh }
10225c6ff4bSstephh 
10325c6ff4bSstephh static int
10425c6ff4bSstephh fmd_scheme_fmd_service_state(nvlist_t *nvl)
10525c6ff4bSstephh {
10625c6ff4bSstephh 	char *name;
10725c6ff4bSstephh 	fmd_module_t *mp;
10825c6ff4bSstephh 	int rv = 1;
10925c6ff4bSstephh 
11025c6ff4bSstephh 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0)
11125c6ff4bSstephh 		return (fmd_fmri_set_errno(EINVAL));
11225c6ff4bSstephh 
113*35f59e50SStephen Hanson 	if (!fmd.d_loaded)
114*35f59e50SStephen Hanson 		return (FMD_SERVICE_STATE_UNKNOWN);
115*35f59e50SStephen Hanson 
11625c6ff4bSstephh 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
11725c6ff4bSstephh 		rv = mp->mod_error != 0;
11825c6ff4bSstephh 		fmd_module_rele(mp);
11925c6ff4bSstephh 	}
12025c6ff4bSstephh 
12125c6ff4bSstephh 	return (rv ? FMD_SERVICE_STATE_UNUSABLE : FMD_SERVICE_STATE_OK);
12225c6ff4bSstephh }
12325c6ff4bSstephh 
12425c6ff4bSstephh static int
1257c478bd9Sstevel@tonic-gate fmd_scheme_fmd_unusable(nvlist_t *nvl)
1267c478bd9Sstevel@tonic-gate {
1277c478bd9Sstevel@tonic-gate 	char *name;
1287c478bd9Sstevel@tonic-gate 	fmd_module_t *mp;
1297c478bd9Sstevel@tonic-gate 	int rv = 1;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0)
1327c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
1337c478bd9Sstevel@tonic-gate 
134*35f59e50SStephen Hanson 	if (!fmd.d_loaded)
135*35f59e50SStephen Hanson 		return (0);
136*35f59e50SStephen Hanson 
1377c478bd9Sstevel@tonic-gate 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
1387c478bd9Sstevel@tonic-gate 		rv = mp->mod_error != 0;
1397c478bd9Sstevel@tonic-gate 		fmd_module_rele(mp);
1407c478bd9Sstevel@tonic-gate 	}
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 	return (rv);
1437c478bd9Sstevel@tonic-gate }
1447c478bd9Sstevel@tonic-gate 
145d9638e54Smws /*ARGSUSED*/
146d9638e54Smws static nvlist_t *
147d9638e54Smws fmd_scheme_notranslate(nvlist_t *fmri, nvlist_t *auth)
148d9638e54Smws {
149d9638e54Smws 	(void) nvlist_xdup(fmri, &fmri, &fmd.d_nva);
150d9638e54Smws 	return (fmri);
151d9638e54Smws }
152d9638e54Smws 
1537c478bd9Sstevel@tonic-gate static long
1547c478bd9Sstevel@tonic-gate fmd_scheme_notsup(void)
1557c478bd9Sstevel@tonic-gate {
1567c478bd9Sstevel@tonic-gate 	return (fmd_set_errno(EFMD_FMRI_NOTSUP));
1577c478bd9Sstevel@tonic-gate }
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate static int
1607c478bd9Sstevel@tonic-gate fmd_scheme_nop(void)
1617c478bd9Sstevel@tonic-gate {
1627c478bd9Sstevel@tonic-gate 	return (0);
1637c478bd9Sstevel@tonic-gate }
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate /*
1667c478bd9Sstevel@tonic-gate  * Default values for the scheme ops.  If a scheme function is not defined in
1677c478bd9Sstevel@tonic-gate  * the module, then this operation is implemented using the default function.
1687c478bd9Sstevel@tonic-gate  */
1697c478bd9Sstevel@tonic-gate static const fmd_scheme_ops_t _fmd_scheme_default_ops = {
1707c478bd9Sstevel@tonic-gate 	(int (*)())fmd_scheme_nop,		/* sop_init */
1717c478bd9Sstevel@tonic-gate 	(void (*)())fmd_scheme_nop,		/* sop_fini */
1727c478bd9Sstevel@tonic-gate 	(ssize_t (*)())fmd_scheme_notsup,	/* sop_nvl2str */
1737c478bd9Sstevel@tonic-gate 	(int (*)())fmd_scheme_nop,		/* sop_expand */
1747c478bd9Sstevel@tonic-gate 	(int (*)())fmd_scheme_notsup,		/* sop_present */
17525c6ff4bSstephh 	(int (*)())fmd_scheme_notsup,		/* sop_replaced */
17625c6ff4bSstephh 	(int (*)())fmd_scheme_notsup,		/* sop_service_state */
1777c478bd9Sstevel@tonic-gate 	(int (*)())fmd_scheme_notsup,		/* sop_unusable */
178d9638e54Smws 	(int (*)())fmd_scheme_notsup,		/* sop_contains */
179d9638e54Smws 	fmd_scheme_notranslate			/* sop_translate */
1807c478bd9Sstevel@tonic-gate };
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate static const fmd_scheme_ops_t _fmd_scheme_builtin_ops = {
1837c478bd9Sstevel@tonic-gate 	(int (*)())fmd_scheme_nop,		/* sop_init */
1847c478bd9Sstevel@tonic-gate 	(void (*)())fmd_scheme_nop,		/* sop_fini */
1857c478bd9Sstevel@tonic-gate 	fmd_scheme_fmd_nvl2str,			/* sop_nvl2str */
1867c478bd9Sstevel@tonic-gate 	(int (*)())fmd_scheme_nop,		/* sop_expand */
1877c478bd9Sstevel@tonic-gate 	fmd_scheme_fmd_present,			/* sop_present */
18825c6ff4bSstephh 	fmd_scheme_fmd_replaced,		/* sop_replaced */
18925c6ff4bSstephh 	fmd_scheme_fmd_service_state,		/* sop_service_state */
1907c478bd9Sstevel@tonic-gate 	fmd_scheme_fmd_unusable,		/* sop_unusable */
191d9638e54Smws 	(int (*)())fmd_scheme_notsup,		/* sop_contains */
192d9638e54Smws 	fmd_scheme_notranslate			/* sop_translate */
1937c478bd9Sstevel@tonic-gate };
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate /*
1967c478bd9Sstevel@tonic-gate  * Scheme ops descriptions.  These names and offsets are used by the function
1977c478bd9Sstevel@tonic-gate  * fmd_scheme_rtld_init(), defined below, to load up a fmd_scheme_ops_t.
1987c478bd9Sstevel@tonic-gate  */
1997c478bd9Sstevel@tonic-gate static const fmd_scheme_opd_t _fmd_scheme_ops[] = {
2007c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_init", offsetof(fmd_scheme_ops_t, sop_init) },
2017c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_fini", offsetof(fmd_scheme_ops_t, sop_fini) },
2027c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_nvl2str", offsetof(fmd_scheme_ops_t, sop_nvl2str) },
2037c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_expand", offsetof(fmd_scheme_ops_t, sop_expand) },
2047c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_present", offsetof(fmd_scheme_ops_t, sop_present) },
20525c6ff4bSstephh 	{ "fmd_fmri_replaced", offsetof(fmd_scheme_ops_t, sop_replaced) },
20625c6ff4bSstephh 	{ "fmd_fmri_service_state", offsetof(fmd_scheme_ops_t,
20725c6ff4bSstephh 	    sop_service_state) },
2087c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_unusable", offsetof(fmd_scheme_ops_t, sop_unusable) },
2097c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_contains", offsetof(fmd_scheme_ops_t, sop_contains) },
210d9638e54Smws 	{ "fmd_fmri_translate", offsetof(fmd_scheme_ops_t, sop_translate) },
2117c478bd9Sstevel@tonic-gate 	{ NULL, 0 }
2127c478bd9Sstevel@tonic-gate };
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate static fmd_scheme_t *
2157c478bd9Sstevel@tonic-gate fmd_scheme_create(const char *name)
2167c478bd9Sstevel@tonic-gate {
2177c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp = fmd_alloc(sizeof (fmd_scheme_t), FMD_SLEEP);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_init(&sp->sch_lock, NULL);
2207c478bd9Sstevel@tonic-gate 	(void) pthread_cond_init(&sp->sch_cv, NULL);
2217c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_init(&sp->sch_opslock, NULL);
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	sp->sch_next = NULL;
2247c478bd9Sstevel@tonic-gate 	sp->sch_name = fmd_strdup(name, FMD_SLEEP);
2257c478bd9Sstevel@tonic-gate 	sp->sch_dlp = NULL;
2267c478bd9Sstevel@tonic-gate 	sp->sch_refs = 1;
2277c478bd9Sstevel@tonic-gate 	sp->sch_loaded = 0;
2287c478bd9Sstevel@tonic-gate 	sp->sch_ops = _fmd_scheme_default_ops;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	return (sp);
2317c478bd9Sstevel@tonic-gate }
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate static void
2347c478bd9Sstevel@tonic-gate fmd_scheme_destroy(fmd_scheme_t *sp)
2357c478bd9Sstevel@tonic-gate {
2367c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->sch_lock));
2377c478bd9Sstevel@tonic-gate 	ASSERT(sp->sch_refs == 0);
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	if (sp->sch_dlp != NULL) {
2407c478bd9Sstevel@tonic-gate 		TRACE((FMD_DBG_FMRI, "dlclose scheme %s", sp->sch_name));
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 		if (sp->sch_ops.sop_fini != NULL)
2437c478bd9Sstevel@tonic-gate 			sp->sch_ops.sop_fini();
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 		(void) dlclose(sp->sch_dlp);
2467c478bd9Sstevel@tonic-gate 	}
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	fmd_strfree(sp->sch_name);
2497c478bd9Sstevel@tonic-gate 	fmd_free(sp, sizeof (fmd_scheme_t));
2507c478bd9Sstevel@tonic-gate }
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate fmd_scheme_hash_t *
2537c478bd9Sstevel@tonic-gate fmd_scheme_hash_create(const char *rootdir, const char *dirpath)
2547c478bd9Sstevel@tonic-gate {
2557c478bd9Sstevel@tonic-gate 	fmd_scheme_hash_t *shp;
2567c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
2577c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp;
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	shp = fmd_alloc(sizeof (fmd_scheme_hash_t), FMD_SLEEP);
2607c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s/%s", rootdir, dirpath);
2617c478bd9Sstevel@tonic-gate 	shp->sch_dirpath = fmd_strdup(path, FMD_SLEEP);
2627c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_init(&shp->sch_rwlock, NULL);
2637c478bd9Sstevel@tonic-gate 	shp->sch_hashlen = fmd.d_str_buckets;
2647c478bd9Sstevel@tonic-gate 	shp->sch_hash = fmd_zalloc(sizeof (fmd_scheme_t *) *
2657c478bd9Sstevel@tonic-gate 	    shp->sch_hashlen, FMD_SLEEP);
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	sp = fmd_scheme_create(FM_FMRI_SCHEME_FMD);
2687c478bd9Sstevel@tonic-gate 	sp->sch_ops = _fmd_scheme_builtin_ops;
2697c478bd9Sstevel@tonic-gate 	sp->sch_loaded = FMD_B_TRUE;
2707c478bd9Sstevel@tonic-gate 	shp->sch_hash[fmd_strhash(sp->sch_name) % shp->sch_hashlen] = sp;
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	return (shp);
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate void
2767c478bd9Sstevel@tonic-gate fmd_scheme_hash_destroy(fmd_scheme_hash_t *shp)
2777c478bd9Sstevel@tonic-gate {
2787c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp, *np;
2797c478bd9Sstevel@tonic-gate 	uint_t i;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	for (i = 0; i < shp->sch_hashlen; i++) {
2827c478bd9Sstevel@tonic-gate 		for (sp = shp->sch_hash[i]; sp != NULL; sp = np) {
2837c478bd9Sstevel@tonic-gate 			np = sp->sch_next;
2847c478bd9Sstevel@tonic-gate 			sp->sch_next = NULL;
2857c478bd9Sstevel@tonic-gate 			fmd_scheme_hash_release(shp, sp);
2867c478bd9Sstevel@tonic-gate 		}
2877c478bd9Sstevel@tonic-gate 	}
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	fmd_free(shp->sch_hash, sizeof (fmd_scheme_t *) * shp->sch_hashlen);
2907c478bd9Sstevel@tonic-gate 	fmd_strfree(shp->sch_dirpath);
2917c478bd9Sstevel@tonic-gate 	fmd_free(shp, sizeof (fmd_scheme_hash_t));
2927c478bd9Sstevel@tonic-gate }
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate void
2957c478bd9Sstevel@tonic-gate fmd_scheme_hash_trygc(fmd_scheme_hash_t *shp)
2967c478bd9Sstevel@tonic-gate {
2977c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp, *np;
2987c478bd9Sstevel@tonic-gate 	uint_t i;
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	if (shp == NULL || pthread_rwlock_trywrlock(&shp->sch_rwlock) != 0)
3017c478bd9Sstevel@tonic-gate 		return; /* failed to acquire lock: just skip garbage collect */
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	for (i = 0; i < shp->sch_hashlen; i++) {
3047c478bd9Sstevel@tonic-gate 		for (sp = shp->sch_hash[i]; sp != NULL; sp = np) {
3057c478bd9Sstevel@tonic-gate 			np = sp->sch_next;
3067c478bd9Sstevel@tonic-gate 			sp->sch_next = NULL;
3077c478bd9Sstevel@tonic-gate 			fmd_scheme_hash_release(shp, sp);
3087c478bd9Sstevel@tonic-gate 		}
3097c478bd9Sstevel@tonic-gate 	}
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	bzero(shp->sch_hash, sizeof (fmd_scheme_t *) * shp->sch_hashlen);
3127c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&shp->sch_rwlock);
3137c478bd9Sstevel@tonic-gate }
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate static int
3167c478bd9Sstevel@tonic-gate fmd_scheme_rtld_init(fmd_scheme_t *sp)
3177c478bd9Sstevel@tonic-gate {
3187c478bd9Sstevel@tonic-gate 	const fmd_scheme_opd_t *opd;
3197c478bd9Sstevel@tonic-gate 	void *p;
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	for (opd = _fmd_scheme_ops; opd->opd_name != NULL; opd++) {
3227c478bd9Sstevel@tonic-gate 		if ((p = dlsym(sp->sch_dlp, opd->opd_name)) != NULL)
3237c478bd9Sstevel@tonic-gate 			*(void **)((uintptr_t)&sp->sch_ops + opd->opd_off) = p;
3247c478bd9Sstevel@tonic-gate 	}
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	return (0);
3277c478bd9Sstevel@tonic-gate }
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate fmd_scheme_t *
3307c478bd9Sstevel@tonic-gate fmd_scheme_hash_xlookup(fmd_scheme_hash_t *shp, const char *name, uint_t h)
3317c478bd9Sstevel@tonic-gate {
3327c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp;
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&shp->sch_rwlock));
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 	for (sp = shp->sch_hash[h]; sp != NULL; sp = sp->sch_next) {
3377c478bd9Sstevel@tonic-gate 		if (strcmp(sp->sch_name, name) == 0)
3387c478bd9Sstevel@tonic-gate 			break;
3397c478bd9Sstevel@tonic-gate 	}
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	return (sp);
3427c478bd9Sstevel@tonic-gate }
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate /*
3457c478bd9Sstevel@tonic-gate  * Lookup a scheme module by name and return with a reference placed on it.  We
3467c478bd9Sstevel@tonic-gate  * use the scheme hash to cache "negative" entries (e.g. missing modules) as
3477c478bd9Sstevel@tonic-gate  * well so this function always returns successfully with a non-NULL scheme.
3487c478bd9Sstevel@tonic-gate  * The caller is responsible for applying fmd_scheme_hash_release() afterward.
3497c478bd9Sstevel@tonic-gate  */
3507c478bd9Sstevel@tonic-gate fmd_scheme_t *
3517c478bd9Sstevel@tonic-gate fmd_scheme_hash_lookup(fmd_scheme_hash_t *shp, const char *name)
3527c478bd9Sstevel@tonic-gate {
3537c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp, *nsp = NULL;
3547c478bd9Sstevel@tonic-gate 	uint_t h;
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 	/*
3577c478bd9Sstevel@tonic-gate 	 * Grab the hash lock as reader and look for the appropriate scheme.
3587c478bd9Sstevel@tonic-gate 	 * If the scheme isn't yet loaded, allocate a new scheme and grab the
3597c478bd9Sstevel@tonic-gate 	 * hash lock as writer to insert it (after checking again for it).
3607c478bd9Sstevel@tonic-gate 	 */
3617c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_rdlock(&shp->sch_rwlock);
3627c478bd9Sstevel@tonic-gate 	h = fmd_strhash(name) % shp->sch_hashlen;
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	if ((sp = fmd_scheme_hash_xlookup(shp, name, h)) == NULL) {
3657c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&shp->sch_rwlock);
3667c478bd9Sstevel@tonic-gate 		nsp = fmd_scheme_create(name);
3677c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_wrlock(&shp->sch_rwlock);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 		if ((sp = fmd_scheme_hash_xlookup(shp, name, h)) == NULL) {
3707c478bd9Sstevel@tonic-gate 			nsp->sch_next = shp->sch_hash[h];
3717c478bd9Sstevel@tonic-gate 			shp->sch_hash[h] = sp = nsp;
3727c478bd9Sstevel@tonic-gate 		} else {
3737c478bd9Sstevel@tonic-gate 			fmd_scheme_hash_release(shp, nsp);
3747c478bd9Sstevel@tonic-gate 			nsp = NULL;
3757c478bd9Sstevel@tonic-gate 		}
3767c478bd9Sstevel@tonic-gate 	}
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	/*
3797c478bd9Sstevel@tonic-gate 	 * Grab the scheme lock so it can't disappear and then drop the hash
3807c478bd9Sstevel@tonic-gate 	 * lock so that other lookups in the scheme hash can proceed.
3817c478bd9Sstevel@tonic-gate 	 */
3827c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&sp->sch_lock);
3837c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&shp->sch_rwlock);
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate 	/*
3867c478bd9Sstevel@tonic-gate 	 * If we created the scheme, compute its path and try to load it.  If
3877c478bd9Sstevel@tonic-gate 	 * we found an existing scheme, wait until its loaded bit is set.  Once
3887c478bd9Sstevel@tonic-gate 	 * we're done with either operation, increment sch_refs and return.
3897c478bd9Sstevel@tonic-gate 	 */
3907c478bd9Sstevel@tonic-gate 	if (nsp != NULL) {
3917c478bd9Sstevel@tonic-gate 		char path[PATH_MAX];
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path),
3947c478bd9Sstevel@tonic-gate 		    "%s/%s.so", shp->sch_dirpath, sp->sch_name);
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 		TRACE((FMD_DBG_FMRI, "dlopen scheme %s", sp->sch_name));
3977c478bd9Sstevel@tonic-gate 		sp->sch_dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW);
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 		if (sp->sch_dlp == NULL) {
4007c478bd9Sstevel@tonic-gate 			fmd_error(EFMD_FMRI_SCHEME,
4017c478bd9Sstevel@tonic-gate 			    "failed to load fmri scheme %s: %s\n", path,
4027c478bd9Sstevel@tonic-gate 			    dlerror());
4037c478bd9Sstevel@tonic-gate 		} else if (fmd_scheme_rtld_init(sp) != 0 ||
4047c478bd9Sstevel@tonic-gate 		    sp->sch_ops.sop_init() != 0) {
4057c478bd9Sstevel@tonic-gate 			fmd_error(EFMD_FMRI_SCHEME,
4067c478bd9Sstevel@tonic-gate 			    "failed to initialize fmri scheme %s", path);
4077c478bd9Sstevel@tonic-gate 			(void) dlclose(sp->sch_dlp);
4087c478bd9Sstevel@tonic-gate 			sp->sch_dlp = NULL;
4097c478bd9Sstevel@tonic-gate 			sp->sch_ops = _fmd_scheme_default_ops;
4107c478bd9Sstevel@tonic-gate 		}
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 		sp->sch_loaded = FMD_B_TRUE; /* set regardless of success */
4137c478bd9Sstevel@tonic-gate 		sp->sch_refs++;
4147c478bd9Sstevel@tonic-gate 		ASSERT(sp->sch_refs != 0);
415d9638e54Smws 
4167c478bd9Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&sp->sch_cv);
417d9638e54Smws 		(void) pthread_mutex_unlock(&sp->sch_lock);
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 	} else {
4207c478bd9Sstevel@tonic-gate 		while (!sp->sch_loaded)
4217c478bd9Sstevel@tonic-gate 			(void) pthread_cond_wait(&sp->sch_cv, &sp->sch_lock);
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 		sp->sch_refs++;
4247c478bd9Sstevel@tonic-gate 		ASSERT(sp->sch_refs != 0);
4257c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&sp->sch_lock);
4267c478bd9Sstevel@tonic-gate 	}
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 	return (sp);
4297c478bd9Sstevel@tonic-gate }
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate /*
4327c478bd9Sstevel@tonic-gate  * Release the hold on a scheme obtained using fmd_scheme_hash_lookup().
4337c478bd9Sstevel@tonic-gate  * We take 'shp' for symmetry and in case we need to use it in future work.
4347c478bd9Sstevel@tonic-gate  */
4357c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4367c478bd9Sstevel@tonic-gate void
4377c478bd9Sstevel@tonic-gate fmd_scheme_hash_release(fmd_scheme_hash_t *shp, fmd_scheme_t *sp)
4387c478bd9Sstevel@tonic-gate {
4397c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&sp->sch_lock);
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	ASSERT(sp->sch_refs != 0);
4427c478bd9Sstevel@tonic-gate 	if (--sp->sch_refs == 0)
4437c478bd9Sstevel@tonic-gate 		fmd_scheme_destroy(sp);
4447c478bd9Sstevel@tonic-gate 	else
4457c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&sp->sch_lock);
4467c478bd9Sstevel@tonic-gate }
447