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