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" }, 82*12967Sgavin.maltby@oracle.com { "fmd.doorthrtotal", FMD_TYPE_UINT32, "total number of door server threads" }, 83*12967Sgavin.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 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 * 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 252*12967Sgavin.maltby@oracle.com (void) fmd_conf_getprop(fmd.d_conf, "client.doorthrlim", 253*12967Sgavin.maltby@oracle.com &mp->mod_stats->ms_doorthrlimit.fmds_value.ui32); 254*12967Sgavin.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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 9610Sstevel@tonic-gate dictnam = alloca(strlen(dict) + 1); 9620Sstevel@tonic-gate (void) strcpy(dictnam, fmd_strbasename(dict)); 9630Sstevel@tonic-gate 9640Sstevel@tonic-gate if ((p = strrchr(dictnam, '.')) != NULL && 9650Sstevel@tonic-gate strcmp(p, ".dict") == 0) 9660Sstevel@tonic-gate *p = '\0'; /* eliminate any trailing .dict suffix */ 9670Sstevel@tonic-gate 9680Sstevel@tonic-gate /* 9690Sstevel@tonic-gate * If 'dict' is an absolute path, dictdir = $rootdir/`dirname dict` 9700Sstevel@tonic-gate * If 'dict' is not an absolute path, dictdir = $dictdir/`dirname dict` 9710Sstevel@tonic-gate */ 9720Sstevel@tonic-gate if (dict[0] == '/') { 9730Sstevel@tonic-gate len = strlen(fmd.d_rootdir) + strlen(dict) + 1; 9740Sstevel@tonic-gate dictdir = alloca(len); 9750Sstevel@tonic-gate (void) snprintf(dictdir, len, "%s%s", fmd.d_rootdir, dict); 9760Sstevel@tonic-gate (void) fmd_strdirname(dictdir); 9770Sstevel@tonic-gate } else { 9780Sstevel@tonic-gate (void) fmd_conf_getprop(fmd.d_conf, "dictdir", &p); 9790Sstevel@tonic-gate len = strlen(fmd.d_rootdir) + strlen(p) + strlen(dict) + 3; 9800Sstevel@tonic-gate dictdir = alloca(len); 9810Sstevel@tonic-gate (void) snprintf(dictdir, len, 9820Sstevel@tonic-gate "%s/%s/%s", fmd.d_rootdir, p, dict); 9830Sstevel@tonic-gate (void) fmd_strdirname(dictdir); 9840Sstevel@tonic-gate } 9850Sstevel@tonic-gate 9860Sstevel@tonic-gate fmd_dprintf(FMD_DBG_MOD, "module %s opening %s -> %s/%s.dict\n", 9870Sstevel@tonic-gate mp->mod_name, dict, dictdir, dictnam); 9880Sstevel@tonic-gate 9890Sstevel@tonic-gate if ((dcp = fm_dc_opendict(FM_DC_VERSION, dictdir, dictnam)) == NULL) 9900Sstevel@tonic-gate return (-1); /* errno is set for us */ 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate dcv = fmd_alloc(sizeof (dcp) * (mp->mod_dictc + 1), FMD_SLEEP); 9930Sstevel@tonic-gate bcopy(mp->mod_dictv, dcv, sizeof (dcp) * mp->mod_dictc); 9940Sstevel@tonic-gate fmd_free(mp->mod_dictv, sizeof (dcp) * mp->mod_dictc); 9950Sstevel@tonic-gate mp->mod_dictv = dcv; 9960Sstevel@tonic-gate mp->mod_dictv[mp->mod_dictc++] = dcp; 9970Sstevel@tonic-gate 9980Sstevel@tonic-gate len = fm_dc_codelen(dcp); 9990Sstevel@tonic-gate mp->mod_codelen = MAX(mp->mod_codelen, len); 10000Sstevel@tonic-gate 10010Sstevel@tonic-gate return (0); 10020Sstevel@tonic-gate } 10030Sstevel@tonic-gate 10040Sstevel@tonic-gate /* 10050Sstevel@tonic-gate * Wrapper around libdiagcode's fm_dc_key2code() that examines all the module's 10060Sstevel@tonic-gate * dictionaries. We adhere to the libdiagcode return values and semantics. 10070Sstevel@tonic-gate */ 10080Sstevel@tonic-gate int 10090Sstevel@tonic-gate fmd_module_dc_key2code(fmd_module_t *mp, 10100Sstevel@tonic-gate char *const keys[], char *code, size_t codelen) 10110Sstevel@tonic-gate { 10120Sstevel@tonic-gate int i, err; 10130Sstevel@tonic-gate 10140Sstevel@tonic-gate for (i = 0; i < mp->mod_dictc; i++) { 10150Sstevel@tonic-gate if ((err = fm_dc_key2code(mp->mod_dictv[i], (const char **)keys, 10160Sstevel@tonic-gate code, codelen)) == 0 || errno != ENOMSG) 10170Sstevel@tonic-gate return (err); 10180Sstevel@tonic-gate } 10190Sstevel@tonic-gate 10200Sstevel@tonic-gate return (fmd_set_errno(ENOMSG)); 10210Sstevel@tonic-gate } 10220Sstevel@tonic-gate 10230Sstevel@tonic-gate fmd_modhash_t * 10240Sstevel@tonic-gate fmd_modhash_create(void) 10250Sstevel@tonic-gate { 10260Sstevel@tonic-gate fmd_modhash_t *mhp = fmd_alloc(sizeof (fmd_modhash_t), FMD_SLEEP); 10270Sstevel@tonic-gate 10280Sstevel@tonic-gate (void) pthread_rwlock_init(&mhp->mh_lock, NULL); 10290Sstevel@tonic-gate mhp->mh_hashlen = fmd.d_str_buckets; 10300Sstevel@tonic-gate mhp->mh_hash = fmd_zalloc(sizeof (void *) * mhp->mh_hashlen, FMD_SLEEP); 10310Sstevel@tonic-gate mhp->mh_nelems = 0; 10320Sstevel@tonic-gate 10330Sstevel@tonic-gate return (mhp); 10340Sstevel@tonic-gate } 10350Sstevel@tonic-gate 10360Sstevel@tonic-gate void 10370Sstevel@tonic-gate fmd_modhash_destroy(fmd_modhash_t *mhp) 10380Sstevel@tonic-gate { 10390Sstevel@tonic-gate fmd_module_t *mp, *nmp; 10400Sstevel@tonic-gate uint_t i; 10410Sstevel@tonic-gate 10420Sstevel@tonic-gate for (i = 0; i < mhp->mh_hashlen; i++) { 10430Sstevel@tonic-gate for (mp = mhp->mh_hash[i]; mp != NULL; mp = nmp) { 10440Sstevel@tonic-gate nmp = mp->mod_next; 10450Sstevel@tonic-gate mp->mod_next = NULL; 10460Sstevel@tonic-gate fmd_module_rele(mp); 10470Sstevel@tonic-gate } 10480Sstevel@tonic-gate } 10490Sstevel@tonic-gate 10500Sstevel@tonic-gate fmd_free(mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen); 10510Sstevel@tonic-gate (void) pthread_rwlock_destroy(&mhp->mh_lock); 10520Sstevel@tonic-gate fmd_free(mhp, sizeof (fmd_modhash_t)); 10530Sstevel@tonic-gate } 10540Sstevel@tonic-gate 10550Sstevel@tonic-gate static void 10560Sstevel@tonic-gate fmd_modhash_loaddir(fmd_modhash_t *mhp, const char *dir, 10571429Smws const fmd_modops_t *ops, const char *suffix) 10580Sstevel@tonic-gate { 10590Sstevel@tonic-gate char path[PATH_MAX]; 1060871Scasper struct dirent *dp; 10610Sstevel@tonic-gate const char *p; 10620Sstevel@tonic-gate DIR *dirp; 10630Sstevel@tonic-gate 10640Sstevel@tonic-gate if ((dirp = opendir(dir)) == NULL) 10650Sstevel@tonic-gate return; /* failed to open directory; just skip it */ 10660Sstevel@tonic-gate 1067871Scasper while ((dp = readdir(dirp)) != NULL) { 10680Sstevel@tonic-gate if (dp->d_name[0] == '.') 10690Sstevel@tonic-gate continue; /* skip "." and ".." */ 10700Sstevel@tonic-gate 10711429Smws p = strrchr(dp->d_name, '.'); 10721429Smws 10731429Smws if (p != NULL && strcmp(p, ".conf") == 0) 10740Sstevel@tonic-gate continue; /* skip .conf files */ 10750Sstevel@tonic-gate 10761429Smws if (suffix != NULL && (p == NULL || strcmp(p, suffix) != 0)) 10771429Smws continue; /* skip files with the wrong suffix */ 10781429Smws 10790Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s/%s", dir, dp->d_name); 10800Sstevel@tonic-gate (void) fmd_modhash_load(mhp, path, ops); 10810Sstevel@tonic-gate } 10820Sstevel@tonic-gate 10830Sstevel@tonic-gate (void) closedir(dirp); 10840Sstevel@tonic-gate } 10850Sstevel@tonic-gate 10860Sstevel@tonic-gate void 10870Sstevel@tonic-gate fmd_modhash_loadall(fmd_modhash_t *mhp, const fmd_conf_path_t *pap, 10881429Smws const fmd_modops_t *ops, const char *suffix) 10890Sstevel@tonic-gate { 10900Sstevel@tonic-gate int i; 10910Sstevel@tonic-gate 10920Sstevel@tonic-gate for (i = 0; i < pap->cpa_argc; i++) 10931429Smws fmd_modhash_loaddir(mhp, pap->cpa_argv[i], ops, suffix); 10940Sstevel@tonic-gate } 10950Sstevel@tonic-gate 10960Sstevel@tonic-gate void 10970Sstevel@tonic-gate fmd_modhash_apply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *)) 10980Sstevel@tonic-gate { 10990Sstevel@tonic-gate fmd_module_t *mp, *np; 11000Sstevel@tonic-gate uint_t i; 11010Sstevel@tonic-gate 11020Sstevel@tonic-gate (void) pthread_rwlock_rdlock(&mhp->mh_lock); 11030Sstevel@tonic-gate 11040Sstevel@tonic-gate for (i = 0; i < mhp->mh_hashlen; i++) { 11050Sstevel@tonic-gate for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) { 11060Sstevel@tonic-gate np = mp->mod_next; 11070Sstevel@tonic-gate func(mp); 11080Sstevel@tonic-gate } 11090Sstevel@tonic-gate } 11100Sstevel@tonic-gate 11110Sstevel@tonic-gate (void) pthread_rwlock_unlock(&mhp->mh_lock); 11120Sstevel@tonic-gate } 11130Sstevel@tonic-gate 11140Sstevel@tonic-gate void 11150Sstevel@tonic-gate fmd_modhash_tryapply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *)) 11160Sstevel@tonic-gate { 11170Sstevel@tonic-gate fmd_module_t *mp, *np; 11180Sstevel@tonic-gate uint_t i; 11190Sstevel@tonic-gate 11200Sstevel@tonic-gate if (mhp == NULL || pthread_rwlock_tryrdlock(&mhp->mh_lock) != 0) 11210Sstevel@tonic-gate return; /* not initialized or couldn't grab lock */ 11220Sstevel@tonic-gate 11230Sstevel@tonic-gate for (i = 0; i < mhp->mh_hashlen; i++) { 11240Sstevel@tonic-gate for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) { 11250Sstevel@tonic-gate np = mp->mod_next; 11260Sstevel@tonic-gate func(mp); 11270Sstevel@tonic-gate } 11280Sstevel@tonic-gate } 11290Sstevel@tonic-gate 11300Sstevel@tonic-gate (void) pthread_rwlock_unlock(&mhp->mh_lock); 11310Sstevel@tonic-gate } 11320Sstevel@tonic-gate 11330Sstevel@tonic-gate void 11340Sstevel@tonic-gate fmd_modhash_dispatch(fmd_modhash_t *mhp, fmd_event_t *ep) 11350Sstevel@tonic-gate { 11360Sstevel@tonic-gate fmd_module_t *mp; 11370Sstevel@tonic-gate uint_t i; 11380Sstevel@tonic-gate 11390Sstevel@tonic-gate fmd_event_hold(ep); 11400Sstevel@tonic-gate (void) pthread_rwlock_rdlock(&mhp->mh_lock); 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate for (i = 0; i < mhp->mh_hashlen; i++) { 11430Sstevel@tonic-gate for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) { 11440Sstevel@tonic-gate /* 11450Sstevel@tonic-gate * If FMD_MOD_INIT is set but MOD_FINI, MOD_QUIT, and 11460Sstevel@tonic-gate * mod_error are all zero, then the module is active: 11470Sstevel@tonic-gate * enqueue the event in the corresponding event queue. 11480Sstevel@tonic-gate */ 11490Sstevel@tonic-gate (void) pthread_mutex_lock(&mp->mod_lock); 11500Sstevel@tonic-gate 11510Sstevel@tonic-gate if ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI | 115212066SRobert.Johnston@Sun.COM FMD_MOD_QUIT)) == FMD_MOD_INIT && !mp->mod_error) { 115312066SRobert.Johnston@Sun.COM 115412066SRobert.Johnston@Sun.COM /* 115512066SRobert.Johnston@Sun.COM * If the event we're dispatching is of type 115612066SRobert.Johnston@Sun.COM * FMD_EVT_TOPO and there are already redundant 115712066SRobert.Johnston@Sun.COM * FMD_EVT_TOPO events in this module's queue, 115812066SRobert.Johnston@Sun.COM * then drop those before adding the new one. 115912066SRobert.Johnston@Sun.COM */ 116012066SRobert.Johnston@Sun.COM if (FMD_EVENT_TYPE(ep) == FMD_EVT_TOPO) 116112066SRobert.Johnston@Sun.COM fmd_eventq_drop_topo(mp->mod_queue); 116212066SRobert.Johnston@Sun.COM 11630Sstevel@tonic-gate fmd_eventq_insert_at_time(mp->mod_queue, ep); 11640Sstevel@tonic-gate 116512066SRobert.Johnston@Sun.COM } 11660Sstevel@tonic-gate (void) pthread_mutex_unlock(&mp->mod_lock); 11670Sstevel@tonic-gate } 11680Sstevel@tonic-gate } 11690Sstevel@tonic-gate 11700Sstevel@tonic-gate (void) pthread_rwlock_unlock(&mhp->mh_lock); 11710Sstevel@tonic-gate fmd_event_rele(ep); 11720Sstevel@tonic-gate } 11730Sstevel@tonic-gate 11740Sstevel@tonic-gate fmd_module_t * 11750Sstevel@tonic-gate fmd_modhash_lookup(fmd_modhash_t *mhp, const char *name) 11760Sstevel@tonic-gate { 11770Sstevel@tonic-gate fmd_module_t *mp; 11780Sstevel@tonic-gate uint_t h; 11790Sstevel@tonic-gate 11800Sstevel@tonic-gate (void) pthread_rwlock_rdlock(&mhp->mh_lock); 11810Sstevel@tonic-gate h = fmd_strhash(name) % mhp->mh_hashlen; 11820Sstevel@tonic-gate 11830Sstevel@tonic-gate for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) { 11840Sstevel@tonic-gate if (strcmp(name, mp->mod_name) == 0) 11850Sstevel@tonic-gate break; 11860Sstevel@tonic-gate } 11870Sstevel@tonic-gate 11880Sstevel@tonic-gate if (mp != NULL) 11890Sstevel@tonic-gate fmd_module_hold(mp); 11900Sstevel@tonic-gate else 11910Sstevel@tonic-gate (void) fmd_set_errno(EFMD_MOD_NOMOD); 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate (void) pthread_rwlock_unlock(&mhp->mh_lock); 11940Sstevel@tonic-gate return (mp); 11950Sstevel@tonic-gate } 11960Sstevel@tonic-gate 11970Sstevel@tonic-gate fmd_module_t * 11980Sstevel@tonic-gate fmd_modhash_load(fmd_modhash_t *mhp, const char *path, const fmd_modops_t *ops) 11990Sstevel@tonic-gate { 12000Sstevel@tonic-gate char name[PATH_MAX], *p; 12010Sstevel@tonic-gate fmd_module_t *mp; 12020Sstevel@tonic-gate int tries = 0; 12030Sstevel@tonic-gate uint_t h; 12040Sstevel@tonic-gate 12050Sstevel@tonic-gate (void) strlcpy(name, fmd_strbasename(path), sizeof (name)); 12060Sstevel@tonic-gate if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0) 12070Sstevel@tonic-gate *p = '\0'; /* strip trailing .so from any module name */ 12080Sstevel@tonic-gate 12090Sstevel@tonic-gate (void) pthread_rwlock_wrlock(&mhp->mh_lock); 12100Sstevel@tonic-gate h = fmd_strhash(name) % mhp->mh_hashlen; 12110Sstevel@tonic-gate 12120Sstevel@tonic-gate /* 12130Sstevel@tonic-gate * First check to see if a module is already present in the hash table 12140Sstevel@tonic-gate * for this name. If so, the module is already loaded: skip it. 12150Sstevel@tonic-gate */ 12160Sstevel@tonic-gate for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) { 12170Sstevel@tonic-gate if (strcmp(name, mp->mod_name) == 0) 12180Sstevel@tonic-gate break; 12190Sstevel@tonic-gate } 12200Sstevel@tonic-gate 12210Sstevel@tonic-gate if (mp != NULL) { 12220Sstevel@tonic-gate (void) pthread_rwlock_unlock(&mhp->mh_lock); 12230Sstevel@tonic-gate (void) fmd_set_errno(EFMD_MOD_LOADED); 12240Sstevel@tonic-gate return (NULL); 12250Sstevel@tonic-gate } 12260Sstevel@tonic-gate 12270Sstevel@tonic-gate /* 12280Sstevel@tonic-gate * fmd_module_create() will return a held (as if by fmd_module_hold()) 12290Sstevel@tonic-gate * module. We leave this hold in place to correspond to the hash-in. 12300Sstevel@tonic-gate */ 12310Sstevel@tonic-gate while ((mp = fmd_module_create(path, ops)) == NULL) { 12320Sstevel@tonic-gate if (tries++ != 0 || errno != EFMD_CKPT_INVAL) { 12330Sstevel@tonic-gate (void) pthread_rwlock_unlock(&mhp->mh_lock); 12340Sstevel@tonic-gate return (NULL); /* errno is set for us */ 12350Sstevel@tonic-gate } 12360Sstevel@tonic-gate } 12370Sstevel@tonic-gate 12380Sstevel@tonic-gate mp->mod_hash = mhp; 12390Sstevel@tonic-gate mp->mod_next = mhp->mh_hash[h]; 12400Sstevel@tonic-gate 12410Sstevel@tonic-gate mhp->mh_hash[h] = mp; 12420Sstevel@tonic-gate mhp->mh_nelems++; 12430Sstevel@tonic-gate 12440Sstevel@tonic-gate (void) pthread_rwlock_unlock(&mhp->mh_lock); 12450Sstevel@tonic-gate return (mp); 12460Sstevel@tonic-gate } 12470Sstevel@tonic-gate 12480Sstevel@tonic-gate int 12490Sstevel@tonic-gate fmd_modhash_unload(fmd_modhash_t *mhp, const char *name) 12500Sstevel@tonic-gate { 12510Sstevel@tonic-gate fmd_module_t *mp, **pp; 12520Sstevel@tonic-gate uint_t h; 12530Sstevel@tonic-gate 12540Sstevel@tonic-gate (void) pthread_rwlock_wrlock(&mhp->mh_lock); 12550Sstevel@tonic-gate h = fmd_strhash(name) % mhp->mh_hashlen; 12560Sstevel@tonic-gate pp = &mhp->mh_hash[h]; 12570Sstevel@tonic-gate 12580Sstevel@tonic-gate for (mp = *pp; mp != NULL; mp = mp->mod_next) { 12590Sstevel@tonic-gate if (strcmp(name, mp->mod_name) == 0) 12600Sstevel@tonic-gate break; 12610Sstevel@tonic-gate else 12620Sstevel@tonic-gate pp = &mp->mod_next; 12630Sstevel@tonic-gate } 12640Sstevel@tonic-gate 12650Sstevel@tonic-gate if (mp == NULL) { 12660Sstevel@tonic-gate (void) pthread_rwlock_unlock(&mhp->mh_lock); 12670Sstevel@tonic-gate return (fmd_set_errno(EFMD_MOD_NOMOD)); 12680Sstevel@tonic-gate } 12690Sstevel@tonic-gate 12700Sstevel@tonic-gate *pp = mp->mod_next; 12710Sstevel@tonic-gate mp->mod_next = NULL; 12720Sstevel@tonic-gate 12730Sstevel@tonic-gate ASSERT(mhp->mh_nelems != 0); 12740Sstevel@tonic-gate mhp->mh_nelems--; 12750Sstevel@tonic-gate 12760Sstevel@tonic-gate (void) pthread_rwlock_unlock(&mhp->mh_lock); 12770Sstevel@tonic-gate 12780Sstevel@tonic-gate fmd_module_unload(mp); 12790Sstevel@tonic-gate fmd_module_rele(mp); 12800Sstevel@tonic-gate 12810Sstevel@tonic-gate return (0); 12820Sstevel@tonic-gate } 12830Sstevel@tonic-gate 12840Sstevel@tonic-gate void 12850Sstevel@tonic-gate fmd_modstat_publish(fmd_module_t *mp) 12860Sstevel@tonic-gate { 12870Sstevel@tonic-gate (void) pthread_mutex_lock(&mp->mod_lock); 12880Sstevel@tonic-gate 12890Sstevel@tonic-gate ASSERT(mp->mod_flags & FMD_MOD_STSUB); 12900Sstevel@tonic-gate mp->mod_flags |= FMD_MOD_STPUB; 12910Sstevel@tonic-gate (void) pthread_cond_broadcast(&mp->mod_cv); 12920Sstevel@tonic-gate 12930Sstevel@tonic-gate while (mp->mod_flags & FMD_MOD_STPUB) 12940Sstevel@tonic-gate (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 12950Sstevel@tonic-gate 12960Sstevel@tonic-gate (void) pthread_mutex_unlock(&mp->mod_lock); 12970Sstevel@tonic-gate } 12980Sstevel@tonic-gate 12990Sstevel@tonic-gate int 13000Sstevel@tonic-gate fmd_modstat_snapshot(fmd_module_t *mp, fmd_ustat_snap_t *uss) 13010Sstevel@tonic-gate { 13020Sstevel@tonic-gate fmd_event_t *e; 13030Sstevel@tonic-gate int err; 13040Sstevel@tonic-gate 13050Sstevel@tonic-gate /* 13060Sstevel@tonic-gate * Grab the module lock and wait for the STSUB bit to be clear. Then 13070Sstevel@tonic-gate * set it to indicate we are a subscriber and everyone else must wait. 13080Sstevel@tonic-gate */ 13090Sstevel@tonic-gate (void) pthread_mutex_lock(&mp->mod_lock); 13100Sstevel@tonic-gate 13110Sstevel@tonic-gate while (mp->mod_error == 0 && (mp->mod_flags & FMD_MOD_STSUB)) 13120Sstevel@tonic-gate (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 13130Sstevel@tonic-gate 13140Sstevel@tonic-gate if (mp->mod_error != 0) { 13150Sstevel@tonic-gate (void) pthread_mutex_unlock(&mp->mod_lock); 13160Sstevel@tonic-gate return (fmd_set_errno(EFMD_HDL_ABORT)); 13170Sstevel@tonic-gate } 13180Sstevel@tonic-gate 13190Sstevel@tonic-gate mp->mod_flags |= FMD_MOD_STSUB; 13201193Smws (void) pthread_cond_broadcast(&mp->mod_cv); 13210Sstevel@tonic-gate (void) pthread_mutex_unlock(&mp->mod_lock); 13220Sstevel@tonic-gate 13230Sstevel@tonic-gate /* 13240Sstevel@tonic-gate * Create a stats pseudo-event and dispatch it to the module, forcing 13250Sstevel@tonic-gate * it to next execute its custom snapshot routine (or the empty one). 13260Sstevel@tonic-gate */ 13270Sstevel@tonic-gate e = fmd_event_create(FMD_EVT_STATS, FMD_HRT_NOW, NULL, NULL); 13280Sstevel@tonic-gate fmd_eventq_insert_at_head(mp->mod_queue, e); 13290Sstevel@tonic-gate 13300Sstevel@tonic-gate /* 13310Sstevel@tonic-gate * Grab the module lock and then wait on mod_cv for STPUB to be set, 13320Sstevel@tonic-gate * indicating the snapshot routine is completed and the module is idle. 13330Sstevel@tonic-gate */ 13340Sstevel@tonic-gate (void) pthread_mutex_lock(&mp->mod_lock); 13350Sstevel@tonic-gate 133612618SStephen.Hanson@Sun.COM while (mp->mod_error == 0 && !(mp->mod_flags & FMD_MOD_STPUB)) { 133712618SStephen.Hanson@Sun.COM struct timespec tms; 133812618SStephen.Hanson@Sun.COM 13390Sstevel@tonic-gate (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 134012618SStephen.Hanson@Sun.COM (void) pthread_mutex_unlock(&mp->mod_lock); 134112618SStephen.Hanson@Sun.COM tms.tv_sec = 0; 134212618SStephen.Hanson@Sun.COM tms.tv_nsec = 10000000; 134312618SStephen.Hanson@Sun.COM (void) nanosleep(&tms, NULL); 134412618SStephen.Hanson@Sun.COM (void) pthread_mutex_lock(&mp->mod_lock); 134512618SStephen.Hanson@Sun.COM } 13460Sstevel@tonic-gate 13470Sstevel@tonic-gate if (mp->mod_error != 0) { 13480Sstevel@tonic-gate (void) pthread_mutex_unlock(&mp->mod_lock); 13490Sstevel@tonic-gate return (fmd_set_errno(EFMD_HDL_ABORT)); 13500Sstevel@tonic-gate } 13510Sstevel@tonic-gate 13521193Smws (void) pthread_cond_broadcast(&mp->mod_cv); 13530Sstevel@tonic-gate (void) pthread_mutex_unlock(&mp->mod_lock); 13540Sstevel@tonic-gate 13550Sstevel@tonic-gate /* 13560Sstevel@tonic-gate * Update ms_snaptime and take the actual snapshot of the various 13570Sstevel@tonic-gate * statistics while the module is quiescent and waiting for us. 13580Sstevel@tonic-gate */ 13590Sstevel@tonic-gate (void) pthread_mutex_lock(&mp->mod_stats_lock); 13600Sstevel@tonic-gate 13610Sstevel@tonic-gate if (mp->mod_stats != NULL) { 13620Sstevel@tonic-gate mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime(); 13630Sstevel@tonic-gate err = fmd_ustat_snapshot(mp->mod_ustat, uss); 13640Sstevel@tonic-gate } else 13650Sstevel@tonic-gate err = fmd_set_errno(EFMD_HDL_ABORT); 13660Sstevel@tonic-gate 13670Sstevel@tonic-gate (void) pthread_mutex_unlock(&mp->mod_stats_lock); 13680Sstevel@tonic-gate 13690Sstevel@tonic-gate /* 13700Sstevel@tonic-gate * With the snapshot complete, grab the module lock and clear both 13710Sstevel@tonic-gate * STSUB and STPUB, permitting everyone to wake up and continue. 13720Sstevel@tonic-gate */ 13730Sstevel@tonic-gate (void) pthread_mutex_lock(&mp->mod_lock); 13740Sstevel@tonic-gate 13750Sstevel@tonic-gate ASSERT(mp->mod_flags & FMD_MOD_STSUB); 13760Sstevel@tonic-gate ASSERT(mp->mod_flags & FMD_MOD_STPUB); 13770Sstevel@tonic-gate mp->mod_flags &= ~(FMD_MOD_STSUB | FMD_MOD_STPUB); 13780Sstevel@tonic-gate 13791193Smws (void) pthread_cond_broadcast(&mp->mod_cv); 13800Sstevel@tonic-gate (void) pthread_mutex_unlock(&mp->mod_lock); 13810Sstevel@tonic-gate 13820Sstevel@tonic-gate return (err); 13830Sstevel@tonic-gate } 13844198Seschrock 13854198Seschrock struct topo_hdl * 13864198Seschrock fmd_module_topo_hold(fmd_module_t *mp) 13874198Seschrock { 13884198Seschrock fmd_modtopo_t *mtp; 13894198Seschrock 13904198Seschrock ASSERT(fmd_module_locked(mp)); 13914198Seschrock 13924198Seschrock mtp = fmd_zalloc(sizeof (fmd_modtopo_t), FMD_SLEEP); 13934198Seschrock mtp->mt_topo = mp->mod_topo_current; 13944198Seschrock fmd_topo_addref(mtp->mt_topo); 13954198Seschrock fmd_list_prepend(&mp->mod_topolist, mtp); 13964198Seschrock 13974198Seschrock return (mtp->mt_topo->ft_hdl); 13984198Seschrock } 13994198Seschrock 14004198Seschrock int 14014198Seschrock fmd_module_topo_rele(fmd_module_t *mp, struct topo_hdl *hdl) 14024198Seschrock { 14034198Seschrock fmd_modtopo_t *mtp; 14044198Seschrock 14054198Seschrock ASSERT(fmd_module_locked(mp)); 14064198Seschrock 14074198Seschrock for (mtp = fmd_list_next(&mp->mod_topolist); mtp != NULL; 14084198Seschrock mtp = fmd_list_next(mtp)) { 14094198Seschrock if (mtp->mt_topo->ft_hdl == hdl) 14104198Seschrock break; 14114198Seschrock } 14124198Seschrock 14134198Seschrock if (mtp == NULL) 14144198Seschrock return (-1); 14154198Seschrock 14164198Seschrock fmd_list_delete(&mp->mod_topolist, mtp); 14174198Seschrock fmd_topo_rele(mtp->mt_topo); 14184198Seschrock fmd_free(mtp, sizeof (fmd_modtopo_t)); 14194198Seschrock return (0); 14204198Seschrock } 1421