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