1eda14cbcSMatt Macy /*
2eda14cbcSMatt Macy  * CDDL HEADER START
3eda14cbcSMatt Macy  *
4eda14cbcSMatt Macy  * The contents of this file are subject to the terms of the
5eda14cbcSMatt Macy  * Common Development and Distribution License (the "License").
6eda14cbcSMatt Macy  * You may not use this file except in compliance with the License.
7eda14cbcSMatt Macy  *
8eda14cbcSMatt Macy  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9271171e0SMartin Matuska  * or https://opensource.org/licenses/CDDL-1.0.
10eda14cbcSMatt Macy  * See the License for the specific language governing permissions
11eda14cbcSMatt Macy  * and limitations under the License.
12eda14cbcSMatt Macy  *
13eda14cbcSMatt Macy  * When distributing Covered Code, include this CDDL HEADER in each
14eda14cbcSMatt Macy  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15eda14cbcSMatt Macy  * If applicable, add the following below this CDDL HEADER, with the
16eda14cbcSMatt Macy  * fields enclosed by brackets "[]" replaced with your own identifying
17eda14cbcSMatt Macy  * information: Portions Copyright [yyyy] [name of copyright owner]
18eda14cbcSMatt Macy  *
19eda14cbcSMatt Macy  * CDDL HEADER END
20eda14cbcSMatt Macy  */
21eda14cbcSMatt Macy /*
22eda14cbcSMatt Macy  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
23eda14cbcSMatt Macy  *
24eda14cbcSMatt Macy  * Copyright (c) 2016, Intel Corporation.
25*e2257b31SMartin Matuska  * Copyright (c) 2023, Klara Inc.
26eda14cbcSMatt Macy  */
27eda14cbcSMatt Macy 
28eda14cbcSMatt Macy /*
29eda14cbcSMatt Macy  * This file implements the minimal FMD module API required to support the
30eda14cbcSMatt Macy  * fault logic modules in ZED. This support includes module registration,
31eda14cbcSMatt Macy  * memory allocation, module property accessors, basic case management,
32eda14cbcSMatt Macy  * one-shot timers and SERD engines.
33eda14cbcSMatt Macy  *
34eda14cbcSMatt Macy  * In the ZED runtime, the modules are called from a single thread so no
35eda14cbcSMatt Macy  * locking is required in this emulated FMD environment.
36eda14cbcSMatt Macy  */
37eda14cbcSMatt Macy 
38eda14cbcSMatt Macy #include <sys/types.h>
39eda14cbcSMatt Macy #include <sys/fm/protocol.h>
40eda14cbcSMatt Macy #include <uuid/uuid.h>
41eda14cbcSMatt Macy #include <signal.h>
42da5137abSMartin Matuska #include <string.h>
43eda14cbcSMatt Macy #include <time.h>
44eda14cbcSMatt Macy 
45eda14cbcSMatt Macy #include "fmd_api.h"
46eda14cbcSMatt Macy #include "fmd_serd.h"
47eda14cbcSMatt Macy 
48eda14cbcSMatt Macy #include "zfs_agents.h"
49eda14cbcSMatt Macy #include "../zed_log.h"
50eda14cbcSMatt Macy 
51eda14cbcSMatt Macy typedef struct fmd_modstat {
52eda14cbcSMatt Macy 	fmd_stat_t	ms_accepted;	/* total events accepted by module */
53eda14cbcSMatt Macy 	fmd_stat_t	ms_caseopen;	/* cases currently open */
54eda14cbcSMatt Macy 	fmd_stat_t	ms_casesolved;	/* total cases solved by module */
55eda14cbcSMatt Macy 	fmd_stat_t	ms_caseclosed;	/* total cases closed by module */
56eda14cbcSMatt Macy } fmd_modstat_t;
57eda14cbcSMatt Macy 
58eda14cbcSMatt Macy typedef struct fmd_module {
59eda14cbcSMatt Macy 	const char	*mod_name;	/* basename of module (ro) */
60eda14cbcSMatt Macy 	const fmd_hdl_info_t *mod_info;	/* module info registered with handle */
61eda14cbcSMatt Macy 	void		*mod_spec;	/* fmd_hdl_get/setspecific data value */
62eda14cbcSMatt Macy 	fmd_stat_t	*mod_ustat;	/* module specific custom stats */
63eda14cbcSMatt Macy 	uint_t		mod_ustat_cnt;	/* count of ustat stats */
64eda14cbcSMatt Macy 	fmd_modstat_t	mod_stats;	/* fmd built-in per-module statistics */
65eda14cbcSMatt Macy 	fmd_serd_hash_t	mod_serds;	/* hash of serd engs owned by module */
66eda14cbcSMatt Macy 	char		*mod_vers;	/* a copy of module version string */
67eda14cbcSMatt Macy } fmd_module_t;
68eda14cbcSMatt Macy 
69eda14cbcSMatt Macy /*
70eda14cbcSMatt Macy  * ZED has two FMD hardwired module instances
71eda14cbcSMatt Macy  */
72eda14cbcSMatt Macy fmd_module_t	zfs_retire_module;
73eda14cbcSMatt Macy fmd_module_t	zfs_diagnosis_module;
74eda14cbcSMatt Macy 
75eda14cbcSMatt Macy /*
76eda14cbcSMatt Macy  * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
77eda14cbcSMatt Macy  */
78eda14cbcSMatt Macy 
79eda14cbcSMatt Macy #ifdef DEBUG
80eda14cbcSMatt Macy const char *
_umem_debug_init(void)81eda14cbcSMatt Macy _umem_debug_init(void)
82eda14cbcSMatt Macy {
83eda14cbcSMatt Macy 	return ("default,verbose"); /* $UMEM_DEBUG setting */
84eda14cbcSMatt Macy }
85eda14cbcSMatt Macy 
86eda14cbcSMatt Macy const char *
_umem_logging_init(void)87eda14cbcSMatt Macy _umem_logging_init(void)
88eda14cbcSMatt Macy {
89eda14cbcSMatt Macy 	return ("fail,contents"); /* $UMEM_LOGGING setting */
90eda14cbcSMatt Macy }
91eda14cbcSMatt Macy #endif
92eda14cbcSMatt Macy 
93eda14cbcSMatt Macy /*
94eda14cbcSMatt Macy  * Register a module with fmd and finish module initialization.
95eda14cbcSMatt Macy  * Returns an integer indicating whether it succeeded (zero) or
96eda14cbcSMatt Macy  * failed (non-zero).
97eda14cbcSMatt Macy  */
98eda14cbcSMatt Macy int
fmd_hdl_register(fmd_hdl_t * hdl,int version,const fmd_hdl_info_t * mip)99eda14cbcSMatt Macy fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip)
100eda14cbcSMatt Macy {
101e92ffd9bSMartin Matuska 	(void) version;
102eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
103eda14cbcSMatt Macy 
104eda14cbcSMatt Macy 	mp->mod_info = mip;
105eda14cbcSMatt Macy 	mp->mod_name = mip->fmdi_desc + 4;	/* drop 'ZFS ' prefix */
106eda14cbcSMatt Macy 	mp->mod_spec = NULL;
107eda14cbcSMatt Macy 
108eda14cbcSMatt Macy 	/* bare minimum module stats */
109eda14cbcSMatt Macy 	(void) strcpy(mp->mod_stats.ms_accepted.fmds_name, "fmd.accepted");
110eda14cbcSMatt Macy 	(void) strcpy(mp->mod_stats.ms_caseopen.fmds_name, "fmd.caseopen");
111eda14cbcSMatt Macy 	(void) strcpy(mp->mod_stats.ms_casesolved.fmds_name, "fmd.casesolved");
112eda14cbcSMatt Macy 	(void) strcpy(mp->mod_stats.ms_caseclosed.fmds_name, "fmd.caseclosed");
113eda14cbcSMatt Macy 
114eda14cbcSMatt Macy 	fmd_serd_hash_create(&mp->mod_serds);
115eda14cbcSMatt Macy 
116eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "register module");
117eda14cbcSMatt Macy 
118eda14cbcSMatt Macy 	return (0);
119eda14cbcSMatt Macy }
120eda14cbcSMatt Macy 
121eda14cbcSMatt Macy void
fmd_hdl_unregister(fmd_hdl_t * hdl)122eda14cbcSMatt Macy fmd_hdl_unregister(fmd_hdl_t *hdl)
123eda14cbcSMatt Macy {
124eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
125eda14cbcSMatt Macy 	fmd_modstat_t *msp = &mp->mod_stats;
126eda14cbcSMatt Macy 	const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;
127eda14cbcSMatt Macy 
128eda14cbcSMatt Macy 	/* dump generic module stats */
129eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "%s: %llu", msp->ms_accepted.fmds_name,
130eda14cbcSMatt Macy 	    msp->ms_accepted.fmds_value.ui64);
131eda14cbcSMatt Macy 	if (ops->fmdo_close != NULL) {
132eda14cbcSMatt Macy 		fmd_hdl_debug(hdl, "%s: %llu", msp->ms_caseopen.fmds_name,
133eda14cbcSMatt Macy 		    msp->ms_caseopen.fmds_value.ui64);
134eda14cbcSMatt Macy 		fmd_hdl_debug(hdl, "%s: %llu", msp->ms_casesolved.fmds_name,
135eda14cbcSMatt Macy 		    msp->ms_casesolved.fmds_value.ui64);
136eda14cbcSMatt Macy 		fmd_hdl_debug(hdl, "%s: %llu", msp->ms_caseclosed.fmds_name,
137eda14cbcSMatt Macy 		    msp->ms_caseclosed.fmds_value.ui64);
138eda14cbcSMatt Macy 	}
139eda14cbcSMatt Macy 
140eda14cbcSMatt Macy 	/* dump module specific stats */
141eda14cbcSMatt Macy 	if (mp->mod_ustat != NULL) {
142eda14cbcSMatt Macy 		int i;
143eda14cbcSMatt Macy 
144eda14cbcSMatt Macy 		for (i = 0; i < mp->mod_ustat_cnt; i++) {
145eda14cbcSMatt Macy 			fmd_hdl_debug(hdl, "%s: %llu",
146eda14cbcSMatt Macy 			    mp->mod_ustat[i].fmds_name,
147eda14cbcSMatt Macy 			    mp->mod_ustat[i].fmds_value.ui64);
148eda14cbcSMatt Macy 		}
149eda14cbcSMatt Macy 	}
150eda14cbcSMatt Macy 
151eda14cbcSMatt Macy 	fmd_serd_hash_destroy(&mp->mod_serds);
152eda14cbcSMatt Macy 
153eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "unregister module");
154eda14cbcSMatt Macy }
155eda14cbcSMatt Macy 
156eda14cbcSMatt Macy /*
157eda14cbcSMatt Macy  * fmd_hdl_setspecific() is used to associate a data pointer with
158eda14cbcSMatt Macy  * the specified handle for the duration of the module's lifetime.
159eda14cbcSMatt Macy  * This pointer can be retrieved using fmd_hdl_getspecific().
160eda14cbcSMatt Macy  */
161eda14cbcSMatt Macy void
fmd_hdl_setspecific(fmd_hdl_t * hdl,void * spec)162eda14cbcSMatt Macy fmd_hdl_setspecific(fmd_hdl_t *hdl, void *spec)
163eda14cbcSMatt Macy {
164eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
165eda14cbcSMatt Macy 
166eda14cbcSMatt Macy 	mp->mod_spec = spec;
167eda14cbcSMatt Macy }
168eda14cbcSMatt Macy 
169eda14cbcSMatt Macy /*
170eda14cbcSMatt Macy  * Return the module-specific data pointer previously associated
171eda14cbcSMatt Macy  * with the handle using fmd_hdl_setspecific().
172eda14cbcSMatt Macy  */
173eda14cbcSMatt Macy void *
fmd_hdl_getspecific(fmd_hdl_t * hdl)174eda14cbcSMatt Macy fmd_hdl_getspecific(fmd_hdl_t *hdl)
175eda14cbcSMatt Macy {
176eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
177eda14cbcSMatt Macy 
178eda14cbcSMatt Macy 	return (mp->mod_spec);
179eda14cbcSMatt Macy }
180eda14cbcSMatt Macy 
181eda14cbcSMatt Macy void *
fmd_hdl_alloc(fmd_hdl_t * hdl,size_t size,int flags)182eda14cbcSMatt Macy fmd_hdl_alloc(fmd_hdl_t *hdl, size_t size, int flags)
183eda14cbcSMatt Macy {
184e92ffd9bSMartin Matuska 	(void) hdl;
185eda14cbcSMatt Macy 	return (umem_alloc(size, flags));
186eda14cbcSMatt Macy }
187eda14cbcSMatt Macy 
188eda14cbcSMatt Macy void *
fmd_hdl_zalloc(fmd_hdl_t * hdl,size_t size,int flags)189eda14cbcSMatt Macy fmd_hdl_zalloc(fmd_hdl_t *hdl, size_t size, int flags)
190eda14cbcSMatt Macy {
191e92ffd9bSMartin Matuska 	(void) hdl;
192eda14cbcSMatt Macy 	return (umem_zalloc(size, flags));
193eda14cbcSMatt Macy }
194eda14cbcSMatt Macy 
195eda14cbcSMatt Macy void
fmd_hdl_free(fmd_hdl_t * hdl,void * data,size_t size)196eda14cbcSMatt Macy fmd_hdl_free(fmd_hdl_t *hdl, void *data, size_t size)
197eda14cbcSMatt Macy {
198e92ffd9bSMartin Matuska 	(void) hdl;
199eda14cbcSMatt Macy 	umem_free(data, size);
200eda14cbcSMatt Macy }
201eda14cbcSMatt Macy 
202eda14cbcSMatt Macy /*
203eda14cbcSMatt Macy  * Record a module debug message using the specified format.
204eda14cbcSMatt Macy  */
205eda14cbcSMatt Macy void
fmd_hdl_debug(fmd_hdl_t * hdl,const char * format,...)206eda14cbcSMatt Macy fmd_hdl_debug(fmd_hdl_t *hdl, const char *format, ...)
207eda14cbcSMatt Macy {
208eda14cbcSMatt Macy 	char message[256];
209eda14cbcSMatt Macy 	va_list vargs;
210eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
211eda14cbcSMatt Macy 
212eda14cbcSMatt Macy 	va_start(vargs, format);
213eda14cbcSMatt Macy 	(void) vsnprintf(message, sizeof (message), format, vargs);
214eda14cbcSMatt Macy 	va_end(vargs);
215eda14cbcSMatt Macy 
216eda14cbcSMatt Macy 	/* prefix message with module name */
217eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "%s: %s", mp->mod_name, message);
218eda14cbcSMatt Macy }
219eda14cbcSMatt Macy 
220eda14cbcSMatt Macy /* Property Retrieval */
221eda14cbcSMatt Macy 
222eda14cbcSMatt Macy int32_t
fmd_prop_get_int32(fmd_hdl_t * hdl,const char * name)223eda14cbcSMatt Macy fmd_prop_get_int32(fmd_hdl_t *hdl, const char *name)
224eda14cbcSMatt Macy {
225e92ffd9bSMartin Matuska 	(void) hdl;
226e92ffd9bSMartin Matuska 
227eda14cbcSMatt Macy 	/*
228eda14cbcSMatt Macy 	 * These can be looked up in mp->modinfo->fmdi_props
229eda14cbcSMatt Macy 	 * For now we just hard code for phase 2. In the
230eda14cbcSMatt Macy 	 * future, there can be a ZED based override.
231eda14cbcSMatt Macy 	 */
232eda14cbcSMatt Macy 	if (strcmp(name, "spare_on_remove") == 0)
233eda14cbcSMatt Macy 		return (1);
234eda14cbcSMatt Macy 
235eda14cbcSMatt Macy 	return (0);
236eda14cbcSMatt Macy }
237eda14cbcSMatt Macy 
238eda14cbcSMatt Macy /* FMD Statistics */
239eda14cbcSMatt Macy 
240eda14cbcSMatt Macy fmd_stat_t *
fmd_stat_create(fmd_hdl_t * hdl,uint_t flags,uint_t nstats,fmd_stat_t * statv)241eda14cbcSMatt Macy fmd_stat_create(fmd_hdl_t *hdl, uint_t flags, uint_t nstats, fmd_stat_t *statv)
242eda14cbcSMatt Macy {
243eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
244eda14cbcSMatt Macy 
245eda14cbcSMatt Macy 	if (flags == FMD_STAT_NOALLOC) {
246eda14cbcSMatt Macy 		mp->mod_ustat = statv;
247eda14cbcSMatt Macy 		mp->mod_ustat_cnt = nstats;
248eda14cbcSMatt Macy 	}
249eda14cbcSMatt Macy 
250eda14cbcSMatt Macy 	return (statv);
251eda14cbcSMatt Macy }
252eda14cbcSMatt Macy 
253eda14cbcSMatt Macy /* Case Management */
254eda14cbcSMatt Macy 
255eda14cbcSMatt Macy fmd_case_t *
fmd_case_open(fmd_hdl_t * hdl,void * data)256eda14cbcSMatt Macy fmd_case_open(fmd_hdl_t *hdl, void *data)
257eda14cbcSMatt Macy {
258eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
259eda14cbcSMatt Macy 	uuid_t uuid;
260eda14cbcSMatt Macy 
261eda14cbcSMatt Macy 	fmd_case_t *cp;
262eda14cbcSMatt Macy 
263eda14cbcSMatt Macy 	cp = fmd_hdl_zalloc(hdl, sizeof (fmd_case_t), FMD_SLEEP);
264eda14cbcSMatt Macy 	cp->ci_mod = hdl;
265eda14cbcSMatt Macy 	cp->ci_state = FMD_CASE_UNSOLVED;
266eda14cbcSMatt Macy 	cp->ci_flags = FMD_CF_DIRTY;
267eda14cbcSMatt Macy 	cp->ci_data = data;
268eda14cbcSMatt Macy 	cp->ci_bufptr = NULL;
269eda14cbcSMatt Macy 	cp->ci_bufsiz = 0;
270eda14cbcSMatt Macy 
271eda14cbcSMatt Macy 	uuid_generate(uuid);
272eda14cbcSMatt Macy 	uuid_unparse(uuid, cp->ci_uuid);
273eda14cbcSMatt Macy 
274eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "case opened (%s)", cp->ci_uuid);
275eda14cbcSMatt Macy 	mp->mod_stats.ms_caseopen.fmds_value.ui64++;
276eda14cbcSMatt Macy 
277eda14cbcSMatt Macy 	return (cp);
278eda14cbcSMatt Macy }
279eda14cbcSMatt Macy 
280eda14cbcSMatt Macy void
fmd_case_solve(fmd_hdl_t * hdl,fmd_case_t * cp)281eda14cbcSMatt Macy fmd_case_solve(fmd_hdl_t *hdl, fmd_case_t *cp)
282eda14cbcSMatt Macy {
283eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
284eda14cbcSMatt Macy 
285eda14cbcSMatt Macy 	/*
286eda14cbcSMatt Macy 	 * For ZED, the event was already sent from fmd_case_add_suspect()
287eda14cbcSMatt Macy 	 */
288eda14cbcSMatt Macy 
289eda14cbcSMatt Macy 	if (cp->ci_state >= FMD_CASE_SOLVED)
290eda14cbcSMatt Macy 		fmd_hdl_debug(hdl, "case is already solved or closed");
291eda14cbcSMatt Macy 
292eda14cbcSMatt Macy 	cp->ci_state = FMD_CASE_SOLVED;
293eda14cbcSMatt Macy 
294eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "case solved (%s)", cp->ci_uuid);
295eda14cbcSMatt Macy 	mp->mod_stats.ms_casesolved.fmds_value.ui64++;
296eda14cbcSMatt Macy }
297eda14cbcSMatt Macy 
298eda14cbcSMatt Macy void
fmd_case_close(fmd_hdl_t * hdl,fmd_case_t * cp)299eda14cbcSMatt Macy fmd_case_close(fmd_hdl_t *hdl, fmd_case_t *cp)
300eda14cbcSMatt Macy {
301eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
302eda14cbcSMatt Macy 	const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;
303eda14cbcSMatt Macy 
304eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "case closed (%s)", cp->ci_uuid);
305eda14cbcSMatt Macy 
306eda14cbcSMatt Macy 	if (ops->fmdo_close != NULL)
307eda14cbcSMatt Macy 		ops->fmdo_close(hdl, cp);
308eda14cbcSMatt Macy 
309eda14cbcSMatt Macy 	mp->mod_stats.ms_caseopen.fmds_value.ui64--;
310eda14cbcSMatt Macy 	mp->mod_stats.ms_caseclosed.fmds_value.ui64++;
311eda14cbcSMatt Macy 
312eda14cbcSMatt Macy 	if (cp->ci_bufptr != NULL && cp->ci_bufsiz > 0)
313eda14cbcSMatt Macy 		fmd_hdl_free(hdl, cp->ci_bufptr, cp->ci_bufsiz);
314eda14cbcSMatt Macy 
315eda14cbcSMatt Macy 	fmd_hdl_free(hdl, cp, sizeof (fmd_case_t));
316eda14cbcSMatt Macy }
317eda14cbcSMatt Macy 
318eda14cbcSMatt Macy void
fmd_case_uuresolved(fmd_hdl_t * hdl,const char * uuid)319eda14cbcSMatt Macy fmd_case_uuresolved(fmd_hdl_t *hdl, const char *uuid)
320eda14cbcSMatt Macy {
321eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "case resolved by uuid (%s)", uuid);
322eda14cbcSMatt Macy }
323eda14cbcSMatt Macy 
324da5137abSMartin Matuska boolean_t
fmd_case_solved(fmd_hdl_t * hdl,fmd_case_t * cp)325eda14cbcSMatt Macy fmd_case_solved(fmd_hdl_t *hdl, fmd_case_t *cp)
326eda14cbcSMatt Macy {
327e92ffd9bSMartin Matuska 	(void) hdl;
328da5137abSMartin Matuska 	return (cp->ci_state >= FMD_CASE_SOLVED);
329eda14cbcSMatt Macy }
330eda14cbcSMatt Macy 
331eda14cbcSMatt Macy void
fmd_case_add_ereport(fmd_hdl_t * hdl,fmd_case_t * cp,fmd_event_t * ep)332eda14cbcSMatt Macy fmd_case_add_ereport(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep)
333eda14cbcSMatt Macy {
334e92ffd9bSMartin Matuska 	(void) hdl, (void) cp, (void) ep;
335eda14cbcSMatt Macy }
336eda14cbcSMatt Macy 
337eda14cbcSMatt Macy static void
zed_log_fault(nvlist_t * nvl,const char * uuid,const char * code)338eda14cbcSMatt Macy zed_log_fault(nvlist_t *nvl, const char *uuid, const char *code)
339eda14cbcSMatt Macy {
340eda14cbcSMatt Macy 	nvlist_t *rsrc;
3412a58b312SMartin Matuska 	const char *strval;
342eda14cbcSMatt Macy 	uint64_t guid;
343eda14cbcSMatt Macy 	uint8_t byte;
344eda14cbcSMatt Macy 
345eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "\nzed_fault_event:");
346eda14cbcSMatt Macy 
347eda14cbcSMatt Macy 	if (uuid != NULL)
348eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %s", FM_SUSPECT_UUID, uuid);
349eda14cbcSMatt Macy 	if (nvlist_lookup_string(nvl, FM_CLASS, &strval) == 0)
350eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %s", FM_CLASS, strval);
351eda14cbcSMatt Macy 	if (code != NULL)
352eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %s", FM_SUSPECT_DIAG_CODE, code);
353eda14cbcSMatt Macy 	if (nvlist_lookup_uint8(nvl, FM_FAULT_CERTAINTY, &byte) == 0)
354c7046f76SMartin Matuska 		zed_log_msg(LOG_INFO, "\t%s: %hhu", FM_FAULT_CERTAINTY, byte);
355eda14cbcSMatt Macy 	if (nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE, &rsrc) == 0) {
356eda14cbcSMatt Macy 		if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &strval) == 0)
357eda14cbcSMatt Macy 			zed_log_msg(LOG_INFO, "\t%s: %s", FM_FMRI_SCHEME,
358eda14cbcSMatt Macy 			    strval);
359eda14cbcSMatt Macy 		if (nvlist_lookup_uint64(rsrc, FM_FMRI_ZFS_POOL, &guid) == 0)
360eda14cbcSMatt Macy 			zed_log_msg(LOG_INFO, "\t%s: %llu", FM_FMRI_ZFS_POOL,
361eda14cbcSMatt Macy 			    guid);
362eda14cbcSMatt Macy 		if (nvlist_lookup_uint64(rsrc, FM_FMRI_ZFS_VDEV, &guid) == 0)
363eda14cbcSMatt Macy 			zed_log_msg(LOG_INFO, "\t%s: %llu \n", FM_FMRI_ZFS_VDEV,
364eda14cbcSMatt Macy 			    guid);
365eda14cbcSMatt Macy 	}
366eda14cbcSMatt Macy }
367eda14cbcSMatt Macy 
368eda14cbcSMatt Macy static const char *
fmd_fault_mkcode(nvlist_t * fault)369eda14cbcSMatt Macy fmd_fault_mkcode(nvlist_t *fault)
370eda14cbcSMatt Macy {
3712a58b312SMartin Matuska 	const char *class;
372a0b956f5SMartin Matuska 	const char *code = "-";
373eda14cbcSMatt Macy 
374eda14cbcSMatt Macy 	/*
375eda14cbcSMatt Macy 	 * Note: message codes come from: openzfs/usr/src/cmd/fm/dicts/ZFS.po
376eda14cbcSMatt Macy 	 */
377eda14cbcSMatt Macy 	if (nvlist_lookup_string(fault, FM_CLASS, &class) == 0) {
378eda14cbcSMatt Macy 		if (strcmp(class, "fault.fs.zfs.vdev.io") == 0)
379eda14cbcSMatt Macy 			code = "ZFS-8000-FD";
380eda14cbcSMatt Macy 		else if (strcmp(class, "fault.fs.zfs.vdev.checksum") == 0)
381eda14cbcSMatt Macy 			code = "ZFS-8000-GH";
382eda14cbcSMatt Macy 		else if (strcmp(class, "fault.fs.zfs.io_failure_wait") == 0)
383eda14cbcSMatt Macy 			code = "ZFS-8000-HC";
384eda14cbcSMatt Macy 		else if (strcmp(class, "fault.fs.zfs.io_failure_continue") == 0)
385eda14cbcSMatt Macy 			code = "ZFS-8000-JQ";
386eda14cbcSMatt Macy 		else if (strcmp(class, "fault.fs.zfs.log_replay") == 0)
387eda14cbcSMatt Macy 			code = "ZFS-8000-K4";
388eda14cbcSMatt Macy 		else if (strcmp(class, "fault.fs.zfs.pool") == 0)
389eda14cbcSMatt Macy 			code = "ZFS-8000-CS";
390eda14cbcSMatt Macy 		else if (strcmp(class, "fault.fs.zfs.device") == 0)
391eda14cbcSMatt Macy 			code = "ZFS-8000-D3";
392eda14cbcSMatt Macy 
393eda14cbcSMatt Macy 	}
394eda14cbcSMatt Macy 	return (code);
395eda14cbcSMatt Macy }
396eda14cbcSMatt Macy 
397eda14cbcSMatt Macy void
fmd_case_add_suspect(fmd_hdl_t * hdl,fmd_case_t * cp,nvlist_t * fault)398eda14cbcSMatt Macy fmd_case_add_suspect(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *fault)
399eda14cbcSMatt Macy {
400eda14cbcSMatt Macy 	nvlist_t *nvl;
401eda14cbcSMatt Macy 	const char *code = fmd_fault_mkcode(fault);
402eda14cbcSMatt Macy 	int64_t tod[2];
403eda14cbcSMatt Macy 	int err = 0;
404eda14cbcSMatt Macy 
405eda14cbcSMatt Macy 	/*
406eda14cbcSMatt Macy 	 * payload derived from fmd_protocol_list()
407eda14cbcSMatt Macy 	 */
408eda14cbcSMatt Macy 
409eda14cbcSMatt Macy 	(void) gettimeofday(&cp->ci_tv, NULL);
410eda14cbcSMatt Macy 	tod[0] = cp->ci_tv.tv_sec;
411eda14cbcSMatt Macy 	tod[1] = cp->ci_tv.tv_usec;
412eda14cbcSMatt Macy 
413eda14cbcSMatt Macy 	nvl = fmd_nvl_alloc(hdl, FMD_SLEEP);
414eda14cbcSMatt Macy 
415eda14cbcSMatt Macy 	err |= nvlist_add_uint8(nvl, FM_VERSION, FM_SUSPECT_VERSION);
416eda14cbcSMatt Macy 	err |= nvlist_add_string(nvl, FM_CLASS, FM_LIST_SUSPECT_CLASS);
417eda14cbcSMatt Macy 	err |= nvlist_add_string(nvl, FM_SUSPECT_UUID, cp->ci_uuid);
418eda14cbcSMatt Macy 	err |= nvlist_add_string(nvl, FM_SUSPECT_DIAG_CODE, code);
419eda14cbcSMatt Macy 	err |= nvlist_add_int64_array(nvl, FM_SUSPECT_DIAG_TIME, tod, 2);
420eda14cbcSMatt Macy 	err |= nvlist_add_uint32(nvl, FM_SUSPECT_FAULT_SZ, 1);
421681ce946SMartin Matuska 	err |= nvlist_add_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST,
422681ce946SMartin Matuska 	    (const nvlist_t **)&fault, 1);
423eda14cbcSMatt Macy 
424eda14cbcSMatt Macy 	if (err)
425eda14cbcSMatt Macy 		zed_log_die("failed to populate nvlist");
426eda14cbcSMatt Macy 
427eda14cbcSMatt Macy 	zed_log_fault(fault, cp->ci_uuid, code);
428eda14cbcSMatt Macy 	zfs_agent_post_event(FM_LIST_SUSPECT_CLASS, NULL, nvl);
429eda14cbcSMatt Macy 
430eda14cbcSMatt Macy 	nvlist_free(nvl);
431eda14cbcSMatt Macy 	nvlist_free(fault);
432eda14cbcSMatt Macy }
433eda14cbcSMatt Macy 
434eda14cbcSMatt Macy void
fmd_case_setspecific(fmd_hdl_t * hdl,fmd_case_t * cp,void * data)435eda14cbcSMatt Macy fmd_case_setspecific(fmd_hdl_t *hdl, fmd_case_t *cp, void *data)
436eda14cbcSMatt Macy {
437e92ffd9bSMartin Matuska 	(void) hdl;
438eda14cbcSMatt Macy 	cp->ci_data = data;
439eda14cbcSMatt Macy }
440eda14cbcSMatt Macy 
441eda14cbcSMatt Macy void *
fmd_case_getspecific(fmd_hdl_t * hdl,fmd_case_t * cp)442eda14cbcSMatt Macy fmd_case_getspecific(fmd_hdl_t *hdl, fmd_case_t *cp)
443eda14cbcSMatt Macy {
444e92ffd9bSMartin Matuska 	(void) hdl;
445eda14cbcSMatt Macy 	return (cp->ci_data);
446eda14cbcSMatt Macy }
447eda14cbcSMatt Macy 
448eda14cbcSMatt Macy void
fmd_buf_create(fmd_hdl_t * hdl,fmd_case_t * cp,const char * name,size_t size)449eda14cbcSMatt Macy fmd_buf_create(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, size_t size)
450eda14cbcSMatt Macy {
451e92ffd9bSMartin Matuska 	assert(strcmp(name, "data") == 0), (void) name;
452eda14cbcSMatt Macy 	assert(cp->ci_bufptr == NULL);
453eda14cbcSMatt Macy 	assert(size < (1024 * 1024));
454eda14cbcSMatt Macy 
455eda14cbcSMatt Macy 	cp->ci_bufptr = fmd_hdl_alloc(hdl, size, FMD_SLEEP);
456eda14cbcSMatt Macy 	cp->ci_bufsiz = size;
457eda14cbcSMatt Macy }
458eda14cbcSMatt Macy 
459eda14cbcSMatt Macy void
fmd_buf_read(fmd_hdl_t * hdl,fmd_case_t * cp,const char * name,void * buf,size_t size)460eda14cbcSMatt Macy fmd_buf_read(fmd_hdl_t *hdl, fmd_case_t *cp,
461eda14cbcSMatt Macy     const char *name, void *buf, size_t size)
462eda14cbcSMatt Macy {
463e92ffd9bSMartin Matuska 	(void) hdl;
464e92ffd9bSMartin Matuska 	assert(strcmp(name, "data") == 0), (void) name;
465eda14cbcSMatt Macy 	assert(cp->ci_bufptr != NULL);
466eda14cbcSMatt Macy 	assert(size <= cp->ci_bufsiz);
467eda14cbcSMatt Macy 
468da5137abSMartin Matuska 	memcpy(buf, cp->ci_bufptr, size);
469eda14cbcSMatt Macy }
470eda14cbcSMatt Macy 
471eda14cbcSMatt Macy void
fmd_buf_write(fmd_hdl_t * hdl,fmd_case_t * cp,const char * name,const void * buf,size_t size)472eda14cbcSMatt Macy fmd_buf_write(fmd_hdl_t *hdl, fmd_case_t *cp,
473eda14cbcSMatt Macy     const char *name, const void *buf, size_t size)
474eda14cbcSMatt Macy {
475e92ffd9bSMartin Matuska 	(void) hdl;
476e92ffd9bSMartin Matuska 	assert(strcmp(name, "data") == 0), (void) name;
477eda14cbcSMatt Macy 	assert(cp->ci_bufptr != NULL);
478eda14cbcSMatt Macy 	assert(cp->ci_bufsiz >= size);
479eda14cbcSMatt Macy 
480da5137abSMartin Matuska 	memcpy(cp->ci_bufptr, buf, size);
481eda14cbcSMatt Macy }
482eda14cbcSMatt Macy 
483eda14cbcSMatt Macy /* SERD Engines */
484eda14cbcSMatt Macy 
485eda14cbcSMatt Macy void
fmd_serd_create(fmd_hdl_t * hdl,const char * name,uint_t n,hrtime_t t)486eda14cbcSMatt Macy fmd_serd_create(fmd_hdl_t *hdl, const char *name, uint_t n, hrtime_t t)
487eda14cbcSMatt Macy {
488eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
489eda14cbcSMatt Macy 
490eda14cbcSMatt Macy 	if (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL) {
491eda14cbcSMatt Macy 		zed_log_msg(LOG_ERR, "failed to create SERD engine '%s': "
492eda14cbcSMatt Macy 		    " name already exists", name);
493eda14cbcSMatt Macy 		return;
494eda14cbcSMatt Macy 	}
495eda14cbcSMatt Macy 
496eda14cbcSMatt Macy 	(void) fmd_serd_eng_insert(&mp->mod_serds, name, n, t);
497eda14cbcSMatt Macy }
498eda14cbcSMatt Macy 
499eda14cbcSMatt Macy void
fmd_serd_destroy(fmd_hdl_t * hdl,const char * name)500eda14cbcSMatt Macy fmd_serd_destroy(fmd_hdl_t *hdl, const char *name)
501eda14cbcSMatt Macy {
502eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
503eda14cbcSMatt Macy 
504eda14cbcSMatt Macy 	fmd_serd_eng_delete(&mp->mod_serds, name);
505eda14cbcSMatt Macy 
506eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "serd_destroy %s", name);
507eda14cbcSMatt Macy }
508eda14cbcSMatt Macy 
509eda14cbcSMatt Macy int
fmd_serd_exists(fmd_hdl_t * hdl,const char * name)510eda14cbcSMatt Macy fmd_serd_exists(fmd_hdl_t *hdl, const char *name)
511eda14cbcSMatt Macy {
512eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
513eda14cbcSMatt Macy 
514eda14cbcSMatt Macy 	return (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL);
515eda14cbcSMatt Macy }
516eda14cbcSMatt Macy 
517*e2257b31SMartin Matuska int
fmd_serd_active(fmd_hdl_t * hdl,const char * name)518*e2257b31SMartin Matuska fmd_serd_active(fmd_hdl_t *hdl, const char *name)
519*e2257b31SMartin Matuska {
520*e2257b31SMartin Matuska 	fmd_module_t *mp = (fmd_module_t *)hdl;
521*e2257b31SMartin Matuska 	fmd_serd_eng_t *sgp;
522*e2257b31SMartin Matuska 
523*e2257b31SMartin Matuska 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
524*e2257b31SMartin Matuska 		zed_log_msg(LOG_ERR, "serd engine '%s' does not exist", name);
525*e2257b31SMartin Matuska 		return (0);
526*e2257b31SMartin Matuska 	}
527*e2257b31SMartin Matuska 	return (fmd_serd_eng_fired(sgp) || !fmd_serd_eng_empty(sgp));
528*e2257b31SMartin Matuska }
529*e2257b31SMartin Matuska 
530eda14cbcSMatt Macy void
fmd_serd_reset(fmd_hdl_t * hdl,const char * name)531eda14cbcSMatt Macy fmd_serd_reset(fmd_hdl_t *hdl, const char *name)
532eda14cbcSMatt Macy {
533eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
534eda14cbcSMatt Macy 	fmd_serd_eng_t *sgp;
535eda14cbcSMatt Macy 
536eda14cbcSMatt Macy 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
537eda14cbcSMatt Macy 		zed_log_msg(LOG_ERR, "serd engine '%s' does not exist", name);
538*e2257b31SMartin Matuska 	} else {
539eda14cbcSMatt Macy 		fmd_serd_eng_reset(sgp);
540eda14cbcSMatt Macy 		fmd_hdl_debug(hdl, "serd_reset %s", name);
541eda14cbcSMatt Macy 	}
542*e2257b31SMartin Matuska }
543eda14cbcSMatt Macy 
544eda14cbcSMatt Macy int
fmd_serd_record(fmd_hdl_t * hdl,const char * name,fmd_event_t * ep)545eda14cbcSMatt Macy fmd_serd_record(fmd_hdl_t *hdl, const char *name, fmd_event_t *ep)
546eda14cbcSMatt Macy {
547eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
548eda14cbcSMatt Macy 	fmd_serd_eng_t *sgp;
549eda14cbcSMatt Macy 
550eda14cbcSMatt Macy 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
551eda14cbcSMatt Macy 		zed_log_msg(LOG_ERR, "failed to add record to SERD engine '%s'",
552eda14cbcSMatt Macy 		    name);
553da5137abSMartin Matuska 		return (0);
554eda14cbcSMatt Macy 	}
555*e2257b31SMartin Matuska 	return (fmd_serd_eng_record(sgp, ep->ev_hrt));
556*e2257b31SMartin Matuska }
557eda14cbcSMatt Macy 
558*e2257b31SMartin Matuska void
fmd_serd_gc(fmd_hdl_t * hdl)559*e2257b31SMartin Matuska fmd_serd_gc(fmd_hdl_t *hdl)
560*e2257b31SMartin Matuska {
561*e2257b31SMartin Matuska 	fmd_module_t *mp = (fmd_module_t *)hdl;
562*e2257b31SMartin Matuska 
563*e2257b31SMartin Matuska 	fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_gc, NULL);
564eda14cbcSMatt Macy }
565eda14cbcSMatt Macy 
566eda14cbcSMatt Macy /* FMD Timers */
567eda14cbcSMatt Macy 
568eda14cbcSMatt Macy static void
_timer_notify(union sigval sv)569eda14cbcSMatt Macy _timer_notify(union sigval sv)
570eda14cbcSMatt Macy {
571eda14cbcSMatt Macy 	fmd_timer_t *ftp = sv.sival_ptr;
572eda14cbcSMatt Macy 	fmd_hdl_t *hdl = ftp->ft_hdl;
573eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
574eda14cbcSMatt Macy 	const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;
575eda14cbcSMatt Macy 	struct itimerspec its;
576eda14cbcSMatt Macy 
577*e2257b31SMartin Matuska 	fmd_hdl_debug(hdl, "%s timer fired (%p)", mp->mod_name, ftp->ft_tid);
578eda14cbcSMatt Macy 
579eda14cbcSMatt Macy 	/* disarm the timer */
580da5137abSMartin Matuska 	memset(&its, 0, sizeof (struct itimerspec));
581eda14cbcSMatt Macy 	timer_settime(ftp->ft_tid, 0, &its, NULL);
582eda14cbcSMatt Macy 
583eda14cbcSMatt Macy 	/* Note that the fmdo_timeout can remove this timer */
584eda14cbcSMatt Macy 	if (ops->fmdo_timeout != NULL)
585eda14cbcSMatt Macy 		ops->fmdo_timeout(hdl, ftp, ftp->ft_arg);
586eda14cbcSMatt Macy }
587eda14cbcSMatt Macy 
588eda14cbcSMatt Macy /*
589eda14cbcSMatt Macy  * Install a new timer which will fire at least delta nanoseconds after the
590eda14cbcSMatt Macy  * current time. After the timeout has expired, the module's fmdo_timeout
591eda14cbcSMatt Macy  * entry point is called.
592eda14cbcSMatt Macy  */
593eda14cbcSMatt Macy fmd_timer_t *
fmd_timer_install(fmd_hdl_t * hdl,void * arg,fmd_event_t * ep,hrtime_t delta)594eda14cbcSMatt Macy fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta)
595eda14cbcSMatt Macy {
596e92ffd9bSMartin Matuska 	(void) ep;
597eda14cbcSMatt Macy 	struct sigevent sev;
598eda14cbcSMatt Macy 	struct itimerspec its;
599eda14cbcSMatt Macy 	fmd_timer_t *ftp;
600eda14cbcSMatt Macy 
601eda14cbcSMatt Macy 	ftp = fmd_hdl_alloc(hdl, sizeof (fmd_timer_t), FMD_SLEEP);
602eda14cbcSMatt Macy 	ftp->ft_arg = arg;
603eda14cbcSMatt Macy 	ftp->ft_hdl = hdl;
604eda14cbcSMatt Macy 
605eda14cbcSMatt Macy 	its.it_value.tv_sec = delta / 1000000000;
606eda14cbcSMatt Macy 	its.it_value.tv_nsec = delta % 1000000000;
607eda14cbcSMatt Macy 	its.it_interval.tv_sec = its.it_value.tv_sec;
608eda14cbcSMatt Macy 	its.it_interval.tv_nsec = its.it_value.tv_nsec;
609eda14cbcSMatt Macy 
610eda14cbcSMatt Macy 	sev.sigev_notify = SIGEV_THREAD;
611eda14cbcSMatt Macy 	sev.sigev_notify_function = _timer_notify;
612eda14cbcSMatt Macy 	sev.sigev_notify_attributes = NULL;
613eda14cbcSMatt Macy 	sev.sigev_value.sival_ptr = ftp;
614dbd5678dSMartin Matuska 	sev.sigev_signo = 0;
615eda14cbcSMatt Macy 
616eda14cbcSMatt Macy 	timer_create(CLOCK_REALTIME, &sev, &ftp->ft_tid);
617eda14cbcSMatt Macy 	timer_settime(ftp->ft_tid, 0, &its, NULL);
618eda14cbcSMatt Macy 
619eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "installing timer for %d secs (%p)",
620eda14cbcSMatt Macy 	    (int)its.it_value.tv_sec, ftp->ft_tid);
621eda14cbcSMatt Macy 
622eda14cbcSMatt Macy 	return (ftp);
623eda14cbcSMatt Macy }
624eda14cbcSMatt Macy 
625eda14cbcSMatt Macy void
fmd_timer_remove(fmd_hdl_t * hdl,fmd_timer_t * ftp)626eda14cbcSMatt Macy fmd_timer_remove(fmd_hdl_t *hdl, fmd_timer_t *ftp)
627eda14cbcSMatt Macy {
628eda14cbcSMatt Macy 	fmd_hdl_debug(hdl, "removing timer (%p)", ftp->ft_tid);
629eda14cbcSMatt Macy 
630eda14cbcSMatt Macy 	timer_delete(ftp->ft_tid);
631eda14cbcSMatt Macy 
632eda14cbcSMatt Macy 	fmd_hdl_free(hdl, ftp, sizeof (fmd_timer_t));
633eda14cbcSMatt Macy }
634eda14cbcSMatt Macy 
635eda14cbcSMatt Macy /* Name-Value Pair Lists */
636eda14cbcSMatt Macy 
637eda14cbcSMatt Macy nvlist_t *
fmd_nvl_create_fault(fmd_hdl_t * hdl,const char * class,uint8_t certainty,nvlist_t * asru,nvlist_t * fru,nvlist_t * resource)638eda14cbcSMatt Macy fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class, uint8_t certainty,
639eda14cbcSMatt Macy     nvlist_t *asru, nvlist_t *fru, nvlist_t *resource)
640eda14cbcSMatt Macy {
641e92ffd9bSMartin Matuska 	(void) hdl;
642eda14cbcSMatt Macy 	nvlist_t *nvl;
643eda14cbcSMatt Macy 	int err = 0;
644eda14cbcSMatt Macy 
645eda14cbcSMatt Macy 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
646eda14cbcSMatt Macy 		zed_log_die("failed to xalloc fault nvlist");
647eda14cbcSMatt Macy 
648eda14cbcSMatt Macy 	err |= nvlist_add_uint8(nvl, FM_VERSION, FM_FAULT_VERSION);
649eda14cbcSMatt Macy 	err |= nvlist_add_string(nvl, FM_CLASS, class);
650eda14cbcSMatt Macy 	err |= nvlist_add_uint8(nvl, FM_FAULT_CERTAINTY, certainty);
651eda14cbcSMatt Macy 
652eda14cbcSMatt Macy 	if (asru != NULL)
653eda14cbcSMatt Macy 		err |= nvlist_add_nvlist(nvl, FM_FAULT_ASRU, asru);
654eda14cbcSMatt Macy 	if (fru != NULL)
655eda14cbcSMatt Macy 		err |= nvlist_add_nvlist(nvl, FM_FAULT_FRU, fru);
656eda14cbcSMatt Macy 	if (resource != NULL)
657eda14cbcSMatt Macy 		err |= nvlist_add_nvlist(nvl, FM_FAULT_RESOURCE, resource);
658eda14cbcSMatt Macy 
659eda14cbcSMatt Macy 	if (err)
660eda14cbcSMatt Macy 		zed_log_die("failed to populate nvlist: %s\n", strerror(err));
661eda14cbcSMatt Macy 
662eda14cbcSMatt Macy 	return (nvl);
663eda14cbcSMatt Macy }
664eda14cbcSMatt Macy 
665eda14cbcSMatt Macy /*
666eda14cbcSMatt Macy  * sourced from fmd_string.c
667eda14cbcSMatt Macy  */
668eda14cbcSMatt Macy static int
fmd_strmatch(const char * s,const char * p)669eda14cbcSMatt Macy fmd_strmatch(const char *s, const char *p)
670eda14cbcSMatt Macy {
671eda14cbcSMatt Macy 	char c;
672eda14cbcSMatt Macy 
673eda14cbcSMatt Macy 	if (p == NULL)
674eda14cbcSMatt Macy 		return (0);
675eda14cbcSMatt Macy 
676eda14cbcSMatt Macy 	if (s == NULL)
677eda14cbcSMatt Macy 		s = ""; /* treat NULL string as the empty string */
678eda14cbcSMatt Macy 
679eda14cbcSMatt Macy 	do {
680eda14cbcSMatt Macy 		if ((c = *p++) == '\0')
681eda14cbcSMatt Macy 			return (*s == '\0');
682eda14cbcSMatt Macy 
683eda14cbcSMatt Macy 		if (c == '*') {
684eda14cbcSMatt Macy 			while (*p == '*')
685eda14cbcSMatt Macy 				p++; /* consecutive *'s can be collapsed */
686eda14cbcSMatt Macy 
687eda14cbcSMatt Macy 			if (*p == '\0')
688eda14cbcSMatt Macy 				return (1);
689eda14cbcSMatt Macy 
690eda14cbcSMatt Macy 			while (*s != '\0') {
691eda14cbcSMatt Macy 				if (fmd_strmatch(s++, p) != 0)
692eda14cbcSMatt Macy 					return (1);
693eda14cbcSMatt Macy 			}
694eda14cbcSMatt Macy 
695eda14cbcSMatt Macy 			return (0);
696eda14cbcSMatt Macy 		}
697eda14cbcSMatt Macy 	} while (c == *s++);
698eda14cbcSMatt Macy 
699eda14cbcSMatt Macy 	return (0);
700eda14cbcSMatt Macy }
701eda14cbcSMatt Macy 
702eda14cbcSMatt Macy int
fmd_nvl_class_match(fmd_hdl_t * hdl,nvlist_t * nvl,const char * pattern)703eda14cbcSMatt Macy fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern)
704eda14cbcSMatt Macy {
705e92ffd9bSMartin Matuska 	(void) hdl;
7062a58b312SMartin Matuska 	const char *class;
707eda14cbcSMatt Macy 
708eda14cbcSMatt Macy 	return (nvl != NULL &&
709eda14cbcSMatt Macy 	    nvlist_lookup_string(nvl, FM_CLASS, &class) == 0 &&
710eda14cbcSMatt Macy 	    fmd_strmatch(class, pattern));
711eda14cbcSMatt Macy }
712eda14cbcSMatt Macy 
713eda14cbcSMatt Macy nvlist_t *
fmd_nvl_alloc(fmd_hdl_t * hdl,int flags)714eda14cbcSMatt Macy fmd_nvl_alloc(fmd_hdl_t *hdl, int flags)
715eda14cbcSMatt Macy {
716e92ffd9bSMartin Matuska 	(void) hdl, (void) flags;
717eda14cbcSMatt Macy 	nvlist_t *nvl = NULL;
718eda14cbcSMatt Macy 
719eda14cbcSMatt Macy 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
720eda14cbcSMatt Macy 		return (NULL);
721eda14cbcSMatt Macy 
722eda14cbcSMatt Macy 	return (nvl);
723eda14cbcSMatt Macy }
724eda14cbcSMatt Macy 
725eda14cbcSMatt Macy 
726eda14cbcSMatt Macy /*
727eda14cbcSMatt Macy  * ZED Agent specific APIs
728eda14cbcSMatt Macy  */
729eda14cbcSMatt Macy 
730eda14cbcSMatt Macy fmd_hdl_t *
fmd_module_hdl(const char * name)731eda14cbcSMatt Macy fmd_module_hdl(const char *name)
732eda14cbcSMatt Macy {
733eda14cbcSMatt Macy 	if (strcmp(name, "zfs-retire") == 0)
734eda14cbcSMatt Macy 		return ((fmd_hdl_t *)&zfs_retire_module);
735eda14cbcSMatt Macy 	if (strcmp(name, "zfs-diagnosis") == 0)
736eda14cbcSMatt Macy 		return ((fmd_hdl_t *)&zfs_diagnosis_module);
737eda14cbcSMatt Macy 
738eda14cbcSMatt Macy 	return (NULL);
739eda14cbcSMatt Macy }
740eda14cbcSMatt Macy 
741eda14cbcSMatt Macy boolean_t
fmd_module_initialized(fmd_hdl_t * hdl)742eda14cbcSMatt Macy fmd_module_initialized(fmd_hdl_t *hdl)
743eda14cbcSMatt Macy {
744eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
745eda14cbcSMatt Macy 
746eda14cbcSMatt Macy 	return (mp->mod_info != NULL);
747eda14cbcSMatt Macy }
748eda14cbcSMatt Macy 
749eda14cbcSMatt Macy /*
750eda14cbcSMatt Macy  * fmd_module_recv is called for each event that is received by
751eda14cbcSMatt Macy  * the fault manager that has a class that matches one of the
752eda14cbcSMatt Macy  * module's subscriptions.
753eda14cbcSMatt Macy  */
754eda14cbcSMatt Macy void
fmd_module_recv(fmd_hdl_t * hdl,nvlist_t * nvl,const char * class)755eda14cbcSMatt Macy fmd_module_recv(fmd_hdl_t *hdl, nvlist_t *nvl, const char *class)
756eda14cbcSMatt Macy {
757eda14cbcSMatt Macy 	fmd_module_t *mp = (fmd_module_t *)hdl;
758eda14cbcSMatt Macy 	const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;
759eda14cbcSMatt Macy 	fmd_event_t faux_event = {0};
760eda14cbcSMatt Macy 	int64_t *tv;
761eda14cbcSMatt Macy 	uint_t n;
762eda14cbcSMatt Macy 
763eda14cbcSMatt Macy 	/*
764eda14cbcSMatt Macy 	 * Will need to normalized this if we persistently store the case data
765eda14cbcSMatt Macy 	 */
766eda14cbcSMatt Macy 	if (nvlist_lookup_int64_array(nvl, FM_EREPORT_TIME, &tv, &n) == 0)
767eda14cbcSMatt Macy 		faux_event.ev_hrt = tv[0] * NANOSEC + tv[1];
768eda14cbcSMatt Macy 	else
769eda14cbcSMatt Macy 		faux_event.ev_hrt = 0;
770eda14cbcSMatt Macy 
771eda14cbcSMatt Macy 	ops->fmdo_recv(hdl, &faux_event, nvl, class);
772eda14cbcSMatt Macy 
773eda14cbcSMatt Macy 	mp->mod_stats.ms_accepted.fmds_value.ui64++;
774eda14cbcSMatt Macy 
775eda14cbcSMatt Macy 	/* TBD - should we initiate fm_module_gc() periodically? */
776eda14cbcSMatt Macy }
777