4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or https://opensource.org/licenses/CDDL-1.0.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2016, Intel Corporation.
28 * This file implements the minimal FMD module API required to support the
29 * fault logic modules in ZED. This support includes module registration,
30 * memory allocation, module property accessors, basic case management,
31 * one-shot timers and SERD engines.
33 * In the ZED runtime, the modules are called from a single thread so no
34 * locking is required in this emulated FMD environment.
37 #include <sys/types.h>
38 #include <sys/fm/protocol.h>
39 #include <uuid/uuid.h>
47 #include "zfs_agents.h"
48 #include "../zed_log.h"
50 typedef struct fmd_modstat
{
51 fmd_stat_t ms_accepted
; /* total events accepted by module */
52 fmd_stat_t ms_caseopen
; /* cases currently open */
53 fmd_stat_t ms_casesolved
; /* total cases solved by module */
54 fmd_stat_t ms_caseclosed
; /* total cases closed by module */
57 typedef struct fmd_module
{
58 const char *mod_name
; /* basename of module (ro) */
59 const fmd_hdl_info_t
*mod_info
; /* module info registered with handle */
60 void *mod_spec
; /* fmd_hdl_get/setspecific data value */
61 fmd_stat_t
*mod_ustat
; /* module specific custom stats */
62 uint_t mod_ustat_cnt
; /* count of ustat stats */
63 fmd_modstat_t mod_stats
; /* fmd built-in per-module statistics */
64 fmd_serd_hash_t mod_serds
; /* hash of serd engs owned by module */
65 char *mod_vers
; /* a copy of module version string */
69 * ZED has two FMD hardwired module instances
71 fmd_module_t zfs_retire_module
;
72 fmd_module_t zfs_diagnosis_module
;
75 * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
80 _umem_debug_init(void)
82 return ("default,verbose"); /* $UMEM_DEBUG setting */
86 _umem_logging_init(void)
88 return ("fail,contents"); /* $UMEM_LOGGING setting */
93 * Register a module with fmd and finish module initialization.
94 * Returns an integer indicating whether it succeeded (zero) or
98 fmd_hdl_register(fmd_hdl_t
*hdl
, int version
, const fmd_hdl_info_t
*mip
)
101 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
104 mp
->mod_name
= mip
->fmdi_desc
+ 4; /* drop 'ZFS ' prefix */
107 /* bare minimum module stats */
108 (void) strcpy(mp
->mod_stats
.ms_accepted
.fmds_name
, "fmd.accepted");
109 (void) strcpy(mp
->mod_stats
.ms_caseopen
.fmds_name
, "fmd.caseopen");
110 (void) strcpy(mp
->mod_stats
.ms_casesolved
.fmds_name
, "fmd.casesolved");
111 (void) strcpy(mp
->mod_stats
.ms_caseclosed
.fmds_name
, "fmd.caseclosed");
113 fmd_serd_hash_create(&mp
->mod_serds
);
115 fmd_hdl_debug(hdl
, "register module");
121 fmd_hdl_unregister(fmd_hdl_t
*hdl
)
123 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
124 fmd_modstat_t
*msp
= &mp
->mod_stats
;
125 const fmd_hdl_ops_t
*ops
= mp
->mod_info
->fmdi_ops
;
127 /* dump generic module stats */
128 fmd_hdl_debug(hdl
, "%s: %llu", msp
->ms_accepted
.fmds_name
,
129 msp
->ms_accepted
.fmds_value
.ui64
);
130 if (ops
->fmdo_close
!= NULL
) {
131 fmd_hdl_debug(hdl
, "%s: %llu", msp
->ms_caseopen
.fmds_name
,
132 msp
->ms_caseopen
.fmds_value
.ui64
);
133 fmd_hdl_debug(hdl
, "%s: %llu", msp
->ms_casesolved
.fmds_name
,
134 msp
->ms_casesolved
.fmds_value
.ui64
);
135 fmd_hdl_debug(hdl
, "%s: %llu", msp
->ms_caseclosed
.fmds_name
,
136 msp
->ms_caseclosed
.fmds_value
.ui64
);
139 /* dump module specific stats */
140 if (mp
->mod_ustat
!= NULL
) {
143 for (i
= 0; i
< mp
->mod_ustat_cnt
; i
++) {
144 fmd_hdl_debug(hdl
, "%s: %llu",
145 mp
->mod_ustat
[i
].fmds_name
,
146 mp
->mod_ustat
[i
].fmds_value
.ui64
);
150 fmd_serd_hash_destroy(&mp
->mod_serds
);
152 fmd_hdl_debug(hdl
, "unregister module");
156 * fmd_hdl_setspecific() is used to associate a data pointer with
157 * the specified handle for the duration of the module's lifetime.
158 * This pointer can be retrieved using fmd_hdl_getspecific().
161 fmd_hdl_setspecific(fmd_hdl_t
*hdl
, void *spec
)
163 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
169 * Return the module-specific data pointer previously associated
170 * with the handle using fmd_hdl_setspecific().
173 fmd_hdl_getspecific(fmd_hdl_t
*hdl
)
175 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
177 return (mp
->mod_spec
);
181 fmd_hdl_alloc(fmd_hdl_t
*hdl
, size_t size
, int flags
)
184 return (umem_alloc(size
, flags
));
188 fmd_hdl_zalloc(fmd_hdl_t
*hdl
, size_t size
, int flags
)
191 return (umem_zalloc(size
, flags
));
195 fmd_hdl_free(fmd_hdl_t
*hdl
, void *data
, size_t size
)
198 umem_free(data
, size
);
202 * Record a module debug message using the specified format.
205 fmd_hdl_debug(fmd_hdl_t
*hdl
, const char *format
, ...)
209 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
211 va_start(vargs
, format
);
212 (void) vsnprintf(message
, sizeof (message
), format
, vargs
);
215 /* prefix message with module name */
216 zed_log_msg(LOG_INFO
, "%s: %s", mp
->mod_name
, message
);
219 /* Property Retrieval */
222 fmd_prop_get_int32(fmd_hdl_t
*hdl
, const char *name
)
227 * These can be looked up in mp->modinfo->fmdi_props
228 * For now we just hard code for phase 2. In the
229 * future, there can be a ZED based override.
231 if (strcmp(name
, "spare_on_remove") == 0)
234 if (strcmp(name
, "io_N") == 0 || strcmp(name
, "checksum_N") == 0)
235 return (10); /* N = 10 events */
241 fmd_prop_get_int64(fmd_hdl_t
*hdl
, const char *name
)
246 * These can be looked up in mp->modinfo->fmdi_props
247 * For now we just hard code for phase 2. In the
248 * future, there can be a ZED based override.
250 if (strcmp(name
, "remove_timeout") == 0)
251 return (15ULL * 1000ULL * 1000ULL * 1000ULL); /* 15 sec */
253 if (strcmp(name
, "io_T") == 0 || strcmp(name
, "checksum_T") == 0)
254 return (1000ULL * 1000ULL * 1000ULL * 600ULL); /* 10 min */
262 fmd_stat_create(fmd_hdl_t
*hdl
, uint_t flags
, uint_t nstats
, fmd_stat_t
*statv
)
264 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
266 if (flags
== FMD_STAT_NOALLOC
) {
267 mp
->mod_ustat
= statv
;
268 mp
->mod_ustat_cnt
= nstats
;
274 /* Case Management */
277 fmd_case_open(fmd_hdl_t
*hdl
, void *data
)
279 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
284 cp
= fmd_hdl_zalloc(hdl
, sizeof (fmd_case_t
), FMD_SLEEP
);
286 cp
->ci_state
= FMD_CASE_UNSOLVED
;
287 cp
->ci_flags
= FMD_CF_DIRTY
;
289 cp
->ci_bufptr
= NULL
;
293 uuid_unparse(uuid
, cp
->ci_uuid
);
295 fmd_hdl_debug(hdl
, "case opened (%s)", cp
->ci_uuid
);
296 mp
->mod_stats
.ms_caseopen
.fmds_value
.ui64
++;
302 fmd_case_solve(fmd_hdl_t
*hdl
, fmd_case_t
*cp
)
304 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
307 * For ZED, the event was already sent from fmd_case_add_suspect()
310 if (cp
->ci_state
>= FMD_CASE_SOLVED
)
311 fmd_hdl_debug(hdl
, "case is already solved or closed");
313 cp
->ci_state
= FMD_CASE_SOLVED
;
315 fmd_hdl_debug(hdl
, "case solved (%s)", cp
->ci_uuid
);
316 mp
->mod_stats
.ms_casesolved
.fmds_value
.ui64
++;
320 fmd_case_close(fmd_hdl_t
*hdl
, fmd_case_t
*cp
)
322 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
323 const fmd_hdl_ops_t
*ops
= mp
->mod_info
->fmdi_ops
;
325 fmd_hdl_debug(hdl
, "case closed (%s)", cp
->ci_uuid
);
327 if (ops
->fmdo_close
!= NULL
)
328 ops
->fmdo_close(hdl
, cp
);
330 mp
->mod_stats
.ms_caseopen
.fmds_value
.ui64
--;
331 mp
->mod_stats
.ms_caseclosed
.fmds_value
.ui64
++;
333 if (cp
->ci_bufptr
!= NULL
&& cp
->ci_bufsiz
> 0)
334 fmd_hdl_free(hdl
, cp
->ci_bufptr
, cp
->ci_bufsiz
);
336 fmd_hdl_free(hdl
, cp
, sizeof (fmd_case_t
));
340 fmd_case_uuresolved(fmd_hdl_t
*hdl
, const char *uuid
)
342 fmd_hdl_debug(hdl
, "case resolved by uuid (%s)", uuid
);
346 fmd_case_solved(fmd_hdl_t
*hdl
, fmd_case_t
*cp
)
349 return (cp
->ci_state
>= FMD_CASE_SOLVED
);
353 fmd_case_add_ereport(fmd_hdl_t
*hdl
, fmd_case_t
*cp
, fmd_event_t
*ep
)
355 (void) hdl
, (void) cp
, (void) ep
;
359 zed_log_fault(nvlist_t
*nvl
, const char *uuid
, const char *code
)
366 zed_log_msg(LOG_INFO
, "\nzed_fault_event:");
369 zed_log_msg(LOG_INFO
, "\t%s: %s", FM_SUSPECT_UUID
, uuid
);
370 if (nvlist_lookup_string(nvl
, FM_CLASS
, &strval
) == 0)
371 zed_log_msg(LOG_INFO
, "\t%s: %s", FM_CLASS
, strval
);
373 zed_log_msg(LOG_INFO
, "\t%s: %s", FM_SUSPECT_DIAG_CODE
, code
);
374 if (nvlist_lookup_uint8(nvl
, FM_FAULT_CERTAINTY
, &byte
) == 0)
375 zed_log_msg(LOG_INFO
, "\t%s: %hhu", FM_FAULT_CERTAINTY
, byte
);
376 if (nvlist_lookup_nvlist(nvl
, FM_FAULT_RESOURCE
, &rsrc
) == 0) {
377 if (nvlist_lookup_string(rsrc
, FM_FMRI_SCHEME
, &strval
) == 0)
378 zed_log_msg(LOG_INFO
, "\t%s: %s", FM_FMRI_SCHEME
,
380 if (nvlist_lookup_uint64(rsrc
, FM_FMRI_ZFS_POOL
, &guid
) == 0)
381 zed_log_msg(LOG_INFO
, "\t%s: %llu", FM_FMRI_ZFS_POOL
,
383 if (nvlist_lookup_uint64(rsrc
, FM_FMRI_ZFS_VDEV
, &guid
) == 0)
384 zed_log_msg(LOG_INFO
, "\t%s: %llu \n", FM_FMRI_ZFS_VDEV
,
390 fmd_fault_mkcode(nvlist_t
*fault
)
393 const char *code
= "-";
396 * Note: message codes come from: openzfs/usr/src/cmd/fm/dicts/ZFS.po
398 if (nvlist_lookup_string(fault
, FM_CLASS
, &class) == 0) {
399 if (strcmp(class, "fault.fs.zfs.vdev.io") == 0)
400 code
= "ZFS-8000-FD";
401 else if (strcmp(class, "fault.fs.zfs.vdev.checksum") == 0)
402 code
= "ZFS-8000-GH";
403 else if (strcmp(class, "fault.fs.zfs.io_failure_wait") == 0)
404 code
= "ZFS-8000-HC";
405 else if (strcmp(class, "fault.fs.zfs.io_failure_continue") == 0)
406 code
= "ZFS-8000-JQ";
407 else if (strcmp(class, "fault.fs.zfs.log_replay") == 0)
408 code
= "ZFS-8000-K4";
409 else if (strcmp(class, "fault.fs.zfs.pool") == 0)
410 code
= "ZFS-8000-CS";
411 else if (strcmp(class, "fault.fs.zfs.device") == 0)
412 code
= "ZFS-8000-D3";
419 fmd_case_add_suspect(fmd_hdl_t
*hdl
, fmd_case_t
*cp
, nvlist_t
*fault
)
422 const char *code
= fmd_fault_mkcode(fault
);
427 * payload derived from fmd_protocol_list()
430 (void) gettimeofday(&cp
->ci_tv
, NULL
);
431 tod
[0] = cp
->ci_tv
.tv_sec
;
432 tod
[1] = cp
->ci_tv
.tv_usec
;
434 nvl
= fmd_nvl_alloc(hdl
, FMD_SLEEP
);
436 err
|= nvlist_add_uint8(nvl
, FM_VERSION
, FM_SUSPECT_VERSION
);
437 err
|= nvlist_add_string(nvl
, FM_CLASS
, FM_LIST_SUSPECT_CLASS
);
438 err
|= nvlist_add_string(nvl
, FM_SUSPECT_UUID
, cp
->ci_uuid
);
439 err
|= nvlist_add_string(nvl
, FM_SUSPECT_DIAG_CODE
, code
);
440 err
|= nvlist_add_int64_array(nvl
, FM_SUSPECT_DIAG_TIME
, tod
, 2);
441 err
|= nvlist_add_uint32(nvl
, FM_SUSPECT_FAULT_SZ
, 1);
442 err
|= nvlist_add_nvlist_array(nvl
, FM_SUSPECT_FAULT_LIST
,
443 (const nvlist_t
**)&fault
, 1);
446 zed_log_die("failed to populate nvlist");
448 zed_log_fault(fault
, cp
->ci_uuid
, code
);
449 zfs_agent_post_event(FM_LIST_SUSPECT_CLASS
, NULL
, nvl
);
456 fmd_case_setspecific(fmd_hdl_t
*hdl
, fmd_case_t
*cp
, void *data
)
463 fmd_case_getspecific(fmd_hdl_t
*hdl
, fmd_case_t
*cp
)
466 return (cp
->ci_data
);
470 fmd_buf_create(fmd_hdl_t
*hdl
, fmd_case_t
*cp
, const char *name
, size_t size
)
472 assert(strcmp(name
, "data") == 0), (void) name
;
473 assert(cp
->ci_bufptr
== NULL
);
474 assert(size
< (1024 * 1024));
476 cp
->ci_bufptr
= fmd_hdl_alloc(hdl
, size
, FMD_SLEEP
);
477 cp
->ci_bufsiz
= size
;
481 fmd_buf_read(fmd_hdl_t
*hdl
, fmd_case_t
*cp
,
482 const char *name
, void *buf
, size_t size
)
485 assert(strcmp(name
, "data") == 0), (void) name
;
486 assert(cp
->ci_bufptr
!= NULL
);
487 assert(size
<= cp
->ci_bufsiz
);
489 memcpy(buf
, cp
->ci_bufptr
, size
);
493 fmd_buf_write(fmd_hdl_t
*hdl
, fmd_case_t
*cp
,
494 const char *name
, const void *buf
, size_t size
)
497 assert(strcmp(name
, "data") == 0), (void) name
;
498 assert(cp
->ci_bufptr
!= NULL
);
499 assert(cp
->ci_bufsiz
>= size
);
501 memcpy(cp
->ci_bufptr
, buf
, size
);
507 fmd_serd_create(fmd_hdl_t
*hdl
, const char *name
, uint_t n
, hrtime_t t
)
509 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
511 if (fmd_serd_eng_lookup(&mp
->mod_serds
, name
) != NULL
) {
512 zed_log_msg(LOG_ERR
, "failed to create SERD engine '%s': "
513 " name already exists", name
);
517 (void) fmd_serd_eng_insert(&mp
->mod_serds
, name
, n
, t
);
521 fmd_serd_destroy(fmd_hdl_t
*hdl
, const char *name
)
523 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
525 fmd_serd_eng_delete(&mp
->mod_serds
, name
);
527 fmd_hdl_debug(hdl
, "serd_destroy %s", name
);
531 fmd_serd_exists(fmd_hdl_t
*hdl
, const char *name
)
533 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
535 return (fmd_serd_eng_lookup(&mp
->mod_serds
, name
) != NULL
);
539 fmd_serd_reset(fmd_hdl_t
*hdl
, const char *name
)
541 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
544 if ((sgp
= fmd_serd_eng_lookup(&mp
->mod_serds
, name
)) == NULL
) {
545 zed_log_msg(LOG_ERR
, "serd engine '%s' does not exist", name
);
549 fmd_serd_eng_reset(sgp
);
551 fmd_hdl_debug(hdl
, "serd_reset %s", name
);
555 fmd_serd_record(fmd_hdl_t
*hdl
, const char *name
, fmd_event_t
*ep
)
557 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
561 if ((sgp
= fmd_serd_eng_lookup(&mp
->mod_serds
, name
)) == NULL
) {
562 zed_log_msg(LOG_ERR
, "failed to add record to SERD engine '%s'",
566 err
= fmd_serd_eng_record(sgp
, ep
->ev_hrt
);
574 _timer_notify(union sigval sv
)
576 fmd_timer_t
*ftp
= sv
.sival_ptr
;
577 fmd_hdl_t
*hdl
= ftp
->ft_hdl
;
578 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
579 const fmd_hdl_ops_t
*ops
= mp
->mod_info
->fmdi_ops
;
580 struct itimerspec its
;
582 fmd_hdl_debug(hdl
, "timer fired (%p)", ftp
->ft_tid
);
584 /* disarm the timer */
585 memset(&its
, 0, sizeof (struct itimerspec
));
586 timer_settime(ftp
->ft_tid
, 0, &its
, NULL
);
588 /* Note that the fmdo_timeout can remove this timer */
589 if (ops
->fmdo_timeout
!= NULL
)
590 ops
->fmdo_timeout(hdl
, ftp
, ftp
->ft_arg
);
594 * Install a new timer which will fire at least delta nanoseconds after the
595 * current time. After the timeout has expired, the module's fmdo_timeout
596 * entry point is called.
599 fmd_timer_install(fmd_hdl_t
*hdl
, void *arg
, fmd_event_t
*ep
, hrtime_t delta
)
603 struct itimerspec its
;
606 ftp
= fmd_hdl_alloc(hdl
, sizeof (fmd_timer_t
), FMD_SLEEP
);
610 its
.it_value
.tv_sec
= delta
/ 1000000000;
611 its
.it_value
.tv_nsec
= delta
% 1000000000;
612 its
.it_interval
.tv_sec
= its
.it_value
.tv_sec
;
613 its
.it_interval
.tv_nsec
= its
.it_value
.tv_nsec
;
615 sev
.sigev_notify
= SIGEV_THREAD
;
616 sev
.sigev_notify_function
= _timer_notify
;
617 sev
.sigev_notify_attributes
= NULL
;
618 sev
.sigev_value
.sival_ptr
= ftp
;
621 timer_create(CLOCK_REALTIME
, &sev
, &ftp
->ft_tid
);
622 timer_settime(ftp
->ft_tid
, 0, &its
, NULL
);
624 fmd_hdl_debug(hdl
, "installing timer for %d secs (%p)",
625 (int)its
.it_value
.tv_sec
, ftp
->ft_tid
);
631 fmd_timer_remove(fmd_hdl_t
*hdl
, fmd_timer_t
*ftp
)
633 fmd_hdl_debug(hdl
, "removing timer (%p)", ftp
->ft_tid
);
635 timer_delete(ftp
->ft_tid
);
637 fmd_hdl_free(hdl
, ftp
, sizeof (fmd_timer_t
));
640 /* Name-Value Pair Lists */
643 fmd_nvl_create_fault(fmd_hdl_t
*hdl
, const char *class, uint8_t certainty
,
644 nvlist_t
*asru
, nvlist_t
*fru
, nvlist_t
*resource
)
650 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0)
651 zed_log_die("failed to xalloc fault nvlist");
653 err
|= nvlist_add_uint8(nvl
, FM_VERSION
, FM_FAULT_VERSION
);
654 err
|= nvlist_add_string(nvl
, FM_CLASS
, class);
655 err
|= nvlist_add_uint8(nvl
, FM_FAULT_CERTAINTY
, certainty
);
658 err
|= nvlist_add_nvlist(nvl
, FM_FAULT_ASRU
, asru
);
660 err
|= nvlist_add_nvlist(nvl
, FM_FAULT_FRU
, fru
);
661 if (resource
!= NULL
)
662 err
|= nvlist_add_nvlist(nvl
, FM_FAULT_RESOURCE
, resource
);
665 zed_log_die("failed to populate nvlist: %s\n", strerror(err
));
671 * sourced from fmd_string.c
674 fmd_strmatch(const char *s
, const char *p
)
682 s
= ""; /* treat NULL string as the empty string */
685 if ((c
= *p
++) == '\0')
690 p
++; /* consecutive *'s can be collapsed */
696 if (fmd_strmatch(s
++, p
) != 0)
708 fmd_nvl_class_match(fmd_hdl_t
*hdl
, nvlist_t
*nvl
, const char *pattern
)
713 return (nvl
!= NULL
&&
714 nvlist_lookup_string(nvl
, FM_CLASS
, &class) == 0 &&
715 fmd_strmatch(class, pattern
));
719 fmd_nvl_alloc(fmd_hdl_t
*hdl
, int flags
)
721 (void) hdl
, (void) flags
;
722 nvlist_t
*nvl
= NULL
;
724 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0)
732 * ZED Agent specific APIs
736 fmd_module_hdl(const char *name
)
738 if (strcmp(name
, "zfs-retire") == 0)
739 return ((fmd_hdl_t
*)&zfs_retire_module
);
740 if (strcmp(name
, "zfs-diagnosis") == 0)
741 return ((fmd_hdl_t
*)&zfs_diagnosis_module
);
747 fmd_module_initialized(fmd_hdl_t
*hdl
)
749 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
751 return (mp
->mod_info
!= NULL
);
755 * fmd_module_recv is called for each event that is received by
756 * the fault manager that has a class that matches one of the
757 * module's subscriptions.
760 fmd_module_recv(fmd_hdl_t
*hdl
, nvlist_t
*nvl
, const char *class)
762 fmd_module_t
*mp
= (fmd_module_t
*)hdl
;
763 const fmd_hdl_ops_t
*ops
= mp
->mod_info
->fmdi_ops
;
764 fmd_event_t faux_event
= {0};
769 * Will need to normalized this if we persistently store the case data
771 if (nvlist_lookup_int64_array(nvl
, FM_EREPORT_TIME
, &tv
, &n
) == 0)
772 faux_event
.ev_hrt
= tv
[0] * NANOSEC
+ tv
[1];
774 faux_event
.ev_hrt
= 0;
776 ops
->fmdo_recv(hdl
, &faux_event
, nvl
, class);
778 mp
->mod_stats
.ms_accepted
.fmds_value
.ui64
++;
780 /* TBD - should we initiate fm_module_gc() periodically? */