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.
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.
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]
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
33 #include <fmd_string.h>
34 #include <fmd_alloc.h>
35 #include <fmd_module.h>
36 #include <fmd_error.h>
38 #include <fmd_dispq.h>
39 #include <fmd_eventq.h>
40 #include <fmd_timerq.h>
42 #include <fmd_thread.h>
43 #include <fmd_ustat.h>
45 #include <fmd_protocol.h>
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.
59 static const fmd_modstat_t _fmd_modstat_tmpl
= {
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" },
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" },
98 fmd_module_start(void *arg
)
100 fmd_module_t
*mp
= arg
;
104 (void) pthread_mutex_lock(&mp
->mod_lock
);
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
;
112 if (fmd
.d_mod_event
!= NULL
)
113 fmd_eventq_insert_at_head(mp
->mod_queue
, fmd
.d_mod_event
);
115 ASSERT(MUTEX_HELD(&mp
->mod_lock
));
116 mp
->mod_flags
|= FMD_MOD_INIT
;
118 (void) pthread_cond_broadcast(&mp
->mod_cv
);
119 (void) pthread_mutex_unlock(&mp
->mod_lock
);
122 * If the module opened any transports while executing _fmd_init(),
123 * they are suspended. Now that _fmd_init() is done, wake them up.
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
);
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.
134 while ((ep
= fmd_eventq_delete(mp
->mod_queue
)) != NULL
) {
136 * If the module has failed, discard the event without ever
137 * passing it to the module and go back to sleep.
139 if (mp
->mod_error
!= 0) {
140 fmd_eventq_done(mp
->mod_queue
);
145 mp
->mod_ops
->mop_dispatch(mp
, ep
);
146 fmd_eventq_done(mp
->mod_queue
);
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.
155 if (FMD_EVENT_TYPE(ep
) == FMD_EVT_CLOSE
)
156 fmd_case_delete(FMD_EVENT_DATA(ep
));
159 fmd_module_unlock(mp
);
163 if (mp
->mod_ops
->mop_fini(mp
) != 0 && mp
->mod_error
== 0)
164 mp
->mod_error
= errno
? errno
: EFMD_MOD_FINI
;
166 (void) pthread_mutex_lock(&mp
->mod_lock
);
167 mp
->mod_flags
|= FMD_MOD_FINI
;
170 (void) pthread_cond_broadcast(&mp
->mod_cv
);
171 (void) pthread_mutex_unlock(&mp
->mod_lock
);
175 fmd_module_create(const char *path
, const fmd_modops_t
*ops
)
177 fmd_module_t
*mp
= fmd_zalloc(sizeof (fmd_module_t
), FMD_SLEEP
);
179 char buf
[PATH_MAX
], *p
;
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 */
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
);
192 mp
->mod_name
= fmd_strdup(buf
, FMD_SLEEP
);
193 mp
->mod_path
= fmd_strdup(path
, FMD_SLEEP
);
195 mp
->mod_ustat
= fmd_ustat_create();
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
);
201 mp
->mod_ckpt
= fmd_strdup(buf
, FMD_SLEEP
);
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
);
207 fmd_buf_hash_create(&mp
->mod_bufs
);
208 fmd_serd_hash_create(&mp
->mod_serds
);
210 mp
->mod_topo_current
= fmd_topo_hold();
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
);
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.
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
);
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
);
238 (void) fmd_conf_getprop(fmd
.d_conf
, "client.evqlim", &limit
);
240 mp
->mod_queue
= fmd_eventq_create(mp
,
241 &mp
->mod_stats
->ms_evqstat
, &mp
->mod_stats_lock
, limit
);
243 (void) fmd_conf_getprop(fmd
.d_conf
, "client.memlim",
244 &mp
->mod_stats
->ms_memlimit
.fmds_value
.ui64
);
246 (void) fmd_conf_getprop(fmd
.d_conf
, "client.buflim",
247 &mp
->mod_stats
->ms_buflimit
.fmds_value
.ui64
);
249 (void) fmd_conf_getprop(fmd
.d_conf
, "client.thrlim",
250 &mp
->mod_stats
->ms_thrlimit
.fmds_value
.ui32
);
252 (void) fmd_conf_getprop(fmd
.d_conf
, "client.doorthrlim",
253 &mp
->mod_stats
->ms_doorthrlimit
.fmds_value
.ui32
);
255 (void) fmd_conf_getprop(fmd
.d_conf
, "client.xprtlim",
256 &mp
->mod_stats
->ms_xprtlimit
.fmds_value
.ui32
);
258 (void) fmd_conf_getprop(fmd
.d_conf
, "client.xprtqlim",
259 &mp
->mod_stats
->ms_xprtqlimit
.fmds_value
.ui32
);
261 (void) fmd_conf_getprop(fmd
.d_conf
, "ckpt.save",
262 &mp
->mod_stats
->ms_ckpt_save
.fmds_value
.bool);
264 (void) fmd_conf_getprop(fmd
.d_conf
, "ckpt.restore",
265 &mp
->mod_stats
->ms_ckpt_restore
.fmds_value
.bool);
267 (void) fmd_conf_getprop(fmd
.d_conf
, "ckpt.zero",
268 &mp
->mod_stats
->ms_ckpt_zeroed
.fmds_value
.bool);
270 if (mp
->mod_stats
->ms_ckpt_zeroed
.fmds_value
.bool)
271 fmd_ckpt_delete(mp
); /* blow away any pre-existing checkpoint */
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.
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
);
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
);
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.
297 while (!(mp
->mod_flags
& FMD_MOD_INIT
) && mp
->mod_error
== 0)
298 (void) pthread_cond_wait(&mp
->mod_cv
, &mp
->mod_lock
);
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.
304 if (!(mp
->mod_flags
& FMD_MOD_INIT
)) {
306 (void) pthread_mutex_unlock(&mp
->mod_lock
);
308 if (err
== EFMD_CKPT_INVAL
)
309 fmd_ckpt_rename(mp
); /* move aside bad checkpoint */
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.
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
));
323 fmd_module_unload(mp
);
326 (void) fmd_set_errno(err
);
330 (void) pthread_cond_broadcast(&mp
->mod_cv
);
331 (void) pthread_mutex_unlock(&mp
->mod_lock
);
333 fmd_dprintf(FMD_DBG_MOD
, "loaded module %s\n", mp
->mod_name
);
338 fmd_module_untimeout(fmd_idspace_t
*ids
, id_t id
, fmd_module_t
*mp
)
340 void *arg
= fmd_timerq_remove(fmd
.d_timers
, ids
, id
);
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.
347 if (arg
!= NULL
&& mp
!= fmd
.d_rmod
)
348 fmd_free(arg
, sizeof (fmd_modtimer_t
));
352 fmd_module_unload(fmd_module_t
*mp
)
356 (void) pthread_mutex_lock(&mp
->mod_lock
);
358 if (mp
->mod_flags
& FMD_MOD_QUIT
) {
359 (void) pthread_mutex_unlock(&mp
->mod_lock
);
360 return; /* module is already unloading */
363 ASSERT(mp
->mod_thread
!= NULL
);
364 mp
->mod_flags
|= FMD_MOD_QUIT
;
366 if (mp
->mod_queue
!= NULL
)
367 fmd_eventq_abort(mp
->mod_queue
);
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.
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
);
377 (void) pthread_cond_broadcast(&mp
->mod_cv
);
378 (void) pthread_mutex_unlock(&mp
->mod_lock
);
380 fmd_thread_destroy(mp
->mod_thread
, FMD_THREAD_JOIN
);
381 mp
->mod_thread
= NULL
;
384 * Once the module is no longer active, clean up any data structures
385 * that are only required when the module is loaded.
389 if (mp
->mod_timerids
!= NULL
) {
390 fmd_idspace_apply(mp
->mod_timerids
,
391 (void (*)())fmd_module_untimeout
, mp
);
393 fmd_idspace_destroy(mp
->mod_timerids
);
394 mp
->mod_timerids
= NULL
;
397 if (mp
->mod_threads
!= NULL
) {
398 fmd_idspace_destroy(mp
->mod_threads
);
399 mp
->mod_threads
= NULL
;
402 (void) fmd_buf_hash_destroy(&mp
->mod_bufs
);
403 fmd_serd_hash_destroy(&mp
->mod_serds
);
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
));
411 fmd_module_unlock(mp
);
412 fmd_dprintf(FMD_DBG_MOD
, "unloaded module %s\n", mp
->mod_name
);
416 fmd_module_destroy(fmd_module_t
*mp
)
418 fmd_conf_formal_t
*cfp
= mp
->mod_argv
;
421 ASSERT(MUTEX_HELD(&mp
->mod_lock
));
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
);
429 ASSERT(mp
->mod_thread
== NULL
);
430 ASSERT(mp
->mod_refs
== 0);
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.
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
);
441 if (mp
->mod_topo_current
!= NULL
)
442 fmd_topo_rele(mp
->mod_topo_current
);
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
);
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.
453 if (mp
->mod_queue
!= NULL
) {
454 fmd_eventq_destroy(mp
->mod_queue
);
455 mp
->mod_queue
= NULL
;
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
);
466 for (i
= 0; i
< mp
->mod_dictc
; i
++)
467 fm_dc_closedict(mp
->mod_dictv
[i
]);
469 fmd_free(mp
->mod_dictv
, sizeof (struct fm_dc_handle
*) * mp
->mod_dictc
);
471 if (mp
->mod_conf
!= NULL
)
472 fmd_conf_close(mp
->mod_conf
);
474 for (i
= 0; i
< mp
->mod_argc
; i
++, cfp
++) {
475 fmd_strfree((char *)cfp
->cf_name
);
476 fmd_strfree((char *)cfp
->cf_default
);
479 fmd_free(mp
->mod_argv
, sizeof (fmd_conf_formal_t
) * mp
->mod_argc
);
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
);
487 fmd_free(mp
, sizeof (fmd_module_t
));
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.
497 fmd_module_error(fmd_module_t
*mp
, int err
)
503 ASSERT(MUTEX_HELD(&mp
->mod_lock
));
506 TRACE((FMD_DBG_MOD
, "module aborted: err=%d", err
));
508 if (mp
->mod_error
== 0)
511 if (mp
== fmd
.d_self
)
512 return; /* do not post event if fmd.d_self itself fails */
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.
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);
528 fmd_module_dispatch(fmd_module_t
*mp
, fmd_event_t
*e
)
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
;
534 fmd_topo_t
*old_topo
;
538 * Before calling the appropriate module callback, enter the module as
539 * if by fmd_module_enter() and establish mod_jmpbuf for any aborts.
541 (void) pthread_mutex_lock(&mp
->mod_lock
);
543 ASSERT(!(mp
->mod_flags
& FMD_MOD_BUSY
));
544 mp
->mod_flags
|= FMD_MOD_BUSY
;
546 if ((err
= setjmp(mp
->mod_jmpbuf
)) != 0) {
547 (void) pthread_mutex_lock(&mp
->mod_lock
);
548 fmd_module_error(mp
, err
);
551 (void) pthread_cond_broadcast(&mp
->mod_cv
);
552 (void) pthread_mutex_unlock(&mp
->mod_lock
);
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.
561 switch (ep
->ev_type
) {
562 case FMD_EVT_PROTOCOL
:
563 ops
->fmdo_recv(hdl
, e
, ep
->ev_nvl
, ep
->ev_data
);
565 case FMD_EVT_TIMEOUT
:
567 ASSERT(t
->mt_mod
== mp
);
568 ops
->fmdo_timeout(hdl
, t
->mt_id
, t
->mt_arg
);
571 ops
->fmdo_close(hdl
, ep
->ev_data
);
574 ops
->fmdo_stats(hdl
);
575 fmd_modstat_publish(mp
);
580 case FMD_EVT_PUBLISH
:
581 fmd_case_publish(ep
->ev_data
, FMD_CASE_CURRENT
);
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
589 * - finishes with old topology since
590 * it is released after updating
592 * - or is blocked while mod_topo_current is updated.
594 old_topo
= mp
->mod_topo_current
;
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
);
609 fmd_module_transport(fmd_module_t
*mp
, fmd_xprt_t
*xp
, fmd_event_t
*e
)
611 fmd_event_impl_t
*ep
= (fmd_event_impl_t
*)e
;
612 fmd_hdl_t
*hdl
= (fmd_hdl_t
*)mp
;
614 ASSERT(ep
->ev_type
== FMD_EVT_PROTOCOL
);
615 return (mp
->mod_info
->fmdi_ops
->fmdo_send(hdl
, xp
, e
, ep
->ev_nvl
));
619 fmd_module_timeout(fmd_modtimer_t
*t
, id_t id
, hrtime_t hrt
)
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
);
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.
633 fmd_module_gc(fmd_module_t
*mp
)
635 fmd_hdl_info_t
*info
;
638 if (mp
->mod_error
!= 0)
639 return; /* do not do anything if the module has failed */
643 if ((info
= mp
->mod_info
) != NULL
) {
644 fmd_serd_hash_apply(&mp
->mod_serds
,
645 (fmd_serd_eng_f
*)fmd_serd_eng_gc
, NULL
);
648 fmd_module_unlock(mp
);
651 e
= fmd_event_create(FMD_EVT_GC
, FMD_HRT_NOW
, NULL
, NULL
);
652 fmd_eventq_insert_at_head(mp
->mod_queue
, e
);
657 fmd_module_trygc(fmd_module_t
*mp
)
659 if (fmd_module_trylock(mp
)) {
660 fmd_serd_hash_apply(&mp
->mod_serds
,
661 (fmd_serd_eng_f
*)fmd_serd_eng_gc
, NULL
);
662 fmd_module_unlock(mp
);
667 fmd_module_contains(fmd_module_t
*mp
, fmd_event_t
*ep
)
674 for (cp
= fmd_list_next(&mp
->mod_cases
);
675 cp
!= NULL
; cp
= fmd_list_next(cp
)) {
676 if ((rv
= fmd_case_contains(cp
, ep
)) != 0)
681 rv
= fmd_serd_hash_contains(&mp
->mod_serds
, ep
);
683 fmd_module_unlock(mp
);
688 fmd_module_setdirty(fmd_module_t
*mp
)
690 (void) pthread_mutex_lock(&mp
->mod_lock
);
691 mp
->mod_flags
|= FMD_MOD_MDIRTY
;
692 (void) pthread_mutex_unlock(&mp
->mod_lock
);
696 fmd_module_setcdirty(fmd_module_t
*mp
)
698 (void) pthread_mutex_lock(&mp
->mod_lock
);
699 mp
->mod_flags
|= FMD_MOD_CDIRTY
;
700 (void) pthread_mutex_unlock(&mp
->mod_lock
);
704 fmd_module_clrdirty(fmd_module_t
*mp
)
710 if (mp
->mod_flags
& FMD_MOD_CDIRTY
) {
711 for (cp
= fmd_list_next(&mp
->mod_cases
);
712 cp
!= NULL
; cp
= fmd_list_next(cp
))
713 fmd_case_clrdirty(cp
);
716 if (mp
->mod_flags
& FMD_MOD_MDIRTY
) {
717 fmd_serd_hash_apply(&mp
->mod_serds
,
718 (fmd_serd_eng_f
*)fmd_serd_eng_clrdirty
, NULL
);
719 fmd_buf_hash_commit(&mp
->mod_bufs
);
722 (void) pthread_mutex_lock(&mp
->mod_lock
);
723 mp
->mod_flags
&= ~(FMD_MOD_MDIRTY
| FMD_MOD_CDIRTY
);
724 (void) pthread_mutex_unlock(&mp
->mod_lock
);
726 fmd_module_unlock(mp
);
730 fmd_module_commit(fmd_module_t
*mp
)
734 ASSERT(fmd_module_locked(mp
));
736 if (mp
->mod_flags
& FMD_MOD_CDIRTY
) {
737 for (cp
= fmd_list_next(&mp
->mod_cases
);
738 cp
!= NULL
; cp
= fmd_list_next(cp
))
742 if (mp
->mod_flags
& FMD_MOD_MDIRTY
) {
743 fmd_serd_hash_apply(&mp
->mod_serds
,
744 (fmd_serd_eng_f
*)fmd_serd_eng_commit
, NULL
);
745 fmd_buf_hash_commit(&mp
->mod_bufs
);
748 (void) pthread_mutex_lock(&mp
->mod_lock
);
749 mp
->mod_flags
&= ~(FMD_MOD_MDIRTY
| FMD_MOD_CDIRTY
);
750 (void) pthread_mutex_unlock(&mp
->mod_lock
);
756 fmd_module_lock(fmd_module_t
*mp
)
758 pthread_t self
= pthread_self();
760 (void) pthread_mutex_lock(&mp
->mod_lock
);
762 while (mp
->mod_flags
& FMD_MOD_LOCK
) {
763 if (mp
->mod_owner
!= self
)
764 (void) pthread_cond_wait(&mp
->mod_cv
, &mp
->mod_lock
);
766 fmd_panic("recursive module lock of %p\n", (void *)mp
);
769 mp
->mod_owner
= self
;
770 mp
->mod_flags
|= FMD_MOD_LOCK
;
772 (void) pthread_cond_broadcast(&mp
->mod_cv
);
773 (void) pthread_mutex_unlock(&mp
->mod_lock
);
777 fmd_module_unlock(fmd_module_t
*mp
)
779 (void) pthread_mutex_lock(&mp
->mod_lock
);
781 ASSERT(mp
->mod_owner
== pthread_self());
782 ASSERT(mp
->mod_flags
& FMD_MOD_LOCK
);
785 mp
->mod_flags
&= ~FMD_MOD_LOCK
;
787 (void) pthread_cond_broadcast(&mp
->mod_cv
);
788 (void) pthread_mutex_unlock(&mp
->mod_lock
);
792 fmd_module_trylock(fmd_module_t
*mp
)
794 (void) pthread_mutex_lock(&mp
->mod_lock
);
796 if (mp
->mod_flags
& FMD_MOD_LOCK
) {
797 (void) pthread_mutex_unlock(&mp
->mod_lock
);
801 mp
->mod_owner
= pthread_self();
802 mp
->mod_flags
|= FMD_MOD_LOCK
;
804 (void) pthread_cond_broadcast(&mp
->mod_cv
);
805 (void) pthread_mutex_unlock(&mp
->mod_lock
);
811 fmd_module_locked(fmd_module_t
*mp
)
813 return ((mp
->mod_flags
& FMD_MOD_LOCK
) &&
814 mp
->mod_owner
== pthread_self());
818 fmd_module_enter(fmd_module_t
*mp
, void (*func
)(fmd_hdl_t
*))
822 (void) pthread_mutex_lock(&mp
->mod_lock
);
824 ASSERT(!(mp
->mod_flags
& FMD_MOD_BUSY
));
825 mp
->mod_flags
|= FMD_MOD_BUSY
;
827 if ((err
= setjmp(mp
->mod_jmpbuf
)) != 0) {
828 (void) pthread_mutex_lock(&mp
->mod_lock
);
829 fmd_module_error(mp
, err
);
832 (void) pthread_cond_broadcast(&mp
->mod_cv
);
833 (void) pthread_mutex_unlock(&mp
->mod_lock
);
836 * If it's the first time through fmd_module_enter(), call the provided
837 * function on the module. If no fmd_module_abort() results, we will
838 * fall through and return zero. Otherwise we'll longjmp with an err,
839 * return to the setjmp() above, and return the error to our caller.
841 if (err
== 0 && func
!= NULL
)
842 (*func
)((fmd_hdl_t
*)mp
);
848 fmd_module_exit(fmd_module_t
*mp
)
850 (void) pthread_mutex_lock(&mp
->mod_lock
);
852 ASSERT(mp
->mod_flags
& FMD_MOD_BUSY
);
853 mp
->mod_flags
&= ~FMD_MOD_BUSY
;
855 (void) pthread_cond_broadcast(&mp
->mod_cv
);
856 (void) pthread_mutex_unlock(&mp
->mod_lock
);
860 * If the client.error policy has been set by a developer, stop or dump core
861 * based on the policy; if we stop and are resumed we'll continue and execute
862 * the default behavior to discard events in fmd_module_start(). If the caller
863 * is the primary module thread, we reach this state by longjmp'ing back to
864 * fmd_module_enter(), above. If the caller is an auxiliary thread, we cancel
865 * ourself and arrange for the primary thread to call fmd_module_abort().
868 fmd_module_abort(fmd_module_t
*mp
, int err
)
870 uint_t policy
= FMD_CERROR_UNLOAD
;
871 pthread_t tid
= pthread_self();
873 (void) fmd_conf_getprop(fmd
.d_conf
, "client.error", &policy
);
875 if (policy
== FMD_CERROR_STOP
) {
876 fmd_error(err
, "stopping after %s in client %s (%p)\n",
877 fmd_errclass(err
), mp
->mod_name
, (void *)mp
);
878 (void) raise(SIGSTOP
);
879 } else if (policy
== FMD_CERROR_ABORT
) {
880 fmd_panic("aborting due to %s in client %s (%p)\n",
881 fmd_errclass(err
), mp
->mod_name
, (void *)mp
);
885 * If the caller is an auxiliary thread, cancel the current thread. We
886 * prefer to cancel because it affords developers the option of using
887 * the pthread_cleanup* APIs. If cancellations have been disabled,
888 * fall through to forcing the current thread to exit. In either case
889 * we update mod_error (if zero) to enter the failed state. Once that
890 * is set, further events received by the module will be discarded.
892 * We also set the FMD_MOD_FAIL bit, indicating an unrecoverable error.
893 * When an auxiliary thread fails, the module is left in a delicate
894 * state where it is likely not able to continue execution (even to
895 * execute its _fmd_fini() routine) because our caller may hold locks
896 * that are private to the module and can no longer be released. The
897 * FMD_MOD_FAIL bit forces fmd_api_module_lock() to abort if any other
898 * module threads reach an API call, in an attempt to get them to exit.
900 if (tid
!= mp
->mod_thread
->thr_tid
) {
901 (void) pthread_mutex_lock(&mp
->mod_lock
);
903 if (mp
->mod_error
== 0)
906 mp
->mod_flags
|= FMD_MOD_FAIL
;
907 (void) pthread_mutex_unlock(&mp
->mod_lock
);
909 (void) pthread_cancel(tid
);
913 ASSERT(mp
->mod_flags
& FMD_MOD_BUSY
);
914 longjmp(mp
->mod_jmpbuf
, err
);
918 fmd_module_hold(fmd_module_t
*mp
)
920 (void) pthread_mutex_lock(&mp
->mod_lock
);
922 TRACE((FMD_DBG_MOD
, "hold %p (%s/%u)\n",
923 (void *)mp
, mp
->mod_name
, mp
->mod_refs
));
926 ASSERT(mp
->mod_refs
!= 0);
928 (void) pthread_mutex_unlock(&mp
->mod_lock
);
932 fmd_module_rele(fmd_module_t
*mp
)
934 (void) pthread_mutex_lock(&mp
->mod_lock
);
936 TRACE((FMD_DBG_MOD
, "rele %p (%s/%u)\n",
937 (void *)mp
, mp
->mod_name
, mp
->mod_refs
));
939 ASSERT(mp
->mod_refs
!= 0);
941 if (--mp
->mod_refs
== 0)
942 fmd_module_destroy(mp
);
944 (void) pthread_mutex_unlock(&mp
->mod_lock
);
948 * Wrapper around libdiagcode's fm_dc_opendict() to load module dictionaries.
949 * If the dictionary open is successful, the new dictionary is added to the
950 * mod_dictv[] array and mod_codelen is updated with the new maximum length.
953 fmd_module_dc_opendict(fmd_module_t
*mp
, const char *dict
)
955 struct fm_dc_handle
*dcp
, **dcv
;
956 char *dictdir
, *dictnam
, *p
;
959 ASSERT(fmd_module_locked(mp
));
961 dictnam
= strdupa(fmd_strbasename(dict
));
963 if ((p
= strrchr(dictnam
, '.')) != NULL
&&
964 strcmp(p
, ".dict") == 0)
965 *p
= '\0'; /* eliminate any trailing .dict suffix */
968 * If 'dict' is an absolute path, dictdir = $rootdir/`dirname dict`
969 * If 'dict' is not an absolute path, dictdir = $dictdir/`dirname dict`
971 if (dict
[0] == '/') {
972 len
= strlen(fmd
.d_rootdir
) + strlen(dict
) + 1;
973 dictdir
= alloca(len
);
974 (void) snprintf(dictdir
, len
, "%s%s", fmd
.d_rootdir
, dict
);
975 (void) fmd_strdirname(dictdir
);
977 (void) fmd_conf_getprop(fmd
.d_conf
, "dictdir", &p
);
978 len
= strlen(fmd
.d_rootdir
) + strlen(p
) + strlen(dict
) + 3;
979 dictdir
= alloca(len
);
980 (void) snprintf(dictdir
, len
,
981 "%s/%s/%s", fmd
.d_rootdir
, p
, dict
);
982 (void) fmd_strdirname(dictdir
);
985 fmd_dprintf(FMD_DBG_MOD
, "module %s opening %s -> %s/%s.dict\n",
986 mp
->mod_name
, dict
, dictdir
, dictnam
);
988 if ((dcp
= fm_dc_opendict(FM_DC_VERSION
, dictdir
, dictnam
)) == NULL
)
989 return (-1); /* errno is set for us */
991 dcv
= fmd_alloc(sizeof (dcp
) * (mp
->mod_dictc
+ 1), FMD_SLEEP
);
992 bcopy(mp
->mod_dictv
, dcv
, sizeof (dcp
) * mp
->mod_dictc
);
993 fmd_free(mp
->mod_dictv
, sizeof (dcp
) * mp
->mod_dictc
);
995 mp
->mod_dictv
[mp
->mod_dictc
++] = dcp
;
997 len
= fm_dc_codelen(dcp
);
998 mp
->mod_codelen
= MAX(mp
->mod_codelen
, len
);
1004 * Wrapper around libdiagcode's fm_dc_key2code() that examines all the module's
1005 * dictionaries. We adhere to the libdiagcode return values and semantics.
1008 fmd_module_dc_key2code(fmd_module_t
*mp
,
1009 char *const keys
[], char *code
, size_t codelen
)
1013 for (i
= 0; i
< mp
->mod_dictc
; i
++) {
1014 if ((err
= fm_dc_key2code(mp
->mod_dictv
[i
], (const char **)keys
,
1015 code
, codelen
)) == 0 || errno
!= ENOMSG
)
1019 return (fmd_set_errno(ENOMSG
));
1023 fmd_modhash_create(void)
1025 fmd_modhash_t
*mhp
= fmd_alloc(sizeof (fmd_modhash_t
), FMD_SLEEP
);
1027 (void) pthread_rwlock_init(&mhp
->mh_lock
, NULL
);
1028 mhp
->mh_hashlen
= fmd
.d_str_buckets
;
1029 mhp
->mh_hash
= fmd_zalloc(sizeof (void *) * mhp
->mh_hashlen
, FMD_SLEEP
);
1036 fmd_modhash_destroy(fmd_modhash_t
*mhp
)
1038 fmd_module_t
*mp
, *nmp
;
1041 for (i
= 0; i
< mhp
->mh_hashlen
; i
++) {
1042 for (mp
= mhp
->mh_hash
[i
]; mp
!= NULL
; mp
= nmp
) {
1044 mp
->mod_next
= NULL
;
1045 fmd_module_rele(mp
);
1049 fmd_free(mhp
->mh_hash
, sizeof (void *) * mhp
->mh_hashlen
);
1050 (void) pthread_rwlock_destroy(&mhp
->mh_lock
);
1051 fmd_free(mhp
, sizeof (fmd_modhash_t
));
1055 fmd_modhash_loaddir(fmd_modhash_t
*mhp
, const char *dir
,
1056 const fmd_modops_t
*ops
, const char *suffix
)
1058 char path
[PATH_MAX
];
1063 if ((dirp
= opendir(dir
)) == NULL
)
1064 return; /* failed to open directory; just skip it */
1066 while ((dp
= readdir(dirp
)) != NULL
) {
1067 if (dp
->d_name
[0] == '.')
1068 continue; /* skip "." and ".." */
1070 p
= strrchr(dp
->d_name
, '.');
1072 if (p
!= NULL
&& strcmp(p
, ".conf") == 0)
1073 continue; /* skip .conf files */
1075 if (suffix
!= NULL
&& (p
== NULL
|| strcmp(p
, suffix
) != 0))
1076 continue; /* skip files with the wrong suffix */
1078 (void) snprintf(path
, sizeof (path
), "%s/%s", dir
, dp
->d_name
);
1079 (void) fmd_modhash_load(mhp
, path
, ops
);
1082 (void) closedir(dirp
);
1086 fmd_modhash_loadall(fmd_modhash_t
*mhp
, const fmd_conf_path_t
*pap
,
1087 const fmd_modops_t
*ops
, const char *suffix
)
1091 for (i
= 0; i
< pap
->cpa_argc
; i
++)
1092 fmd_modhash_loaddir(mhp
, pap
->cpa_argv
[i
], ops
, suffix
);
1096 fmd_modhash_apply(fmd_modhash_t
*mhp
, void (*func
)(fmd_module_t
*))
1098 fmd_module_t
*mp
, *np
;
1101 (void) pthread_rwlock_rdlock(&mhp
->mh_lock
);
1103 for (i
= 0; i
< mhp
->mh_hashlen
; i
++) {
1104 for (mp
= mhp
->mh_hash
[i
]; mp
!= NULL
; mp
= np
) {
1110 (void) pthread_rwlock_unlock(&mhp
->mh_lock
);
1114 fmd_modhash_tryapply(fmd_modhash_t
*mhp
, void (*func
)(fmd_module_t
*))
1116 fmd_module_t
*mp
, *np
;
1119 if (mhp
== NULL
|| pthread_rwlock_tryrdlock(&mhp
->mh_lock
) != 0)
1120 return; /* not initialized or couldn't grab lock */
1122 for (i
= 0; i
< mhp
->mh_hashlen
; i
++) {
1123 for (mp
= mhp
->mh_hash
[i
]; mp
!= NULL
; mp
= np
) {
1129 (void) pthread_rwlock_unlock(&mhp
->mh_lock
);
1133 fmd_modhash_dispatch(fmd_modhash_t
*mhp
, fmd_event_t
*ep
)
1139 (void) pthread_rwlock_rdlock(&mhp
->mh_lock
);
1141 for (i
= 0; i
< mhp
->mh_hashlen
; i
++) {
1142 for (mp
= mhp
->mh_hash
[i
]; mp
!= NULL
; mp
= mp
->mod_next
) {
1144 * If FMD_MOD_INIT is set but MOD_FINI, MOD_QUIT, and
1145 * mod_error are all zero, then the module is active:
1146 * enqueue the event in the corresponding event queue.
1148 (void) pthread_mutex_lock(&mp
->mod_lock
);
1150 if ((mp
->mod_flags
& (FMD_MOD_INIT
| FMD_MOD_FINI
|
1151 FMD_MOD_QUIT
)) == FMD_MOD_INIT
&& !mp
->mod_error
) {
1154 * If the event we're dispatching is of type
1155 * FMD_EVT_TOPO and there are already redundant
1156 * FMD_EVT_TOPO events in this module's queue,
1157 * then drop those before adding the new one.
1159 if (FMD_EVENT_TYPE(ep
) == FMD_EVT_TOPO
)
1160 fmd_eventq_drop_topo(mp
->mod_queue
);
1162 fmd_eventq_insert_at_time(mp
->mod_queue
, ep
);
1165 (void) pthread_mutex_unlock(&mp
->mod_lock
);
1169 (void) pthread_rwlock_unlock(&mhp
->mh_lock
);
1174 fmd_modhash_lookup(fmd_modhash_t
*mhp
, const char *name
)
1179 (void) pthread_rwlock_rdlock(&mhp
->mh_lock
);
1180 h
= fmd_strhash(name
) % mhp
->mh_hashlen
;
1182 for (mp
= mhp
->mh_hash
[h
]; mp
!= NULL
; mp
= mp
->mod_next
) {
1183 if (strcmp(name
, mp
->mod_name
) == 0)
1188 fmd_module_hold(mp
);
1190 (void) fmd_set_errno(EFMD_MOD_NOMOD
);
1192 (void) pthread_rwlock_unlock(&mhp
->mh_lock
);
1197 fmd_modhash_load(fmd_modhash_t
*mhp
, const char *path
, const fmd_modops_t
*ops
)
1199 char name
[PATH_MAX
], *p
;
1204 (void) strlcpy(name
, fmd_strbasename(path
), sizeof (name
));
1205 if ((p
= strrchr(name
, '.')) != NULL
&& strcmp(p
, ".so") == 0)
1206 *p
= '\0'; /* strip trailing .so from any module name */
1208 (void) pthread_rwlock_wrlock(&mhp
->mh_lock
);
1209 h
= fmd_strhash(name
) % mhp
->mh_hashlen
;
1212 * First check to see if a module is already present in the hash table
1213 * for this name. If so, the module is already loaded: skip it.
1215 for (mp
= mhp
->mh_hash
[h
]; mp
!= NULL
; mp
= mp
->mod_next
) {
1216 if (strcmp(name
, mp
->mod_name
) == 0)
1221 (void) pthread_rwlock_unlock(&mhp
->mh_lock
);
1222 (void) fmd_set_errno(EFMD_MOD_LOADED
);
1227 * fmd_module_create() will return a held (as if by fmd_module_hold())
1228 * module. We leave this hold in place to correspond to the hash-in.
1230 while ((mp
= fmd_module_create(path
, ops
)) == NULL
) {
1231 if (tries
++ != 0 || errno
!= EFMD_CKPT_INVAL
) {
1232 (void) pthread_rwlock_unlock(&mhp
->mh_lock
);
1233 return (NULL
); /* errno is set for us */
1238 mp
->mod_next
= mhp
->mh_hash
[h
];
1240 mhp
->mh_hash
[h
] = mp
;
1243 (void) pthread_rwlock_unlock(&mhp
->mh_lock
);
1248 fmd_modhash_unload(fmd_modhash_t
*mhp
, const char *name
)
1250 fmd_module_t
*mp
, **pp
;
1253 (void) pthread_rwlock_wrlock(&mhp
->mh_lock
);
1254 h
= fmd_strhash(name
) % mhp
->mh_hashlen
;
1255 pp
= &mhp
->mh_hash
[h
];
1257 for (mp
= *pp
; mp
!= NULL
; mp
= mp
->mod_next
) {
1258 if (strcmp(name
, mp
->mod_name
) == 0)
1265 (void) pthread_rwlock_unlock(&mhp
->mh_lock
);
1266 return (fmd_set_errno(EFMD_MOD_NOMOD
));
1270 mp
->mod_next
= NULL
;
1272 ASSERT(mhp
->mh_nelems
!= 0);
1275 (void) pthread_rwlock_unlock(&mhp
->mh_lock
);
1277 fmd_module_unload(mp
);
1278 fmd_module_rele(mp
);
1284 fmd_modstat_publish(fmd_module_t
*mp
)
1286 (void) pthread_mutex_lock(&mp
->mod_lock
);
1288 ASSERT(mp
->mod_flags
& FMD_MOD_STSUB
);
1289 mp
->mod_flags
|= FMD_MOD_STPUB
;
1290 (void) pthread_cond_broadcast(&mp
->mod_cv
);
1292 while (mp
->mod_flags
& FMD_MOD_STPUB
)
1293 (void) pthread_cond_wait(&mp
->mod_cv
, &mp
->mod_lock
);
1295 (void) pthread_mutex_unlock(&mp
->mod_lock
);
1299 fmd_modstat_snapshot(fmd_module_t
*mp
, fmd_ustat_snap_t
*uss
)
1305 * Grab the module lock and wait for the STSUB bit to be clear. Then
1306 * set it to indicate we are a subscriber and everyone else must wait.
1308 (void) pthread_mutex_lock(&mp
->mod_lock
);
1310 while (mp
->mod_error
== 0 && (mp
->mod_flags
& FMD_MOD_STSUB
))
1311 (void) pthread_cond_wait(&mp
->mod_cv
, &mp
->mod_lock
);
1313 if (mp
->mod_error
!= 0) {
1314 (void) pthread_mutex_unlock(&mp
->mod_lock
);
1315 return (fmd_set_errno(EFMD_HDL_ABORT
));
1318 mp
->mod_flags
|= FMD_MOD_STSUB
;
1319 (void) pthread_cond_broadcast(&mp
->mod_cv
);
1320 (void) pthread_mutex_unlock(&mp
->mod_lock
);
1323 * Create a stats pseudo-event and dispatch it to the module, forcing
1324 * it to next execute its custom snapshot routine (or the empty one).
1326 e
= fmd_event_create(FMD_EVT_STATS
, FMD_HRT_NOW
, NULL
, NULL
);
1327 fmd_eventq_insert_at_head(mp
->mod_queue
, e
);
1330 * Grab the module lock and then wait on mod_cv for STPUB to be set,
1331 * indicating the snapshot routine is completed and the module is idle.
1333 (void) pthread_mutex_lock(&mp
->mod_lock
);
1335 while (mp
->mod_error
== 0 && !(mp
->mod_flags
& FMD_MOD_STPUB
)) {
1336 struct timespec tms
;
1338 (void) pthread_cond_wait(&mp
->mod_cv
, &mp
->mod_lock
);
1339 (void) pthread_mutex_unlock(&mp
->mod_lock
);
1341 tms
.tv_nsec
= 10000000;
1342 (void) nanosleep(&tms
, NULL
);
1343 (void) pthread_mutex_lock(&mp
->mod_lock
);
1346 if (mp
->mod_error
!= 0) {
1347 (void) pthread_mutex_unlock(&mp
->mod_lock
);
1348 return (fmd_set_errno(EFMD_HDL_ABORT
));
1351 (void) pthread_cond_broadcast(&mp
->mod_cv
);
1352 (void) pthread_mutex_unlock(&mp
->mod_lock
);
1355 * Update ms_snaptime and take the actual snapshot of the various
1356 * statistics while the module is quiescent and waiting for us.
1358 (void) pthread_mutex_lock(&mp
->mod_stats_lock
);
1360 if (mp
->mod_stats
!= NULL
) {
1361 mp
->mod_stats
->ms_snaptime
.fmds_value
.ui64
= gethrtime();
1362 err
= fmd_ustat_snapshot(mp
->mod_ustat
, uss
);
1364 err
= fmd_set_errno(EFMD_HDL_ABORT
);
1366 (void) pthread_mutex_unlock(&mp
->mod_stats_lock
);
1369 * With the snapshot complete, grab the module lock and clear both
1370 * STSUB and STPUB, permitting everyone to wake up and continue.
1372 (void) pthread_mutex_lock(&mp
->mod_lock
);
1374 ASSERT(mp
->mod_flags
& FMD_MOD_STSUB
);
1375 ASSERT(mp
->mod_flags
& FMD_MOD_STPUB
);
1376 mp
->mod_flags
&= ~(FMD_MOD_STSUB
| FMD_MOD_STPUB
);
1378 (void) pthread_cond_broadcast(&mp
->mod_cv
);
1379 (void) pthread_mutex_unlock(&mp
->mod_lock
);
1385 fmd_module_topo_hold(fmd_module_t
*mp
)
1389 ASSERT(fmd_module_locked(mp
));
1391 mtp
= fmd_zalloc(sizeof (fmd_modtopo_t
), FMD_SLEEP
);
1392 mtp
->mt_topo
= mp
->mod_topo_current
;
1393 fmd_topo_addref(mtp
->mt_topo
);
1394 fmd_list_prepend(&mp
->mod_topolist
, mtp
);
1396 return (mtp
->mt_topo
->ft_hdl
);
1400 fmd_module_topo_rele(fmd_module_t
*mp
, struct topo_hdl
*hdl
)
1404 ASSERT(fmd_module_locked(mp
));
1406 for (mtp
= fmd_list_next(&mp
->mod_topolist
); mtp
!= NULL
;
1407 mtp
= fmd_list_next(mtp
)) {
1408 if (mtp
->mt_topo
->ft_hdl
== hdl
)
1415 fmd_list_delete(&mp
->mod_topolist
, mtp
);
1416 fmd_topo_rele(mtp
->mt_topo
);
1417 fmd_free(mtp
, sizeof (fmd_modtopo_t
));