Path: blob/main/sys/contrib/openzfs/cmd/zed/agents/fmd_api.c
48529 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.23*24* Copyright (c) 2016, Intel Corporation.25* Copyright (c) 2023, Klara Inc.26*/2728/*29* This file implements the minimal FMD module API required to support the30* fault logic modules in ZED. This support includes module registration,31* memory allocation, module property accessors, basic case management,32* one-shot timers and SERD engines.33*34* In the ZED runtime, the modules are called from a single thread so no35* locking is required in this emulated FMD environment.36*/3738#include <sys/types.h>39#include <sys/fm/protocol.h>40#include <uuid/uuid.h>41#include <signal.h>42#include <string.h>43#include <time.h>4445#include "fmd_api.h"46#include "fmd_serd.h"4748#include "zfs_agents.h"49#include "../zed_log.h"5051typedef struct fmd_modstat {52fmd_stat_t ms_accepted; /* total events accepted by module */53fmd_stat_t ms_caseopen; /* cases currently open */54fmd_stat_t ms_casesolved; /* total cases solved by module */55fmd_stat_t ms_caseclosed; /* total cases closed by module */56} fmd_modstat_t;5758typedef struct fmd_module {59const char *mod_name; /* basename of module (ro) */60const fmd_hdl_info_t *mod_info; /* module info registered with handle */61void *mod_spec; /* fmd_hdl_get/setspecific data value */62fmd_stat_t *mod_ustat; /* module specific custom stats */63uint_t mod_ustat_cnt; /* count of ustat stats */64fmd_modstat_t mod_stats; /* fmd built-in per-module statistics */65fmd_serd_hash_t mod_serds; /* hash of serd engs owned by module */66char *mod_vers; /* a copy of module version string */67} fmd_module_t;6869/*70* ZED has two FMD hardwired module instances71*/72fmd_module_t zfs_retire_module;73fmd_module_t zfs_diagnosis_module;7475/*76* Enable a reasonable set of defaults for libumem debugging on DEBUG builds.77*/7879#ifdef DEBUG80const char *81_umem_debug_init(void)82{83return ("default,verbose"); /* $UMEM_DEBUG setting */84}8586const char *87_umem_logging_init(void)88{89return ("fail,contents"); /* $UMEM_LOGGING setting */90}91#endif9293/*94* Register a module with fmd and finish module initialization.95* Returns an integer indicating whether it succeeded (zero) or96* failed (non-zero).97*/98int99fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip)100{101(void) version;102fmd_module_t *mp = (fmd_module_t *)hdl;103104mp->mod_info = mip;105mp->mod_name = mip->fmdi_desc + 4; /* drop 'ZFS ' prefix */106mp->mod_spec = NULL;107108/* bare minimum module stats */109(void) strcpy(mp->mod_stats.ms_accepted.fmds_name, "fmd.accepted");110(void) strcpy(mp->mod_stats.ms_caseopen.fmds_name, "fmd.caseopen");111(void) strcpy(mp->mod_stats.ms_casesolved.fmds_name, "fmd.casesolved");112(void) strcpy(mp->mod_stats.ms_caseclosed.fmds_name, "fmd.caseclosed");113114fmd_serd_hash_create(&mp->mod_serds);115116fmd_hdl_debug(hdl, "register module");117118return (0);119}120121void122fmd_hdl_unregister(fmd_hdl_t *hdl)123{124fmd_module_t *mp = (fmd_module_t *)hdl;125fmd_modstat_t *msp = &mp->mod_stats;126const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;127128/* dump generic module stats */129fmd_hdl_debug(hdl, "%s: %llu", msp->ms_accepted.fmds_name,130msp->ms_accepted.fmds_value.ui64);131if (ops->fmdo_close != NULL) {132fmd_hdl_debug(hdl, "%s: %llu", msp->ms_caseopen.fmds_name,133msp->ms_caseopen.fmds_value.ui64);134fmd_hdl_debug(hdl, "%s: %llu", msp->ms_casesolved.fmds_name,135msp->ms_casesolved.fmds_value.ui64);136fmd_hdl_debug(hdl, "%s: %llu", msp->ms_caseclosed.fmds_name,137msp->ms_caseclosed.fmds_value.ui64);138}139140/* dump module specific stats */141if (mp->mod_ustat != NULL) {142int i;143144for (i = 0; i < mp->mod_ustat_cnt; i++) {145fmd_hdl_debug(hdl, "%s: %llu",146mp->mod_ustat[i].fmds_name,147mp->mod_ustat[i].fmds_value.ui64);148}149}150151fmd_serd_hash_destroy(&mp->mod_serds);152153fmd_hdl_debug(hdl, "unregister module");154}155156/*157* fmd_hdl_setspecific() is used to associate a data pointer with158* the specified handle for the duration of the module's lifetime.159* This pointer can be retrieved using fmd_hdl_getspecific().160*/161void162fmd_hdl_setspecific(fmd_hdl_t *hdl, void *spec)163{164fmd_module_t *mp = (fmd_module_t *)hdl;165166mp->mod_spec = spec;167}168169/*170* Return the module-specific data pointer previously associated171* with the handle using fmd_hdl_setspecific().172*/173void *174fmd_hdl_getspecific(fmd_hdl_t *hdl)175{176fmd_module_t *mp = (fmd_module_t *)hdl;177178return (mp->mod_spec);179}180181void *182fmd_hdl_alloc(fmd_hdl_t *hdl, size_t size, int flags)183{184(void) hdl;185return (umem_alloc(size, flags));186}187188void *189fmd_hdl_zalloc(fmd_hdl_t *hdl, size_t size, int flags)190{191(void) hdl;192return (umem_zalloc(size, flags));193}194195void196fmd_hdl_free(fmd_hdl_t *hdl, void *data, size_t size)197{198(void) hdl;199umem_free(data, size);200}201202/*203* Record a module debug message using the specified format.204*/205void206fmd_hdl_debug(fmd_hdl_t *hdl, const char *format, ...)207{208char message[256];209va_list vargs;210fmd_module_t *mp = (fmd_module_t *)hdl;211212va_start(vargs, format);213(void) vsnprintf(message, sizeof (message), format, vargs);214va_end(vargs);215216/* prefix message with module name */217zed_log_msg(LOG_INFO, "%s: %s", mp->mod_name, message);218}219220/* Property Retrieval */221222int32_t223fmd_prop_get_int32(fmd_hdl_t *hdl, const char *name)224{225(void) hdl;226227/*228* These can be looked up in mp->modinfo->fmdi_props229* For now we just hard code for phase 2. In the230* future, there can be a ZED based override.231*/232if (strcmp(name, "spare_on_remove") == 0)233return (1);234235return (0);236}237238/* FMD Statistics */239240fmd_stat_t *241fmd_stat_create(fmd_hdl_t *hdl, uint_t flags, uint_t nstats, fmd_stat_t *statv)242{243fmd_module_t *mp = (fmd_module_t *)hdl;244245if (flags == FMD_STAT_NOALLOC) {246mp->mod_ustat = statv;247mp->mod_ustat_cnt = nstats;248}249250return (statv);251}252253/* Case Management */254255fmd_case_t *256fmd_case_open(fmd_hdl_t *hdl, void *data)257{258fmd_module_t *mp = (fmd_module_t *)hdl;259uuid_t uuid;260261fmd_case_t *cp;262263cp = fmd_hdl_zalloc(hdl, sizeof (fmd_case_t), FMD_SLEEP);264cp->ci_mod = hdl;265cp->ci_state = FMD_CASE_UNSOLVED;266cp->ci_flags = FMD_CF_DIRTY;267cp->ci_data = data;268cp->ci_bufptr = NULL;269cp->ci_bufsiz = 0;270271uuid_generate(uuid);272uuid_unparse(uuid, cp->ci_uuid);273274fmd_hdl_debug(hdl, "case opened (%s)", cp->ci_uuid);275mp->mod_stats.ms_caseopen.fmds_value.ui64++;276277return (cp);278}279280void281fmd_case_solve(fmd_hdl_t *hdl, fmd_case_t *cp)282{283fmd_module_t *mp = (fmd_module_t *)hdl;284285/*286* For ZED, the event was already sent from fmd_case_add_suspect()287*/288289if (cp->ci_state >= FMD_CASE_SOLVED)290fmd_hdl_debug(hdl, "case is already solved or closed");291292cp->ci_state = FMD_CASE_SOLVED;293294fmd_hdl_debug(hdl, "case solved (%s)", cp->ci_uuid);295mp->mod_stats.ms_casesolved.fmds_value.ui64++;296}297298void299fmd_case_close(fmd_hdl_t *hdl, fmd_case_t *cp)300{301fmd_module_t *mp = (fmd_module_t *)hdl;302const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;303304fmd_hdl_debug(hdl, "case closed (%s)", cp->ci_uuid);305306if (ops->fmdo_close != NULL)307ops->fmdo_close(hdl, cp);308309mp->mod_stats.ms_caseopen.fmds_value.ui64--;310mp->mod_stats.ms_caseclosed.fmds_value.ui64++;311312if (cp->ci_bufptr != NULL && cp->ci_bufsiz > 0)313fmd_hdl_free(hdl, cp->ci_bufptr, cp->ci_bufsiz);314315fmd_hdl_free(hdl, cp, sizeof (fmd_case_t));316}317318void319fmd_case_uuresolved(fmd_hdl_t *hdl, const char *uuid)320{321fmd_hdl_debug(hdl, "case resolved by uuid (%s)", uuid);322}323324boolean_t325fmd_case_solved(fmd_hdl_t *hdl, fmd_case_t *cp)326{327(void) hdl;328return (cp->ci_state >= FMD_CASE_SOLVED);329}330331void332fmd_case_add_ereport(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep)333{334(void) hdl, (void) cp, (void) ep;335}336337static void338zed_log_fault(nvlist_t *nvl, const char *uuid, const char *code)339{340nvlist_t *rsrc;341const char *strval;342uint64_t guid;343uint8_t byte;344345zed_log_msg(LOG_INFO, "\nzed_fault_event:");346347if (uuid != NULL)348zed_log_msg(LOG_INFO, "\t%s: %s", FM_SUSPECT_UUID, uuid);349if (nvlist_lookup_string(nvl, FM_CLASS, &strval) == 0)350zed_log_msg(LOG_INFO, "\t%s: %s", FM_CLASS, strval);351if (code != NULL)352zed_log_msg(LOG_INFO, "\t%s: %s", FM_SUSPECT_DIAG_CODE, code);353if (nvlist_lookup_uint8(nvl, FM_FAULT_CERTAINTY, &byte) == 0)354zed_log_msg(LOG_INFO, "\t%s: %hhu", FM_FAULT_CERTAINTY, byte);355if (nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE, &rsrc) == 0) {356if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &strval) == 0)357zed_log_msg(LOG_INFO, "\t%s: %s", FM_FMRI_SCHEME,358strval);359if (nvlist_lookup_uint64(rsrc, FM_FMRI_ZFS_POOL, &guid) == 0)360zed_log_msg(LOG_INFO, "\t%s: %llu", FM_FMRI_ZFS_POOL,361guid);362if (nvlist_lookup_uint64(rsrc, FM_FMRI_ZFS_VDEV, &guid) == 0)363zed_log_msg(LOG_INFO, "\t%s: %llu \n", FM_FMRI_ZFS_VDEV,364guid);365}366}367368static const char *369fmd_fault_mkcode(nvlist_t *fault)370{371const char *class;372const char *code = "-";373374/*375* Note: message codes come from: openzfs/usr/src/cmd/fm/dicts/ZFS.po376*/377if (nvlist_lookup_string(fault, FM_CLASS, &class) == 0) {378if (strcmp(class, "fault.fs.zfs.vdev.io") == 0)379code = "ZFS-8000-FD";380else if (strcmp(class, "fault.fs.zfs.vdev.checksum") == 0)381code = "ZFS-8000-GH";382else if (strcmp(class, "fault.fs.zfs.io_failure_wait") == 0)383code = "ZFS-8000-HC";384else if (strcmp(class, "fault.fs.zfs.io_failure_continue") == 0)385code = "ZFS-8000-JQ";386else if (strcmp(class, "fault.fs.zfs.log_replay") == 0)387code = "ZFS-8000-K4";388else if (strcmp(class, "fault.fs.zfs.pool") == 0)389code = "ZFS-8000-CS";390else if (strcmp(class, "fault.fs.zfs.device") == 0)391code = "ZFS-8000-D3";392393}394return (code);395}396397void398fmd_case_add_suspect(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *fault)399{400nvlist_t *nvl;401const char *code = fmd_fault_mkcode(fault);402int64_t tod[2];403int err = 0;404405/*406* payload derived from fmd_protocol_list()407*/408409(void) gettimeofday(&cp->ci_tv, NULL);410tod[0] = cp->ci_tv.tv_sec;411tod[1] = cp->ci_tv.tv_usec;412413nvl = fmd_nvl_alloc(hdl, FMD_SLEEP);414415err |= nvlist_add_uint8(nvl, FM_VERSION, FM_SUSPECT_VERSION);416err |= nvlist_add_string(nvl, FM_CLASS, FM_LIST_SUSPECT_CLASS);417err |= nvlist_add_string(nvl, FM_SUSPECT_UUID, cp->ci_uuid);418err |= nvlist_add_string(nvl, FM_SUSPECT_DIAG_CODE, code);419err |= nvlist_add_int64_array(nvl, FM_SUSPECT_DIAG_TIME, tod, 2);420err |= nvlist_add_uint32(nvl, FM_SUSPECT_FAULT_SZ, 1);421err |= nvlist_add_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST,422(const nvlist_t **)&fault, 1);423424if (err)425zed_log_die("failed to populate nvlist");426427zed_log_fault(fault, cp->ci_uuid, code);428zfs_agent_post_event(FM_LIST_SUSPECT_CLASS, NULL, nvl);429430nvlist_free(nvl);431nvlist_free(fault);432}433434void435fmd_case_setspecific(fmd_hdl_t *hdl, fmd_case_t *cp, void *data)436{437(void) hdl;438cp->ci_data = data;439}440441void *442fmd_case_getspecific(fmd_hdl_t *hdl, fmd_case_t *cp)443{444(void) hdl;445return (cp->ci_data);446}447448void449fmd_buf_create(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, size_t size)450{451assert(strcmp(name, "data") == 0), (void) name;452assert(cp->ci_bufptr == NULL);453assert(size < (1024 * 1024));454455cp->ci_bufptr = fmd_hdl_alloc(hdl, size, FMD_SLEEP);456cp->ci_bufsiz = size;457}458459void460fmd_buf_read(fmd_hdl_t *hdl, fmd_case_t *cp,461const char *name, void *buf, size_t size)462{463(void) hdl;464assert(strcmp(name, "data") == 0), (void) name;465assert(cp->ci_bufptr != NULL);466assert(size <= cp->ci_bufsiz);467468memcpy(buf, cp->ci_bufptr, size);469}470471void472fmd_buf_write(fmd_hdl_t *hdl, fmd_case_t *cp,473const char *name, const void *buf, size_t size)474{475(void) hdl;476assert(strcmp(name, "data") == 0), (void) name;477assert(cp->ci_bufptr != NULL);478assert(cp->ci_bufsiz >= size);479480memcpy(cp->ci_bufptr, buf, size);481}482483/* SERD Engines */484485void486fmd_serd_create(fmd_hdl_t *hdl, const char *name, uint_t n, hrtime_t t)487{488fmd_module_t *mp = (fmd_module_t *)hdl;489490if (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL) {491zed_log_msg(LOG_ERR, "failed to create SERD engine '%s': "492" name already exists", name);493return;494}495496(void) fmd_serd_eng_insert(&mp->mod_serds, name, n, t);497}498499void500fmd_serd_destroy(fmd_hdl_t *hdl, const char *name)501{502fmd_module_t *mp = (fmd_module_t *)hdl;503504fmd_serd_eng_delete(&mp->mod_serds, name);505506fmd_hdl_debug(hdl, "serd_destroy %s", name);507}508509int510fmd_serd_exists(fmd_hdl_t *hdl, const char *name)511{512fmd_module_t *mp = (fmd_module_t *)hdl;513514return (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL);515}516517int518fmd_serd_active(fmd_hdl_t *hdl, const char *name)519{520fmd_module_t *mp = (fmd_module_t *)hdl;521fmd_serd_eng_t *sgp;522523if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {524zed_log_msg(LOG_ERR, "serd engine '%s' does not exist", name);525return (0);526}527return (fmd_serd_eng_fired(sgp) || !fmd_serd_eng_empty(sgp));528}529530void531fmd_serd_reset(fmd_hdl_t *hdl, const char *name)532{533fmd_module_t *mp = (fmd_module_t *)hdl;534fmd_serd_eng_t *sgp;535536if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {537zed_log_msg(LOG_ERR, "serd engine '%s' does not exist", name);538} else {539fmd_serd_eng_reset(sgp);540fmd_hdl_debug(hdl, "serd_reset %s", name);541}542}543544int545fmd_serd_record(fmd_hdl_t *hdl, const char *name, fmd_event_t *ep)546{547fmd_module_t *mp = (fmd_module_t *)hdl;548fmd_serd_eng_t *sgp;549550if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {551zed_log_msg(LOG_ERR, "failed to add record to SERD engine '%s'",552name);553return (0);554}555return (fmd_serd_eng_record(sgp, ep->ev_hrt));556}557558void559fmd_serd_gc(fmd_hdl_t *hdl)560{561fmd_module_t *mp = (fmd_module_t *)hdl;562563fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_gc, NULL);564}565566/* FMD Timers */567568static void569_timer_notify(union sigval sv)570{571fmd_timer_t *ftp = sv.sival_ptr;572fmd_hdl_t *hdl = ftp->ft_hdl;573fmd_module_t *mp = (fmd_module_t *)hdl;574const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;575struct itimerspec its;576577fmd_hdl_debug(hdl, "%s timer fired (%p)", mp->mod_name, ftp->ft_tid);578579/* disarm the timer */580memset(&its, 0, sizeof (struct itimerspec));581timer_settime(ftp->ft_tid, 0, &its, NULL);582583/* Note that the fmdo_timeout can remove this timer */584if (ops->fmdo_timeout != NULL)585ops->fmdo_timeout(hdl, ftp, ftp->ft_arg);586}587588/*589* Install a new timer which will fire at least delta nanoseconds after the590* current time. After the timeout has expired, the module's fmdo_timeout591* entry point is called.592*/593fmd_timer_t *594fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta)595{596(void) ep;597struct sigevent sev;598struct itimerspec its;599fmd_timer_t *ftp;600601ftp = fmd_hdl_alloc(hdl, sizeof (fmd_timer_t), FMD_SLEEP);602ftp->ft_arg = arg;603ftp->ft_hdl = hdl;604605its.it_value.tv_sec = delta / 1000000000;606its.it_value.tv_nsec = delta % 1000000000;607its.it_interval.tv_sec = its.it_value.tv_sec;608its.it_interval.tv_nsec = its.it_value.tv_nsec;609610sev.sigev_notify = SIGEV_THREAD;611sev.sigev_notify_function = _timer_notify;612sev.sigev_notify_attributes = NULL;613sev.sigev_value.sival_ptr = ftp;614sev.sigev_signo = 0;615616timer_create(CLOCK_REALTIME, &sev, &ftp->ft_tid);617timer_settime(ftp->ft_tid, 0, &its, NULL);618619fmd_hdl_debug(hdl, "installing timer for %d secs (%p)",620(int)its.it_value.tv_sec, ftp->ft_tid);621622return (ftp);623}624625void626fmd_timer_remove(fmd_hdl_t *hdl, fmd_timer_t *ftp)627{628fmd_hdl_debug(hdl, "removing timer (%p)", ftp->ft_tid);629630timer_delete(ftp->ft_tid);631632fmd_hdl_free(hdl, ftp, sizeof (fmd_timer_t));633}634635/* Name-Value Pair Lists */636637nvlist_t *638fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class, uint8_t certainty,639nvlist_t *asru, nvlist_t *fru, nvlist_t *resource)640{641(void) hdl;642nvlist_t *nvl;643int err = 0;644645if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)646zed_log_die("failed to xalloc fault nvlist");647648err |= nvlist_add_uint8(nvl, FM_VERSION, FM_FAULT_VERSION);649err |= nvlist_add_string(nvl, FM_CLASS, class);650err |= nvlist_add_uint8(nvl, FM_FAULT_CERTAINTY, certainty);651652if (asru != NULL)653err |= nvlist_add_nvlist(nvl, FM_FAULT_ASRU, asru);654if (fru != NULL)655err |= nvlist_add_nvlist(nvl, FM_FAULT_FRU, fru);656if (resource != NULL)657err |= nvlist_add_nvlist(nvl, FM_FAULT_RESOURCE, resource);658659if (err)660zed_log_die("failed to populate nvlist: %s\n", strerror(err));661662return (nvl);663}664665/*666* sourced from fmd_string.c667*/668static int669fmd_strmatch(const char *s, const char *p)670{671char c;672673if (p == NULL)674return (0);675676if (s == NULL)677s = ""; /* treat NULL string as the empty string */678679do {680if ((c = *p++) == '\0')681return (*s == '\0');682683if (c == '*') {684while (*p == '*')685p++; /* consecutive *'s can be collapsed */686687if (*p == '\0')688return (1);689690while (*s != '\0') {691if (fmd_strmatch(s++, p) != 0)692return (1);693}694695return (0);696}697} while (c == *s++);698699return (0);700}701702int703fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern)704{705(void) hdl;706const char *class;707708return (nvl != NULL &&709nvlist_lookup_string(nvl, FM_CLASS, &class) == 0 &&710fmd_strmatch(class, pattern));711}712713nvlist_t *714fmd_nvl_alloc(fmd_hdl_t *hdl, int flags)715{716(void) hdl, (void) flags;717nvlist_t *nvl = NULL;718719if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)720return (NULL);721722return (nvl);723}724725726/*727* ZED Agent specific APIs728*/729730fmd_hdl_t *731fmd_module_hdl(const char *name)732{733if (strcmp(name, "zfs-retire") == 0)734return ((fmd_hdl_t *)&zfs_retire_module);735if (strcmp(name, "zfs-diagnosis") == 0)736return ((fmd_hdl_t *)&zfs_diagnosis_module);737738return (NULL);739}740741boolean_t742fmd_module_initialized(fmd_hdl_t *hdl)743{744fmd_module_t *mp = (fmd_module_t *)hdl;745746return (mp->mod_info != NULL);747}748749/*750* fmd_module_recv is called for each event that is received by751* the fault manager that has a class that matches one of the752* module's subscriptions.753*/754void755fmd_module_recv(fmd_hdl_t *hdl, nvlist_t *nvl, const char *class)756{757fmd_module_t *mp = (fmd_module_t *)hdl;758const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;759fmd_event_t faux_event = {0};760int64_t *tv;761uint_t n;762763/*764* Will need to normalized this if we persistently store the case data765*/766if (nvlist_lookup_int64_array(nvl, FM_EREPORT_TIME, &tv, &n) == 0)767faux_event.ev_hrt = tv[0] * NANOSEC + tv[1];768else769faux_event.ev_hrt = 0;770771ops->fmdo_recv(hdl, &faux_event, nvl, class);772773mp->mod_stats.ms_accepted.fmds_value.ui64++;774775/* TBD - should we initiate fm_module_gc() periodically? */776}777778779