xref: /titanic_44/usr/src/cmd/fm/fmd/common/fmd_module.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
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
524db4641Seschrock  * Common Development and Distribution License (the "License").
624db4641Seschrock  * 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  */
210b9e3e76Smws 
227c478bd9Sstevel@tonic-gate /*
232a417b23SRobert Johnston  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <signal.h>
277c478bd9Sstevel@tonic-gate #include <dirent.h>
287c478bd9Sstevel@tonic-gate #include <limits.h>
297c478bd9Sstevel@tonic-gate #include <alloca.h>
307c478bd9Sstevel@tonic-gate #include <unistd.h>
317c478bd9Sstevel@tonic-gate #include <stdio.h>
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <fmd_string.h>
347c478bd9Sstevel@tonic-gate #include <fmd_alloc.h>
357c478bd9Sstevel@tonic-gate #include <fmd_module.h>
367c478bd9Sstevel@tonic-gate #include <fmd_error.h>
377c478bd9Sstevel@tonic-gate #include <fmd_conf.h>
387c478bd9Sstevel@tonic-gate #include <fmd_dispq.h>
397c478bd9Sstevel@tonic-gate #include <fmd_eventq.h>
407c478bd9Sstevel@tonic-gate #include <fmd_timerq.h>
417c478bd9Sstevel@tonic-gate #include <fmd_subr.h>
427c478bd9Sstevel@tonic-gate #include <fmd_thread.h>
437c478bd9Sstevel@tonic-gate #include <fmd_ustat.h>
447c478bd9Sstevel@tonic-gate #include <fmd_case.h>
457c478bd9Sstevel@tonic-gate #include <fmd_protocol.h>
467c478bd9Sstevel@tonic-gate #include <fmd_buf.h>
477c478bd9Sstevel@tonic-gate #include <fmd_ckpt.h>
48d9638e54Smws #include <fmd_xprt.h>
4924db4641Seschrock #include <fmd_topo.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate #include <fmd.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate /*
547c478bd9Sstevel@tonic-gate  * Template for per-module statistics installed by fmd on behalf of each active
557c478bd9Sstevel@tonic-gate  * module.  These are used to initialize the per-module mp->mod_stats below.
567c478bd9Sstevel@tonic-gate  * NOTE: FMD_TYPE_STRING statistics should not be used here.  If they are
577c478bd9Sstevel@tonic-gate  * required in the future, the FMD_ADM_MODDSTAT service routine must change.
587c478bd9Sstevel@tonic-gate  */
597c478bd9Sstevel@tonic-gate static const fmd_modstat_t _fmd_modstat_tmpl = {
60d9638e54Smws {
617c478bd9Sstevel@tonic-gate { "fmd.dispatched", FMD_TYPE_UINT64, "total events dispatched to module" },
627c478bd9Sstevel@tonic-gate { "fmd.dequeued", FMD_TYPE_UINT64, "total events dequeued by module" },
637c478bd9Sstevel@tonic-gate { "fmd.prdequeued", FMD_TYPE_UINT64, "protocol events dequeued by module" },
647c478bd9Sstevel@tonic-gate { "fmd.dropped", FMD_TYPE_UINT64, "total events dropped on queue overflow" },
657c478bd9Sstevel@tonic-gate { "fmd.wcnt", FMD_TYPE_UINT32, "count of events waiting on queue" },
667c478bd9Sstevel@tonic-gate { "fmd.wtime", FMD_TYPE_TIME, "total wait time on queue" },
677c478bd9Sstevel@tonic-gate { "fmd.wlentime", FMD_TYPE_TIME, "total wait length * time product" },
687c478bd9Sstevel@tonic-gate { "fmd.wlastupdate", FMD_TYPE_TIME, "hrtime of last wait queue update" },
697c478bd9Sstevel@tonic-gate { "fmd.dtime", FMD_TYPE_TIME, "total processing time after dequeue" },
707c478bd9Sstevel@tonic-gate { "fmd.dlastupdate", FMD_TYPE_TIME, "hrtime of last event dequeue completion" },
71d9638e54Smws },
72d9638e54Smws { "fmd.loadtime", FMD_TYPE_TIME, "hrtime at which module was loaded" },
73d9638e54Smws { "fmd.snaptime", FMD_TYPE_TIME, "hrtime of last statistics snapshot" },
74d9638e54Smws { "fmd.accepted", FMD_TYPE_UINT64, "total events accepted by module" },
757c478bd9Sstevel@tonic-gate { "fmd.debugdrop", FMD_TYPE_UINT64, "dropped debug messages" },
767c478bd9Sstevel@tonic-gate { "fmd.memtotal", FMD_TYPE_SIZE, "total memory allocated by module" },
777c478bd9Sstevel@tonic-gate { "fmd.memlimit", FMD_TYPE_SIZE, "limit on total memory allocated" },
787c478bd9Sstevel@tonic-gate { "fmd.buftotal", FMD_TYPE_SIZE, "total buffer space used by module" },
797c478bd9Sstevel@tonic-gate { "fmd.buflimit", FMD_TYPE_SIZE, "limit on total buffer space" },
807c478bd9Sstevel@tonic-gate { "fmd.thrtotal", FMD_TYPE_UINT32, "total number of auxiliary threads" },
817c478bd9Sstevel@tonic-gate { "fmd.thrlimit", FMD_TYPE_UINT32, "limit on number of auxiliary threads" },
82f6e214c7SGavin Maltby { "fmd.doorthrtotal", FMD_TYPE_UINT32, "total number of door server threads" },
83f6e214c7SGavin Maltby { "fmd.doorthrlimit", FMD_TYPE_UINT32, "limit on door server threads" },
847c478bd9Sstevel@tonic-gate { "fmd.caseopen", FMD_TYPE_UINT64, "cases currently open by module" },
857c478bd9Sstevel@tonic-gate { "fmd.casesolved", FMD_TYPE_UINT64, "total cases solved by module" },
867c478bd9Sstevel@tonic-gate { "fmd.caseclosed", FMD_TYPE_UINT64, "total cases closed by module" },
877c478bd9Sstevel@tonic-gate { "fmd.ckptsave", FMD_TYPE_BOOL, "save checkpoints for module" },
887c478bd9Sstevel@tonic-gate { "fmd.ckptrestore", FMD_TYPE_BOOL, "restore checkpoints for module" },
897c478bd9Sstevel@tonic-gate { "fmd.ckptzero", FMD_TYPE_BOOL, "zeroed checkpoint at startup" },
907c478bd9Sstevel@tonic-gate { "fmd.ckptcnt", FMD_TYPE_UINT64, "number of checkpoints taken" },
917c478bd9Sstevel@tonic-gate { "fmd.ckpttime", FMD_TYPE_TIME, "total checkpoint time" },
92d9638e54Smws { "fmd.xprtopen", FMD_TYPE_UINT32, "total number of open transports" },
93d9638e54Smws { "fmd.xprtlimit", FMD_TYPE_UINT32, "limit on number of open transports" },
94d9638e54Smws { "fmd.xprtqlimit", FMD_TYPE_UINT32, "limit on transport event queue length" },
957c478bd9Sstevel@tonic-gate };
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate static void
fmd_module_start(void * arg)987c478bd9Sstevel@tonic-gate fmd_module_start(void *arg)
997c478bd9Sstevel@tonic-gate {
1007c478bd9Sstevel@tonic-gate 	fmd_module_t *mp = arg;
1017c478bd9Sstevel@tonic-gate 	fmd_event_t *ep;
102d9638e54Smws 	fmd_xprt_t *xp;
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	if (mp->mod_ops->mop_init(mp) != 0 || mp->mod_error != 0) {
1077c478bd9Sstevel@tonic-gate 		if (mp->mod_error == 0)
1087c478bd9Sstevel@tonic-gate 			mp->mod_error = errno ? errno : EFMD_MOD_INIT;
1097c478bd9Sstevel@tonic-gate 		goto out;
1107c478bd9Sstevel@tonic-gate 	}
1117c478bd9Sstevel@tonic-gate 
112d9638e54Smws 	if (fmd.d_mod_event != NULL)
113d9638e54Smws 		fmd_eventq_insert_at_head(mp->mod_queue, fmd.d_mod_event);
114d9638e54Smws 
1157c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&mp->mod_lock));
1167c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_INIT;
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
119d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
120d9638e54Smws 
121d9638e54Smws 	/*
122d9638e54Smws 	 * If the module opened any transports while executing _fmd_init(),
123d9638e54Smws 	 * they are suspended. Now that _fmd_init() is done, wake them up.
124d9638e54Smws 	 */
125d9638e54Smws 	for (xp = fmd_list_next(&mp->mod_transports);
126d9638e54Smws 	    xp != NULL; xp = fmd_list_next(xp))
127d9638e54Smws 		fmd_xprt_xresume(xp, FMD_XPRT_ISUSPENDED);
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	/*
1307c478bd9Sstevel@tonic-gate 	 * Wait for events to arrive by checking mod_error and then sleeping in
1317c478bd9Sstevel@tonic-gate 	 * fmd_eventq_delete().  If a NULL event is returned, the eventq has
1327c478bd9Sstevel@tonic-gate 	 * been aborted and we continue on to call fini and exit the thread.
1337c478bd9Sstevel@tonic-gate 	 */
1347c478bd9Sstevel@tonic-gate 	while ((ep = fmd_eventq_delete(mp->mod_queue)) != NULL) {
1357c478bd9Sstevel@tonic-gate 		/*
136d9638e54Smws 		 * If the module has failed, discard the event without ever
137d9638e54Smws 		 * passing it to the module and go back to sleep.
1387c478bd9Sstevel@tonic-gate 		 */
139d9638e54Smws 		if (mp->mod_error != 0) {
140d9638e54Smws 			fmd_eventq_done(mp->mod_queue);
1417c478bd9Sstevel@tonic-gate 			fmd_event_rele(ep);
1427c478bd9Sstevel@tonic-gate 			continue;
1437c478bd9Sstevel@tonic-gate 		}
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 		mp->mod_ops->mop_dispatch(mp, ep);
146d9638e54Smws 		fmd_eventq_done(mp->mod_queue);
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 		/*
1497c478bd9Sstevel@tonic-gate 		 * Once mop_dispatch() is complete, grab the lock and perform
1507c478bd9Sstevel@tonic-gate 		 * any event-specific post-processing.  Finally, if necessary,
1517c478bd9Sstevel@tonic-gate 		 * checkpoint the state of the module after this event.
1527c478bd9Sstevel@tonic-gate 		 */
1537c478bd9Sstevel@tonic-gate 		fmd_module_lock(mp);
1547c478bd9Sstevel@tonic-gate 
155d9638e54Smws 		if (FMD_EVENT_TYPE(ep) == FMD_EVT_CLOSE)
156d9638e54Smws 			fmd_case_delete(FMD_EVENT_DATA(ep));
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 		fmd_ckpt_save(mp);
1597c478bd9Sstevel@tonic-gate 		fmd_module_unlock(mp);
1607c478bd9Sstevel@tonic-gate 		fmd_event_rele(ep);
1617c478bd9Sstevel@tonic-gate 	}
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	if (mp->mod_ops->mop_fini(mp) != 0 && mp->mod_error == 0)
1647c478bd9Sstevel@tonic-gate 		mp->mod_error = errno ? errno : EFMD_MOD_FINI;
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
1677c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_FINI;
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate out:
1707c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
171d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
1727c478bd9Sstevel@tonic-gate }
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate fmd_module_t *
fmd_module_create(const char * path,const fmd_modops_t * ops)1757c478bd9Sstevel@tonic-gate fmd_module_create(const char *path, const fmd_modops_t *ops)
1767c478bd9Sstevel@tonic-gate {
1777c478bd9Sstevel@tonic-gate 	fmd_module_t *mp = fmd_zalloc(sizeof (fmd_module_t), FMD_SLEEP);
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 	char buf[PATH_MAX], *p;
1807c478bd9Sstevel@tonic-gate 	const char *dir;
1817c478bd9Sstevel@tonic-gate 	uint32_t limit;
1827c478bd9Sstevel@tonic-gate 	int err;
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	(void) strlcpy(buf, fmd_strbasename(path), sizeof (buf));
1857c478bd9Sstevel@tonic-gate 	if ((p = strrchr(buf, '.')) != NULL && strcmp(p, ".so") == 0)
1867c478bd9Sstevel@tonic-gate 		*p = '\0'; /* strip trailing .so from any module name */
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_init(&mp->mod_lock, NULL);
1897c478bd9Sstevel@tonic-gate 	(void) pthread_cond_init(&mp->mod_cv, NULL);
1907c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_init(&mp->mod_stats_lock, NULL);
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	mp->mod_name = fmd_strdup(buf, FMD_SLEEP);
1937c478bd9Sstevel@tonic-gate 	mp->mod_path = fmd_strdup(path, FMD_SLEEP);
1947c478bd9Sstevel@tonic-gate 	mp->mod_ops = ops;
1957c478bd9Sstevel@tonic-gate 	mp->mod_ustat = fmd_ustat_create();
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.dir", &dir);
1987c478bd9Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf),
1997c478bd9Sstevel@tonic-gate 	    "%s/%s/%s", fmd.d_rootdir, dir, mp->mod_name);
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	mp->mod_ckpt = fmd_strdup(buf, FMD_SLEEP);
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.tmrlim", &limit);
2047c478bd9Sstevel@tonic-gate 	mp->mod_timerids = fmd_idspace_create(mp->mod_name, 1, limit + 1);
2057c478bd9Sstevel@tonic-gate 	mp->mod_threads = fmd_idspace_create(mp->mod_name, 0, INT_MAX);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	fmd_buf_hash_create(&mp->mod_bufs);
2087c478bd9Sstevel@tonic-gate 	fmd_serd_hash_create(&mp->mod_serds);
2097c478bd9Sstevel@tonic-gate 
21024db4641Seschrock 	mp->mod_topo_current = fmd_topo_hold();
21124db4641Seschrock 
2127c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&fmd.d_mod_lock);
2137c478bd9Sstevel@tonic-gate 	fmd_list_append(&fmd.d_mod_list, mp);
2147c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&fmd.d_mod_lock);
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	/*
2177c478bd9Sstevel@tonic-gate 	 * Initialize the module statistics that are kept on its behalf by fmd.
2187c478bd9Sstevel@tonic-gate 	 * These are set up using a template defined at the top of this file.
2197c478bd9Sstevel@tonic-gate 	 */
2207c478bd9Sstevel@tonic-gate 	if ((mp->mod_stats = (fmd_modstat_t *)fmd_ustat_insert(mp->mod_ustat,
2217c478bd9Sstevel@tonic-gate 	    FMD_USTAT_ALLOC, sizeof (_fmd_modstat_tmpl) / sizeof (fmd_stat_t),
2227c478bd9Sstevel@tonic-gate 	    (fmd_stat_t *)&_fmd_modstat_tmpl, NULL)) == NULL) {
2237c478bd9Sstevel@tonic-gate 		fmd_error(EFMD_MOD_INIT, "failed to initialize per-mod stats");
2247c478bd9Sstevel@tonic-gate 		fmd_module_destroy(mp);
2257c478bd9Sstevel@tonic-gate 		return (NULL);
2267c478bd9Sstevel@tonic-gate 	}
2277c478bd9Sstevel@tonic-gate 
2289af3851aSeschrock 	if (nv_alloc_init(&mp->mod_nva_sleep,
2299af3851aSeschrock 	    &fmd_module_nva_ops_sleep, mp) != 0 ||
2309af3851aSeschrock 	    nv_alloc_init(&mp->mod_nva_nosleep,
2319af3851aSeschrock 	    &fmd_module_nva_ops_nosleep, mp) != 0) {
2329af3851aSeschrock 		fmd_error(EFMD_MOD_INIT, "failed to initialize nvlist "
2339af3851aSeschrock 		    "allocation routines");
2349af3851aSeschrock 		fmd_module_destroy(mp);
2359af3851aSeschrock 		return (NULL);
2369af3851aSeschrock 	}
2379af3851aSeschrock 
238d9638e54Smws 	(void) fmd_conf_getprop(fmd.d_conf, "client.evqlim", &limit);
239d9638e54Smws 
240d9638e54Smws 	mp->mod_queue = fmd_eventq_create(mp,
241d9638e54Smws 	    &mp->mod_stats->ms_evqstat, &mp->mod_stats_lock, limit);
242d9638e54Smws 
2437c478bd9Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.memlim",
2447c478bd9Sstevel@tonic-gate 	    &mp->mod_stats->ms_memlimit.fmds_value.ui64);
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.buflim",
2477c478bd9Sstevel@tonic-gate 	    &mp->mod_stats->ms_buflimit.fmds_value.ui64);
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.thrlim",
2507c478bd9Sstevel@tonic-gate 	    &mp->mod_stats->ms_thrlimit.fmds_value.ui32);
2517c478bd9Sstevel@tonic-gate 
252f6e214c7SGavin Maltby 	(void) fmd_conf_getprop(fmd.d_conf, "client.doorthrlim",
253f6e214c7SGavin Maltby 	    &mp->mod_stats->ms_doorthrlimit.fmds_value.ui32);
254f6e214c7SGavin Maltby 
255d9638e54Smws 	(void) fmd_conf_getprop(fmd.d_conf, "client.xprtlim",
256d9638e54Smws 	    &mp->mod_stats->ms_xprtlimit.fmds_value.ui32);
257d9638e54Smws 
258d9638e54Smws 	(void) fmd_conf_getprop(fmd.d_conf, "client.xprtqlim",
259d9638e54Smws 	    &mp->mod_stats->ms_xprtqlimit.fmds_value.ui32);
260d9638e54Smws 
2617c478bd9Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.save",
2627c478bd9Sstevel@tonic-gate 	    &mp->mod_stats->ms_ckpt_save.fmds_value.bool);
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.restore",
2657c478bd9Sstevel@tonic-gate 	    &mp->mod_stats->ms_ckpt_restore.fmds_value.bool);
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.zero",
2687c478bd9Sstevel@tonic-gate 	    &mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool);
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	if (mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool)
2717c478bd9Sstevel@tonic-gate 		fmd_ckpt_delete(mp); /* blow away any pre-existing checkpoint */
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	/*
2747c478bd9Sstevel@tonic-gate 	 * Place a hold on the module and grab the module lock before creating
2757c478bd9Sstevel@tonic-gate 	 * the module's thread to ensure that it cannot destroy the module and
2767c478bd9Sstevel@tonic-gate 	 * that it cannot call ops->mop_init() before we're done setting up.
2777c478bd9Sstevel@tonic-gate 	 * NOTE: from now on, we must use fmd_module_rele() for error paths.
2787c478bd9Sstevel@tonic-gate 	 */
2797c478bd9Sstevel@tonic-gate 	fmd_module_hold(mp);
2807c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
2817c478bd9Sstevel@tonic-gate 	mp->mod_stats->ms_loadtime.fmds_value.ui64 = gethrtime();
2827c478bd9Sstevel@tonic-gate 	mp->mod_thread = fmd_thread_create(mp, fmd_module_start, mp);
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	if (mp->mod_thread == NULL) {
2857c478bd9Sstevel@tonic-gate 		fmd_error(EFMD_MOD_THR, "failed to create thread for %s", path);
2867c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
2877c478bd9Sstevel@tonic-gate 		fmd_module_rele(mp);
2887c478bd9Sstevel@tonic-gate 		return (NULL);
2897c478bd9Sstevel@tonic-gate 	}
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	/*
2927c478bd9Sstevel@tonic-gate 	 * At this point our module structure is nearly finished and its thread
2937c478bd9Sstevel@tonic-gate 	 * is starting execution in fmd_module_start() above, which will begin
2947c478bd9Sstevel@tonic-gate 	 * by blocking for mod_lock.  We now drop mod_lock and wait for either
2957c478bd9Sstevel@tonic-gate 	 * FMD_MOD_INIT or mod_error to be set before proceeding.
2967c478bd9Sstevel@tonic-gate 	 */
2977c478bd9Sstevel@tonic-gate 	while (!(mp->mod_flags & FMD_MOD_INIT) && mp->mod_error == 0)
2987c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	/*
3017c478bd9Sstevel@tonic-gate 	 * If the module has failed to initialize, copy its errno to the errno
3027c478bd9Sstevel@tonic-gate 	 * of the caller, wait for it to unload, and then destroy it.
3037c478bd9Sstevel@tonic-gate 	 */
3047c478bd9Sstevel@tonic-gate 	if (!(mp->mod_flags & FMD_MOD_INIT)) {
3057c478bd9Sstevel@tonic-gate 		err = mp->mod_error;
3067c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 		if (err == EFMD_CKPT_INVAL)
3097c478bd9Sstevel@tonic-gate 			fmd_ckpt_rename(mp); /* move aside bad checkpoint */
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 		/*
3127c478bd9Sstevel@tonic-gate 		 * If we're in the background, keep quiet about failure to
3137c478bd9Sstevel@tonic-gate 		 * load because a handle wasn't registered: this is a module's
3147c478bd9Sstevel@tonic-gate 		 * way of telling us it didn't want to be loaded for some
3157c478bd9Sstevel@tonic-gate 		 * reason related to system configuration.  If we're in the
3167c478bd9Sstevel@tonic-gate 		 * foreground we log this too in order to inform developers.
3177c478bd9Sstevel@tonic-gate 		 */
3187c478bd9Sstevel@tonic-gate 		if (fmd.d_fg || err != EFMD_HDL_INIT) {
3197c478bd9Sstevel@tonic-gate 			fmd_error(EFMD_MOD_INIT, "failed to load %s: %s\n",
3207c478bd9Sstevel@tonic-gate 			    path, fmd_strerror(err));
3217c478bd9Sstevel@tonic-gate 		}
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 		fmd_module_unload(mp);
3247c478bd9Sstevel@tonic-gate 		fmd_module_rele(mp);
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 		(void) fmd_set_errno(err);
3277c478bd9Sstevel@tonic-gate 		return (NULL);
3287c478bd9Sstevel@tonic-gate 	}
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
331d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 	fmd_dprintf(FMD_DBG_MOD, "loaded module %s\n", mp->mod_name);
3347c478bd9Sstevel@tonic-gate 	return (mp);
3357c478bd9Sstevel@tonic-gate }
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate static void
fmd_module_untimeout(fmd_idspace_t * ids,id_t id,fmd_module_t * mp)338d9638e54Smws fmd_module_untimeout(fmd_idspace_t *ids, id_t id, fmd_module_t *mp)
3397c478bd9Sstevel@tonic-gate {
340d9638e54Smws 	void *arg = fmd_timerq_remove(fmd.d_timers, ids, id);
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	/*
3437c478bd9Sstevel@tonic-gate 	 * The root module calls fmd_timerq_install() directly and must take
3447c478bd9Sstevel@tonic-gate 	 * responsibility for any cleanup of timer arguments that is required.
3457c478bd9Sstevel@tonic-gate 	 * All other modules use fmd_modtimer_t's as the arg data; free them.
3467c478bd9Sstevel@tonic-gate 	 */
3477c478bd9Sstevel@tonic-gate 	if (arg != NULL && mp != fmd.d_rmod)
3487c478bd9Sstevel@tonic-gate 		fmd_free(arg, sizeof (fmd_modtimer_t));
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate void
fmd_module_unload(fmd_module_t * mp)3527c478bd9Sstevel@tonic-gate fmd_module_unload(fmd_module_t *mp)
3537c478bd9Sstevel@tonic-gate {
35424db4641Seschrock 	fmd_modtopo_t *mtp;
35524db4641Seschrock 
3567c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_QUIT) {
3597c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
3607c478bd9Sstevel@tonic-gate 		return; /* module is already unloading */
3617c478bd9Sstevel@tonic-gate 	}
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_thread != NULL);
3647c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_QUIT;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	if (mp->mod_queue != NULL)
3677c478bd9Sstevel@tonic-gate 		fmd_eventq_abort(mp->mod_queue);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	/*
3707c478bd9Sstevel@tonic-gate 	 * Wait for the module's thread to stop processing events and call
3717c478bd9Sstevel@tonic-gate 	 * _fmd_fini() and exit.  We do this by waiting for FMD_MOD_FINI to be
3727c478bd9Sstevel@tonic-gate 	 * set if INIT was set, and then attempting to join with the thread.
3737c478bd9Sstevel@tonic-gate 	 */
3747c478bd9Sstevel@tonic-gate 	while ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI)) == FMD_MOD_INIT)
3757c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
378d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	fmd_thread_destroy(mp->mod_thread, FMD_THREAD_JOIN);
3817c478bd9Sstevel@tonic-gate 	mp->mod_thread = NULL;
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	/*
3847c478bd9Sstevel@tonic-gate 	 * Once the module is no longer active, clean up any data structures
385d9638e54Smws 	 * that are only required when the module is loaded.
3867c478bd9Sstevel@tonic-gate 	 */
3877c478bd9Sstevel@tonic-gate 	fmd_module_lock(mp);
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 	if (mp->mod_timerids != NULL) {
3907c478bd9Sstevel@tonic-gate 		fmd_idspace_apply(mp->mod_timerids,
3917c478bd9Sstevel@tonic-gate 		    (void (*)())fmd_module_untimeout, mp);
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 		fmd_idspace_destroy(mp->mod_timerids);
3947c478bd9Sstevel@tonic-gate 		mp->mod_timerids = NULL;
3957c478bd9Sstevel@tonic-gate 	}
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	if (mp->mod_threads != NULL) {
3987c478bd9Sstevel@tonic-gate 		fmd_idspace_destroy(mp->mod_threads);
3997c478bd9Sstevel@tonic-gate 		mp->mod_threads = NULL;
4007c478bd9Sstevel@tonic-gate 	}
4017c478bd9Sstevel@tonic-gate 
4020b9e3e76Smws 	(void) fmd_buf_hash_destroy(&mp->mod_bufs);
4037c478bd9Sstevel@tonic-gate 	fmd_serd_hash_destroy(&mp->mod_serds);
4047c478bd9Sstevel@tonic-gate 
40524db4641Seschrock 	while ((mtp = fmd_list_next(&mp->mod_topolist)) != NULL) {
40624db4641Seschrock 		fmd_list_delete(&mp->mod_topolist, mtp);
40724db4641Seschrock 		fmd_topo_rele(mtp->mt_topo);
40824db4641Seschrock 		fmd_free(mtp, sizeof (fmd_modtopo_t));
40924db4641Seschrock 	}
41024db4641Seschrock 
4117c478bd9Sstevel@tonic-gate 	fmd_module_unlock(mp);
4127c478bd9Sstevel@tonic-gate 	fmd_dprintf(FMD_DBG_MOD, "unloaded module %s\n", mp->mod_name);
4137c478bd9Sstevel@tonic-gate }
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate void
fmd_module_destroy(fmd_module_t * mp)4167c478bd9Sstevel@tonic-gate fmd_module_destroy(fmd_module_t *mp)
4177c478bd9Sstevel@tonic-gate {
4187c478bd9Sstevel@tonic-gate 	fmd_conf_formal_t *cfp = mp->mod_argv;
4197c478bd9Sstevel@tonic-gate 	int i;
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&mp->mod_lock));
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	if (mp->mod_thread != NULL) {
4247c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
4257c478bd9Sstevel@tonic-gate 		fmd_module_unload(mp);
4267c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&mp->mod_lock);
4277c478bd9Sstevel@tonic-gate 	}
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_thread == NULL);
4307c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_refs == 0);
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	/*
4337c478bd9Sstevel@tonic-gate 	 * Once the module's thread is dead, we can safely remove the module
4347c478bd9Sstevel@tonic-gate 	 * from global visibility and by removing it from d_mod_list.  Any
4357c478bd9Sstevel@tonic-gate 	 * modhash pointers are already gone by virtue of mod_refs being zero.
4367c478bd9Sstevel@tonic-gate 	 */
4377c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&fmd.d_mod_lock);
4387c478bd9Sstevel@tonic-gate 	fmd_list_delete(&fmd.d_mod_list, mp);
4397c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&fmd.d_mod_lock);
4407c478bd9Sstevel@tonic-gate 
44124db4641Seschrock 	if (mp->mod_topo_current != NULL)
44224db4641Seschrock 		fmd_topo_rele(mp->mod_topo_current);
44324db4641Seschrock 
4449af3851aSeschrock 	if (mp->mod_nva_sleep.nva_ops != NULL)
4459af3851aSeschrock 		nv_alloc_fini(&mp->mod_nva_sleep);
4469af3851aSeschrock 	if (mp->mod_nva_nosleep.nva_ops != NULL)
4479af3851aSeschrock 		nv_alloc_fini(&mp->mod_nva_nosleep);
4489af3851aSeschrock 
4497c478bd9Sstevel@tonic-gate 	/*
4507c478bd9Sstevel@tonic-gate 	 * Once the module is no longer processing events and no longer visible
4517c478bd9Sstevel@tonic-gate 	 * through any program data structures, we can free all of its content.
4527c478bd9Sstevel@tonic-gate 	 */
4537c478bd9Sstevel@tonic-gate 	if (mp->mod_queue != NULL) {
4547c478bd9Sstevel@tonic-gate 		fmd_eventq_destroy(mp->mod_queue);
4557c478bd9Sstevel@tonic-gate 		mp->mod_queue = NULL;
4567c478bd9Sstevel@tonic-gate 	}
4577c478bd9Sstevel@tonic-gate 
458d9638e54Smws 	if (mp->mod_ustat != NULL) {
459d9638e54Smws 		(void) pthread_mutex_lock(&mp->mod_stats_lock);
460d9638e54Smws 		fmd_ustat_destroy(mp->mod_ustat);
461d9638e54Smws 		mp->mod_ustat = NULL;
462d9638e54Smws 		mp->mod_stats = NULL;
463d9638e54Smws 		(void) pthread_mutex_unlock(&mp->mod_stats_lock);
464d9638e54Smws 	}
465d9638e54Smws 
4667c478bd9Sstevel@tonic-gate 	for (i = 0; i < mp->mod_dictc; i++)
4677c478bd9Sstevel@tonic-gate 		fm_dc_closedict(mp->mod_dictv[i]);
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	fmd_free(mp->mod_dictv, sizeof (struct fm_dc_handle *) * mp->mod_dictc);
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 	if (mp->mod_conf != NULL)
4727c478bd9Sstevel@tonic-gate 		fmd_conf_close(mp->mod_conf);
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 	for (i = 0; i < mp->mod_argc; i++, cfp++) {
4757c478bd9Sstevel@tonic-gate 		fmd_strfree((char *)cfp->cf_name);
4767c478bd9Sstevel@tonic-gate 		fmd_strfree((char *)cfp->cf_default);
4777c478bd9Sstevel@tonic-gate 	}
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	fmd_free(mp->mod_argv, sizeof (fmd_conf_formal_t) * mp->mod_argc);
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	fmd_strfree(mp->mod_name);
4827c478bd9Sstevel@tonic-gate 	fmd_strfree(mp->mod_path);
4837c478bd9Sstevel@tonic-gate 	fmd_strfree(mp->mod_ckpt);
4847c478bd9Sstevel@tonic-gate 	nvlist_free(mp->mod_fmri);
48519e1255fScy152378 	fmd_strfree(mp->mod_vers);
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	fmd_free(mp, sizeof (fmd_module_t));
4887c478bd9Sstevel@tonic-gate }
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate /*
4917c478bd9Sstevel@tonic-gate  * fmd_module_error() is called after the stack is unwound from a call to
4927c478bd9Sstevel@tonic-gate  * fmd_module_abort() to indicate that the module has failed.  The mod_error
4937c478bd9Sstevel@tonic-gate  * field is used to hold the error code of the first fatal error to the module.
4947c478bd9Sstevel@tonic-gate  * An EFMD_MOD_FAIL event is then created and sent to fmd-self-diagnosis.
4957c478bd9Sstevel@tonic-gate  */
4967c478bd9Sstevel@tonic-gate static void
fmd_module_error(fmd_module_t * mp,int err)4977c478bd9Sstevel@tonic-gate fmd_module_error(fmd_module_t *mp, int err)
4987c478bd9Sstevel@tonic-gate {
4997c478bd9Sstevel@tonic-gate 	fmd_event_t *e;
5007c478bd9Sstevel@tonic-gate 	nvlist_t *nvl;
5017c478bd9Sstevel@tonic-gate 	char *class;
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&mp->mod_lock));
5047c478bd9Sstevel@tonic-gate 	ASSERT(err != 0);
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate 	TRACE((FMD_DBG_MOD, "module aborted: err=%d", err));
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	if (mp->mod_error == 0)
5097c478bd9Sstevel@tonic-gate 		mp->mod_error = err;
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	if (mp == fmd.d_self)
5127c478bd9Sstevel@tonic-gate 		return; /* do not post event if fmd.d_self itself fails */
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 	/*
5157c478bd9Sstevel@tonic-gate 	 * Send an error indicating the module has now failed to fmd.d_self.
5167c478bd9Sstevel@tonic-gate 	 * Since the error causing the failure has already been logged by
5177c478bd9Sstevel@tonic-gate 	 * fmd_api_xerror(), we do not need to bother logging this event.
5187c478bd9Sstevel@tonic-gate 	 * It only exists for the purpose of notifying fmd.d_self that it can
5197c478bd9Sstevel@tonic-gate 	 * close the case associated with this module because mod_error is set.
5207c478bd9Sstevel@tonic-gate 	 */
5217c478bd9Sstevel@tonic-gate 	nvl = fmd_protocol_moderror(mp, EFMD_MOD_FAIL, fmd_strerror(err));
5227c478bd9Sstevel@tonic-gate 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
5237c478bd9Sstevel@tonic-gate 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
5247c478bd9Sstevel@tonic-gate 	fmd_dispq_dispatch(fmd.d_disp, e, class);
5257c478bd9Sstevel@tonic-gate }
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate void
fmd_module_dispatch(fmd_module_t * mp,fmd_event_t * e)5287c478bd9Sstevel@tonic-gate fmd_module_dispatch(fmd_module_t *mp, fmd_event_t *e)
5297c478bd9Sstevel@tonic-gate {
5307c478bd9Sstevel@tonic-gate 	const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;
5317c478bd9Sstevel@tonic-gate 	fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
5327c478bd9Sstevel@tonic-gate 	fmd_hdl_t *hdl = (fmd_hdl_t *)mp;
5337c478bd9Sstevel@tonic-gate 	fmd_modtimer_t *t;
534d10eddf7SHyon Kim 	fmd_topo_t *old_topo;
5357c478bd9Sstevel@tonic-gate 	volatile int err;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	/*
5387c478bd9Sstevel@tonic-gate 	 * Before calling the appropriate module callback, enter the module as
5397c478bd9Sstevel@tonic-gate 	 * if by fmd_module_enter() and establish mod_jmpbuf for any aborts.
5407c478bd9Sstevel@tonic-gate 	 */
5417c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 	ASSERT(!(mp->mod_flags & FMD_MOD_BUSY));
5447c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_BUSY;
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	if ((err = setjmp(mp->mod_jmpbuf)) != 0) {
5477c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&mp->mod_lock);
5487c478bd9Sstevel@tonic-gate 		fmd_module_error(mp, err);
5497c478bd9Sstevel@tonic-gate 	}
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
552d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	/*
5557c478bd9Sstevel@tonic-gate 	 * If it's the first time through fmd_module_dispatch(), call the
5567c478bd9Sstevel@tonic-gate 	 * appropriate module callback based on the event type.  If the call
5577c478bd9Sstevel@tonic-gate 	 * triggers an fmd_module_abort(), we'll return to setjmp() above with
5587c478bd9Sstevel@tonic-gate 	 * err set to a non-zero value and then bypass this before exiting.
5597c478bd9Sstevel@tonic-gate 	 */
5607c478bd9Sstevel@tonic-gate 	if (err == 0) {
5617c478bd9Sstevel@tonic-gate 		switch (ep->ev_type) {
5627c478bd9Sstevel@tonic-gate 		case FMD_EVT_PROTOCOL:
5637c478bd9Sstevel@tonic-gate 			ops->fmdo_recv(hdl, e, ep->ev_nvl, ep->ev_data);
5647c478bd9Sstevel@tonic-gate 			break;
5657c478bd9Sstevel@tonic-gate 		case FMD_EVT_TIMEOUT:
5667c478bd9Sstevel@tonic-gate 			t = ep->ev_data;
5677c478bd9Sstevel@tonic-gate 			ASSERT(t->mt_mod == mp);
5687c478bd9Sstevel@tonic-gate 			ops->fmdo_timeout(hdl, t->mt_id, t->mt_arg);
5697c478bd9Sstevel@tonic-gate 			break;
5707c478bd9Sstevel@tonic-gate 		case FMD_EVT_CLOSE:
5717c478bd9Sstevel@tonic-gate 			ops->fmdo_close(hdl, ep->ev_data);
5727c478bd9Sstevel@tonic-gate 			break;
5737c478bd9Sstevel@tonic-gate 		case FMD_EVT_STATS:
5747c478bd9Sstevel@tonic-gate 			ops->fmdo_stats(hdl);
5757c478bd9Sstevel@tonic-gate 			fmd_modstat_publish(mp);
5767c478bd9Sstevel@tonic-gate 			break;
5777c478bd9Sstevel@tonic-gate 		case FMD_EVT_GC:
5787c478bd9Sstevel@tonic-gate 			ops->fmdo_gc(hdl);
5797c478bd9Sstevel@tonic-gate 			break;
580d9638e54Smws 		case FMD_EVT_PUBLISH:
581d9638e54Smws 			fmd_case_publish(ep->ev_data, FMD_CASE_CURRENT);
582d9638e54Smws 			break;
58324db4641Seschrock 		case FMD_EVT_TOPO:
584d10eddf7SHyon Kim 			/*
585d10eddf7SHyon Kim 			 * Save the pointer to the old topology and update
586d10eddf7SHyon Kim 			 * the pointer with the updated topology.
587d10eddf7SHyon Kim 			 * With this approach, other threads that reference the
588d10eddf7SHyon Kim 			 * topology either
589d10eddf7SHyon Kim 			 *  - finishes with old topology since
590d10eddf7SHyon Kim 			 *	it is released after updating
591d10eddf7SHyon Kim 			 *	mod_topo_current.
592d10eddf7SHyon Kim 			 *  - or is blocked while mod_topo_current is updated.
593d10eddf7SHyon Kim 			 */
594d10eddf7SHyon Kim 			old_topo = mp->mod_topo_current;
595d10eddf7SHyon Kim 			fmd_module_lock(mp);
59624db4641Seschrock 			mp->mod_topo_current = (fmd_topo_t *)ep->ev_data;
59724db4641Seschrock 			fmd_topo_addref(mp->mod_topo_current);
598d10eddf7SHyon Kim 			fmd_module_unlock(mp);
599d10eddf7SHyon Kim 			fmd_topo_rele(old_topo);
60024db4641Seschrock 			ops->fmdo_topo(hdl, mp->mod_topo_current->ft_hdl);
60124db4641Seschrock 			break;
6027c478bd9Sstevel@tonic-gate 		}
6037c478bd9Sstevel@tonic-gate 	}
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	fmd_module_exit(mp);
6067c478bd9Sstevel@tonic-gate }
6077c478bd9Sstevel@tonic-gate 
608d9638e54Smws int
fmd_module_transport(fmd_module_t * mp,fmd_xprt_t * xp,fmd_event_t * e)609d9638e54Smws fmd_module_transport(fmd_module_t *mp, fmd_xprt_t *xp, fmd_event_t *e)
610d9638e54Smws {
611d9638e54Smws 	fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
612d9638e54Smws 	fmd_hdl_t *hdl = (fmd_hdl_t *)mp;
613d9638e54Smws 
614d9638e54Smws 	ASSERT(ep->ev_type == FMD_EVT_PROTOCOL);
615d9638e54Smws 	return (mp->mod_info->fmdi_ops->fmdo_send(hdl, xp, e, ep->ev_nvl));
616d9638e54Smws }
617d9638e54Smws 
6187c478bd9Sstevel@tonic-gate void
fmd_module_timeout(fmd_modtimer_t * t,id_t id,hrtime_t hrt)6197c478bd9Sstevel@tonic-gate fmd_module_timeout(fmd_modtimer_t *t, id_t id, hrtime_t hrt)
6207c478bd9Sstevel@tonic-gate {
6217c478bd9Sstevel@tonic-gate 	fmd_event_t *e;
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	t->mt_id = id; /* save id in case we need to delete from eventq */
6247c478bd9Sstevel@tonic-gate 	e = fmd_event_create(FMD_EVT_TIMEOUT, hrt, NULL, t);
6257c478bd9Sstevel@tonic-gate 	fmd_eventq_insert_at_time(t->mt_mod->mod_queue, e);
6267c478bd9Sstevel@tonic-gate }
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate /*
6297c478bd9Sstevel@tonic-gate  * Garbage collection is initiated by a timer callback once per day or at the
6307c478bd9Sstevel@tonic-gate  * request of fmadm.  Purge old SERD entries and send the module a GC event.
6317c478bd9Sstevel@tonic-gate  */
6327c478bd9Sstevel@tonic-gate void
fmd_module_gc(fmd_module_t * mp)6337c478bd9Sstevel@tonic-gate fmd_module_gc(fmd_module_t *mp)
6347c478bd9Sstevel@tonic-gate {
6357c478bd9Sstevel@tonic-gate 	fmd_hdl_info_t *info;
6367c478bd9Sstevel@tonic-gate 	fmd_event_t *e;
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 	if (mp->mod_error != 0)
6397c478bd9Sstevel@tonic-gate 		return; /* do not do anything if the module has failed */
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	fmd_module_lock(mp);
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	if ((info = mp->mod_info) != NULL) {
6447c478bd9Sstevel@tonic-gate 		fmd_serd_hash_apply(&mp->mod_serds,
6457c478bd9Sstevel@tonic-gate 		    (fmd_serd_eng_f *)fmd_serd_eng_gc, NULL);
6467c478bd9Sstevel@tonic-gate 	}
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	fmd_module_unlock(mp);
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 	if (info != NULL) {
6517c478bd9Sstevel@tonic-gate 		e = fmd_event_create(FMD_EVT_GC, FMD_HRT_NOW, NULL, NULL);
6527c478bd9Sstevel@tonic-gate 		fmd_eventq_insert_at_head(mp->mod_queue, e);
6537c478bd9Sstevel@tonic-gate 	}
6547c478bd9Sstevel@tonic-gate }
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate void
fmd_module_trygc(fmd_module_t * mp)6577c478bd9Sstevel@tonic-gate fmd_module_trygc(fmd_module_t *mp)
6587c478bd9Sstevel@tonic-gate {
6597c478bd9Sstevel@tonic-gate 	if (fmd_module_trylock(mp)) {
6607c478bd9Sstevel@tonic-gate 		fmd_serd_hash_apply(&mp->mod_serds,
6617c478bd9Sstevel@tonic-gate 		    (fmd_serd_eng_f *)fmd_serd_eng_gc, NULL);
6627c478bd9Sstevel@tonic-gate 		fmd_module_unlock(mp);
6637c478bd9Sstevel@tonic-gate 	}
6647c478bd9Sstevel@tonic-gate }
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate int
fmd_module_contains(fmd_module_t * mp,fmd_event_t * ep)6677c478bd9Sstevel@tonic-gate fmd_module_contains(fmd_module_t *mp, fmd_event_t *ep)
6687c478bd9Sstevel@tonic-gate {
6697c478bd9Sstevel@tonic-gate 	fmd_case_t *cp;
6707c478bd9Sstevel@tonic-gate 	int rv = 0;
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 	fmd_module_lock(mp);
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	for (cp = fmd_list_next(&mp->mod_cases);
6757c478bd9Sstevel@tonic-gate 	    cp != NULL; cp = fmd_list_next(cp)) {
6767c478bd9Sstevel@tonic-gate 		if ((rv = fmd_case_contains(cp, ep)) != 0)
6777c478bd9Sstevel@tonic-gate 			break;
6787c478bd9Sstevel@tonic-gate 	}
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	if (rv == 0)
6817c478bd9Sstevel@tonic-gate 		rv = fmd_serd_hash_contains(&mp->mod_serds, ep);
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	fmd_module_unlock(mp);
6847c478bd9Sstevel@tonic-gate 	return (rv);
6857c478bd9Sstevel@tonic-gate }
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate void
fmd_module_setdirty(fmd_module_t * mp)6887c478bd9Sstevel@tonic-gate fmd_module_setdirty(fmd_module_t *mp)
6897c478bd9Sstevel@tonic-gate {
6907c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
6917c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_MDIRTY;
6927c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
6937c478bd9Sstevel@tonic-gate }
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate void
fmd_module_setcdirty(fmd_module_t * mp)6967c478bd9Sstevel@tonic-gate fmd_module_setcdirty(fmd_module_t *mp)
6977c478bd9Sstevel@tonic-gate {
6987c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
6997c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_CDIRTY;
7007c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
7017c478bd9Sstevel@tonic-gate }
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate void
fmd_module_clrdirty(fmd_module_t * mp)7047c478bd9Sstevel@tonic-gate fmd_module_clrdirty(fmd_module_t *mp)
7057c478bd9Sstevel@tonic-gate {
7067c478bd9Sstevel@tonic-gate 	fmd_case_t *cp;
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	fmd_module_lock(mp);
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_CDIRTY) {
7117c478bd9Sstevel@tonic-gate 		for (cp = fmd_list_next(&mp->mod_cases);
7127c478bd9Sstevel@tonic-gate 		    cp != NULL; cp = fmd_list_next(cp))
7137c478bd9Sstevel@tonic-gate 			fmd_case_clrdirty(cp);
7147c478bd9Sstevel@tonic-gate 	}
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_MDIRTY) {
7177c478bd9Sstevel@tonic-gate 		fmd_serd_hash_apply(&mp->mod_serds,
7187c478bd9Sstevel@tonic-gate 		    (fmd_serd_eng_f *)fmd_serd_eng_clrdirty, NULL);
7197c478bd9Sstevel@tonic-gate 		fmd_buf_hash_commit(&mp->mod_bufs);
7207c478bd9Sstevel@tonic-gate 	}
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7237c478bd9Sstevel@tonic-gate 	mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY);
7247c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 	fmd_module_unlock(mp);
7277c478bd9Sstevel@tonic-gate }
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate void
fmd_module_commit(fmd_module_t * mp)7307c478bd9Sstevel@tonic-gate fmd_module_commit(fmd_module_t *mp)
7317c478bd9Sstevel@tonic-gate {
7327c478bd9Sstevel@tonic-gate 	fmd_case_t *cp;
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	ASSERT(fmd_module_locked(mp));
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_CDIRTY) {
7377c478bd9Sstevel@tonic-gate 		for (cp = fmd_list_next(&mp->mod_cases);
7387c478bd9Sstevel@tonic-gate 		    cp != NULL; cp = fmd_list_next(cp))
7397c478bd9Sstevel@tonic-gate 			fmd_case_commit(cp);
7407c478bd9Sstevel@tonic-gate 	}
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_MDIRTY) {
7437c478bd9Sstevel@tonic-gate 		fmd_serd_hash_apply(&mp->mod_serds,
7447c478bd9Sstevel@tonic-gate 		    (fmd_serd_eng_f *)fmd_serd_eng_commit, NULL);
7457c478bd9Sstevel@tonic-gate 		fmd_buf_hash_commit(&mp->mod_bufs);
7467c478bd9Sstevel@tonic-gate 	}
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7497c478bd9Sstevel@tonic-gate 	mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY);
7507c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	mp->mod_gen++;
7537c478bd9Sstevel@tonic-gate }
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate void
fmd_module_lock(fmd_module_t * mp)7567c478bd9Sstevel@tonic-gate fmd_module_lock(fmd_module_t *mp)
7577c478bd9Sstevel@tonic-gate {
7587c478bd9Sstevel@tonic-gate 	pthread_t self = pthread_self();
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	while (mp->mod_flags & FMD_MOD_LOCK) {
7637c478bd9Sstevel@tonic-gate 		if (mp->mod_owner != self)
7647c478bd9Sstevel@tonic-gate 			(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
7657c478bd9Sstevel@tonic-gate 		else
7667c478bd9Sstevel@tonic-gate 			fmd_panic("recursive module lock of %p\n", (void *)mp);
7677c478bd9Sstevel@tonic-gate 	}
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 	mp->mod_owner = self;
7707c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_LOCK;
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
773d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
7747c478bd9Sstevel@tonic-gate }
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate void
fmd_module_unlock(fmd_module_t * mp)7777c478bd9Sstevel@tonic-gate fmd_module_unlock(fmd_module_t *mp)
7787c478bd9Sstevel@tonic-gate {
7797c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_owner == pthread_self());
7827c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_LOCK);
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 	mp->mod_owner = 0;
7857c478bd9Sstevel@tonic-gate 	mp->mod_flags &= ~FMD_MOD_LOCK;
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
788d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
7897c478bd9Sstevel@tonic-gate }
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate int
fmd_module_trylock(fmd_module_t * mp)7927c478bd9Sstevel@tonic-gate fmd_module_trylock(fmd_module_t *mp)
7937c478bd9Sstevel@tonic-gate {
7947c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 	if (mp->mod_flags & FMD_MOD_LOCK) {
7977c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
7987c478bd9Sstevel@tonic-gate 		return (0);
7997c478bd9Sstevel@tonic-gate 	}
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	mp->mod_owner = pthread_self();
8027c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_LOCK;
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
805d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	return (1);
8087c478bd9Sstevel@tonic-gate }
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate int
fmd_module_locked(fmd_module_t * mp)8117c478bd9Sstevel@tonic-gate fmd_module_locked(fmd_module_t *mp)
8127c478bd9Sstevel@tonic-gate {
8137c478bd9Sstevel@tonic-gate 	return ((mp->mod_flags & FMD_MOD_LOCK) &&
8147c478bd9Sstevel@tonic-gate 	    mp->mod_owner == pthread_self());
8157c478bd9Sstevel@tonic-gate }
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate int
fmd_module_enter(fmd_module_t * mp,void (* func)(fmd_hdl_t *))8187c478bd9Sstevel@tonic-gate fmd_module_enter(fmd_module_t *mp, void (*func)(fmd_hdl_t *))
8197c478bd9Sstevel@tonic-gate {
8207c478bd9Sstevel@tonic-gate 	volatile int err;
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	ASSERT(!(mp->mod_flags & FMD_MOD_BUSY));
8257c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_BUSY;
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 	if ((err = setjmp(mp->mod_jmpbuf)) != 0) {
8287c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&mp->mod_lock);
8297c478bd9Sstevel@tonic-gate 		fmd_module_error(mp, err);
8307c478bd9Sstevel@tonic-gate 	}
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
833d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 	/*
8367c478bd9Sstevel@tonic-gate 	 * If it's the first time through fmd_module_enter(), call the provided
8377c478bd9Sstevel@tonic-gate 	 * function on the module.  If no fmd_module_abort() results, we will
8387c478bd9Sstevel@tonic-gate 	 * fall through and return zero.  Otherwise we'll longjmp with an err,
8397c478bd9Sstevel@tonic-gate 	 * return to the setjmp() above, and return the error to our caller.
8407c478bd9Sstevel@tonic-gate 	 */
8417c478bd9Sstevel@tonic-gate 	if (err == 0 && func != NULL)
8427c478bd9Sstevel@tonic-gate 		(*func)((fmd_hdl_t *)mp);
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 	return (err);
8457c478bd9Sstevel@tonic-gate }
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate void
fmd_module_exit(fmd_module_t * mp)8487c478bd9Sstevel@tonic-gate fmd_module_exit(fmd_module_t *mp)
8497c478bd9Sstevel@tonic-gate {
8507c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_BUSY);
8537c478bd9Sstevel@tonic-gate 	mp->mod_flags &= ~FMD_MOD_BUSY;
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
856d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
8577c478bd9Sstevel@tonic-gate }
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate /*
8607c478bd9Sstevel@tonic-gate  * If the client.error policy has been set by a developer, stop or dump core
8617c478bd9Sstevel@tonic-gate  * based on the policy; if we stop and are resumed we'll continue and execute
8627c478bd9Sstevel@tonic-gate  * the default behavior to discard events in fmd_module_start().  If the caller
8637c478bd9Sstevel@tonic-gate  * is the primary module thread, we reach this state by longjmp'ing back to
8647c478bd9Sstevel@tonic-gate  * fmd_module_enter(), above.  If the caller is an auxiliary thread, we cancel
8657c478bd9Sstevel@tonic-gate  * ourself and arrange for the primary thread to call fmd_module_abort().
8667c478bd9Sstevel@tonic-gate  */
8677c478bd9Sstevel@tonic-gate void
fmd_module_abort(fmd_module_t * mp,int err)8687c478bd9Sstevel@tonic-gate fmd_module_abort(fmd_module_t *mp, int err)
8697c478bd9Sstevel@tonic-gate {
8707c478bd9Sstevel@tonic-gate 	uint_t policy = FMD_CERROR_UNLOAD;
8717c478bd9Sstevel@tonic-gate 	pthread_t tid = pthread_self();
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "client.error", &policy);
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	if (policy == FMD_CERROR_STOP) {
8767c478bd9Sstevel@tonic-gate 		fmd_error(err, "stopping after %s in client %s (%p)\n",
8777c478bd9Sstevel@tonic-gate 		    fmd_errclass(err), mp->mod_name, (void *)mp);
8787c478bd9Sstevel@tonic-gate 		(void) raise(SIGSTOP);
8797c478bd9Sstevel@tonic-gate 	} else if (policy == FMD_CERROR_ABORT) {
8807c478bd9Sstevel@tonic-gate 		fmd_panic("aborting due to %s in client %s (%p)\n",
8817c478bd9Sstevel@tonic-gate 		    fmd_errclass(err), mp->mod_name, (void *)mp);
8827c478bd9Sstevel@tonic-gate 	}
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	/*
8857c478bd9Sstevel@tonic-gate 	 * If the caller is an auxiliary thread, cancel the current thread.  We
8867c478bd9Sstevel@tonic-gate 	 * prefer to cancel because it affords developers the option of using
8877c478bd9Sstevel@tonic-gate 	 * the pthread_cleanup* APIs.  If cancellations have been disabled,
8887c478bd9Sstevel@tonic-gate 	 * fall through to forcing the current thread to exit.  In either case
8897c478bd9Sstevel@tonic-gate 	 * we update mod_error (if zero) to enter the failed state.  Once that
8907c478bd9Sstevel@tonic-gate 	 * is set, further events received by the module will be discarded.
8917c478bd9Sstevel@tonic-gate 	 *
8927c478bd9Sstevel@tonic-gate 	 * We also set the FMD_MOD_FAIL bit, indicating an unrecoverable error.
8937c478bd9Sstevel@tonic-gate 	 * When an auxiliary thread fails, the module is left in a delicate
8947c478bd9Sstevel@tonic-gate 	 * state where it is likely not able to continue execution (even to
8957c478bd9Sstevel@tonic-gate 	 * execute its _fmd_fini() routine) because our caller may hold locks
8967c478bd9Sstevel@tonic-gate 	 * that are private to the module and can no longer be released.  The
8977c478bd9Sstevel@tonic-gate 	 * FMD_MOD_FAIL bit forces fmd_api_module_lock() to abort if any other
8987c478bd9Sstevel@tonic-gate 	 * module threads reach an API call, in an attempt to get them to exit.
8997c478bd9Sstevel@tonic-gate 	 */
9007c478bd9Sstevel@tonic-gate 	if (tid != mp->mod_thread->thr_tid) {
9017c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&mp->mod_lock);
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 		if (mp->mod_error == 0)
9047c478bd9Sstevel@tonic-gate 			mp->mod_error = err;
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		mp->mod_flags |= FMD_MOD_FAIL;
9077c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 		(void) pthread_cancel(tid);
9107c478bd9Sstevel@tonic-gate 		pthread_exit(NULL);
9117c478bd9Sstevel@tonic-gate 	}
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_BUSY);
9147c478bd9Sstevel@tonic-gate 	longjmp(mp->mod_jmpbuf, err);
9157c478bd9Sstevel@tonic-gate }
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate void
fmd_module_hold(fmd_module_t * mp)9187c478bd9Sstevel@tonic-gate fmd_module_hold(fmd_module_t *mp)
9197c478bd9Sstevel@tonic-gate {
9207c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate 	TRACE((FMD_DBG_MOD, "hold %p (%s/%u)\n",
9237c478bd9Sstevel@tonic-gate 	    (void *)mp, mp->mod_name, mp->mod_refs));
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate 	mp->mod_refs++;
9267c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_refs != 0);
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
9297c478bd9Sstevel@tonic-gate }
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate void
fmd_module_rele(fmd_module_t * mp)9327c478bd9Sstevel@tonic-gate fmd_module_rele(fmd_module_t *mp)
9337c478bd9Sstevel@tonic-gate {
9347c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
9357c478bd9Sstevel@tonic-gate 
9367c478bd9Sstevel@tonic-gate 	TRACE((FMD_DBG_MOD, "rele %p (%s/%u)\n",
9377c478bd9Sstevel@tonic-gate 	    (void *)mp, mp->mod_name, mp->mod_refs));
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_refs != 0);
9407c478bd9Sstevel@tonic-gate 
941d9638e54Smws 	if (--mp->mod_refs == 0)
942d9638e54Smws 		fmd_module_destroy(mp);
943d9638e54Smws 	else
9447c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
9457c478bd9Sstevel@tonic-gate }
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate /*
9487c478bd9Sstevel@tonic-gate  * Wrapper around libdiagcode's fm_dc_opendict() to load module dictionaries.
9497c478bd9Sstevel@tonic-gate  * If the dictionary open is successful, the new dictionary is added to the
9507c478bd9Sstevel@tonic-gate  * mod_dictv[] array and mod_codelen is updated with the new maximum length.
9517c478bd9Sstevel@tonic-gate  */
9527c478bd9Sstevel@tonic-gate int
fmd_module_dc_opendict(fmd_module_t * mp,const char * dict)9537c478bd9Sstevel@tonic-gate fmd_module_dc_opendict(fmd_module_t *mp, const char *dict)
9547c478bd9Sstevel@tonic-gate {
9557c478bd9Sstevel@tonic-gate 	struct fm_dc_handle *dcp, **dcv;
9567c478bd9Sstevel@tonic-gate 	char *dictdir, *dictnam, *p;
9577c478bd9Sstevel@tonic-gate 	size_t len;
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 	ASSERT(fmd_module_locked(mp));
9607c478bd9Sstevel@tonic-gate 
961*23a1cceaSRoger A. Faulkner 	dictnam = strdupa(fmd_strbasename(dict));
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate 	if ((p = strrchr(dictnam, '.')) != NULL &&
9647c478bd9Sstevel@tonic-gate 	    strcmp(p, ".dict") == 0)
9657c478bd9Sstevel@tonic-gate 		*p = '\0'; /* eliminate any trailing .dict suffix */
9667c478bd9Sstevel@tonic-gate 
9677c478bd9Sstevel@tonic-gate 	/*
9687c478bd9Sstevel@tonic-gate 	 * If 'dict' is an absolute path, dictdir = $rootdir/`dirname dict`
9697c478bd9Sstevel@tonic-gate 	 * If 'dict' is not an absolute path, dictdir = $dictdir/`dirname dict`
9707c478bd9Sstevel@tonic-gate 	 */
9717c478bd9Sstevel@tonic-gate 	if (dict[0] == '/') {
9727c478bd9Sstevel@tonic-gate 		len = strlen(fmd.d_rootdir) + strlen(dict) + 1;
9737c478bd9Sstevel@tonic-gate 		dictdir = alloca(len);
9747c478bd9Sstevel@tonic-gate 		(void) snprintf(dictdir, len, "%s%s", fmd.d_rootdir, dict);
9757c478bd9Sstevel@tonic-gate 		(void) fmd_strdirname(dictdir);
9767c478bd9Sstevel@tonic-gate 	} else {
9777c478bd9Sstevel@tonic-gate 		(void) fmd_conf_getprop(fmd.d_conf, "dictdir", &p);
9787c478bd9Sstevel@tonic-gate 		len = strlen(fmd.d_rootdir) + strlen(p) + strlen(dict) + 3;
9797c478bd9Sstevel@tonic-gate 		dictdir = alloca(len);
9807c478bd9Sstevel@tonic-gate 		(void) snprintf(dictdir, len,
9817c478bd9Sstevel@tonic-gate 		    "%s/%s/%s", fmd.d_rootdir, p, dict);
9827c478bd9Sstevel@tonic-gate 		(void) fmd_strdirname(dictdir);
9837c478bd9Sstevel@tonic-gate 	}
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 	fmd_dprintf(FMD_DBG_MOD, "module %s opening %s -> %s/%s.dict\n",
9867c478bd9Sstevel@tonic-gate 	    mp->mod_name, dict, dictdir, dictnam);
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	if ((dcp = fm_dc_opendict(FM_DC_VERSION, dictdir, dictnam)) == NULL)
9897c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	dcv = fmd_alloc(sizeof (dcp) * (mp->mod_dictc + 1), FMD_SLEEP);
9927c478bd9Sstevel@tonic-gate 	bcopy(mp->mod_dictv, dcv, sizeof (dcp) * mp->mod_dictc);
9937c478bd9Sstevel@tonic-gate 	fmd_free(mp->mod_dictv, sizeof (dcp) * mp->mod_dictc);
9947c478bd9Sstevel@tonic-gate 	mp->mod_dictv = dcv;
9957c478bd9Sstevel@tonic-gate 	mp->mod_dictv[mp->mod_dictc++] = dcp;
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	len = fm_dc_codelen(dcp);
9987c478bd9Sstevel@tonic-gate 	mp->mod_codelen = MAX(mp->mod_codelen, len);
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 	return (0);
10017c478bd9Sstevel@tonic-gate }
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate /*
10047c478bd9Sstevel@tonic-gate  * Wrapper around libdiagcode's fm_dc_key2code() that examines all the module's
10057c478bd9Sstevel@tonic-gate  * dictionaries.  We adhere to the libdiagcode return values and semantics.
10067c478bd9Sstevel@tonic-gate  */
10077c478bd9Sstevel@tonic-gate int
fmd_module_dc_key2code(fmd_module_t * mp,char * const keys[],char * code,size_t codelen)10087c478bd9Sstevel@tonic-gate fmd_module_dc_key2code(fmd_module_t *mp,
10097c478bd9Sstevel@tonic-gate     char *const keys[], char *code, size_t codelen)
10107c478bd9Sstevel@tonic-gate {
10117c478bd9Sstevel@tonic-gate 	int i, err;
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate 	for (i = 0; i < mp->mod_dictc; i++) {
10147c478bd9Sstevel@tonic-gate 		if ((err = fm_dc_key2code(mp->mod_dictv[i], (const char **)keys,
10157c478bd9Sstevel@tonic-gate 		    code, codelen)) == 0 || errno != ENOMSG)
10167c478bd9Sstevel@tonic-gate 			return (err);
10177c478bd9Sstevel@tonic-gate 	}
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate 	return (fmd_set_errno(ENOMSG));
10207c478bd9Sstevel@tonic-gate }
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate fmd_modhash_t *
fmd_modhash_create(void)10237c478bd9Sstevel@tonic-gate fmd_modhash_create(void)
10247c478bd9Sstevel@tonic-gate {
10257c478bd9Sstevel@tonic-gate 	fmd_modhash_t *mhp = fmd_alloc(sizeof (fmd_modhash_t), FMD_SLEEP);
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_init(&mhp->mh_lock, NULL);
10287c478bd9Sstevel@tonic-gate 	mhp->mh_hashlen = fmd.d_str_buckets;
10297c478bd9Sstevel@tonic-gate 	mhp->mh_hash = fmd_zalloc(sizeof (void *) * mhp->mh_hashlen, FMD_SLEEP);
10307c478bd9Sstevel@tonic-gate 	mhp->mh_nelems = 0;
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate 	return (mhp);
10337c478bd9Sstevel@tonic-gate }
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate void
fmd_modhash_destroy(fmd_modhash_t * mhp)10367c478bd9Sstevel@tonic-gate fmd_modhash_destroy(fmd_modhash_t *mhp)
10377c478bd9Sstevel@tonic-gate {
10387c478bd9Sstevel@tonic-gate 	fmd_module_t *mp, *nmp;
10397c478bd9Sstevel@tonic-gate 	uint_t i;
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 	for (i = 0; i < mhp->mh_hashlen; i++) {
10427c478bd9Sstevel@tonic-gate 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = nmp) {
10437c478bd9Sstevel@tonic-gate 			nmp = mp->mod_next;
10447c478bd9Sstevel@tonic-gate 			mp->mod_next = NULL;
10457c478bd9Sstevel@tonic-gate 			fmd_module_rele(mp);
10467c478bd9Sstevel@tonic-gate 		}
10477c478bd9Sstevel@tonic-gate 	}
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	fmd_free(mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen);
10507c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_destroy(&mhp->mh_lock);
10517c478bd9Sstevel@tonic-gate 	fmd_free(mhp, sizeof (fmd_modhash_t));
10527c478bd9Sstevel@tonic-gate }
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate static void
fmd_modhash_loaddir(fmd_modhash_t * mhp,const char * dir,const fmd_modops_t * ops,const char * suffix)10557c478bd9Sstevel@tonic-gate fmd_modhash_loaddir(fmd_modhash_t *mhp, const char *dir,
10560b9e3e76Smws     const fmd_modops_t *ops, const char *suffix)
10577c478bd9Sstevel@tonic-gate {
10587c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
10594bc0a2efScasper 	struct dirent *dp;
10607c478bd9Sstevel@tonic-gate 	const char *p;
10617c478bd9Sstevel@tonic-gate 	DIR *dirp;
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 	if ((dirp = opendir(dir)) == NULL)
10647c478bd9Sstevel@tonic-gate 		return; /* failed to open directory; just skip it */
10657c478bd9Sstevel@tonic-gate 
10664bc0a2efScasper 	while ((dp = readdir(dirp)) != NULL) {
10677c478bd9Sstevel@tonic-gate 		if (dp->d_name[0] == '.')
10687c478bd9Sstevel@tonic-gate 			continue; /* skip "." and ".." */
10697c478bd9Sstevel@tonic-gate 
10700b9e3e76Smws 		p = strrchr(dp->d_name, '.');
10710b9e3e76Smws 
10720b9e3e76Smws 		if (p != NULL && strcmp(p, ".conf") == 0)
10737c478bd9Sstevel@tonic-gate 			continue; /* skip .conf files */
10747c478bd9Sstevel@tonic-gate 
10750b9e3e76Smws 		if (suffix != NULL && (p == NULL || strcmp(p, suffix) != 0))
10760b9e3e76Smws 			continue; /* skip files with the wrong suffix */
10770b9e3e76Smws 
10787c478bd9Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s/%s", dir, dp->d_name);
10797c478bd9Sstevel@tonic-gate 		(void) fmd_modhash_load(mhp, path, ops);
10807c478bd9Sstevel@tonic-gate 	}
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	(void) closedir(dirp);
10837c478bd9Sstevel@tonic-gate }
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate void
fmd_modhash_loadall(fmd_modhash_t * mhp,const fmd_conf_path_t * pap,const fmd_modops_t * ops,const char * suffix)10867c478bd9Sstevel@tonic-gate fmd_modhash_loadall(fmd_modhash_t *mhp, const fmd_conf_path_t *pap,
10870b9e3e76Smws     const fmd_modops_t *ops, const char *suffix)
10887c478bd9Sstevel@tonic-gate {
10897c478bd9Sstevel@tonic-gate 	int i;
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	for (i = 0; i < pap->cpa_argc; i++)
10920b9e3e76Smws 		fmd_modhash_loaddir(mhp, pap->cpa_argv[i], ops, suffix);
10937c478bd9Sstevel@tonic-gate }
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate void
fmd_modhash_apply(fmd_modhash_t * mhp,void (* func)(fmd_module_t *))10967c478bd9Sstevel@tonic-gate fmd_modhash_apply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *))
10977c478bd9Sstevel@tonic-gate {
10987c478bd9Sstevel@tonic-gate 	fmd_module_t *mp, *np;
10997c478bd9Sstevel@tonic-gate 	uint_t i;
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	for (i = 0; i < mhp->mh_hashlen; i++) {
11047c478bd9Sstevel@tonic-gate 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) {
11057c478bd9Sstevel@tonic-gate 			np = mp->mod_next;
11067c478bd9Sstevel@tonic-gate 			func(mp);
11077c478bd9Sstevel@tonic-gate 		}
11087c478bd9Sstevel@tonic-gate 	}
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
11117c478bd9Sstevel@tonic-gate }
11127c478bd9Sstevel@tonic-gate 
11137c478bd9Sstevel@tonic-gate void
fmd_modhash_tryapply(fmd_modhash_t * mhp,void (* func)(fmd_module_t *))11147c478bd9Sstevel@tonic-gate fmd_modhash_tryapply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *))
11157c478bd9Sstevel@tonic-gate {
11167c478bd9Sstevel@tonic-gate 	fmd_module_t *mp, *np;
11177c478bd9Sstevel@tonic-gate 	uint_t i;
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 	if (mhp == NULL || pthread_rwlock_tryrdlock(&mhp->mh_lock) != 0)
11207c478bd9Sstevel@tonic-gate 		return; /* not initialized or couldn't grab lock */
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate 	for (i = 0; i < mhp->mh_hashlen; i++) {
11237c478bd9Sstevel@tonic-gate 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) {
11247c478bd9Sstevel@tonic-gate 			np = mp->mod_next;
11257c478bd9Sstevel@tonic-gate 			func(mp);
11267c478bd9Sstevel@tonic-gate 		}
11277c478bd9Sstevel@tonic-gate 	}
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
11307c478bd9Sstevel@tonic-gate }
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate void
fmd_modhash_dispatch(fmd_modhash_t * mhp,fmd_event_t * ep)11337c478bd9Sstevel@tonic-gate fmd_modhash_dispatch(fmd_modhash_t *mhp, fmd_event_t *ep)
11347c478bd9Sstevel@tonic-gate {
11357c478bd9Sstevel@tonic-gate 	fmd_module_t *mp;
11367c478bd9Sstevel@tonic-gate 	uint_t i;
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	fmd_event_hold(ep);
11397c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	for (i = 0; i < mhp->mh_hashlen; i++) {
11427c478bd9Sstevel@tonic-gate 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) {
11437c478bd9Sstevel@tonic-gate 			/*
11447c478bd9Sstevel@tonic-gate 			 * If FMD_MOD_INIT is set but MOD_FINI, MOD_QUIT, and
11457c478bd9Sstevel@tonic-gate 			 * mod_error are all zero, then the module is active:
11467c478bd9Sstevel@tonic-gate 			 * enqueue the event in the corresponding event queue.
11477c478bd9Sstevel@tonic-gate 			 */
11487c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&mp->mod_lock);
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate 			if ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI |
11512a417b23SRobert Johnston 			    FMD_MOD_QUIT)) == FMD_MOD_INIT && !mp->mod_error) {
11522a417b23SRobert Johnston 
11532a417b23SRobert Johnston 				/*
11542a417b23SRobert Johnston 				 * If the event we're dispatching is of type
11552a417b23SRobert Johnston 				 * FMD_EVT_TOPO and there are already redundant
11562a417b23SRobert Johnston 				 * FMD_EVT_TOPO events in this module's queue,
11572a417b23SRobert Johnston 				 * then drop those before adding the new one.
11582a417b23SRobert Johnston 				 */
11592a417b23SRobert Johnston 				if (FMD_EVENT_TYPE(ep) == FMD_EVT_TOPO)
11602a417b23SRobert Johnston 					fmd_eventq_drop_topo(mp->mod_queue);
11612a417b23SRobert Johnston 
11627c478bd9Sstevel@tonic-gate 				fmd_eventq_insert_at_time(mp->mod_queue, ep);
11637c478bd9Sstevel@tonic-gate 
11642a417b23SRobert Johnston 			}
11657c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&mp->mod_lock);
11667c478bd9Sstevel@tonic-gate 		}
11677c478bd9Sstevel@tonic-gate 	}
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
11707c478bd9Sstevel@tonic-gate 	fmd_event_rele(ep);
11717c478bd9Sstevel@tonic-gate }
11727c478bd9Sstevel@tonic-gate 
11737c478bd9Sstevel@tonic-gate fmd_module_t *
fmd_modhash_lookup(fmd_modhash_t * mhp,const char * name)11747c478bd9Sstevel@tonic-gate fmd_modhash_lookup(fmd_modhash_t *mhp, const char *name)
11757c478bd9Sstevel@tonic-gate {
11767c478bd9Sstevel@tonic-gate 	fmd_module_t *mp;
11777c478bd9Sstevel@tonic-gate 	uint_t h;
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
11807c478bd9Sstevel@tonic-gate 	h = fmd_strhash(name) % mhp->mh_hashlen;
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 	for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) {
11837c478bd9Sstevel@tonic-gate 		if (strcmp(name, mp->mod_name) == 0)
11847c478bd9Sstevel@tonic-gate 			break;
11857c478bd9Sstevel@tonic-gate 	}
11867c478bd9Sstevel@tonic-gate 
11877c478bd9Sstevel@tonic-gate 	if (mp != NULL)
11887c478bd9Sstevel@tonic-gate 		fmd_module_hold(mp);
11897c478bd9Sstevel@tonic-gate 	else
11907c478bd9Sstevel@tonic-gate 		(void) fmd_set_errno(EFMD_MOD_NOMOD);
11917c478bd9Sstevel@tonic-gate 
11927c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
11937c478bd9Sstevel@tonic-gate 	return (mp);
11947c478bd9Sstevel@tonic-gate }
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate fmd_module_t *
fmd_modhash_load(fmd_modhash_t * mhp,const char * path,const fmd_modops_t * ops)11977c478bd9Sstevel@tonic-gate fmd_modhash_load(fmd_modhash_t *mhp, const char *path, const fmd_modops_t *ops)
11987c478bd9Sstevel@tonic-gate {
11997c478bd9Sstevel@tonic-gate 	char name[PATH_MAX], *p;
12007c478bd9Sstevel@tonic-gate 	fmd_module_t *mp;
12017c478bd9Sstevel@tonic-gate 	int tries = 0;
12027c478bd9Sstevel@tonic-gate 	uint_t h;
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	(void) strlcpy(name, fmd_strbasename(path), sizeof (name));
12057c478bd9Sstevel@tonic-gate 	if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0)
12067c478bd9Sstevel@tonic-gate 		*p = '\0'; /* strip trailing .so from any module name */
12077c478bd9Sstevel@tonic-gate 
12087c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&mhp->mh_lock);
12097c478bd9Sstevel@tonic-gate 	h = fmd_strhash(name) % mhp->mh_hashlen;
12107c478bd9Sstevel@tonic-gate 
12117c478bd9Sstevel@tonic-gate 	/*
12127c478bd9Sstevel@tonic-gate 	 * First check to see if a module is already present in the hash table
12137c478bd9Sstevel@tonic-gate 	 * for this name.  If so, the module is already loaded: skip it.
12147c478bd9Sstevel@tonic-gate 	 */
12157c478bd9Sstevel@tonic-gate 	for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) {
12167c478bd9Sstevel@tonic-gate 		if (strcmp(name, mp->mod_name) == 0)
12177c478bd9Sstevel@tonic-gate 			break;
12187c478bd9Sstevel@tonic-gate 	}
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate 	if (mp != NULL) {
12217c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&mhp->mh_lock);
12227c478bd9Sstevel@tonic-gate 		(void) fmd_set_errno(EFMD_MOD_LOADED);
12237c478bd9Sstevel@tonic-gate 		return (NULL);
12247c478bd9Sstevel@tonic-gate 	}
12257c478bd9Sstevel@tonic-gate 
12267c478bd9Sstevel@tonic-gate 	/*
12277c478bd9Sstevel@tonic-gate 	 * fmd_module_create() will return a held (as if by fmd_module_hold())
12287c478bd9Sstevel@tonic-gate 	 * module.  We leave this hold in place to correspond to the hash-in.
12297c478bd9Sstevel@tonic-gate 	 */
12307c478bd9Sstevel@tonic-gate 	while ((mp = fmd_module_create(path, ops)) == NULL) {
12317c478bd9Sstevel@tonic-gate 		if (tries++ != 0 || errno != EFMD_CKPT_INVAL) {
12327c478bd9Sstevel@tonic-gate 			(void) pthread_rwlock_unlock(&mhp->mh_lock);
12337c478bd9Sstevel@tonic-gate 			return (NULL); /* errno is set for us */
12347c478bd9Sstevel@tonic-gate 		}
12357c478bd9Sstevel@tonic-gate 	}
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 	mp->mod_hash = mhp;
12387c478bd9Sstevel@tonic-gate 	mp->mod_next = mhp->mh_hash[h];
12397c478bd9Sstevel@tonic-gate 
12407c478bd9Sstevel@tonic-gate 	mhp->mh_hash[h] = mp;
12417c478bd9Sstevel@tonic-gate 	mhp->mh_nelems++;
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
12447c478bd9Sstevel@tonic-gate 	return (mp);
12457c478bd9Sstevel@tonic-gate }
12467c478bd9Sstevel@tonic-gate 
12477c478bd9Sstevel@tonic-gate int
fmd_modhash_unload(fmd_modhash_t * mhp,const char * name)12487c478bd9Sstevel@tonic-gate fmd_modhash_unload(fmd_modhash_t *mhp, const char *name)
12497c478bd9Sstevel@tonic-gate {
12507c478bd9Sstevel@tonic-gate 	fmd_module_t *mp, **pp;
12517c478bd9Sstevel@tonic-gate 	uint_t h;
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&mhp->mh_lock);
12547c478bd9Sstevel@tonic-gate 	h = fmd_strhash(name) % mhp->mh_hashlen;
12557c478bd9Sstevel@tonic-gate 	pp = &mhp->mh_hash[h];
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 	for (mp = *pp; mp != NULL; mp = mp->mod_next) {
12587c478bd9Sstevel@tonic-gate 		if (strcmp(name, mp->mod_name) == 0)
12597c478bd9Sstevel@tonic-gate 			break;
12607c478bd9Sstevel@tonic-gate 		else
12617c478bd9Sstevel@tonic-gate 			pp = &mp->mod_next;
12627c478bd9Sstevel@tonic-gate 	}
12637c478bd9Sstevel@tonic-gate 
12647c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
12657c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&mhp->mh_lock);
12667c478bd9Sstevel@tonic-gate 		return (fmd_set_errno(EFMD_MOD_NOMOD));
12677c478bd9Sstevel@tonic-gate 	}
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	*pp = mp->mod_next;
12707c478bd9Sstevel@tonic-gate 	mp->mod_next = NULL;
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 	ASSERT(mhp->mh_nelems != 0);
12737c478bd9Sstevel@tonic-gate 	mhp->mh_nelems--;
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate 	fmd_module_unload(mp);
12787c478bd9Sstevel@tonic-gate 	fmd_module_rele(mp);
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 	return (0);
12817c478bd9Sstevel@tonic-gate }
12827c478bd9Sstevel@tonic-gate 
12837c478bd9Sstevel@tonic-gate void
fmd_modstat_publish(fmd_module_t * mp)12847c478bd9Sstevel@tonic-gate fmd_modstat_publish(fmd_module_t *mp)
12857c478bd9Sstevel@tonic-gate {
12867c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
12877c478bd9Sstevel@tonic-gate 
12887c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_STSUB);
12897c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_STPUB;
12907c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
12917c478bd9Sstevel@tonic-gate 
12927c478bd9Sstevel@tonic-gate 	while (mp->mod_flags & FMD_MOD_STPUB)
12937c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_lock);
12967c478bd9Sstevel@tonic-gate }
12977c478bd9Sstevel@tonic-gate 
12987c478bd9Sstevel@tonic-gate int
fmd_modstat_snapshot(fmd_module_t * mp,fmd_ustat_snap_t * uss)12997c478bd9Sstevel@tonic-gate fmd_modstat_snapshot(fmd_module_t *mp, fmd_ustat_snap_t *uss)
13007c478bd9Sstevel@tonic-gate {
13017c478bd9Sstevel@tonic-gate 	fmd_event_t *e;
13027c478bd9Sstevel@tonic-gate 	int err;
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate 	/*
13057c478bd9Sstevel@tonic-gate 	 * Grab the module lock and wait for the STSUB bit to be clear.  Then
13067c478bd9Sstevel@tonic-gate 	 * set it to indicate we are a subscriber and everyone else must wait.
13077c478bd9Sstevel@tonic-gate 	 */
13087c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 	while (mp->mod_error == 0 && (mp->mod_flags & FMD_MOD_STSUB))
13117c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 	if (mp->mod_error != 0) {
13147c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
13157c478bd9Sstevel@tonic-gate 		return (fmd_set_errno(EFMD_HDL_ABORT));
13167c478bd9Sstevel@tonic-gate 	}
13177c478bd9Sstevel@tonic-gate 
13187c478bd9Sstevel@tonic-gate 	mp->mod_flags |= FMD_MOD_STSUB;
13197c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
1320d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
13217c478bd9Sstevel@tonic-gate 
13227c478bd9Sstevel@tonic-gate 	/*
13237c478bd9Sstevel@tonic-gate 	 * Create a stats pseudo-event and dispatch it to the module, forcing
13247c478bd9Sstevel@tonic-gate 	 * it to next execute its custom snapshot routine (or the empty one).
13257c478bd9Sstevel@tonic-gate 	 */
13267c478bd9Sstevel@tonic-gate 	e = fmd_event_create(FMD_EVT_STATS, FMD_HRT_NOW, NULL, NULL);
13277c478bd9Sstevel@tonic-gate 	fmd_eventq_insert_at_head(mp->mod_queue, e);
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	/*
13307c478bd9Sstevel@tonic-gate 	 * Grab the module lock and then wait on mod_cv for STPUB to be set,
13317c478bd9Sstevel@tonic-gate 	 * indicating the snapshot routine is completed and the module is idle.
13327c478bd9Sstevel@tonic-gate 	 */
13337c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
13347c478bd9Sstevel@tonic-gate 
1335e58a33b6SStephen Hanson 	while (mp->mod_error == 0 && !(mp->mod_flags & FMD_MOD_STPUB)) {
1336e58a33b6SStephen Hanson 		struct timespec tms;
1337e58a33b6SStephen Hanson 
13387c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
1339e58a33b6SStephen Hanson 		(void) pthread_mutex_unlock(&mp->mod_lock);
1340e58a33b6SStephen Hanson 		tms.tv_sec = 0;
1341e58a33b6SStephen Hanson 		tms.tv_nsec = 10000000;
1342e58a33b6SStephen Hanson 		(void) nanosleep(&tms, NULL);
1343e58a33b6SStephen Hanson 		(void) pthread_mutex_lock(&mp->mod_lock);
1344e58a33b6SStephen Hanson 	}
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 	if (mp->mod_error != 0) {
13477c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&mp->mod_lock);
13487c478bd9Sstevel@tonic-gate 		return (fmd_set_errno(EFMD_HDL_ABORT));
13497c478bd9Sstevel@tonic-gate 	}
13507c478bd9Sstevel@tonic-gate 
13517c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
1352d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
13537c478bd9Sstevel@tonic-gate 
13547c478bd9Sstevel@tonic-gate 	/*
13557c478bd9Sstevel@tonic-gate 	 * Update ms_snaptime and take the actual snapshot of the various
13567c478bd9Sstevel@tonic-gate 	 * statistics while the module is quiescent and waiting for us.
13577c478bd9Sstevel@tonic-gate 	 */
13587c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_stats_lock);
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 	if (mp->mod_stats != NULL) {
13617c478bd9Sstevel@tonic-gate 		mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime();
13627c478bd9Sstevel@tonic-gate 		err = fmd_ustat_snapshot(mp->mod_ustat, uss);
13637c478bd9Sstevel@tonic-gate 	} else
13647c478bd9Sstevel@tonic-gate 		err = fmd_set_errno(EFMD_HDL_ABORT);
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&mp->mod_stats_lock);
13677c478bd9Sstevel@tonic-gate 
13687c478bd9Sstevel@tonic-gate 	/*
13697c478bd9Sstevel@tonic-gate 	 * With the snapshot complete, grab the module lock and clear both
13707c478bd9Sstevel@tonic-gate 	 * STSUB and STPUB, permitting everyone to wake up and continue.
13717c478bd9Sstevel@tonic-gate 	 */
13727c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&mp->mod_lock);
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_STSUB);
13757c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_flags & FMD_MOD_STPUB);
13767c478bd9Sstevel@tonic-gate 	mp->mod_flags &= ~(FMD_MOD_STSUB | FMD_MOD_STPUB);
13777c478bd9Sstevel@tonic-gate 
13787c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&mp->mod_cv);
1379d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_lock);
13807c478bd9Sstevel@tonic-gate 
13817c478bd9Sstevel@tonic-gate 	return (err);
13827c478bd9Sstevel@tonic-gate }
138324db4641Seschrock 
138424db4641Seschrock struct topo_hdl *
fmd_module_topo_hold(fmd_module_t * mp)138524db4641Seschrock fmd_module_topo_hold(fmd_module_t *mp)
138624db4641Seschrock {
138724db4641Seschrock 	fmd_modtopo_t *mtp;
138824db4641Seschrock 
138924db4641Seschrock 	ASSERT(fmd_module_locked(mp));
139024db4641Seschrock 
139124db4641Seschrock 	mtp = fmd_zalloc(sizeof (fmd_modtopo_t), FMD_SLEEP);
139224db4641Seschrock 	mtp->mt_topo = mp->mod_topo_current;
139324db4641Seschrock 	fmd_topo_addref(mtp->mt_topo);
139424db4641Seschrock 	fmd_list_prepend(&mp->mod_topolist, mtp);
139524db4641Seschrock 
139624db4641Seschrock 	return (mtp->mt_topo->ft_hdl);
139724db4641Seschrock }
139824db4641Seschrock 
139924db4641Seschrock int
fmd_module_topo_rele(fmd_module_t * mp,struct topo_hdl * hdl)140024db4641Seschrock fmd_module_topo_rele(fmd_module_t *mp, struct topo_hdl *hdl)
140124db4641Seschrock {
140224db4641Seschrock 	fmd_modtopo_t *mtp;
140324db4641Seschrock 
140424db4641Seschrock 	ASSERT(fmd_module_locked(mp));
140524db4641Seschrock 
140624db4641Seschrock 	for (mtp = fmd_list_next(&mp->mod_topolist); mtp != NULL;
140724db4641Seschrock 	    mtp = fmd_list_next(mtp)) {
140824db4641Seschrock 		if (mtp->mt_topo->ft_hdl == hdl)
140924db4641Seschrock 			break;
141024db4641Seschrock 	}
141124db4641Seschrock 
141224db4641Seschrock 	if (mtp == NULL)
141324db4641Seschrock 		return (-1);
141424db4641Seschrock 
141524db4641Seschrock 	fmd_list_delete(&mp->mod_topolist, mtp);
141624db4641Seschrock 	fmd_topo_rele(mtp->mt_topo);
141724db4641Seschrock 	fmd_free(mtp, sizeof (fmd_modtopo_t));
141824db4641Seschrock 	return (0);
141924db4641Seschrock }
1420