xref: /onnv-gate/usr/src/cmd/fm/fmd/common/fmd_module.c (revision 13093:48f2dbca79a2)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54198Seschrock  * Common Development and Distribution License (the "License").
64198Seschrock  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211429Smws 
220Sstevel@tonic-gate /*
2312066SRobert.Johnston@Sun.COM  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <signal.h>
270Sstevel@tonic-gate #include <dirent.h>
280Sstevel@tonic-gate #include <limits.h>
290Sstevel@tonic-gate #include <alloca.h>
300Sstevel@tonic-gate #include <unistd.h>
310Sstevel@tonic-gate #include <stdio.h>
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <fmd_string.h>
340Sstevel@tonic-gate #include <fmd_alloc.h>
350Sstevel@tonic-gate #include <fmd_module.h>
360Sstevel@tonic-gate #include <fmd_error.h>
370Sstevel@tonic-gate #include <fmd_conf.h>
380Sstevel@tonic-gate #include <fmd_dispq.h>
390Sstevel@tonic-gate #include <fmd_eventq.h>
400Sstevel@tonic-gate #include <fmd_timerq.h>
410Sstevel@tonic-gate #include <fmd_subr.h>
420Sstevel@tonic-gate #include <fmd_thread.h>
430Sstevel@tonic-gate #include <fmd_ustat.h>
440Sstevel@tonic-gate #include <fmd_case.h>
450Sstevel@tonic-gate #include <fmd_protocol.h>
460Sstevel@tonic-gate #include <fmd_buf.h>
470Sstevel@tonic-gate #include <fmd_ckpt.h>
481193Smws #include <fmd_xprt.h>
494198Seschrock #include <fmd_topo.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #include <fmd.h>
520Sstevel@tonic-gate 
530Sstevel@tonic-gate /*
540Sstevel@tonic-gate  * Template for per-module statistics installed by fmd on behalf of each active
550Sstevel@tonic-gate  * module.  These are used to initialize the per-module mp->mod_stats below.
560Sstevel@tonic-gate  * NOTE: FMD_TYPE_STRING statistics should not be used here.  If they are
570Sstevel@tonic-gate  * required in the future, the FMD_ADM_MODDSTAT service routine must change.
580Sstevel@tonic-gate  */
590Sstevel@tonic-gate static const fmd_modstat_t _fmd_modstat_tmpl = {
601193Smws {
610Sstevel@tonic-gate { "fmd.dispatched", FMD_TYPE_UINT64, "total events dispatched to module" },
620Sstevel@tonic-gate { "fmd.dequeued", FMD_TYPE_UINT64, "total events dequeued by module" },
630Sstevel@tonic-gate { "fmd.prdequeued", FMD_TYPE_UINT64, "protocol events dequeued by module" },
640Sstevel@tonic-gate { "fmd.dropped", FMD_TYPE_UINT64, "total events dropped on queue overflow" },
650Sstevel@tonic-gate { "fmd.wcnt", FMD_TYPE_UINT32, "count of events waiting on queue" },
660Sstevel@tonic-gate { "fmd.wtime", FMD_TYPE_TIME, "total wait time on queue" },
670Sstevel@tonic-gate { "fmd.wlentime", FMD_TYPE_TIME, "total wait length * time product" },
680Sstevel@tonic-gate { "fmd.wlastupdate", FMD_TYPE_TIME, "hrtime of last wait queue update" },
690Sstevel@tonic-gate { "fmd.dtime", FMD_TYPE_TIME, "total processing time after dequeue" },
700Sstevel@tonic-gate { "fmd.dlastupdate", FMD_TYPE_TIME, "hrtime of last event dequeue completion" },
711193Smws },
721193Smws { "fmd.loadtime", FMD_TYPE_TIME, "hrtime at which module was loaded" },
731193Smws { "fmd.snaptime", FMD_TYPE_TIME, "hrtime of last statistics snapshot" },
741193Smws { "fmd.accepted", FMD_TYPE_UINT64, "total events accepted by module" },
750Sstevel@tonic-gate { "fmd.debugdrop", FMD_TYPE_UINT64, "dropped debug messages" },
760Sstevel@tonic-gate { "fmd.memtotal", FMD_TYPE_SIZE, "total memory allocated by module" },
770Sstevel@tonic-gate { "fmd.memlimit", FMD_TYPE_SIZE, "limit on total memory allocated" },
780Sstevel@tonic-gate { "fmd.buftotal", FMD_TYPE_SIZE, "total buffer space used by module" },
790Sstevel@tonic-gate { "fmd.buflimit", FMD_TYPE_SIZE, "limit on total buffer space" },
800Sstevel@tonic-gate { "fmd.thrtotal", FMD_TYPE_UINT32, "total number of auxiliary threads" },
810Sstevel@tonic-gate { "fmd.thrlimit", FMD_TYPE_UINT32, "limit on number of auxiliary threads" },
8212967Sgavin.maltby@oracle.com { "fmd.doorthrtotal", FMD_TYPE_UINT32, "total number of door server threads" },
8312967Sgavin.maltby@oracle.com { "fmd.doorthrlimit", FMD_TYPE_UINT32, "limit on door server threads" },
840Sstevel@tonic-gate { "fmd.caseopen", FMD_TYPE_UINT64, "cases currently open by module" },
850Sstevel@tonic-gate { "fmd.casesolved", FMD_TYPE_UINT64, "total cases solved by module" },
860Sstevel@tonic-gate { "fmd.caseclosed", FMD_TYPE_UINT64, "total cases closed by module" },
870Sstevel@tonic-gate { "fmd.ckptsave", FMD_TYPE_BOOL, "save checkpoints for module" },
880Sstevel@tonic-gate { "fmd.ckptrestore", FMD_TYPE_BOOL, "restore checkpoints for module" },
890Sstevel@tonic-gate { "fmd.ckptzero", FMD_TYPE_BOOL, "zeroed checkpoint at startup" },
900Sstevel@tonic-gate { "fmd.ckptcnt", FMD_TYPE_UINT64, "number of checkpoints taken" },
910Sstevel@tonic-gate { "fmd.ckpttime", FMD_TYPE_TIME, "total checkpoint time" },
921193Smws { "fmd.xprtopen", FMD_TYPE_UINT32, "total number of open transports" },
931193Smws { "fmd.xprtlimit", FMD_TYPE_UINT32, "limit on number of open transports" },
941193Smws { "fmd.xprtqlimit", FMD_TYPE_UINT32, "limit on transport event queue length" },
950Sstevel@tonic-gate };
960Sstevel@tonic-gate 
970Sstevel@tonic-gate static void
fmd_module_start(void * arg)980Sstevel@tonic-gate fmd_module_start(void *arg)
990Sstevel@tonic-gate {
1000Sstevel@tonic-gate 	fmd_module_t *mp = arg;
1010Sstevel@tonic-gate 	fmd_event_t *ep;
1021193Smws 	fmd_xprt_t *xp;
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate 	if (mp->mod_ops->mop_init(mp) != 0 || mp->mod_error != 0) {
1070Sstevel@tonic-gate 		if (mp->mod_error == 0)
1080Sstevel@tonic-gate 			mp->mod_error = errno ? errno : EFMD_MOD_INIT;
1090Sstevel@tonic-gate 		goto out;
1100Sstevel@tonic-gate 	}
1110Sstevel@tonic-gate 
1121193Smws 	if (fmd.d_mod_event != NULL)
1131193Smws 		fmd_eventq_insert_at_head(mp->mod_queue, fmd.d_mod_event);
1141193Smws 
1150Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&mp->mod_lock));
1160Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_INIT;
1170Sstevel@tonic-gate 
1181193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
1190Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
1201193Smws 
1211193Smws 	/*
1221193Smws 	 * If the module opened any transports while executing _fmd_init(),
1231193Smws 	 * they are suspended. Now that _fmd_init() is done, wake them up.
1241193Smws 	 */
1251193Smws 	for (xp = fmd_list_next(&mp->mod_transports);
1261193Smws 	    xp != NULL; xp = fmd_list_next(xp))
1271193Smws 		fmd_xprt_xresume(xp, FMD_XPRT_ISUSPENDED);
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	/*
1300Sstevel@tonic-gate 	 * Wait for events to arrive by checking mod_error and then sleeping in
1310Sstevel@tonic-gate 	 * fmd_eventq_delete().  If a NULL event is returned, the eventq has
1320Sstevel@tonic-gate 	 * been aborted and we continue on to call fini and exit the thread.
1330Sstevel@tonic-gate 	 */
1340Sstevel@tonic-gate 	while ((ep = fmd_eventq_delete(mp->mod_queue)) != NULL) {
1350Sstevel@tonic-gate 		/*
1361193Smws 		 * If the module has failed, discard the event without ever
1371193Smws 		 * passing it to the module and go back to sleep.
1380Sstevel@tonic-gate 		 */
1391193Smws 		if (mp->mod_error != 0) {
1401193Smws 			fmd_eventq_done(mp->mod_queue);
1410Sstevel@tonic-gate 			fmd_event_rele(ep);
1420Sstevel@tonic-gate 			continue;
1430Sstevel@tonic-gate 		}
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 		mp->mod_ops->mop_dispatch(mp, ep);
1461193Smws 		fmd_eventq_done(mp->mod_queue);
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 		/*
1490Sstevel@tonic-gate 		 * Once mop_dispatch() is complete, grab the lock and perform
1500Sstevel@tonic-gate 		 * any event-specific post-processing.  Finally, if necessary,
1510Sstevel@tonic-gate 		 * checkpoint the state of the module after this event.
1520Sstevel@tonic-gate 		 */
1530Sstevel@tonic-gate 		fmd_module_lock(mp);
1540Sstevel@tonic-gate 
1551193Smws 		if (FMD_EVENT_TYPE(ep) == FMD_EVT_CLOSE)
1561193Smws 			fmd_case_delete(FMD_EVENT_DATA(ep));
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 		fmd_ckpt_save(mp);
1590Sstevel@tonic-gate 		fmd_module_unlock(mp);
1600Sstevel@tonic-gate 		fmd_event_rele(ep);
1610Sstevel@tonic-gate 	}
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	if (mp->mod_ops->mop_fini(mp) != 0 && mp->mod_error == 0)
1640Sstevel@tonic-gate 		mp->mod_error = errno ? errno : EFMD_MOD_FINI;
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
1670Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_FINI;
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate out:
1701193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
1710Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate fmd_module_t *
fmd_module_create(const char * path,const fmd_modops_t * ops)1750Sstevel@tonic-gate fmd_module_create(const char *path, const fmd_modops_t *ops)
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate 	fmd_module_t *mp = fmd_zalloc(sizeof (fmd_module_t), FMD_SLEEP);
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	char buf[PATH_MAX], *p;
1800Sstevel@tonic-gate 	const char *dir;
1810Sstevel@tonic-gate 	uint32_t limit;
1820Sstevel@tonic-gate 	int err;
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	(void) strlcpy(buf, fmd_strbasename(path), sizeof (buf));
1850Sstevel@tonic-gate 	if ((p = strrchr(buf, '.')) != NULL && strcmp(p, ".so") == 0)
1860Sstevel@tonic-gate 		*p = '\0'; /* strip trailing .so from any module name */
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	(void) pthread_mutex_init(&mp->mod_lock, NULL);
1890Sstevel@tonic-gate 	(void) pthread_cond_init(&mp->mod_cv, NULL);
1900Sstevel@tonic-gate 	(void) pthread_mutex_init(&mp->mod_stats_lock, NULL);
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	mp->mod_name = fmd_strdup(buf, FMD_SLEEP);
1930Sstevel@tonic-gate 	mp->mod_path = fmd_strdup(path, FMD_SLEEP);
1940Sstevel@tonic-gate 	mp->mod_ops = ops;
1950Sstevel@tonic-gate 	mp->mod_ustat = fmd_ustat_create();
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.dir", &dir);
1980Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf),
1990Sstevel@tonic-gate 	    "%s/%s/%s", fmd.d_rootdir, dir, mp->mod_name);
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	mp->mod_ckpt = fmd_strdup(buf, FMD_SLEEP);
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.tmrlim", &limit);
2040Sstevel@tonic-gate 	mp->mod_timerids = fmd_idspace_create(mp->mod_name, 1, limit + 1);
2050Sstevel@tonic-gate 	mp->mod_threads = fmd_idspace_create(mp->mod_name, 0, INT_MAX);
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	fmd_buf_hash_create(&mp->mod_bufs);
2080Sstevel@tonic-gate 	fmd_serd_hash_create(&mp->mod_serds);
2090Sstevel@tonic-gate 
2104198Seschrock 	mp->mod_topo_current = fmd_topo_hold();
2114198Seschrock 
2120Sstevel@tonic-gate 	(void) pthread_mutex_lock(&fmd.d_mod_lock);
2130Sstevel@tonic-gate 	fmd_list_append(&fmd.d_mod_list, mp);
2140Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&fmd.d_mod_lock);
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 	/*
2170Sstevel@tonic-gate 	 * Initialize the module statistics that are kept on its behalf by fmd.
2180Sstevel@tonic-gate 	 * These are set up using a template defined at the top of this file.
2190Sstevel@tonic-gate 	 */
2200Sstevel@tonic-gate 	if ((mp->mod_stats = (fmd_modstat_t *)fmd_ustat_insert(mp->mod_ustat,
2210Sstevel@tonic-gate 	    FMD_USTAT_ALLOC, sizeof (_fmd_modstat_tmpl) / sizeof (fmd_stat_t),
2220Sstevel@tonic-gate 	    (fmd_stat_t *)&_fmd_modstat_tmpl, NULL)) == NULL) {
2230Sstevel@tonic-gate 		fmd_error(EFMD_MOD_INIT, "failed to initialize per-mod stats");
2240Sstevel@tonic-gate 		fmd_module_destroy(mp);
2250Sstevel@tonic-gate 		return (NULL);
2260Sstevel@tonic-gate 	}
2270Sstevel@tonic-gate 
2287171Seschrock 	if (nv_alloc_init(&mp->mod_nva_sleep,
2297171Seschrock 	    &fmd_module_nva_ops_sleep, mp) != 0 ||
2307171Seschrock 	    nv_alloc_init(&mp->mod_nva_nosleep,
2317171Seschrock 	    &fmd_module_nva_ops_nosleep, mp) != 0) {
2327171Seschrock 		fmd_error(EFMD_MOD_INIT, "failed to initialize nvlist "
2337171Seschrock 		    "allocation routines");
2347171Seschrock 		fmd_module_destroy(mp);
2357171Seschrock 		return (NULL);
2367171Seschrock 	}
2377171Seschrock 
2381193Smws 	(void) fmd_conf_getprop(fmd.d_conf, "client.evqlim", &limit);
2391193Smws 
2401193Smws 	mp->mod_queue = fmd_eventq_create(mp,
2411193Smws 	    &mp->mod_stats->ms_evqstat, &mp->mod_stats_lock, limit);
2421193Smws 
2430Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.memlim",
2440Sstevel@tonic-gate 	    &mp->mod_stats->ms_memlimit.fmds_value.ui64);
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.buflim",
2470Sstevel@tonic-gate 	    &mp->mod_stats->ms_buflimit.fmds_value.ui64);
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.thrlim",
2500Sstevel@tonic-gate 	    &mp->mod_stats->ms_thrlimit.fmds_value.ui32);
2510Sstevel@tonic-gate 
25212967Sgavin.maltby@oracle.com 	(void) fmd_conf_getprop(fmd.d_conf, "client.doorthrlim",
25312967Sgavin.maltby@oracle.com 	    &mp->mod_stats->ms_doorthrlimit.fmds_value.ui32);
25412967Sgavin.maltby@oracle.com 
2551193Smws 	(void) fmd_conf_getprop(fmd.d_conf, "client.xprtlim",
2561193Smws 	    &mp->mod_stats->ms_xprtlimit.fmds_value.ui32);
2571193Smws 
2581193Smws 	(void) fmd_conf_getprop(fmd.d_conf, "client.xprtqlim",
2591193Smws 	    &mp->mod_stats->ms_xprtqlimit.fmds_value.ui32);
2601193Smws 
2610Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.save",
2620Sstevel@tonic-gate 	    &mp->mod_stats->ms_ckpt_save.fmds_value.bool);
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.restore",
2650Sstevel@tonic-gate 	    &mp->mod_stats->ms_ckpt_restore.fmds_value.bool);
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.zero",
2680Sstevel@tonic-gate 	    &mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool);
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	if (mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool)
2710Sstevel@tonic-gate 		fmd_ckpt_delete(mp); /* blow away any pre-existing checkpoint */
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 	/*
2740Sstevel@tonic-gate 	 * Place a hold on the module and grab the module lock before creating
2750Sstevel@tonic-gate 	 * the module's thread to ensure that it cannot destroy the module and
2760Sstevel@tonic-gate 	 * that it cannot call ops->mop_init() before we're done setting up.
2770Sstevel@tonic-gate 	 * NOTE: from now on, we must use fmd_module_rele() for error paths.
2780Sstevel@tonic-gate 	 */
2790Sstevel@tonic-gate 	fmd_module_hold(mp);
2800Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
2810Sstevel@tonic-gate 	mp->mod_stats->ms_loadtime.fmds_value.ui64 = gethrtime();
2820Sstevel@tonic-gate 	mp->mod_thread = fmd_thread_create(mp, fmd_module_start, mp);
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	if (mp->mod_thread == NULL) {
2850Sstevel@tonic-gate 		fmd_error(EFMD_MOD_THR, "failed to create thread for %s", path);
2860Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
2870Sstevel@tonic-gate 		fmd_module_rele(mp);
2880Sstevel@tonic-gate 		return (NULL);
2890Sstevel@tonic-gate 	}
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	/*
2920Sstevel@tonic-gate 	 * At this point our module structure is nearly finished and its thread
2930Sstevel@tonic-gate 	 * is starting execution in fmd_module_start() above, which will begin
2940Sstevel@tonic-gate 	 * by blocking for mod_lock.  We now drop mod_lock and wait for either
2950Sstevel@tonic-gate 	 * FMD_MOD_INIT or mod_error to be set before proceeding.
2960Sstevel@tonic-gate 	 */
2970Sstevel@tonic-gate 	while (!(mp->mod_flags & FMD_MOD_INIT) && mp->mod_error == 0)
2980Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	/*
3010Sstevel@tonic-gate 	 * If the module has failed to initialize, copy its errno to the errno
3020Sstevel@tonic-gate 	 * of the caller, wait for it to unload, and then destroy it.
3030Sstevel@tonic-gate 	 */
3040Sstevel@tonic-gate 	if (!(mp->mod_flags & FMD_MOD_INIT)) {
3050Sstevel@tonic-gate 		err = mp->mod_error;
3060Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 		if (err == EFMD_CKPT_INVAL)
3090Sstevel@tonic-gate 			fmd_ckpt_rename(mp); /* move aside bad checkpoint */
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 		/*
3120Sstevel@tonic-gate 		 * If we're in the background, keep quiet about failure to
3130Sstevel@tonic-gate 		 * load because a handle wasn't registered: this is a module's
3140Sstevel@tonic-gate 		 * way of telling us it didn't want to be loaded for some
3150Sstevel@tonic-gate 		 * reason related to system configuration.  If we're in the
3160Sstevel@tonic-gate 		 * foreground we log this too in order to inform developers.
3170Sstevel@tonic-gate 		 */
3180Sstevel@tonic-gate 		if (fmd.d_fg || err != EFMD_HDL_INIT) {
3190Sstevel@tonic-gate 			fmd_error(EFMD_MOD_INIT, "failed to load %s: %s\n",
3200Sstevel@tonic-gate 			    path, fmd_strerror(err));
3210Sstevel@tonic-gate 		}
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 		fmd_module_unload(mp);
3240Sstevel@tonic-gate 		fmd_module_rele(mp);
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 		(void) fmd_set_errno(err);
3270Sstevel@tonic-gate 		return (NULL);
3280Sstevel@tonic-gate 	}
3290Sstevel@tonic-gate 
3301193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
3310Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 	fmd_dprintf(FMD_DBG_MOD, "loaded module %s\n", mp->mod_name);
3340Sstevel@tonic-gate 	return (mp);
3350Sstevel@tonic-gate }
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate static void
fmd_module_untimeout(fmd_idspace_t * ids,id_t id,fmd_module_t * mp)3381193Smws fmd_module_untimeout(fmd_idspace_t *ids, id_t id, fmd_module_t *mp)
3390Sstevel@tonic-gate {
3401193Smws 	void *arg = fmd_timerq_remove(fmd.d_timers, ids, id);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	/*
3430Sstevel@tonic-gate 	 * The root module calls fmd_timerq_install() directly and must take
3440Sstevel@tonic-gate 	 * responsibility for any cleanup of timer arguments that is required.
3450Sstevel@tonic-gate 	 * All other modules use fmd_modtimer_t's as the arg data; free them.
3460Sstevel@tonic-gate 	 */
3470Sstevel@tonic-gate 	if (arg != NULL && mp != fmd.d_rmod)
3480Sstevel@tonic-gate 		fmd_free(arg, sizeof (fmd_modtimer_t));
3490Sstevel@tonic-gate }
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate void
fmd_module_unload(fmd_module_t * mp)3520Sstevel@tonic-gate fmd_module_unload(fmd_module_t *mp)
3530Sstevel@tonic-gate {
3544198Seschrock 	fmd_modtopo_t *mtp;
3554198Seschrock 
3560Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_QUIT) {
3590Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
3600Sstevel@tonic-gate 		return; /* module is already unloading */
3610Sstevel@tonic-gate 	}
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	ASSERT(mp->mod_thread != NULL);
3640Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_QUIT;
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	if (mp->mod_queue != NULL)
3670Sstevel@tonic-gate 		fmd_eventq_abort(mp->mod_queue);
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate 	/*
3700Sstevel@tonic-gate 	 * Wait for the module's thread to stop processing events and call
3710Sstevel@tonic-gate 	 * _fmd_fini() and exit.  We do this by waiting for FMD_MOD_FINI to be
3720Sstevel@tonic-gate 	 * set if INIT was set, and then attempting to join with the thread.
3730Sstevel@tonic-gate 	 */
3740Sstevel@tonic-gate 	while ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI)) == FMD_MOD_INIT)
3750Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
3760Sstevel@tonic-gate 
3771193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
3780Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	fmd_thread_destroy(mp->mod_thread, FMD_THREAD_JOIN);
3810Sstevel@tonic-gate 	mp->mod_thread = NULL;
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 	/*
3840Sstevel@tonic-gate 	 * Once the module is no longer active, clean up any data structures
3851193Smws 	 * that are only required when the module is loaded.
3860Sstevel@tonic-gate 	 */
3870Sstevel@tonic-gate 	fmd_module_lock(mp);
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	if (mp->mod_timerids != NULL) {
3900Sstevel@tonic-gate 		fmd_idspace_apply(mp->mod_timerids,
3910Sstevel@tonic-gate 		    (void (*)())fmd_module_untimeout, mp);
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 		fmd_idspace_destroy(mp->mod_timerids);
3940Sstevel@tonic-gate 		mp->mod_timerids = NULL;
3950Sstevel@tonic-gate 	}
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	if (mp->mod_threads != NULL) {
3980Sstevel@tonic-gate 		fmd_idspace_destroy(mp->mod_threads);
3990Sstevel@tonic-gate 		mp->mod_threads = NULL;
4000Sstevel@tonic-gate 	}
4010Sstevel@tonic-gate 
4021429Smws 	(void) fmd_buf_hash_destroy(&mp->mod_bufs);
4030Sstevel@tonic-gate 	fmd_serd_hash_destroy(&mp->mod_serds);
4040Sstevel@tonic-gate 
4054198Seschrock 	while ((mtp = fmd_list_next(&mp->mod_topolist)) != NULL) {
4064198Seschrock 		fmd_list_delete(&mp->mod_topolist, mtp);
4074198Seschrock 		fmd_topo_rele(mtp->mt_topo);
4084198Seschrock 		fmd_free(mtp, sizeof (fmd_modtopo_t));
4094198Seschrock 	}
4104198Seschrock 
4110Sstevel@tonic-gate 	fmd_module_unlock(mp);
4120Sstevel@tonic-gate 	fmd_dprintf(FMD_DBG_MOD, "unloaded module %s\n", mp->mod_name);
4130Sstevel@tonic-gate }
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate void
fmd_module_destroy(fmd_module_t * mp)4160Sstevel@tonic-gate fmd_module_destroy(fmd_module_t *mp)
4170Sstevel@tonic-gate {
4180Sstevel@tonic-gate 	fmd_conf_formal_t *cfp = mp->mod_argv;
4190Sstevel@tonic-gate 	int i;
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&mp->mod_lock));
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	if (mp->mod_thread != NULL) {
4240Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
4250Sstevel@tonic-gate 		fmd_module_unload(mp);
4260Sstevel@tonic-gate 		(void) pthread_mutex_lock(&mp->mod_lock);
4270Sstevel@tonic-gate 	}
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	ASSERT(mp->mod_thread == NULL);
4300Sstevel@tonic-gate 	ASSERT(mp->mod_refs == 0);
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	/*
4330Sstevel@tonic-gate 	 * Once the module's thread is dead, we can safely remove the module
4340Sstevel@tonic-gate 	 * from global visibility and by removing it from d_mod_list.  Any
4350Sstevel@tonic-gate 	 * modhash pointers are already gone by virtue of mod_refs being zero.
4360Sstevel@tonic-gate 	 */
4370Sstevel@tonic-gate 	(void) pthread_mutex_lock(&fmd.d_mod_lock);
4380Sstevel@tonic-gate 	fmd_list_delete(&fmd.d_mod_list, mp);
4390Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&fmd.d_mod_lock);
4400Sstevel@tonic-gate 
4414198Seschrock 	if (mp->mod_topo_current != NULL)
4424198Seschrock 		fmd_topo_rele(mp->mod_topo_current);
4434198Seschrock 
4447171Seschrock 	if (mp->mod_nva_sleep.nva_ops != NULL)
4457171Seschrock 		nv_alloc_fini(&mp->mod_nva_sleep);
4467171Seschrock 	if (mp->mod_nva_nosleep.nva_ops != NULL)
4477171Seschrock 		nv_alloc_fini(&mp->mod_nva_nosleep);
4487171Seschrock 
4490Sstevel@tonic-gate 	/*
4500Sstevel@tonic-gate 	 * Once the module is no longer processing events and no longer visible
4510Sstevel@tonic-gate 	 * through any program data structures, we can free all of its content.
4520Sstevel@tonic-gate 	 */
4530Sstevel@tonic-gate 	if (mp->mod_queue != NULL) {
4540Sstevel@tonic-gate 		fmd_eventq_destroy(mp->mod_queue);
4550Sstevel@tonic-gate 		mp->mod_queue = NULL;
4560Sstevel@tonic-gate 	}
4570Sstevel@tonic-gate 
4581193Smws 	if (mp->mod_ustat != NULL) {
4591193Smws 		(void) pthread_mutex_lock(&mp->mod_stats_lock);
4601193Smws 		fmd_ustat_destroy(mp->mod_ustat);
4611193Smws 		mp->mod_ustat = NULL;
4621193Smws 		mp->mod_stats = NULL;
4631193Smws 		(void) pthread_mutex_unlock(&mp->mod_stats_lock);
4641193Smws 	}
4651193Smws 
4660Sstevel@tonic-gate 	for (i = 0; i < mp->mod_dictc; i++)
4670Sstevel@tonic-gate 		fm_dc_closedict(mp->mod_dictv[i]);
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 	fmd_free(mp->mod_dictv, sizeof (struct fm_dc_handle *) * mp->mod_dictc);
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	if (mp->mod_conf != NULL)
4720Sstevel@tonic-gate 		fmd_conf_close(mp->mod_conf);
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	for (i = 0; i < mp->mod_argc; i++, cfp++) {
4750Sstevel@tonic-gate 		fmd_strfree((char *)cfp->cf_name);
4760Sstevel@tonic-gate 		fmd_strfree((char *)cfp->cf_default);
4770Sstevel@tonic-gate 	}
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	fmd_free(mp->mod_argv, sizeof (fmd_conf_formal_t) * mp->mod_argc);
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate 	fmd_strfree(mp->mod_name);
4820Sstevel@tonic-gate 	fmd_strfree(mp->mod_path);
4830Sstevel@tonic-gate 	fmd_strfree(mp->mod_ckpt);
4840Sstevel@tonic-gate 	nvlist_free(mp->mod_fmri);
4857139Scy152378 	fmd_strfree(mp->mod_vers);
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 	fmd_free(mp, sizeof (fmd_module_t));
4880Sstevel@tonic-gate }
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate /*
4910Sstevel@tonic-gate  * fmd_module_error() is called after the stack is unwound from a call to
4920Sstevel@tonic-gate  * fmd_module_abort() to indicate that the module has failed.  The mod_error
4930Sstevel@tonic-gate  * field is used to hold the error code of the first fatal error to the module.
4940Sstevel@tonic-gate  * An EFMD_MOD_FAIL event is then created and sent to fmd-self-diagnosis.
4950Sstevel@tonic-gate  */
4960Sstevel@tonic-gate static void
fmd_module_error(fmd_module_t * mp,int err)4970Sstevel@tonic-gate fmd_module_error(fmd_module_t *mp, int err)
4980Sstevel@tonic-gate {
4990Sstevel@tonic-gate 	fmd_event_t *e;
5000Sstevel@tonic-gate 	nvlist_t *nvl;
5010Sstevel@tonic-gate 	char *class;
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&mp->mod_lock));
5040Sstevel@tonic-gate 	ASSERT(err != 0);
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 	TRACE((FMD_DBG_MOD, "module aborted: err=%d", err));
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	if (mp->mod_error == 0)
5090Sstevel@tonic-gate 		mp->mod_error = err;
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	if (mp == fmd.d_self)
5120Sstevel@tonic-gate 		return; /* do not post event if fmd.d_self itself fails */
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 	/*
5150Sstevel@tonic-gate 	 * Send an error indicating the module has now failed to fmd.d_self.
5160Sstevel@tonic-gate 	 * Since the error causing the failure has already been logged by
5170Sstevel@tonic-gate 	 * fmd_api_xerror(), we do not need to bother logging this event.
5180Sstevel@tonic-gate 	 * It only exists for the purpose of notifying fmd.d_self that it can
5190Sstevel@tonic-gate 	 * close the case associated with this module because mod_error is set.
5200Sstevel@tonic-gate 	 */
5210Sstevel@tonic-gate 	nvl = fmd_protocol_moderror(mp, EFMD_MOD_FAIL, fmd_strerror(err));
5220Sstevel@tonic-gate 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
5230Sstevel@tonic-gate 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
5240Sstevel@tonic-gate 	fmd_dispq_dispatch(fmd.d_disp, e, class);
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate void
fmd_module_dispatch(fmd_module_t * mp,fmd_event_t * e)5280Sstevel@tonic-gate fmd_module_dispatch(fmd_module_t *mp, fmd_event_t *e)
5290Sstevel@tonic-gate {
5300Sstevel@tonic-gate 	const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;
5310Sstevel@tonic-gate 	fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
5320Sstevel@tonic-gate 	fmd_hdl_t *hdl = (fmd_hdl_t *)mp;
5330Sstevel@tonic-gate 	fmd_modtimer_t *t;
53412952SHyon.Kim@Sun.COM 	fmd_topo_t *old_topo;
5350Sstevel@tonic-gate 	volatile int err;
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 	/*
5380Sstevel@tonic-gate 	 * Before calling the appropriate module callback, enter the module as
5390Sstevel@tonic-gate 	 * if by fmd_module_enter() and establish mod_jmpbuf for any aborts.
5400Sstevel@tonic-gate 	 */
5410Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	ASSERT(!(mp->mod_flags & FMD_MOD_BUSY));
5440Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_BUSY;
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 	if ((err = setjmp(mp->mod_jmpbuf)) != 0) {
5470Sstevel@tonic-gate 		(void) pthread_mutex_lock(&mp->mod_lock);
5480Sstevel@tonic-gate 		fmd_module_error(mp, err);
5490Sstevel@tonic-gate 	}
5500Sstevel@tonic-gate 
5511193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
5520Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	/*
5550Sstevel@tonic-gate 	 * If it's the first time through fmd_module_dispatch(), call the
5560Sstevel@tonic-gate 	 * appropriate module callback based on the event type.  If the call
5570Sstevel@tonic-gate 	 * triggers an fmd_module_abort(), we'll return to setjmp() above with
5580Sstevel@tonic-gate 	 * err set to a non-zero value and then bypass this before exiting.
5590Sstevel@tonic-gate 	 */
5600Sstevel@tonic-gate 	if (err == 0) {
5610Sstevel@tonic-gate 		switch (ep->ev_type) {
5620Sstevel@tonic-gate 		case FMD_EVT_PROTOCOL:
5630Sstevel@tonic-gate 			ops->fmdo_recv(hdl, e, ep->ev_nvl, ep->ev_data);
5640Sstevel@tonic-gate 			break;
5650Sstevel@tonic-gate 		case FMD_EVT_TIMEOUT:
5660Sstevel@tonic-gate 			t = ep->ev_data;
5670Sstevel@tonic-gate 			ASSERT(t->mt_mod == mp);
5680Sstevel@tonic-gate 			ops->fmdo_timeout(hdl, t->mt_id, t->mt_arg);
5690Sstevel@tonic-gate 			break;
5700Sstevel@tonic-gate 		case FMD_EVT_CLOSE:
5710Sstevel@tonic-gate 			ops->fmdo_close(hdl, ep->ev_data);
5720Sstevel@tonic-gate 			break;
5730Sstevel@tonic-gate 		case FMD_EVT_STATS:
5740Sstevel@tonic-gate 			ops->fmdo_stats(hdl);
5750Sstevel@tonic-gate 			fmd_modstat_publish(mp);
5760Sstevel@tonic-gate 			break;
5770Sstevel@tonic-gate 		case FMD_EVT_GC:
5780Sstevel@tonic-gate 			ops->fmdo_gc(hdl);
5790Sstevel@tonic-gate 			break;
5801193Smws 		case FMD_EVT_PUBLISH:
5811193Smws 			fmd_case_publish(ep->ev_data, FMD_CASE_CURRENT);
5821193Smws 			break;
5834198Seschrock 		case FMD_EVT_TOPO:
58412952SHyon.Kim@Sun.COM 			/*
58512952SHyon.Kim@Sun.COM 			 * Save the pointer to the old topology and update
58612952SHyon.Kim@Sun.COM 			 * the pointer with the updated topology.
58712952SHyon.Kim@Sun.COM 			 * With this approach, other threads that reference the
58812952SHyon.Kim@Sun.COM 			 * topology either
58912952SHyon.Kim@Sun.COM 			 *  - finishes with old topology since
59012952SHyon.Kim@Sun.COM 			 *	it is released after updating
59112952SHyon.Kim@Sun.COM 			 *	mod_topo_current.
59212952SHyon.Kim@Sun.COM 			 *  - or is blocked while mod_topo_current is updated.
59312952SHyon.Kim@Sun.COM 			 */
59412952SHyon.Kim@Sun.COM 			old_topo = mp->mod_topo_current;
59512952SHyon.Kim@Sun.COM 			fmd_module_lock(mp);
5964198Seschrock 			mp->mod_topo_current = (fmd_topo_t *)ep->ev_data;
5974198Seschrock 			fmd_topo_addref(mp->mod_topo_current);
59812952SHyon.Kim@Sun.COM 			fmd_module_unlock(mp);
59912952SHyon.Kim@Sun.COM 			fmd_topo_rele(old_topo);
6004198Seschrock 			ops->fmdo_topo(hdl, mp->mod_topo_current->ft_hdl);
6014198Seschrock 			break;
6020Sstevel@tonic-gate 		}
6030Sstevel@tonic-gate 	}
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	fmd_module_exit(mp);
6060Sstevel@tonic-gate }
6070Sstevel@tonic-gate 
6081193Smws int
fmd_module_transport(fmd_module_t * mp,fmd_xprt_t * xp,fmd_event_t * e)6091193Smws fmd_module_transport(fmd_module_t *mp, fmd_xprt_t *xp, fmd_event_t *e)
6101193Smws {
6111193Smws 	fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
6121193Smws 	fmd_hdl_t *hdl = (fmd_hdl_t *)mp;
6131193Smws 
6141193Smws 	ASSERT(ep->ev_type == FMD_EVT_PROTOCOL);
6151193Smws 	return (mp->mod_info->fmdi_ops->fmdo_send(hdl, xp, e, ep->ev_nvl));
6161193Smws }
6171193Smws 
6180Sstevel@tonic-gate void
fmd_module_timeout(fmd_modtimer_t * t,id_t id,hrtime_t hrt)6190Sstevel@tonic-gate fmd_module_timeout(fmd_modtimer_t *t, id_t id, hrtime_t hrt)
6200Sstevel@tonic-gate {
6210Sstevel@tonic-gate 	fmd_event_t *e;
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 	t->mt_id = id; /* save id in case we need to delete from eventq */
6240Sstevel@tonic-gate 	e = fmd_event_create(FMD_EVT_TIMEOUT, hrt, NULL, t);
6250Sstevel@tonic-gate 	fmd_eventq_insert_at_time(t->mt_mod->mod_queue, e);
6260Sstevel@tonic-gate }
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate /*
6290Sstevel@tonic-gate  * Garbage collection is initiated by a timer callback once per day or at the
6300Sstevel@tonic-gate  * request of fmadm.  Purge old SERD entries and send the module a GC event.
6310Sstevel@tonic-gate  */
6320Sstevel@tonic-gate void
fmd_module_gc(fmd_module_t * mp)6330Sstevel@tonic-gate fmd_module_gc(fmd_module_t *mp)
6340Sstevel@tonic-gate {
6350Sstevel@tonic-gate 	fmd_hdl_info_t *info;
6360Sstevel@tonic-gate 	fmd_event_t *e;
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	if (mp->mod_error != 0)
6390Sstevel@tonic-gate 		return; /* do not do anything if the module has failed */
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	fmd_module_lock(mp);
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	if ((info = mp->mod_info) != NULL) {
6440Sstevel@tonic-gate 		fmd_serd_hash_apply(&mp->mod_serds,
6450Sstevel@tonic-gate 		    (fmd_serd_eng_f *)fmd_serd_eng_gc, NULL);
6460Sstevel@tonic-gate 	}
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 	fmd_module_unlock(mp);
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate 	if (info != NULL) {
6510Sstevel@tonic-gate 		e = fmd_event_create(FMD_EVT_GC, FMD_HRT_NOW, NULL, NULL);
6520Sstevel@tonic-gate 		fmd_eventq_insert_at_head(mp->mod_queue, e);
6530Sstevel@tonic-gate 	}
6540Sstevel@tonic-gate }
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate void
fmd_module_trygc(fmd_module_t * mp)6570Sstevel@tonic-gate fmd_module_trygc(fmd_module_t *mp)
6580Sstevel@tonic-gate {
6590Sstevel@tonic-gate 	if (fmd_module_trylock(mp)) {
6600Sstevel@tonic-gate 		fmd_serd_hash_apply(&mp->mod_serds,
6610Sstevel@tonic-gate 		    (fmd_serd_eng_f *)fmd_serd_eng_gc, NULL);
6620Sstevel@tonic-gate 		fmd_module_unlock(mp);
6630Sstevel@tonic-gate 	}
6640Sstevel@tonic-gate }
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate int
fmd_module_contains(fmd_module_t * mp,fmd_event_t * ep)6670Sstevel@tonic-gate fmd_module_contains(fmd_module_t *mp, fmd_event_t *ep)
6680Sstevel@tonic-gate {
6690Sstevel@tonic-gate 	fmd_case_t *cp;
6700Sstevel@tonic-gate 	int rv = 0;
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate 	fmd_module_lock(mp);
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 	for (cp = fmd_list_next(&mp->mod_cases);
6750Sstevel@tonic-gate 	    cp != NULL; cp = fmd_list_next(cp)) {
6760Sstevel@tonic-gate 		if ((rv = fmd_case_contains(cp, ep)) != 0)
6770Sstevel@tonic-gate 			break;
6780Sstevel@tonic-gate 	}
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	if (rv == 0)
6810Sstevel@tonic-gate 		rv = fmd_serd_hash_contains(&mp->mod_serds, ep);
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 	fmd_module_unlock(mp);
6840Sstevel@tonic-gate 	return (rv);
6850Sstevel@tonic-gate }
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate void
fmd_module_setdirty(fmd_module_t * mp)6880Sstevel@tonic-gate fmd_module_setdirty(fmd_module_t *mp)
6890Sstevel@tonic-gate {
6900Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
6910Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_MDIRTY;
6920Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate void
fmd_module_setcdirty(fmd_module_t * mp)6960Sstevel@tonic-gate fmd_module_setcdirty(fmd_module_t *mp)
6970Sstevel@tonic-gate {
6980Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
6990Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_CDIRTY;
7000Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
7010Sstevel@tonic-gate }
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate void
fmd_module_clrdirty(fmd_module_t * mp)7040Sstevel@tonic-gate fmd_module_clrdirty(fmd_module_t *mp)
7050Sstevel@tonic-gate {
7060Sstevel@tonic-gate 	fmd_case_t *cp;
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 	fmd_module_lock(mp);
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_CDIRTY) {
7110Sstevel@tonic-gate 		for (cp = fmd_list_next(&mp->mod_cases);
7120Sstevel@tonic-gate 		    cp != NULL; cp = fmd_list_next(cp))
7130Sstevel@tonic-gate 			fmd_case_clrdirty(cp);
7140Sstevel@tonic-gate 	}
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_MDIRTY) {
7170Sstevel@tonic-gate 		fmd_serd_hash_apply(&mp->mod_serds,
7180Sstevel@tonic-gate 		    (fmd_serd_eng_f *)fmd_serd_eng_clrdirty, NULL);
7190Sstevel@tonic-gate 		fmd_buf_hash_commit(&mp->mod_bufs);
7200Sstevel@tonic-gate 	}
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7230Sstevel@tonic-gate 	mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY);
7240Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	fmd_module_unlock(mp);
7270Sstevel@tonic-gate }
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate void
fmd_module_commit(fmd_module_t * mp)7300Sstevel@tonic-gate fmd_module_commit(fmd_module_t *mp)
7310Sstevel@tonic-gate {
7320Sstevel@tonic-gate 	fmd_case_t *cp;
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	ASSERT(fmd_module_locked(mp));
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_CDIRTY) {
7370Sstevel@tonic-gate 		for (cp = fmd_list_next(&mp->mod_cases);
7380Sstevel@tonic-gate 		    cp != NULL; cp = fmd_list_next(cp))
7390Sstevel@tonic-gate 			fmd_case_commit(cp);
7400Sstevel@tonic-gate 	}
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_MDIRTY) {
7430Sstevel@tonic-gate 		fmd_serd_hash_apply(&mp->mod_serds,
7440Sstevel@tonic-gate 		    (fmd_serd_eng_f *)fmd_serd_eng_commit, NULL);
7450Sstevel@tonic-gate 		fmd_buf_hash_commit(&mp->mod_bufs);
7460Sstevel@tonic-gate 	}
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7490Sstevel@tonic-gate 	mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY);
7500Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 	mp->mod_gen++;
7530Sstevel@tonic-gate }
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate void
fmd_module_lock(fmd_module_t * mp)7560Sstevel@tonic-gate fmd_module_lock(fmd_module_t *mp)
7570Sstevel@tonic-gate {
7580Sstevel@tonic-gate 	pthread_t self = pthread_self();
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	while (mp->mod_flags & FMD_MOD_LOCK) {
7630Sstevel@tonic-gate 		if (mp->mod_owner != self)
7640Sstevel@tonic-gate 			(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
7650Sstevel@tonic-gate 		else
7660Sstevel@tonic-gate 			fmd_panic("recursive module lock of %p\n", (void *)mp);
7670Sstevel@tonic-gate 	}
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	mp->mod_owner = self;
7700Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_LOCK;
7710Sstevel@tonic-gate 
7721193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
7730Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
7740Sstevel@tonic-gate }
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate void
fmd_module_unlock(fmd_module_t * mp)7770Sstevel@tonic-gate fmd_module_unlock(fmd_module_t *mp)
7780Sstevel@tonic-gate {
7790Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	ASSERT(mp->mod_owner == pthread_self());
7820Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_LOCK);
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate 	mp->mod_owner = 0;
7850Sstevel@tonic-gate 	mp->mod_flags &= ~FMD_MOD_LOCK;
7860Sstevel@tonic-gate 
7871193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
7880Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
7890Sstevel@tonic-gate }
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate int
fmd_module_trylock(fmd_module_t * mp)7920Sstevel@tonic-gate fmd_module_trylock(fmd_module_t *mp)
7930Sstevel@tonic-gate {
7940Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7950Sstevel@tonic-gate 
7960Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_LOCK) {
7970Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
7980Sstevel@tonic-gate 		return (0);
7990Sstevel@tonic-gate 	}
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	mp->mod_owner = pthread_self();
8020Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_LOCK;
8030Sstevel@tonic-gate 
8041193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
8050Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	return (1);
8080Sstevel@tonic-gate }
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate int
fmd_module_locked(fmd_module_t * mp)8110Sstevel@tonic-gate fmd_module_locked(fmd_module_t *mp)
8120Sstevel@tonic-gate {
8130Sstevel@tonic-gate 	return ((mp->mod_flags & FMD_MOD_LOCK) &&
8140Sstevel@tonic-gate 	    mp->mod_owner == pthread_self());
8150Sstevel@tonic-gate }
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate int
fmd_module_enter(fmd_module_t * mp,void (* func)(fmd_hdl_t *))8180Sstevel@tonic-gate fmd_module_enter(fmd_module_t *mp, void (*func)(fmd_hdl_t *))
8190Sstevel@tonic-gate {
8200Sstevel@tonic-gate 	volatile int err;
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	ASSERT(!(mp->mod_flags & FMD_MOD_BUSY));
8250Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_BUSY;
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 	if ((err = setjmp(mp->mod_jmpbuf)) != 0) {
8280Sstevel@tonic-gate 		(void) pthread_mutex_lock(&mp->mod_lock);
8290Sstevel@tonic-gate 		fmd_module_error(mp, err);
8300Sstevel@tonic-gate 	}
8310Sstevel@tonic-gate 
8321193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
8330Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate 	/*
8360Sstevel@tonic-gate 	 * If it's the first time through fmd_module_enter(), call the provided
8370Sstevel@tonic-gate 	 * function on the module.  If no fmd_module_abort() results, we will
8380Sstevel@tonic-gate 	 * fall through and return zero.  Otherwise we'll longjmp with an err,
8390Sstevel@tonic-gate 	 * return to the setjmp() above, and return the error to our caller.
8400Sstevel@tonic-gate 	 */
8410Sstevel@tonic-gate 	if (err == 0 && func != NULL)
8420Sstevel@tonic-gate 		(*func)((fmd_hdl_t *)mp);
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate 	return (err);
8450Sstevel@tonic-gate }
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate void
fmd_module_exit(fmd_module_t * mp)8480Sstevel@tonic-gate fmd_module_exit(fmd_module_t *mp)
8490Sstevel@tonic-gate {
8500Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_BUSY);
8530Sstevel@tonic-gate 	mp->mod_flags &= ~FMD_MOD_BUSY;
8540Sstevel@tonic-gate 
8551193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
8560Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
8570Sstevel@tonic-gate }
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate /*
8600Sstevel@tonic-gate  * If the client.error policy has been set by a developer, stop or dump core
8610Sstevel@tonic-gate  * based on the policy; if we stop and are resumed we'll continue and execute
8620Sstevel@tonic-gate  * the default behavior to discard events in fmd_module_start().  If the caller
8630Sstevel@tonic-gate  * is the primary module thread, we reach this state by longjmp'ing back to
8640Sstevel@tonic-gate  * fmd_module_enter(), above.  If the caller is an auxiliary thread, we cancel
8650Sstevel@tonic-gate  * ourself and arrange for the primary thread to call fmd_module_abort().
8660Sstevel@tonic-gate  */
8670Sstevel@tonic-gate void
fmd_module_abort(fmd_module_t * mp,int err)8680Sstevel@tonic-gate fmd_module_abort(fmd_module_t *mp, int err)
8690Sstevel@tonic-gate {
8700Sstevel@tonic-gate 	uint_t policy = FMD_CERROR_UNLOAD;
8710Sstevel@tonic-gate 	pthread_t tid = pthread_self();
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.error", &policy);
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 	if (policy == FMD_CERROR_STOP) {
8760Sstevel@tonic-gate 		fmd_error(err, "stopping after %s in client %s (%p)\n",
8770Sstevel@tonic-gate 		    fmd_errclass(err), mp->mod_name, (void *)mp);
8780Sstevel@tonic-gate 		(void) raise(SIGSTOP);
8790Sstevel@tonic-gate 	} else if (policy == FMD_CERROR_ABORT) {
8800Sstevel@tonic-gate 		fmd_panic("aborting due to %s in client %s (%p)\n",
8810Sstevel@tonic-gate 		    fmd_errclass(err), mp->mod_name, (void *)mp);
8820Sstevel@tonic-gate 	}
8830Sstevel@tonic-gate 
8840Sstevel@tonic-gate 	/*
8850Sstevel@tonic-gate 	 * If the caller is an auxiliary thread, cancel the current thread.  We
8860Sstevel@tonic-gate 	 * prefer to cancel because it affords developers the option of using
8870Sstevel@tonic-gate 	 * the pthread_cleanup* APIs.  If cancellations have been disabled,
8880Sstevel@tonic-gate 	 * fall through to forcing the current thread to exit.  In either case
8890Sstevel@tonic-gate 	 * we update mod_error (if zero) to enter the failed state.  Once that
8900Sstevel@tonic-gate 	 * is set, further events received by the module will be discarded.
8910Sstevel@tonic-gate 	 *
8920Sstevel@tonic-gate 	 * We also set the FMD_MOD_FAIL bit, indicating an unrecoverable error.
8930Sstevel@tonic-gate 	 * When an auxiliary thread fails, the module is left in a delicate
8940Sstevel@tonic-gate 	 * state where it is likely not able to continue execution (even to
8950Sstevel@tonic-gate 	 * execute its _fmd_fini() routine) because our caller may hold locks
8960Sstevel@tonic-gate 	 * that are private to the module and can no longer be released.  The
8970Sstevel@tonic-gate 	 * FMD_MOD_FAIL bit forces fmd_api_module_lock() to abort if any other
8980Sstevel@tonic-gate 	 * module threads reach an API call, in an attempt to get them to exit.
8990Sstevel@tonic-gate 	 */
9000Sstevel@tonic-gate 	if (tid != mp->mod_thread->thr_tid) {
9010Sstevel@tonic-gate 		(void) pthread_mutex_lock(&mp->mod_lock);
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 		if (mp->mod_error == 0)
9040Sstevel@tonic-gate 			mp->mod_error = err;
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate 		mp->mod_flags |= FMD_MOD_FAIL;
9070Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 		(void) pthread_cancel(tid);
9100Sstevel@tonic-gate 		pthread_exit(NULL);
9110Sstevel@tonic-gate 	}
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_BUSY);
9140Sstevel@tonic-gate 	longjmp(mp->mod_jmpbuf, err);
9150Sstevel@tonic-gate }
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate void
fmd_module_hold(fmd_module_t * mp)9180Sstevel@tonic-gate fmd_module_hold(fmd_module_t *mp)
9190Sstevel@tonic-gate {
9200Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 	TRACE((FMD_DBG_MOD, "hold %p (%s/%u)\n",
9230Sstevel@tonic-gate 	    (void *)mp, mp->mod_name, mp->mod_refs));
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 	mp->mod_refs++;
9260Sstevel@tonic-gate 	ASSERT(mp->mod_refs != 0);
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
9290Sstevel@tonic-gate }
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate void
fmd_module_rele(fmd_module_t * mp)9320Sstevel@tonic-gate fmd_module_rele(fmd_module_t *mp)
9330Sstevel@tonic-gate {
9340Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate 	TRACE((FMD_DBG_MOD, "rele %p (%s/%u)\n",
9370Sstevel@tonic-gate 	    (void *)mp, mp->mod_name, mp->mod_refs));
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 	ASSERT(mp->mod_refs != 0);
9401193Smws 
9411193Smws 	if (--mp->mod_refs == 0)
9420Sstevel@tonic-gate 		fmd_module_destroy(mp);
9431193Smws 	else
9441193Smws 		(void) pthread_mutex_unlock(&mp->mod_lock);
9450Sstevel@tonic-gate }
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate /*
9480Sstevel@tonic-gate  * Wrapper around libdiagcode's fm_dc_opendict() to load module dictionaries.
9490Sstevel@tonic-gate  * If the dictionary open is successful, the new dictionary is added to the
9500Sstevel@tonic-gate  * mod_dictv[] array and mod_codelen is updated with the new maximum length.
9510Sstevel@tonic-gate  */
9520Sstevel@tonic-gate int
fmd_module_dc_opendict(fmd_module_t * mp,const char * dict)9530Sstevel@tonic-gate fmd_module_dc_opendict(fmd_module_t *mp, const char *dict)
9540Sstevel@tonic-gate {
9550Sstevel@tonic-gate 	struct fm_dc_handle *dcp, **dcv;
9560Sstevel@tonic-gate 	char *dictdir, *dictnam, *p;
9570Sstevel@tonic-gate 	size_t len;
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 	ASSERT(fmd_module_locked(mp));
9600Sstevel@tonic-gate 
961*13093SRoger.Faulkner@Oracle.COM 	dictnam = strdupa(fmd_strbasename(dict));
9620Sstevel@tonic-gate 
9630Sstevel@tonic-gate 	if ((p = strrchr(dictnam, '.')) != NULL &&
9640Sstevel@tonic-gate 	    strcmp(p, ".dict") == 0)
9650Sstevel@tonic-gate 		*p = '\0'; /* eliminate any trailing .dict suffix */
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 	/*
9680Sstevel@tonic-gate 	 * If 'dict' is an absolute path, dictdir = $rootdir/`dirname dict`
9690Sstevel@tonic-gate 	 * If 'dict' is not an absolute path, dictdir = $dictdir/`dirname dict`
9700Sstevel@tonic-gate 	 */
9710Sstevel@tonic-gate 	if (dict[0] == '/') {
9720Sstevel@tonic-gate 		len = strlen(fmd.d_rootdir) + strlen(dict) + 1;
9730Sstevel@tonic-gate 		dictdir = alloca(len);
9740Sstevel@tonic-gate 		(void) snprintf(dictdir, len, "%s%s", fmd.d_rootdir, dict);
9750Sstevel@tonic-gate 		(void) fmd_strdirname(dictdir);
9760Sstevel@tonic-gate 	} else {
9770Sstevel@tonic-gate 		(void) fmd_conf_getprop(fmd.d_conf, "dictdir", &p);
9780Sstevel@tonic-gate 		len = strlen(fmd.d_rootdir) + strlen(p) + strlen(dict) + 3;
9790Sstevel@tonic-gate 		dictdir = alloca(len);
9800Sstevel@tonic-gate 		(void) snprintf(dictdir, len,
9810Sstevel@tonic-gate 		    "%s/%s/%s", fmd.d_rootdir, p, dict);
9820Sstevel@tonic-gate 		(void) fmd_strdirname(dictdir);
9830Sstevel@tonic-gate 	}
9840Sstevel@tonic-gate 
9850Sstevel@tonic-gate 	fmd_dprintf(FMD_DBG_MOD, "module %s opening %s -> %s/%s.dict\n",
9860Sstevel@tonic-gate 	    mp->mod_name, dict, dictdir, dictnam);
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 	if ((dcp = fm_dc_opendict(FM_DC_VERSION, dictdir, dictnam)) == NULL)
9890Sstevel@tonic-gate 		return (-1); /* errno is set for us */
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	dcv = fmd_alloc(sizeof (dcp) * (mp->mod_dictc + 1), FMD_SLEEP);
9920Sstevel@tonic-gate 	bcopy(mp->mod_dictv, dcv, sizeof (dcp) * mp->mod_dictc);
9930Sstevel@tonic-gate 	fmd_free(mp->mod_dictv, sizeof (dcp) * mp->mod_dictc);
9940Sstevel@tonic-gate 	mp->mod_dictv = dcv;
9950Sstevel@tonic-gate 	mp->mod_dictv[mp->mod_dictc++] = dcp;
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	len = fm_dc_codelen(dcp);
9980Sstevel@tonic-gate 	mp->mod_codelen = MAX(mp->mod_codelen, len);
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate 	return (0);
10010Sstevel@tonic-gate }
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate /*
10040Sstevel@tonic-gate  * Wrapper around libdiagcode's fm_dc_key2code() that examines all the module's
10050Sstevel@tonic-gate  * dictionaries.  We adhere to the libdiagcode return values and semantics.
10060Sstevel@tonic-gate  */
10070Sstevel@tonic-gate int
fmd_module_dc_key2code(fmd_module_t * mp,char * const keys[],char * code,size_t codelen)10080Sstevel@tonic-gate fmd_module_dc_key2code(fmd_module_t *mp,
10090Sstevel@tonic-gate     char *const keys[], char *code, size_t codelen)
10100Sstevel@tonic-gate {
10110Sstevel@tonic-gate 	int i, err;
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate 	for (i = 0; i < mp->mod_dictc; i++) {
10140Sstevel@tonic-gate 		if ((err = fm_dc_key2code(mp->mod_dictv[i], (const char **)keys,
10150Sstevel@tonic-gate 		    code, codelen)) == 0 || errno != ENOMSG)
10160Sstevel@tonic-gate 			return (err);
10170Sstevel@tonic-gate 	}
10180Sstevel@tonic-gate 
10190Sstevel@tonic-gate 	return (fmd_set_errno(ENOMSG));
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate fmd_modhash_t *
fmd_modhash_create(void)10230Sstevel@tonic-gate fmd_modhash_create(void)
10240Sstevel@tonic-gate {
10250Sstevel@tonic-gate 	fmd_modhash_t *mhp = fmd_alloc(sizeof (fmd_modhash_t), FMD_SLEEP);
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 	(void) pthread_rwlock_init(&mhp->mh_lock, NULL);
10280Sstevel@tonic-gate 	mhp->mh_hashlen = fmd.d_str_buckets;
10290Sstevel@tonic-gate 	mhp->mh_hash = fmd_zalloc(sizeof (void *) * mhp->mh_hashlen, FMD_SLEEP);
10300Sstevel@tonic-gate 	mhp->mh_nelems = 0;
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 	return (mhp);
10330Sstevel@tonic-gate }
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate void
fmd_modhash_destroy(fmd_modhash_t * mhp)10360Sstevel@tonic-gate fmd_modhash_destroy(fmd_modhash_t *mhp)
10370Sstevel@tonic-gate {
10380Sstevel@tonic-gate 	fmd_module_t *mp, *nmp;
10390Sstevel@tonic-gate 	uint_t i;
10400Sstevel@tonic-gate 
10410Sstevel@tonic-gate 	for (i = 0; i < mhp->mh_hashlen; i++) {
10420Sstevel@tonic-gate 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = nmp) {
10430Sstevel@tonic-gate 			nmp = mp->mod_next;
10440Sstevel@tonic-gate 			mp->mod_next = NULL;
10450Sstevel@tonic-gate 			fmd_module_rele(mp);
10460Sstevel@tonic-gate 		}
10470Sstevel@tonic-gate 	}
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 	fmd_free(mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen);
10500Sstevel@tonic-gate 	(void) pthread_rwlock_destroy(&mhp->mh_lock);
10510Sstevel@tonic-gate 	fmd_free(mhp, sizeof (fmd_modhash_t));
10520Sstevel@tonic-gate }
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate static void
fmd_modhash_loaddir(fmd_modhash_t * mhp,const char * dir,const fmd_modops_t * ops,const char * suffix)10550Sstevel@tonic-gate fmd_modhash_loaddir(fmd_modhash_t *mhp, const char *dir,
10561429Smws     const fmd_modops_t *ops, const char *suffix)
10570Sstevel@tonic-gate {
10580Sstevel@tonic-gate 	char path[PATH_MAX];
1059871Scasper 	struct dirent *dp;
10600Sstevel@tonic-gate 	const char *p;
10610Sstevel@tonic-gate 	DIR *dirp;
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate 	if ((dirp = opendir(dir)) == NULL)
10640Sstevel@tonic-gate 		return; /* failed to open directory; just skip it */
10650Sstevel@tonic-gate 
1066871Scasper 	while ((dp = readdir(dirp)) != NULL) {
10670Sstevel@tonic-gate 		if (dp->d_name[0] == '.')
10680Sstevel@tonic-gate 			continue; /* skip "." and ".." */
10690Sstevel@tonic-gate 
10701429Smws 		p = strrchr(dp->d_name, '.');
10711429Smws 
10721429Smws 		if (p != NULL && strcmp(p, ".conf") == 0)
10730Sstevel@tonic-gate 			continue; /* skip .conf files */
10740Sstevel@tonic-gate 
10751429Smws 		if (suffix != NULL && (p == NULL || strcmp(p, suffix) != 0))
10761429Smws 			continue; /* skip files with the wrong suffix */
10771429Smws 
10780Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s/%s", dir, dp->d_name);
10790Sstevel@tonic-gate 		(void) fmd_modhash_load(mhp, path, ops);
10800Sstevel@tonic-gate 	}
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 	(void) closedir(dirp);
10830Sstevel@tonic-gate }
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate void
fmd_modhash_loadall(fmd_modhash_t * mhp,const fmd_conf_path_t * pap,const fmd_modops_t * ops,const char * suffix)10860Sstevel@tonic-gate fmd_modhash_loadall(fmd_modhash_t *mhp, const fmd_conf_path_t *pap,
10871429Smws     const fmd_modops_t *ops, const char *suffix)
10880Sstevel@tonic-gate {
10890Sstevel@tonic-gate 	int i;
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	for (i = 0; i < pap->cpa_argc; i++)
10921429Smws 		fmd_modhash_loaddir(mhp, pap->cpa_argv[i], ops, suffix);
10930Sstevel@tonic-gate }
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate void
fmd_modhash_apply(fmd_modhash_t * mhp,void (* func)(fmd_module_t *))10960Sstevel@tonic-gate fmd_modhash_apply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *))
10970Sstevel@tonic-gate {
10980Sstevel@tonic-gate 	fmd_module_t *mp, *np;
10990Sstevel@tonic-gate 	uint_t i;
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 	for (i = 0; i < mhp->mh_hashlen; i++) {
11040Sstevel@tonic-gate 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) {
11050Sstevel@tonic-gate 			np = mp->mod_next;
11060Sstevel@tonic-gate 			func(mp);
11070Sstevel@tonic-gate 		}
11080Sstevel@tonic-gate 	}
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
11110Sstevel@tonic-gate }
11120Sstevel@tonic-gate 
11130Sstevel@tonic-gate void
fmd_modhash_tryapply(fmd_modhash_t * mhp,void (* func)(fmd_module_t *))11140Sstevel@tonic-gate fmd_modhash_tryapply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *))
11150Sstevel@tonic-gate {
11160Sstevel@tonic-gate 	fmd_module_t *mp, *np;
11170Sstevel@tonic-gate 	uint_t i;
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 	if (mhp == NULL || pthread_rwlock_tryrdlock(&mhp->mh_lock) != 0)
11200Sstevel@tonic-gate 		return; /* not initialized or couldn't grab lock */
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	for (i = 0; i < mhp->mh_hashlen; i++) {
11230Sstevel@tonic-gate 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) {
11240Sstevel@tonic-gate 			np = mp->mod_next;
11250Sstevel@tonic-gate 			func(mp);
11260Sstevel@tonic-gate 		}
11270Sstevel@tonic-gate 	}
11280Sstevel@tonic-gate 
11290Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
11300Sstevel@tonic-gate }
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate void
fmd_modhash_dispatch(fmd_modhash_t * mhp,fmd_event_t * ep)11330Sstevel@tonic-gate fmd_modhash_dispatch(fmd_modhash_t *mhp, fmd_event_t *ep)
11340Sstevel@tonic-gate {
11350Sstevel@tonic-gate 	fmd_module_t *mp;
11360Sstevel@tonic-gate 	uint_t i;
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 	fmd_event_hold(ep);
11390Sstevel@tonic-gate 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
11400Sstevel@tonic-gate 
11410Sstevel@tonic-gate 	for (i = 0; i < mhp->mh_hashlen; i++) {
11420Sstevel@tonic-gate 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) {
11430Sstevel@tonic-gate 			/*
11440Sstevel@tonic-gate 			 * If FMD_MOD_INIT is set but MOD_FINI, MOD_QUIT, and
11450Sstevel@tonic-gate 			 * mod_error are all zero, then the module is active:
11460Sstevel@tonic-gate 			 * enqueue the event in the corresponding event queue.
11470Sstevel@tonic-gate 			 */
11480Sstevel@tonic-gate 			(void) pthread_mutex_lock(&mp->mod_lock);
11490Sstevel@tonic-gate 
11500Sstevel@tonic-gate 			if ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI |
115112066SRobert.Johnston@Sun.COM 			    FMD_MOD_QUIT)) == FMD_MOD_INIT && !mp->mod_error) {
115212066SRobert.Johnston@Sun.COM 
115312066SRobert.Johnston@Sun.COM 				/*
115412066SRobert.Johnston@Sun.COM 				 * If the event we're dispatching is of type
115512066SRobert.Johnston@Sun.COM 				 * FMD_EVT_TOPO and there are already redundant
115612066SRobert.Johnston@Sun.COM 				 * FMD_EVT_TOPO events in this module's queue,
115712066SRobert.Johnston@Sun.COM 				 * then drop those before adding the new one.
115812066SRobert.Johnston@Sun.COM 				 */
115912066SRobert.Johnston@Sun.COM 				if (FMD_EVENT_TYPE(ep) == FMD_EVT_TOPO)
116012066SRobert.Johnston@Sun.COM 					fmd_eventq_drop_topo(mp->mod_queue);
116112066SRobert.Johnston@Sun.COM 
11620Sstevel@tonic-gate 				fmd_eventq_insert_at_time(mp->mod_queue, ep);
11630Sstevel@tonic-gate 
116412066SRobert.Johnston@Sun.COM 			}
11650Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&mp->mod_lock);
11660Sstevel@tonic-gate 		}
11670Sstevel@tonic-gate 	}
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
11700Sstevel@tonic-gate 	fmd_event_rele(ep);
11710Sstevel@tonic-gate }
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate fmd_module_t *
fmd_modhash_lookup(fmd_modhash_t * mhp,const char * name)11740Sstevel@tonic-gate fmd_modhash_lookup(fmd_modhash_t *mhp, const char *name)
11750Sstevel@tonic-gate {
11760Sstevel@tonic-gate 	fmd_module_t *mp;
11770Sstevel@tonic-gate 	uint_t h;
11780Sstevel@tonic-gate 
11790Sstevel@tonic-gate 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
11800Sstevel@tonic-gate 	h = fmd_strhash(name) % mhp->mh_hashlen;
11810Sstevel@tonic-gate 
11820Sstevel@tonic-gate 	for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) {
11830Sstevel@tonic-gate 		if (strcmp(name, mp->mod_name) == 0)
11840Sstevel@tonic-gate 			break;
11850Sstevel@tonic-gate 	}
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate 	if (mp != NULL)
11880Sstevel@tonic-gate 		fmd_module_hold(mp);
11890Sstevel@tonic-gate 	else
11900Sstevel@tonic-gate 		(void) fmd_set_errno(EFMD_MOD_NOMOD);
11910Sstevel@tonic-gate 
11920Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
11930Sstevel@tonic-gate 	return (mp);
11940Sstevel@tonic-gate }
11950Sstevel@tonic-gate 
11960Sstevel@tonic-gate fmd_module_t *
fmd_modhash_load(fmd_modhash_t * mhp,const char * path,const fmd_modops_t * ops)11970Sstevel@tonic-gate fmd_modhash_load(fmd_modhash_t *mhp, const char *path, const fmd_modops_t *ops)
11980Sstevel@tonic-gate {
11990Sstevel@tonic-gate 	char name[PATH_MAX], *p;
12000Sstevel@tonic-gate 	fmd_module_t *mp;
12010Sstevel@tonic-gate 	int tries = 0;
12020Sstevel@tonic-gate 	uint_t h;
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate 	(void) strlcpy(name, fmd_strbasename(path), sizeof (name));
12050Sstevel@tonic-gate 	if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0)
12060Sstevel@tonic-gate 		*p = '\0'; /* strip trailing .so from any module name */
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&mhp->mh_lock);
12090Sstevel@tonic-gate 	h = fmd_strhash(name) % mhp->mh_hashlen;
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate 	/*
12120Sstevel@tonic-gate 	 * First check to see if a module is already present in the hash table
12130Sstevel@tonic-gate 	 * for this name.  If so, the module is already loaded: skip it.
12140Sstevel@tonic-gate 	 */
12150Sstevel@tonic-gate 	for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) {
12160Sstevel@tonic-gate 		if (strcmp(name, mp->mod_name) == 0)
12170Sstevel@tonic-gate 			break;
12180Sstevel@tonic-gate 	}
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 	if (mp != NULL) {
12210Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&mhp->mh_lock);
12220Sstevel@tonic-gate 		(void) fmd_set_errno(EFMD_MOD_LOADED);
12230Sstevel@tonic-gate 		return (NULL);
12240Sstevel@tonic-gate 	}
12250Sstevel@tonic-gate 
12260Sstevel@tonic-gate 	/*
12270Sstevel@tonic-gate 	 * fmd_module_create() will return a held (as if by fmd_module_hold())
12280Sstevel@tonic-gate 	 * module.  We leave this hold in place to correspond to the hash-in.
12290Sstevel@tonic-gate 	 */
12300Sstevel@tonic-gate 	while ((mp = fmd_module_create(path, ops)) == NULL) {
12310Sstevel@tonic-gate 		if (tries++ != 0 || errno != EFMD_CKPT_INVAL) {
12320Sstevel@tonic-gate 			(void) pthread_rwlock_unlock(&mhp->mh_lock);
12330Sstevel@tonic-gate 			return (NULL); /* errno is set for us */
12340Sstevel@tonic-gate 		}
12350Sstevel@tonic-gate 	}
12360Sstevel@tonic-gate 
12370Sstevel@tonic-gate 	mp->mod_hash = mhp;
12380Sstevel@tonic-gate 	mp->mod_next = mhp->mh_hash[h];
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 	mhp->mh_hash[h] = mp;
12410Sstevel@tonic-gate 	mhp->mh_nelems++;
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
12440Sstevel@tonic-gate 	return (mp);
12450Sstevel@tonic-gate }
12460Sstevel@tonic-gate 
12470Sstevel@tonic-gate int
fmd_modhash_unload(fmd_modhash_t * mhp,const char * name)12480Sstevel@tonic-gate fmd_modhash_unload(fmd_modhash_t *mhp, const char *name)
12490Sstevel@tonic-gate {
12500Sstevel@tonic-gate 	fmd_module_t *mp, **pp;
12510Sstevel@tonic-gate 	uint_t h;
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&mhp->mh_lock);
12540Sstevel@tonic-gate 	h = fmd_strhash(name) % mhp->mh_hashlen;
12550Sstevel@tonic-gate 	pp = &mhp->mh_hash[h];
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 	for (mp = *pp; mp != NULL; mp = mp->mod_next) {
12580Sstevel@tonic-gate 		if (strcmp(name, mp->mod_name) == 0)
12590Sstevel@tonic-gate 			break;
12600Sstevel@tonic-gate 		else
12610Sstevel@tonic-gate 			pp = &mp->mod_next;
12620Sstevel@tonic-gate 	}
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	if (mp == NULL) {
12650Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&mhp->mh_lock);
12660Sstevel@tonic-gate 		return (fmd_set_errno(EFMD_MOD_NOMOD));
12670Sstevel@tonic-gate 	}
12680Sstevel@tonic-gate 
12690Sstevel@tonic-gate 	*pp = mp->mod_next;
12700Sstevel@tonic-gate 	mp->mod_next = NULL;
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 	ASSERT(mhp->mh_nelems != 0);
12730Sstevel@tonic-gate 	mhp->mh_nelems--;
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
12760Sstevel@tonic-gate 
12770Sstevel@tonic-gate 	fmd_module_unload(mp);
12780Sstevel@tonic-gate 	fmd_module_rele(mp);
12790Sstevel@tonic-gate 
12800Sstevel@tonic-gate 	return (0);
12810Sstevel@tonic-gate }
12820Sstevel@tonic-gate 
12830Sstevel@tonic-gate void
fmd_modstat_publish(fmd_module_t * mp)12840Sstevel@tonic-gate fmd_modstat_publish(fmd_module_t *mp)
12850Sstevel@tonic-gate {
12860Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
12870Sstevel@tonic-gate 
12880Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_STSUB);
12890Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_STPUB;
12900Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
12910Sstevel@tonic-gate 
12920Sstevel@tonic-gate 	while (mp->mod_flags & FMD_MOD_STPUB)
12930Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
12940Sstevel@tonic-gate 
12950Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
12960Sstevel@tonic-gate }
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate int
fmd_modstat_snapshot(fmd_module_t * mp,fmd_ustat_snap_t * uss)12990Sstevel@tonic-gate fmd_modstat_snapshot(fmd_module_t *mp, fmd_ustat_snap_t *uss)
13000Sstevel@tonic-gate {
13010Sstevel@tonic-gate 	fmd_event_t *e;
13020Sstevel@tonic-gate 	int err;
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate 	/*
13050Sstevel@tonic-gate 	 * Grab the module lock and wait for the STSUB bit to be clear.  Then
13060Sstevel@tonic-gate 	 * set it to indicate we are a subscriber and everyone else must wait.
13070Sstevel@tonic-gate 	 */
13080Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
13090Sstevel@tonic-gate 
13100Sstevel@tonic-gate 	while (mp->mod_error == 0 && (mp->mod_flags & FMD_MOD_STSUB))
13110Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 	if (mp->mod_error != 0) {
13140Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
13150Sstevel@tonic-gate 		return (fmd_set_errno(EFMD_HDL_ABORT));
13160Sstevel@tonic-gate 	}
13170Sstevel@tonic-gate 
13180Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_STSUB;
13191193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
13200Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
13210Sstevel@tonic-gate 
13220Sstevel@tonic-gate 	/*
13230Sstevel@tonic-gate 	 * Create a stats pseudo-event and dispatch it to the module, forcing
13240Sstevel@tonic-gate 	 * it to next execute its custom snapshot routine (or the empty one).
13250Sstevel@tonic-gate 	 */
13260Sstevel@tonic-gate 	e = fmd_event_create(FMD_EVT_STATS, FMD_HRT_NOW, NULL, NULL);
13270Sstevel@tonic-gate 	fmd_eventq_insert_at_head(mp->mod_queue, e);
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 	/*
13300Sstevel@tonic-gate 	 * Grab the module lock and then wait on mod_cv for STPUB to be set,
13310Sstevel@tonic-gate 	 * indicating the snapshot routine is completed and the module is idle.
13320Sstevel@tonic-gate 	 */
13330Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
13340Sstevel@tonic-gate 
133512618SStephen.Hanson@Sun.COM 	while (mp->mod_error == 0 && !(mp->mod_flags & FMD_MOD_STPUB)) {
133612618SStephen.Hanson@Sun.COM 		struct timespec tms;
133712618SStephen.Hanson@Sun.COM 
13380Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
133912618SStephen.Hanson@Sun.COM 		(void) pthread_mutex_unlock(&mp->mod_lock);
134012618SStephen.Hanson@Sun.COM 		tms.tv_sec = 0;
134112618SStephen.Hanson@Sun.COM 		tms.tv_nsec = 10000000;
134212618SStephen.Hanson@Sun.COM 		(void) nanosleep(&tms, NULL);
134312618SStephen.Hanson@Sun.COM 		(void) pthread_mutex_lock(&mp->mod_lock);
134412618SStephen.Hanson@Sun.COM 	}
13450Sstevel@tonic-gate 
13460Sstevel@tonic-gate 	if (mp->mod_error != 0) {
13470Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
13480Sstevel@tonic-gate 		return (fmd_set_errno(EFMD_HDL_ABORT));
13490Sstevel@tonic-gate 	}
13500Sstevel@tonic-gate 
13511193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
13520Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
13530Sstevel@tonic-gate 
13540Sstevel@tonic-gate 	/*
13550Sstevel@tonic-gate 	 * Update ms_snaptime and take the actual snapshot of the various
13560Sstevel@tonic-gate 	 * statistics while the module is quiescent and waiting for us.
13570Sstevel@tonic-gate 	 */
13580Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_stats_lock);
13590Sstevel@tonic-gate 
13600Sstevel@tonic-gate 	if (mp->mod_stats != NULL) {
13610Sstevel@tonic-gate 		mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime();
13620Sstevel@tonic-gate 		err = fmd_ustat_snapshot(mp->mod_ustat, uss);
13630Sstevel@tonic-gate 	} else
13640Sstevel@tonic-gate 		err = fmd_set_errno(EFMD_HDL_ABORT);
13650Sstevel@tonic-gate 
13660Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_stats_lock);
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate 	/*
13690Sstevel@tonic-gate 	 * With the snapshot complete, grab the module lock and clear both
13700Sstevel@tonic-gate 	 * STSUB and STPUB, permitting everyone to wake up and continue.
13710Sstevel@tonic-gate 	 */
13720Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
13730Sstevel@tonic-gate 
13740Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_STSUB);
13750Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_STPUB);
13760Sstevel@tonic-gate 	mp->mod_flags &= ~(FMD_MOD_STSUB | FMD_MOD_STPUB);
13770Sstevel@tonic-gate 
13781193Smws 	(void) pthread_cond_broadcast(&mp->mod_cv);
13790Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
13800Sstevel@tonic-gate 
13810Sstevel@tonic-gate 	return (err);
13820Sstevel@tonic-gate }
13834198Seschrock 
13844198Seschrock struct topo_hdl *
fmd_module_topo_hold(fmd_module_t * mp)13854198Seschrock fmd_module_topo_hold(fmd_module_t *mp)
13864198Seschrock {
13874198Seschrock 	fmd_modtopo_t *mtp;
13884198Seschrock 
13894198Seschrock 	ASSERT(fmd_module_locked(mp));
13904198Seschrock 
13914198Seschrock 	mtp = fmd_zalloc(sizeof (fmd_modtopo_t), FMD_SLEEP);
13924198Seschrock 	mtp->mt_topo = mp->mod_topo_current;
13934198Seschrock 	fmd_topo_addref(mtp->mt_topo);
13944198Seschrock 	fmd_list_prepend(&mp->mod_topolist, mtp);
13954198Seschrock 
13964198Seschrock 	return (mtp->mt_topo->ft_hdl);
13974198Seschrock }
13984198Seschrock 
13994198Seschrock int
fmd_module_topo_rele(fmd_module_t * mp,struct topo_hdl * hdl)14004198Seschrock fmd_module_topo_rele(fmd_module_t *mp, struct topo_hdl *hdl)
14014198Seschrock {
14024198Seschrock 	fmd_modtopo_t *mtp;
14034198Seschrock 
14044198Seschrock 	ASSERT(fmd_module_locked(mp));
14054198Seschrock 
14064198Seschrock 	for (mtp = fmd_list_next(&mp->mod_topolist); mtp != NULL;
14074198Seschrock 	    mtp = fmd_list_next(mtp)) {
14084198Seschrock 		if (mtp->mt_topo->ft_hdl == hdl)
14094198Seschrock 			break;
14104198Seschrock 	}
14114198Seschrock 
14124198Seschrock 	if (mtp == NULL)
14134198Seschrock 		return (-1);
14144198Seschrock 
14154198Seschrock 	fmd_list_delete(&mp->mod_topolist, mtp);
14164198Seschrock 	fmd_topo_rele(mtp->mt_topo);
14174198Seschrock 	fmd_free(mtp, sizeof (fmd_modtopo_t));
14184198Seschrock 	return (0);
14194198Seschrock }
1420