xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_module.c (revision 3cac7b0d73edf3f2674ad0f64d1fff3d2e59ae8c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <signal.h>
27 #include <dirent.h>
28 #include <limits.h>
29 #include <alloca.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 
33 #include <fmd_string.h>
34 #include <fmd_alloc.h>
35 #include <fmd_module.h>
36 #include <fmd_error.h>
37 #include <fmd_conf.h>
38 #include <fmd_dispq.h>
39 #include <fmd_eventq.h>
40 #include <fmd_timerq.h>
41 #include <fmd_subr.h>
42 #include <fmd_thread.h>
43 #include <fmd_ustat.h>
44 #include <fmd_case.h>
45 #include <fmd_protocol.h>
46 #include <fmd_buf.h>
47 #include <fmd_ckpt.h>
48 #include <fmd_xprt.h>
49 #include <fmd_topo.h>
50 
51 #include <fmd.h>
52 
53 /*
54  * Template for per-module statistics installed by fmd on behalf of each active
55  * module.  These are used to initialize the per-module mp->mod_stats below.
56  * NOTE: FMD_TYPE_STRING statistics should not be used here.  If they are
57  * required in the future, the FMD_ADM_MODDSTAT service routine must change.
58  */
59 static const fmd_modstat_t _fmd_modstat_tmpl = {
60 {
61 { "fmd.dispatched", FMD_TYPE_UINT64, "total events dispatched to module" },
62 { "fmd.dequeued", FMD_TYPE_UINT64, "total events dequeued by module" },
63 { "fmd.prdequeued", FMD_TYPE_UINT64, "protocol events dequeued by module" },
64 { "fmd.dropped", FMD_TYPE_UINT64, "total events dropped on queue overflow" },
65 { "fmd.wcnt", FMD_TYPE_UINT32, "count of events waiting on queue" },
66 { "fmd.wtime", FMD_TYPE_TIME, "total wait time on queue" },
67 { "fmd.wlentime", FMD_TYPE_TIME, "total wait length * time product" },
68 { "fmd.wlastupdate", FMD_TYPE_TIME, "hrtime of last wait queue update" },
69 { "fmd.dtime", FMD_TYPE_TIME, "total processing time after dequeue" },
70 { "fmd.dlastupdate", FMD_TYPE_TIME, "hrtime of last event dequeue completion" },
71 },
72 { "fmd.loadtime", FMD_TYPE_TIME, "hrtime at which module was loaded" },
73 { "fmd.snaptime", FMD_TYPE_TIME, "hrtime of last statistics snapshot" },
74 { "fmd.accepted", FMD_TYPE_UINT64, "total events accepted by module" },
75 { "fmd.debugdrop", FMD_TYPE_UINT64, "dropped debug messages" },
76 { "fmd.memtotal", FMD_TYPE_SIZE, "total memory allocated by module" },
77 { "fmd.memlimit", FMD_TYPE_SIZE, "limit on total memory allocated" },
78 { "fmd.buftotal", FMD_TYPE_SIZE, "total buffer space used by module" },
79 { "fmd.buflimit", FMD_TYPE_SIZE, "limit on total buffer space" },
80 { "fmd.thrtotal", FMD_TYPE_UINT32, "total number of auxiliary threads" },
81 { "fmd.thrlimit", FMD_TYPE_UINT32, "limit on number of auxiliary threads" },
82 { "fmd.doorthrtotal", FMD_TYPE_UINT32, "total number of door server threads" },
83 { "fmd.doorthrlimit", FMD_TYPE_UINT32, "limit on door server threads" },
84 { "fmd.caseopen", FMD_TYPE_UINT64, "cases currently open by module" },
85 { "fmd.casesolved", FMD_TYPE_UINT64, "total cases solved by module" },
86 { "fmd.caseclosed", FMD_TYPE_UINT64, "total cases closed by module" },
87 { "fmd.ckptsave", FMD_TYPE_BOOL, "save checkpoints for module" },
88 { "fmd.ckptrestore", FMD_TYPE_BOOL, "restore checkpoints for module" },
89 { "fmd.ckptzero", FMD_TYPE_BOOL, "zeroed checkpoint at startup" },
90 { "fmd.ckptcnt", FMD_TYPE_UINT64, "number of checkpoints taken" },
91 { "fmd.ckpttime", FMD_TYPE_TIME, "total checkpoint time" },
92 { "fmd.xprtopen", FMD_TYPE_UINT32, "total number of open transports" },
93 { "fmd.xprtlimit", FMD_TYPE_UINT32, "limit on number of open transports" },
94 { "fmd.xprtqlimit", FMD_TYPE_UINT32, "limit on transport event queue length" },
95 };
96 
97 static void
98 fmd_module_start(void *arg)
99 {
100 	fmd_module_t *mp = arg;
101 	fmd_event_t *ep;
102 	fmd_xprt_t *xp;
103 
104 	(void) pthread_mutex_lock(&mp->mod_lock);
105 
106 	if (mp->mod_ops->mop_init(mp) != 0 || mp->mod_error != 0) {
107 		if (mp->mod_error == 0)
108 			mp->mod_error = errno ? errno : EFMD_MOD_INIT;
109 		goto out;
110 	}
111 
112 	if (fmd.d_mod_event != NULL)
113 		fmd_eventq_insert_at_head(mp->mod_queue, fmd.d_mod_event);
114 
115 	ASSERT(MUTEX_HELD(&mp->mod_lock));
116 	mp->mod_flags |= FMD_MOD_INIT;
117 
118 	(void) pthread_cond_broadcast(&mp->mod_cv);
119 	(void) pthread_mutex_unlock(&mp->mod_lock);
120 
121 	/*
122 	 * If the module opened any transports while executing _fmd_init(),
123 	 * they are suspended. Now that _fmd_init() is done, wake them up.
124 	 */
125 	for (xp = fmd_list_next(&mp->mod_transports);
126 	    xp != NULL; xp = fmd_list_next(xp))
127 		fmd_xprt_xresume(xp, FMD_XPRT_ISUSPENDED);
128 
129 	/*
130 	 * Wait for events to arrive by checking mod_error and then sleeping in
131 	 * fmd_eventq_delete().  If a NULL event is returned, the eventq has
132 	 * been aborted and we continue on to call fini and exit the thread.
133 	 */
134 	while ((ep = fmd_eventq_delete(mp->mod_queue)) != NULL) {
135 		/*
136 		 * If the module has failed, discard the event without ever
137 		 * passing it to the module and go back to sleep.
138 		 */
139 		if (mp->mod_error != 0) {
140 			fmd_eventq_done(mp->mod_queue);
141 			fmd_event_rele(ep);
142 			continue;
143 		}
144 
145 		mp->mod_ops->mop_dispatch(mp, ep);
146 		fmd_eventq_done(mp->mod_queue);
147 
148 		/*
149 		 * Once mop_dispatch() is complete, grab the lock and perform
150 		 * any event-specific post-processing.  Finally, if necessary,
151 		 * checkpoint the state of the module after this event.
152 		 */
153 		fmd_module_lock(mp);
154 
155 		if (FMD_EVENT_TYPE(ep) == FMD_EVT_CLOSE)
156 			fmd_case_delete(FMD_EVENT_DATA(ep));
157 
158 		fmd_ckpt_save(mp);
159 		fmd_module_unlock(mp);
160 		fmd_event_rele(ep);
161 	}
162 
163 	if (mp->mod_ops->mop_fini(mp) != 0 && mp->mod_error == 0)
164 		mp->mod_error = errno ? errno : EFMD_MOD_FINI;
165 
166 	(void) pthread_mutex_lock(&mp->mod_lock);
167 	mp->mod_flags |= FMD_MOD_FINI;
168 
169 out:
170 	(void) pthread_cond_broadcast(&mp->mod_cv);
171 	(void) pthread_mutex_unlock(&mp->mod_lock);
172 }
173 
174 fmd_module_t *
175 fmd_module_create(const char *path, const fmd_modops_t *ops)
176 {
177 	fmd_module_t *mp = fmd_zalloc(sizeof (fmd_module_t), FMD_SLEEP);
178 
179 	char buf[PATH_MAX], *p;
180 	const char *dir;
181 	uint32_t limit;
182 	int err;
183 
184 	(void) strlcpy(buf, fmd_strbasename(path), sizeof (buf));
185 	if ((p = strrchr(buf, '.')) != NULL && strcmp(p, ".so") == 0)
186 		*p = '\0'; /* strip trailing .so from any module name */
187 
188 	(void) pthread_mutex_init(&mp->mod_lock, NULL);
189 	(void) pthread_cond_init(&mp->mod_cv, NULL);
190 	(void) pthread_mutex_init(&mp->mod_stats_lock, NULL);
191 
192 	mp->mod_name = fmd_strdup(buf, FMD_SLEEP);
193 	mp->mod_path = fmd_strdup(path, FMD_SLEEP);
194 	mp->mod_ops = ops;
195 	mp->mod_ustat = fmd_ustat_create();
196 
197 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.dir", &dir);
198 	(void) snprintf(buf, sizeof (buf),
199 	    "%s/%s/%s", fmd.d_rootdir, dir, mp->mod_name);
200 
201 	mp->mod_ckpt = fmd_strdup(buf, FMD_SLEEP);
202 
203 	(void) fmd_conf_getprop(fmd.d_conf, "client.tmrlim", &limit);
204 	mp->mod_timerids = fmd_idspace_create(mp->mod_name, 1, limit + 1);
205 	mp->mod_threads = fmd_idspace_create(mp->mod_name, 0, INT_MAX);
206 
207 	fmd_buf_hash_create(&mp->mod_bufs);
208 	fmd_serd_hash_create(&mp->mod_serds);
209 
210 	mp->mod_topo_current = fmd_topo_hold();
211 
212 	(void) pthread_mutex_lock(&fmd.d_mod_lock);
213 	fmd_list_append(&fmd.d_mod_list, mp);
214 	(void) pthread_mutex_unlock(&fmd.d_mod_lock);
215 
216 	/*
217 	 * Initialize the module statistics that are kept on its behalf by fmd.
218 	 * These are set up using a template defined at the top of this file.
219 	 */
220 	if ((mp->mod_stats = (fmd_modstat_t *)fmd_ustat_insert(mp->mod_ustat,
221 	    FMD_USTAT_ALLOC, sizeof (_fmd_modstat_tmpl) / sizeof (fmd_stat_t),
222 	    (fmd_stat_t *)&_fmd_modstat_tmpl, NULL)) == NULL) {
223 		fmd_error(EFMD_MOD_INIT, "failed to initialize per-mod stats");
224 		fmd_module_destroy(mp);
225 		return (NULL);
226 	}
227 
228 	if (nv_alloc_init(&mp->mod_nva_sleep,
229 	    &fmd_module_nva_ops_sleep, mp) != 0 ||
230 	    nv_alloc_init(&mp->mod_nva_nosleep,
231 	    &fmd_module_nva_ops_nosleep, mp) != 0) {
232 		fmd_error(EFMD_MOD_INIT, "failed to initialize nvlist "
233 		    "allocation routines");
234 		fmd_module_destroy(mp);
235 		return (NULL);
236 	}
237 
238 	(void) fmd_conf_getprop(fmd.d_conf, "client.evqlim", &limit);
239 
240 	mp->mod_queue = fmd_eventq_create(mp,
241 	    &mp->mod_stats->ms_evqstat, &mp->mod_stats_lock, limit);
242 
243 	(void) fmd_conf_getprop(fmd.d_conf, "client.memlim",
244 	    &mp->mod_stats->ms_memlimit.fmds_value.ui64);
245 
246 	(void) fmd_conf_getprop(fmd.d_conf, "client.buflim",
247 	    &mp->mod_stats->ms_buflimit.fmds_value.ui64);
248 
249 	(void) fmd_conf_getprop(fmd.d_conf, "client.thrlim",
250 	    &mp->mod_stats->ms_thrlimit.fmds_value.ui32);
251 
252 	(void) fmd_conf_getprop(fmd.d_conf, "client.doorthrlim",
253 	    &mp->mod_stats->ms_doorthrlimit.fmds_value.ui32);
254 
255 	(void) fmd_conf_getprop(fmd.d_conf, "client.xprtlim",
256 	    &mp->mod_stats->ms_xprtlimit.fmds_value.ui32);
257 
258 	(void) fmd_conf_getprop(fmd.d_conf, "client.xprtqlim",
259 	    &mp->mod_stats->ms_xprtqlimit.fmds_value.ui32);
260 
261 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.save",
262 	    &mp->mod_stats->ms_ckpt_save.fmds_value.bool);
263 
264 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.restore",
265 	    &mp->mod_stats->ms_ckpt_restore.fmds_value.bool);
266 
267 	(void) fmd_conf_getprop(fmd.d_conf, "ckpt.zero",
268 	    &mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool);
269 
270 	if (mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool)
271 		fmd_ckpt_delete(mp); /* blow away any pre-existing checkpoint */
272 
273 	/*
274 	 * Place a hold on the module and grab the module lock before creating
275 	 * the module's thread to ensure that it cannot destroy the module and
276 	 * that it cannot call ops->mop_init() before we're done setting up.
277 	 * NOTE: from now on, we must use fmd_module_rele() for error paths.
278 	 */
279 	fmd_module_hold(mp);
280 	(void) pthread_mutex_lock(&mp->mod_lock);
281 	mp->mod_stats->ms_loadtime.fmds_value.ui64 = gethrtime();
282 	mp->mod_thread = fmd_thread_create(mp, fmd_module_start, mp);
283 
284 	if (mp->mod_thread == NULL) {
285 		fmd_error(EFMD_MOD_THR, "failed to create thread for %s", path);
286 		(void) pthread_mutex_unlock(&mp->mod_lock);
287 		fmd_module_rele(mp);
288 		return (NULL);
289 	}
290 
291 	/*
292 	 * At this point our module structure is nearly finished and its thread
293 	 * is starting execution in fmd_module_start() above, which will begin
294 	 * by blocking for mod_lock.  We now drop mod_lock and wait for either
295 	 * FMD_MOD_INIT or mod_error to be set before proceeding.
296 	 */
297 	while (!(mp->mod_flags & FMD_MOD_INIT) && mp->mod_error == 0)
298 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
299 
300 	/*
301 	 * If the module has failed to initialize, copy its errno to the errno
302 	 * of the caller, wait for it to unload, and then destroy it.
303 	 */
304 	if (!(mp->mod_flags & FMD_MOD_INIT)) {
305 		err = mp->mod_error;
306 		(void) pthread_mutex_unlock(&mp->mod_lock);
307 
308 		if (err == EFMD_CKPT_INVAL)
309 			fmd_ckpt_rename(mp); /* move aside bad checkpoint */
310 
311 		/*
312 		 * If we're in the background, keep quiet about failure to
313 		 * load because a handle wasn't registered: this is a module's
314 		 * way of telling us it didn't want to be loaded for some
315 		 * reason related to system configuration.  If we're in the
316 		 * foreground we log this too in order to inform developers.
317 		 */
318 		if (fmd.d_fg || err != EFMD_HDL_INIT) {
319 			fmd_error(EFMD_MOD_INIT, "failed to load %s: %s\n",
320 			    path, fmd_strerror(err));
321 		}
322 
323 		fmd_module_unload(mp);
324 		fmd_module_rele(mp);
325 
326 		(void) fmd_set_errno(err);
327 		return (NULL);
328 	}
329 
330 	(void) pthread_cond_broadcast(&mp->mod_cv);
331 	(void) pthread_mutex_unlock(&mp->mod_lock);
332 
333 	fmd_dprintf(FMD_DBG_MOD, "loaded module %s\n", mp->mod_name);
334 	return (mp);
335 }
336 
337 static void
338 fmd_module_untimeout(fmd_idspace_t *ids, id_t id, fmd_module_t *mp)
339 {
340 	void *arg = fmd_timerq_remove(fmd.d_timers, ids, id);
341 
342 	/*
343 	 * The root module calls fmd_timerq_install() directly and must take
344 	 * responsibility for any cleanup of timer arguments that is required.
345 	 * All other modules use fmd_modtimer_t's as the arg data; free them.
346 	 */
347 	if (arg != NULL && mp != fmd.d_rmod)
348 		fmd_free(arg, sizeof (fmd_modtimer_t));
349 }
350 
351 void
352 fmd_module_unload(fmd_module_t *mp)
353 {
354 	fmd_modtopo_t *mtp;
355 
356 	(void) pthread_mutex_lock(&mp->mod_lock);
357 
358 	if (mp->mod_flags & FMD_MOD_QUIT) {
359 		(void) pthread_mutex_unlock(&mp->mod_lock);
360 		return; /* module is already unloading */
361 	}
362 
363 	ASSERT(mp->mod_thread != NULL);
364 	mp->mod_flags |= FMD_MOD_QUIT;
365 
366 	if (mp->mod_queue != NULL)
367 		fmd_eventq_abort(mp->mod_queue);
368 
369 	/*
370 	 * Wait for the module's thread to stop processing events and call
371 	 * _fmd_fini() and exit.  We do this by waiting for FMD_MOD_FINI to be
372 	 * set if INIT was set, and then attempting to join with the thread.
373 	 */
374 	while ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI)) == FMD_MOD_INIT)
375 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
376 
377 	(void) pthread_cond_broadcast(&mp->mod_cv);
378 	(void) pthread_mutex_unlock(&mp->mod_lock);
379 
380 	fmd_thread_destroy(mp->mod_thread, FMD_THREAD_JOIN);
381 	mp->mod_thread = NULL;
382 
383 	/*
384 	 * Once the module is no longer active, clean up any data structures
385 	 * that are only required when the module is loaded.
386 	 */
387 	fmd_module_lock(mp);
388 
389 	if (mp->mod_timerids != NULL) {
390 		fmd_idspace_apply(mp->mod_timerids,
391 		    (void (*)())fmd_module_untimeout, mp);
392 
393 		fmd_idspace_destroy(mp->mod_timerids);
394 		mp->mod_timerids = NULL;
395 	}
396 
397 	if (mp->mod_threads != NULL) {
398 		fmd_idspace_destroy(mp->mod_threads);
399 		mp->mod_threads = NULL;
400 	}
401 
402 	(void) fmd_buf_hash_destroy(&mp->mod_bufs);
403 	fmd_serd_hash_destroy(&mp->mod_serds);
404 
405 	while ((mtp = fmd_list_next(&mp->mod_topolist)) != NULL) {
406 		fmd_list_delete(&mp->mod_topolist, mtp);
407 		fmd_topo_rele(mtp->mt_topo);
408 		fmd_free(mtp, sizeof (fmd_modtopo_t));
409 	}
410 
411 	fmd_module_unlock(mp);
412 	fmd_dprintf(FMD_DBG_MOD, "unloaded module %s\n", mp->mod_name);
413 }
414 
415 void
416 fmd_module_destroy(fmd_module_t *mp)
417 {
418 	fmd_conf_formal_t *cfp = mp->mod_argv;
419 	int i;
420 
421 	ASSERT(MUTEX_HELD(&mp->mod_lock));
422 
423 	if (mp->mod_thread != NULL) {
424 		(void) pthread_mutex_unlock(&mp->mod_lock);
425 		fmd_module_unload(mp);
426 		(void) pthread_mutex_lock(&mp->mod_lock);
427 	}
428 
429 	ASSERT(mp->mod_thread == NULL);
430 	ASSERT(mp->mod_refs == 0);
431 
432 	/*
433 	 * Once the module's thread is dead, we can safely remove the module
434 	 * from global visibility and by removing it from d_mod_list.  Any
435 	 * modhash pointers are already gone by virtue of mod_refs being zero.
436 	 */
437 	(void) pthread_mutex_lock(&fmd.d_mod_lock);
438 	fmd_list_delete(&fmd.d_mod_list, mp);
439 	(void) pthread_mutex_unlock(&fmd.d_mod_lock);
440 
441 	if (mp->mod_topo_current != NULL)
442 		fmd_topo_rele(mp->mod_topo_current);
443 
444 	if (mp->mod_nva_sleep.nva_ops != NULL)
445 		nv_alloc_fini(&mp->mod_nva_sleep);
446 	if (mp->mod_nva_nosleep.nva_ops != NULL)
447 		nv_alloc_fini(&mp->mod_nva_nosleep);
448 
449 	/*
450 	 * Once the module is no longer processing events and no longer visible
451 	 * through any program data structures, we can free all of its content.
452 	 */
453 	if (mp->mod_queue != NULL) {
454 		fmd_eventq_destroy(mp->mod_queue);
455 		mp->mod_queue = NULL;
456 	}
457 
458 	if (mp->mod_ustat != NULL) {
459 		(void) pthread_mutex_lock(&mp->mod_stats_lock);
460 		fmd_ustat_destroy(mp->mod_ustat);
461 		mp->mod_ustat = NULL;
462 		mp->mod_stats = NULL;
463 		(void) pthread_mutex_unlock(&mp->mod_stats_lock);
464 	}
465 
466 	for (i = 0; i < mp->mod_dictc; i++)
467 		fm_dc_closedict(mp->mod_dictv[i]);
468 
469 	fmd_free(mp->mod_dictv, sizeof (struct fm_dc_handle *) * mp->mod_dictc);
470 
471 	if (mp->mod_conf != NULL)
472 		fmd_conf_close(mp->mod_conf);
473 
474 	for (i = 0; i < mp->mod_argc; i++, cfp++) {
475 		fmd_strfree((char *)cfp->cf_name);
476 		fmd_strfree((char *)cfp->cf_default);
477 	}
478 
479 	fmd_free(mp->mod_argv, sizeof (fmd_conf_formal_t) * mp->mod_argc);
480 
481 	fmd_strfree(mp->mod_name);
482 	fmd_strfree(mp->mod_path);
483 	fmd_strfree(mp->mod_ckpt);
484 	nvlist_free(mp->mod_fmri);
485 	fmd_strfree(mp->mod_vers);
486 
487 	fmd_free(mp, sizeof (fmd_module_t));
488 }
489 
490 /*
491  * fmd_module_error() is called after the stack is unwound from a call to
492  * fmd_module_abort() to indicate that the module has failed.  The mod_error
493  * field is used to hold the error code of the first fatal error to the module.
494  * An EFMD_MOD_FAIL event is then created and sent to fmd-self-diagnosis.
495  */
496 static void
497 fmd_module_error(fmd_module_t *mp, int err)
498 {
499 	fmd_event_t *e;
500 	nvlist_t *nvl;
501 	char *class;
502 
503 	ASSERT(MUTEX_HELD(&mp->mod_lock));
504 	ASSERT(err != 0);
505 
506 	TRACE((FMD_DBG_MOD, "module aborted: err=%d", err));
507 
508 	if (mp->mod_error == 0)
509 		mp->mod_error = err;
510 
511 	if (mp == fmd.d_self)
512 		return; /* do not post event if fmd.d_self itself fails */
513 
514 	/*
515 	 * Send an error indicating the module has now failed to fmd.d_self.
516 	 * Since the error causing the failure has already been logged by
517 	 * fmd_api_xerror(), we do not need to bother logging this event.
518 	 * It only exists for the purpose of notifying fmd.d_self that it can
519 	 * close the case associated with this module because mod_error is set.
520 	 */
521 	nvl = fmd_protocol_moderror(mp, EFMD_MOD_FAIL, fmd_strerror(err));
522 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
523 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
524 	fmd_dispq_dispatch(fmd.d_disp, e, class);
525 }
526 
527 void
528 fmd_module_dispatch(fmd_module_t *mp, fmd_event_t *e)
529 {
530 	const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;
531 	fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
532 	fmd_hdl_t *hdl = (fmd_hdl_t *)mp;
533 	fmd_modtimer_t *t;
534 	fmd_topo_t *old_topo;
535 	volatile int err;
536 
537 	/*
538 	 * Before calling the appropriate module callback, enter the module as
539 	 * if by fmd_module_enter() and establish mod_jmpbuf for any aborts.
540 	 */
541 	(void) pthread_mutex_lock(&mp->mod_lock);
542 
543 	ASSERT(!(mp->mod_flags & FMD_MOD_BUSY));
544 	mp->mod_flags |= FMD_MOD_BUSY;
545 
546 	if ((err = setjmp(mp->mod_jmpbuf)) != 0) {
547 		(void) pthread_mutex_lock(&mp->mod_lock);
548 		fmd_module_error(mp, err);
549 	}
550 
551 	(void) pthread_cond_broadcast(&mp->mod_cv);
552 	(void) pthread_mutex_unlock(&mp->mod_lock);
553 
554 	/*
555 	 * If it's the first time through fmd_module_dispatch(), call the
556 	 * appropriate module callback based on the event type.  If the call
557 	 * triggers an fmd_module_abort(), we'll return to setjmp() above with
558 	 * err set to a non-zero value and then bypass this before exiting.
559 	 */
560 	if (err == 0) {
561 		switch (ep->ev_type) {
562 		case FMD_EVT_PROTOCOL:
563 			ops->fmdo_recv(hdl, e, ep->ev_nvl, ep->ev_data);
564 			break;
565 		case FMD_EVT_TIMEOUT:
566 			t = ep->ev_data;
567 			ASSERT(t->mt_mod == mp);
568 			ops->fmdo_timeout(hdl, t->mt_id, t->mt_arg);
569 			break;
570 		case FMD_EVT_CLOSE:
571 			ops->fmdo_close(hdl, ep->ev_data);
572 			break;
573 		case FMD_EVT_STATS:
574 			ops->fmdo_stats(hdl);
575 			fmd_modstat_publish(mp);
576 			break;
577 		case FMD_EVT_GC:
578 			ops->fmdo_gc(hdl);
579 			break;
580 		case FMD_EVT_PUBLISH:
581 			fmd_case_publish(ep->ev_data, FMD_CASE_CURRENT);
582 			break;
583 		case FMD_EVT_TOPO:
584 			/*
585 			 * Save the pointer to the old topology and update
586 			 * the pointer with the updated topology.
587 			 * With this approach, other threads that reference the
588 			 * topology either
589 			 *  - finishes with old topology since
590 			 *	it is released after updating
591 			 *	mod_topo_current.
592 			 *  - or is blocked while mod_topo_current is updated.
593 			 */
594 			old_topo = mp->mod_topo_current;
595 			fmd_module_lock(mp);
596 			mp->mod_topo_current = (fmd_topo_t *)ep->ev_data;
597 			fmd_topo_addref(mp->mod_topo_current);
598 			fmd_module_unlock(mp);
599 			fmd_topo_rele(old_topo);
600 			ops->fmdo_topo(hdl, mp->mod_topo_current->ft_hdl);
601 			break;
602 		}
603 	}
604 
605 	fmd_module_exit(mp);
606 }
607 
608 int
609 fmd_module_transport(fmd_module_t *mp, fmd_xprt_t *xp, fmd_event_t *e)
610 {
611 	fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
612 	fmd_hdl_t *hdl = (fmd_hdl_t *)mp;
613 
614 	ASSERT(ep->ev_type == FMD_EVT_PROTOCOL);
615 	return (mp->mod_info->fmdi_ops->fmdo_send(hdl, xp, e, ep->ev_nvl));
616 }
617 
618 void
619 fmd_module_timeout(fmd_modtimer_t *t, id_t id, hrtime_t hrt)
620 {
621 	fmd_event_t *e;
622 
623 	t->mt_id = id; /* save id in case we need to delete from eventq */
624 	e = fmd_event_create(FMD_EVT_TIMEOUT, hrt, NULL, t);
625 	fmd_eventq_insert_at_time(t->mt_mod->mod_queue, e);
626 }
627 
628 /*
629  * Garbage collection is initiated by a timer callback once per day or at the
630  * request of fmadm.  Purge old SERD entries and send the module a GC event.
631  */
632 void
633 fmd_module_gc(fmd_module_t *mp)
634 {
635 	fmd_hdl_info_t *info;
636 	fmd_event_t *e;
637 
638 	if (mp->mod_error != 0)
639 		return; /* do not do anything if the module has failed */
640 
641 	fmd_module_lock(mp);
642 
643 	if ((info = mp->mod_info) != NULL) {
644 		fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_gc, NULL);
645 	}
646 
647 	fmd_module_unlock(mp);
648 
649 	if (info != NULL) {
650 		e = fmd_event_create(FMD_EVT_GC, FMD_HRT_NOW, NULL, NULL);
651 		fmd_eventq_insert_at_head(mp->mod_queue, e);
652 	}
653 }
654 
655 void
656 fmd_module_trygc(fmd_module_t *mp)
657 {
658 	if (fmd_module_trylock(mp)) {
659 		fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_gc, NULL);
660 		fmd_module_unlock(mp);
661 	}
662 }
663 
664 int
665 fmd_module_contains(fmd_module_t *mp, fmd_event_t *ep)
666 {
667 	fmd_case_t *cp;
668 	int rv = 0;
669 
670 	fmd_module_lock(mp);
671 
672 	for (cp = fmd_list_next(&mp->mod_cases);
673 	    cp != NULL; cp = fmd_list_next(cp)) {
674 		if ((rv = fmd_case_contains(cp, ep)) != 0)
675 			break;
676 	}
677 
678 	if (rv == 0)
679 		rv = fmd_serd_hash_contains(&mp->mod_serds, ep);
680 
681 	fmd_module_unlock(mp);
682 	return (rv);
683 }
684 
685 void
686 fmd_module_setdirty(fmd_module_t *mp)
687 {
688 	(void) pthread_mutex_lock(&mp->mod_lock);
689 	mp->mod_flags |= FMD_MOD_MDIRTY;
690 	(void) pthread_mutex_unlock(&mp->mod_lock);
691 }
692 
693 void
694 fmd_module_setcdirty(fmd_module_t *mp)
695 {
696 	(void) pthread_mutex_lock(&mp->mod_lock);
697 	mp->mod_flags |= FMD_MOD_CDIRTY;
698 	(void) pthread_mutex_unlock(&mp->mod_lock);
699 }
700 
701 void
702 fmd_module_clrdirty(fmd_module_t *mp)
703 {
704 	fmd_case_t *cp;
705 
706 	fmd_module_lock(mp);
707 
708 	if (mp->mod_flags & FMD_MOD_CDIRTY) {
709 		for (cp = fmd_list_next(&mp->mod_cases);
710 		    cp != NULL; cp = fmd_list_next(cp))
711 			fmd_case_clrdirty(cp);
712 	}
713 
714 	if (mp->mod_flags & FMD_MOD_MDIRTY) {
715 		fmd_serd_hash_apply(&mp->mod_serds,
716 		    fmd_serd_eng_clrdirty, NULL);
717 		fmd_buf_hash_commit(&mp->mod_bufs);
718 	}
719 
720 	(void) pthread_mutex_lock(&mp->mod_lock);
721 	mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY);
722 	(void) pthread_mutex_unlock(&mp->mod_lock);
723 
724 	fmd_module_unlock(mp);
725 }
726 
727 void
728 fmd_module_commit(fmd_module_t *mp)
729 {
730 	fmd_case_t *cp;
731 
732 	ASSERT(fmd_module_locked(mp));
733 
734 	if (mp->mod_flags & FMD_MOD_CDIRTY) {
735 		for (cp = fmd_list_next(&mp->mod_cases);
736 		    cp != NULL; cp = fmd_list_next(cp))
737 			fmd_case_commit(cp);
738 	}
739 
740 	if (mp->mod_flags & FMD_MOD_MDIRTY) {
741 		fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_commit, NULL);
742 		fmd_buf_hash_commit(&mp->mod_bufs);
743 	}
744 
745 	(void) pthread_mutex_lock(&mp->mod_lock);
746 	mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY);
747 	(void) pthread_mutex_unlock(&mp->mod_lock);
748 
749 	mp->mod_gen++;
750 }
751 
752 void
753 fmd_module_lock(fmd_module_t *mp)
754 {
755 	pthread_t self = pthread_self();
756 
757 	(void) pthread_mutex_lock(&mp->mod_lock);
758 
759 	while (mp->mod_flags & FMD_MOD_LOCK) {
760 		if (mp->mod_owner != self)
761 			(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
762 		else
763 			fmd_panic("recursive module lock of %p\n", (void *)mp);
764 	}
765 
766 	mp->mod_owner = self;
767 	mp->mod_flags |= FMD_MOD_LOCK;
768 
769 	(void) pthread_cond_broadcast(&mp->mod_cv);
770 	(void) pthread_mutex_unlock(&mp->mod_lock);
771 }
772 
773 void
774 fmd_module_unlock(fmd_module_t *mp)
775 {
776 	(void) pthread_mutex_lock(&mp->mod_lock);
777 
778 	ASSERT(mp->mod_owner == pthread_self());
779 	ASSERT(mp->mod_flags & FMD_MOD_LOCK);
780 
781 	mp->mod_owner = 0;
782 	mp->mod_flags &= ~FMD_MOD_LOCK;
783 
784 	(void) pthread_cond_broadcast(&mp->mod_cv);
785 	(void) pthread_mutex_unlock(&mp->mod_lock);
786 }
787 
788 int
789 fmd_module_trylock(fmd_module_t *mp)
790 {
791 	(void) pthread_mutex_lock(&mp->mod_lock);
792 
793 	if (mp->mod_flags & FMD_MOD_LOCK) {
794 		(void) pthread_mutex_unlock(&mp->mod_lock);
795 		return (0);
796 	}
797 
798 	mp->mod_owner = pthread_self();
799 	mp->mod_flags |= FMD_MOD_LOCK;
800 
801 	(void) pthread_cond_broadcast(&mp->mod_cv);
802 	(void) pthread_mutex_unlock(&mp->mod_lock);
803 
804 	return (1);
805 }
806 
807 int
808 fmd_module_locked(fmd_module_t *mp)
809 {
810 	return ((mp->mod_flags & FMD_MOD_LOCK) &&
811 	    mp->mod_owner == pthread_self());
812 }
813 
814 int
815 fmd_module_enter(fmd_module_t *mp, void (*func)(fmd_hdl_t *))
816 {
817 	volatile int err;
818 
819 	(void) pthread_mutex_lock(&mp->mod_lock);
820 
821 	ASSERT(!(mp->mod_flags & FMD_MOD_BUSY));
822 	mp->mod_flags |= FMD_MOD_BUSY;
823 
824 	if ((err = setjmp(mp->mod_jmpbuf)) != 0) {
825 		(void) pthread_mutex_lock(&mp->mod_lock);
826 		fmd_module_error(mp, err);
827 	}
828 
829 	(void) pthread_cond_broadcast(&mp->mod_cv);
830 	(void) pthread_mutex_unlock(&mp->mod_lock);
831 
832 	/*
833 	 * If it's the first time through fmd_module_enter(), call the provided
834 	 * function on the module.  If no fmd_module_abort() results, we will
835 	 * fall through and return zero.  Otherwise we'll longjmp with an err,
836 	 * return to the setjmp() above, and return the error to our caller.
837 	 */
838 	if (err == 0 && func != NULL)
839 		(*func)((fmd_hdl_t *)mp);
840 
841 	return (err);
842 }
843 
844 void
845 fmd_module_exit(fmd_module_t *mp)
846 {
847 	(void) pthread_mutex_lock(&mp->mod_lock);
848 
849 	ASSERT(mp->mod_flags & FMD_MOD_BUSY);
850 	mp->mod_flags &= ~FMD_MOD_BUSY;
851 
852 	(void) pthread_cond_broadcast(&mp->mod_cv);
853 	(void) pthread_mutex_unlock(&mp->mod_lock);
854 }
855 
856 /*
857  * If the client.error policy has been set by a developer, stop or dump core
858  * based on the policy; if we stop and are resumed we'll continue and execute
859  * the default behavior to discard events in fmd_module_start().  If the caller
860  * is the primary module thread, we reach this state by longjmp'ing back to
861  * fmd_module_enter(), above.  If the caller is an auxiliary thread, we cancel
862  * ourself and arrange for the primary thread to call fmd_module_abort().
863  */
864 void
865 fmd_module_abort(fmd_module_t *mp, int err)
866 {
867 	uint_t policy = FMD_CERROR_UNLOAD;
868 	pthread_t tid = pthread_self();
869 
870 	(void) fmd_conf_getprop(fmd.d_conf, "client.error", &policy);
871 
872 	if (policy == FMD_CERROR_STOP) {
873 		fmd_error(err, "stopping after %s in client %s (%p)\n",
874 		    fmd_errclass(err), mp->mod_name, (void *)mp);
875 		(void) raise(SIGSTOP);
876 	} else if (policy == FMD_CERROR_ABORT) {
877 		fmd_panic("aborting due to %s in client %s (%p)\n",
878 		    fmd_errclass(err), mp->mod_name, (void *)mp);
879 	}
880 
881 	/*
882 	 * If the caller is an auxiliary thread, cancel the current thread.  We
883 	 * prefer to cancel because it affords developers the option of using
884 	 * the pthread_cleanup* APIs.  If cancellations have been disabled,
885 	 * fall through to forcing the current thread to exit.  In either case
886 	 * we update mod_error (if zero) to enter the failed state.  Once that
887 	 * is set, further events received by the module will be discarded.
888 	 *
889 	 * We also set the FMD_MOD_FAIL bit, indicating an unrecoverable error.
890 	 * When an auxiliary thread fails, the module is left in a delicate
891 	 * state where it is likely not able to continue execution (even to
892 	 * execute its _fmd_fini() routine) because our caller may hold locks
893 	 * that are private to the module and can no longer be released.  The
894 	 * FMD_MOD_FAIL bit forces fmd_api_module_lock() to abort if any other
895 	 * module threads reach an API call, in an attempt to get them to exit.
896 	 */
897 	if (tid != mp->mod_thread->thr_tid) {
898 		(void) pthread_mutex_lock(&mp->mod_lock);
899 
900 		if (mp->mod_error == 0)
901 			mp->mod_error = err;
902 
903 		mp->mod_flags |= FMD_MOD_FAIL;
904 		(void) pthread_mutex_unlock(&mp->mod_lock);
905 
906 		(void) pthread_cancel(tid);
907 		pthread_exit(NULL);
908 	}
909 
910 	ASSERT(mp->mod_flags & FMD_MOD_BUSY);
911 	longjmp(mp->mod_jmpbuf, err);
912 }
913 
914 void
915 fmd_module_hold(fmd_module_t *mp)
916 {
917 	(void) pthread_mutex_lock(&mp->mod_lock);
918 
919 	TRACE((FMD_DBG_MOD, "hold %p (%s/%u)\n",
920 	    (void *)mp, mp->mod_name, mp->mod_refs));
921 
922 	mp->mod_refs++;
923 	ASSERT(mp->mod_refs != 0);
924 
925 	(void) pthread_mutex_unlock(&mp->mod_lock);
926 }
927 
928 void
929 fmd_module_rele(fmd_module_t *mp)
930 {
931 	(void) pthread_mutex_lock(&mp->mod_lock);
932 
933 	TRACE((FMD_DBG_MOD, "rele %p (%s/%u)\n",
934 	    (void *)mp, mp->mod_name, mp->mod_refs));
935 
936 	ASSERT(mp->mod_refs != 0);
937 
938 	if (--mp->mod_refs == 0)
939 		fmd_module_destroy(mp);
940 	else
941 		(void) pthread_mutex_unlock(&mp->mod_lock);
942 }
943 
944 /*
945  * Wrapper around libdiagcode's fm_dc_opendict() to load module dictionaries.
946  * If the dictionary open is successful, the new dictionary is added to the
947  * mod_dictv[] array and mod_codelen is updated with the new maximum length.
948  */
949 int
950 fmd_module_dc_opendict(fmd_module_t *mp, const char *dict)
951 {
952 	struct fm_dc_handle *dcp, **dcv;
953 	char *dictdir, *dictnam, *p;
954 	size_t len;
955 
956 	ASSERT(fmd_module_locked(mp));
957 
958 	dictnam = strdupa(fmd_strbasename(dict));
959 
960 	if ((p = strrchr(dictnam, '.')) != NULL &&
961 	    strcmp(p, ".dict") == 0)
962 		*p = '\0'; /* eliminate any trailing .dict suffix */
963 
964 	/*
965 	 * If 'dict' is an absolute path, dictdir = $rootdir/`dirname dict`
966 	 * If 'dict' is not an absolute path, dictdir = $dictdir/`dirname dict`
967 	 */
968 	if (dict[0] == '/') {
969 		len = strlen(fmd.d_rootdir) + strlen(dict) + 1;
970 		dictdir = alloca(len);
971 		(void) snprintf(dictdir, len, "%s%s", fmd.d_rootdir, dict);
972 		(void) fmd_strdirname(dictdir);
973 	} else {
974 		(void) fmd_conf_getprop(fmd.d_conf, "dictdir", &p);
975 		len = strlen(fmd.d_rootdir) + strlen(p) + strlen(dict) + 3;
976 		dictdir = alloca(len);
977 		(void) snprintf(dictdir, len,
978 		    "%s/%s/%s", fmd.d_rootdir, p, dict);
979 		(void) fmd_strdirname(dictdir);
980 	}
981 
982 	fmd_dprintf(FMD_DBG_MOD, "module %s opening %s -> %s/%s.dict\n",
983 	    mp->mod_name, dict, dictdir, dictnam);
984 
985 	if ((dcp = fm_dc_opendict(FM_DC_VERSION, dictdir, dictnam)) == NULL)
986 		return (-1); /* errno is set for us */
987 
988 	dcv = fmd_alloc(sizeof (dcp) * (mp->mod_dictc + 1), FMD_SLEEP);
989 	bcopy(mp->mod_dictv, dcv, sizeof (dcp) * mp->mod_dictc);
990 	fmd_free(mp->mod_dictv, sizeof (dcp) * mp->mod_dictc);
991 	mp->mod_dictv = dcv;
992 	mp->mod_dictv[mp->mod_dictc++] = dcp;
993 
994 	len = fm_dc_codelen(dcp);
995 	mp->mod_codelen = MAX(mp->mod_codelen, len);
996 
997 	return (0);
998 }
999 
1000 /*
1001  * Wrapper around libdiagcode's fm_dc_key2code() that examines all the module's
1002  * dictionaries.  We adhere to the libdiagcode return values and semantics.
1003  */
1004 int
1005 fmd_module_dc_key2code(fmd_module_t *mp,
1006     char *const keys[], char *code, size_t codelen)
1007 {
1008 	int i, err;
1009 
1010 	for (i = 0; i < mp->mod_dictc; i++) {
1011 		if ((err = fm_dc_key2code(mp->mod_dictv[i], (const char **)keys,
1012 		    code, codelen)) == 0 || errno != ENOMSG)
1013 			return (err);
1014 	}
1015 
1016 	return (fmd_set_errno(ENOMSG));
1017 }
1018 
1019 fmd_modhash_t *
1020 fmd_modhash_create(void)
1021 {
1022 	fmd_modhash_t *mhp = fmd_alloc(sizeof (fmd_modhash_t), FMD_SLEEP);
1023 
1024 	(void) pthread_rwlock_init(&mhp->mh_lock, NULL);
1025 	mhp->mh_hashlen = fmd.d_str_buckets;
1026 	mhp->mh_hash = fmd_zalloc(sizeof (void *) * mhp->mh_hashlen, FMD_SLEEP);
1027 	mhp->mh_nelems = 0;
1028 
1029 	return (mhp);
1030 }
1031 
1032 void
1033 fmd_modhash_destroy(fmd_modhash_t *mhp)
1034 {
1035 	fmd_module_t *mp, *nmp;
1036 	uint_t i;
1037 
1038 	for (i = 0; i < mhp->mh_hashlen; i++) {
1039 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = nmp) {
1040 			nmp = mp->mod_next;
1041 			mp->mod_next = NULL;
1042 			fmd_module_rele(mp);
1043 		}
1044 	}
1045 
1046 	fmd_free(mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen);
1047 	(void) pthread_rwlock_destroy(&mhp->mh_lock);
1048 	fmd_free(mhp, sizeof (fmd_modhash_t));
1049 }
1050 
1051 static void
1052 fmd_modhash_loaddir(fmd_modhash_t *mhp, const char *dir,
1053     const fmd_modops_t *ops, const char *suffix)
1054 {
1055 	char path[PATH_MAX];
1056 	struct dirent *dp;
1057 	const char *p;
1058 	DIR *dirp;
1059 
1060 	if ((dirp = opendir(dir)) == NULL)
1061 		return; /* failed to open directory; just skip it */
1062 
1063 	while ((dp = readdir(dirp)) != NULL) {
1064 		if (dp->d_name[0] == '.')
1065 			continue; /* skip "." and ".." */
1066 
1067 		p = strrchr(dp->d_name, '.');
1068 
1069 		if (p != NULL && strcmp(p, ".conf") == 0)
1070 			continue; /* skip .conf files */
1071 
1072 		if (suffix != NULL && (p == NULL || strcmp(p, suffix) != 0))
1073 			continue; /* skip files with the wrong suffix */
1074 
1075 		(void) snprintf(path, sizeof (path), "%s/%s", dir, dp->d_name);
1076 		(void) fmd_modhash_load(mhp, path, ops);
1077 	}
1078 
1079 	(void) closedir(dirp);
1080 }
1081 
1082 void
1083 fmd_modhash_loadall(fmd_modhash_t *mhp, const fmd_conf_path_t *pap,
1084     const fmd_modops_t *ops, const char *suffix)
1085 {
1086 	int i;
1087 
1088 	for (i = 0; i < pap->cpa_argc; i++)
1089 		fmd_modhash_loaddir(mhp, pap->cpa_argv[i], ops, suffix);
1090 }
1091 
1092 void
1093 fmd_modhash_apply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *))
1094 {
1095 	fmd_module_t *mp, *np;
1096 	uint_t i;
1097 
1098 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
1099 
1100 	for (i = 0; i < mhp->mh_hashlen; i++) {
1101 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) {
1102 			np = mp->mod_next;
1103 			func(mp);
1104 		}
1105 	}
1106 
1107 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
1108 }
1109 
1110 void
1111 fmd_modhash_tryapply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *))
1112 {
1113 	fmd_module_t *mp, *np;
1114 	uint_t i;
1115 
1116 	if (mhp == NULL || pthread_rwlock_tryrdlock(&mhp->mh_lock) != 0)
1117 		return; /* not initialized or couldn't grab lock */
1118 
1119 	for (i = 0; i < mhp->mh_hashlen; i++) {
1120 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) {
1121 			np = mp->mod_next;
1122 			func(mp);
1123 		}
1124 	}
1125 
1126 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
1127 }
1128 
1129 void
1130 fmd_modhash_dispatch(fmd_modhash_t *mhp, fmd_event_t *ep)
1131 {
1132 	fmd_module_t *mp;
1133 	uint_t i;
1134 
1135 	fmd_event_hold(ep);
1136 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
1137 
1138 	for (i = 0; i < mhp->mh_hashlen; i++) {
1139 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) {
1140 			/*
1141 			 * If FMD_MOD_INIT is set but MOD_FINI, MOD_QUIT, and
1142 			 * mod_error are all zero, then the module is active:
1143 			 * enqueue the event in the corresponding event queue.
1144 			 */
1145 			(void) pthread_mutex_lock(&mp->mod_lock);
1146 
1147 			if ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI |
1148 			    FMD_MOD_QUIT)) == FMD_MOD_INIT && !mp->mod_error) {
1149 
1150 				/*
1151 				 * If the event we're dispatching is of type
1152 				 * FMD_EVT_TOPO and there are already redundant
1153 				 * FMD_EVT_TOPO events in this module's queue,
1154 				 * then drop those before adding the new one.
1155 				 */
1156 				if (FMD_EVENT_TYPE(ep) == FMD_EVT_TOPO)
1157 					fmd_eventq_drop_topo(mp->mod_queue);
1158 
1159 				fmd_eventq_insert_at_time(mp->mod_queue, ep);
1160 
1161 			}
1162 			(void) pthread_mutex_unlock(&mp->mod_lock);
1163 		}
1164 	}
1165 
1166 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
1167 	fmd_event_rele(ep);
1168 }
1169 
1170 fmd_module_t *
1171 fmd_modhash_lookup(fmd_modhash_t *mhp, const char *name)
1172 {
1173 	fmd_module_t *mp;
1174 	uint_t h;
1175 
1176 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
1177 	h = fmd_strhash(name) % mhp->mh_hashlen;
1178 
1179 	for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) {
1180 		if (strcmp(name, mp->mod_name) == 0)
1181 			break;
1182 	}
1183 
1184 	if (mp != NULL)
1185 		fmd_module_hold(mp);
1186 	else
1187 		(void) fmd_set_errno(EFMD_MOD_NOMOD);
1188 
1189 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
1190 	return (mp);
1191 }
1192 
1193 fmd_module_t *
1194 fmd_modhash_load(fmd_modhash_t *mhp, const char *path, const fmd_modops_t *ops)
1195 {
1196 	char name[PATH_MAX], *p;
1197 	fmd_module_t *mp;
1198 	int tries = 0;
1199 	uint_t h;
1200 
1201 	(void) strlcpy(name, fmd_strbasename(path), sizeof (name));
1202 	if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0)
1203 		*p = '\0'; /* strip trailing .so from any module name */
1204 
1205 	(void) pthread_rwlock_wrlock(&mhp->mh_lock);
1206 	h = fmd_strhash(name) % mhp->mh_hashlen;
1207 
1208 	/*
1209 	 * First check to see if a module is already present in the hash table
1210 	 * for this name.  If so, the module is already loaded: skip it.
1211 	 */
1212 	for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) {
1213 		if (strcmp(name, mp->mod_name) == 0)
1214 			break;
1215 	}
1216 
1217 	if (mp != NULL) {
1218 		(void) pthread_rwlock_unlock(&mhp->mh_lock);
1219 		(void) fmd_set_errno(EFMD_MOD_LOADED);
1220 		return (NULL);
1221 	}
1222 
1223 	/*
1224 	 * fmd_module_create() will return a held (as if by fmd_module_hold())
1225 	 * module.  We leave this hold in place to correspond to the hash-in.
1226 	 */
1227 	while ((mp = fmd_module_create(path, ops)) == NULL) {
1228 		if (tries++ != 0 || errno != EFMD_CKPT_INVAL) {
1229 			(void) pthread_rwlock_unlock(&mhp->mh_lock);
1230 			return (NULL); /* errno is set for us */
1231 		}
1232 	}
1233 
1234 	mp->mod_hash = mhp;
1235 	mp->mod_next = mhp->mh_hash[h];
1236 
1237 	mhp->mh_hash[h] = mp;
1238 	mhp->mh_nelems++;
1239 
1240 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
1241 	return (mp);
1242 }
1243 
1244 int
1245 fmd_modhash_unload(fmd_modhash_t *mhp, const char *name)
1246 {
1247 	fmd_module_t *mp, **pp;
1248 	uint_t h;
1249 
1250 	(void) pthread_rwlock_wrlock(&mhp->mh_lock);
1251 	h = fmd_strhash(name) % mhp->mh_hashlen;
1252 	pp = &mhp->mh_hash[h];
1253 
1254 	for (mp = *pp; mp != NULL; mp = mp->mod_next) {
1255 		if (strcmp(name, mp->mod_name) == 0)
1256 			break;
1257 		else
1258 			pp = &mp->mod_next;
1259 	}
1260 
1261 	if (mp == NULL) {
1262 		(void) pthread_rwlock_unlock(&mhp->mh_lock);
1263 		return (fmd_set_errno(EFMD_MOD_NOMOD));
1264 	}
1265 
1266 	*pp = mp->mod_next;
1267 	mp->mod_next = NULL;
1268 
1269 	ASSERT(mhp->mh_nelems != 0);
1270 	mhp->mh_nelems--;
1271 
1272 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
1273 
1274 	fmd_module_unload(mp);
1275 	fmd_module_rele(mp);
1276 
1277 	return (0);
1278 }
1279 
1280 void
1281 fmd_modstat_publish(fmd_module_t *mp)
1282 {
1283 	(void) pthread_mutex_lock(&mp->mod_lock);
1284 
1285 	ASSERT(mp->mod_flags & FMD_MOD_STSUB);
1286 	mp->mod_flags |= FMD_MOD_STPUB;
1287 	(void) pthread_cond_broadcast(&mp->mod_cv);
1288 
1289 	while (mp->mod_flags & FMD_MOD_STPUB)
1290 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
1291 
1292 	(void) pthread_mutex_unlock(&mp->mod_lock);
1293 }
1294 
1295 int
1296 fmd_modstat_snapshot(fmd_module_t *mp, fmd_ustat_snap_t *uss)
1297 {
1298 	fmd_event_t *e;
1299 	int err;
1300 
1301 	/*
1302 	 * Grab the module lock and wait for the STSUB bit to be clear.  Then
1303 	 * set it to indicate we are a subscriber and everyone else must wait.
1304 	 */
1305 	(void) pthread_mutex_lock(&mp->mod_lock);
1306 
1307 	while (mp->mod_error == 0 && (mp->mod_flags & FMD_MOD_STSUB))
1308 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
1309 
1310 	if (mp->mod_error != 0) {
1311 		(void) pthread_mutex_unlock(&mp->mod_lock);
1312 		return (fmd_set_errno(EFMD_HDL_ABORT));
1313 	}
1314 
1315 	mp->mod_flags |= FMD_MOD_STSUB;
1316 	(void) pthread_cond_broadcast(&mp->mod_cv);
1317 	(void) pthread_mutex_unlock(&mp->mod_lock);
1318 
1319 	/*
1320 	 * Create a stats pseudo-event and dispatch it to the module, forcing
1321 	 * it to next execute its custom snapshot routine (or the empty one).
1322 	 */
1323 	e = fmd_event_create(FMD_EVT_STATS, FMD_HRT_NOW, NULL, NULL);
1324 	fmd_eventq_insert_at_head(mp->mod_queue, e);
1325 
1326 	/*
1327 	 * Grab the module lock and then wait on mod_cv for STPUB to be set,
1328 	 * indicating the snapshot routine is completed and the module is idle.
1329 	 */
1330 	(void) pthread_mutex_lock(&mp->mod_lock);
1331 
1332 	while (mp->mod_error == 0 && !(mp->mod_flags & FMD_MOD_STPUB)) {
1333 		struct timespec tms;
1334 
1335 		(void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock);
1336 		(void) pthread_mutex_unlock(&mp->mod_lock);
1337 		tms.tv_sec = 0;
1338 		tms.tv_nsec = 10000000;
1339 		(void) nanosleep(&tms, NULL);
1340 		(void) pthread_mutex_lock(&mp->mod_lock);
1341 	}
1342 
1343 	if (mp->mod_error != 0) {
1344 		(void) pthread_mutex_unlock(&mp->mod_lock);
1345 		return (fmd_set_errno(EFMD_HDL_ABORT));
1346 	}
1347 
1348 	(void) pthread_cond_broadcast(&mp->mod_cv);
1349 	(void) pthread_mutex_unlock(&mp->mod_lock);
1350 
1351 	/*
1352 	 * Update ms_snaptime and take the actual snapshot of the various
1353 	 * statistics while the module is quiescent and waiting for us.
1354 	 */
1355 	(void) pthread_mutex_lock(&mp->mod_stats_lock);
1356 
1357 	if (mp->mod_stats != NULL) {
1358 		mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime();
1359 		err = fmd_ustat_snapshot(mp->mod_ustat, uss);
1360 	} else
1361 		err = fmd_set_errno(EFMD_HDL_ABORT);
1362 
1363 	(void) pthread_mutex_unlock(&mp->mod_stats_lock);
1364 
1365 	/*
1366 	 * With the snapshot complete, grab the module lock and clear both
1367 	 * STSUB and STPUB, permitting everyone to wake up and continue.
1368 	 */
1369 	(void) pthread_mutex_lock(&mp->mod_lock);
1370 
1371 	ASSERT(mp->mod_flags & FMD_MOD_STSUB);
1372 	ASSERT(mp->mod_flags & FMD_MOD_STPUB);
1373 	mp->mod_flags &= ~(FMD_MOD_STSUB | FMD_MOD_STPUB);
1374 
1375 	(void) pthread_cond_broadcast(&mp->mod_cv);
1376 	(void) pthread_mutex_unlock(&mp->mod_lock);
1377 
1378 	return (err);
1379 }
1380 
1381 struct topo_hdl *
1382 fmd_module_topo_hold(fmd_module_t *mp)
1383 {
1384 	fmd_modtopo_t *mtp;
1385 
1386 	ASSERT(fmd_module_locked(mp));
1387 
1388 	mtp = fmd_zalloc(sizeof (fmd_modtopo_t), FMD_SLEEP);
1389 	mtp->mt_topo = mp->mod_topo_current;
1390 	fmd_topo_addref(mtp->mt_topo);
1391 	fmd_list_prepend(&mp->mod_topolist, mtp);
1392 
1393 	return (mtp->mt_topo->ft_hdl);
1394 }
1395 
1396 int
1397 fmd_module_topo_rele(fmd_module_t *mp, struct topo_hdl *hdl)
1398 {
1399 	fmd_modtopo_t *mtp;
1400 
1401 	ASSERT(fmd_module_locked(mp));
1402 
1403 	for (mtp = fmd_list_next(&mp->mod_topolist); mtp != NULL;
1404 	    mtp = fmd_list_next(mtp)) {
1405 		if (mtp->mt_topo->ft_hdl == hdl)
1406 			break;
1407 	}
1408 
1409 	if (mtp == NULL)
1410 		return (-1);
1411 
1412 	fmd_list_delete(&mp->mod_topolist, mtp);
1413 	fmd_topo_rele(mtp->mt_topo);
1414 	fmd_free(mtp, sizeof (fmd_modtopo_t));
1415 	return (0);
1416 }
1417