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