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