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 https://opensource.org/licenses/CDDL-1.0.
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 <string.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 *
80 _umem_debug_init(void)
81 {
82 return ("default,verbose"); /* $UMEM_DEBUG setting */
83 }
84
85 const char *
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
98 fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip)
99 {
100 (void) version;
101 fmd_module_t *mp = (fmd_module_t *)hdl;
102
103 mp->mod_info = mip;
104 mp->mod_name = mip->fmdi_desc + 4; /* drop 'ZFS ' prefix */
105 mp->mod_spec = NULL;
106
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");
112
113 fmd_serd_hash_create(&mp->mod_serds);
114
115 fmd_hdl_debug(hdl, "register module");
116
117 return (0);
118 }
119
120 void
121 fmd_hdl_unregister(fmd_hdl_t *hdl)
122 {
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;
126
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);
137 }
138
139 /* dump module specific stats */
140 if (mp->mod_ustat != NULL) {
141 int i;
142
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);
147 }
148 }
149
150 fmd_serd_hash_destroy(&mp->mod_serds);
151
152 fmd_hdl_debug(hdl, "unregister module");
153 }
154
155 /*
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().
159 */
160 void
161 fmd_hdl_setspecific(fmd_hdl_t *hdl, void *spec)
162 {
163 fmd_module_t *mp = (fmd_module_t *)hdl;
164
165 mp->mod_spec = spec;
166 }
167
168 /*
169 * Return the module-specific data pointer previously associated
170 * with the handle using fmd_hdl_setspecific().
171 */
172 void *
173 fmd_hdl_getspecific(fmd_hdl_t *hdl)
174 {
175 fmd_module_t *mp = (fmd_module_t *)hdl;
176
177 return (mp->mod_spec);
178 }
179
180 void *
181 fmd_hdl_alloc(fmd_hdl_t *hdl, size_t size, int flags)
182 {
183 (void) hdl;
184 return (umem_alloc(size, flags));
185 }
186
187 void *
188 fmd_hdl_zalloc(fmd_hdl_t *hdl, size_t size, int flags)
189 {
190 (void) hdl;
191 return (umem_zalloc(size, flags));
192 }
193
194 void
195 fmd_hdl_free(fmd_hdl_t *hdl, void *data, size_t size)
196 {
197 (void) hdl;
198 umem_free(data, size);
199 }
200
201 /*
202 * Record a module debug message using the specified format.
203 */
204 void
205 fmd_hdl_debug(fmd_hdl_t *hdl, const char *format, ...)
206 {
207 char message[256];
208 va_list vargs;
209 fmd_module_t *mp = (fmd_module_t *)hdl;
210
211 va_start(vargs, format);
212 (void) vsnprintf(message, sizeof (message), format, vargs);
213 va_end(vargs);
214
215 /* prefix message with module name */
216 zed_log_msg(LOG_INFO, "%s: %s", mp->mod_name, message);
217 }
218
219 /* Property Retrieval */
220
221 int32_t
222 fmd_prop_get_int32(fmd_hdl_t *hdl, const char *name)
223 {
224 (void) hdl;
225
226 /*
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.
230 */
231 if (strcmp(name, "spare_on_remove") == 0)
232 return (1);
233
234 if (strcmp(name, "io_N") == 0 || strcmp(name, "checksum_N") == 0)
235 return (10); /* N = 10 events */
236
237 return (0);
238 }
239
240 int64_t
241 fmd_prop_get_int64(fmd_hdl_t *hdl, const char *name)
242 {
243 (void) hdl;
244
245 /*
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.
249 */
250 if (strcmp(name, "remove_timeout") == 0)
251 return (15ULL * 1000ULL * 1000ULL * 1000ULL); /* 15 sec */
252
253 if (strcmp(name, "io_T") == 0 || strcmp(name, "checksum_T") == 0)
254 return (1000ULL * 1000ULL * 1000ULL * 600ULL); /* 10 min */
255
256 return (0);
257 }
258
259 /* FMD Statistics */
260
261 fmd_stat_t *
262 fmd_stat_create(fmd_hdl_t *hdl, uint_t flags, uint_t nstats, fmd_stat_t *statv)
263 {
264 fmd_module_t *mp = (fmd_module_t *)hdl;
265
266 if (flags == FMD_STAT_NOALLOC) {
267 mp->mod_ustat = statv;
268 mp->mod_ustat_cnt = nstats;
269 }
270
271 return (statv);
272 }
273
274 /* Case Management */
275
276 fmd_case_t *
277 fmd_case_open(fmd_hdl_t *hdl, void *data)
278 {
279 fmd_module_t *mp = (fmd_module_t *)hdl;
280 uuid_t uuid;
281
282 fmd_case_t *cp;
283
284 cp = fmd_hdl_zalloc(hdl, sizeof (fmd_case_t), FMD_SLEEP);
285 cp->ci_mod = hdl;
286 cp->ci_state = FMD_CASE_UNSOLVED;
287 cp->ci_flags = FMD_CF_DIRTY;
288 cp->ci_data = data;
289 cp->ci_bufptr = NULL;
290 cp->ci_bufsiz = 0;
291
292 uuid_generate(uuid);
293 uuid_unparse(uuid, cp->ci_uuid);
294
295 fmd_hdl_debug(hdl, "case opened (%s)", cp->ci_uuid);
296 mp->mod_stats.ms_caseopen.fmds_value.ui64++;
297
298 return (cp);
299 }
300
301 void
302 fmd_case_solve(fmd_hdl_t *hdl, fmd_case_t *cp)
303 {
304 fmd_module_t *mp = (fmd_module_t *)hdl;
305
306 /*
307 * For ZED, the event was already sent from fmd_case_add_suspect()
308 */
309
310 if (cp->ci_state >= FMD_CASE_SOLVED)
311 fmd_hdl_debug(hdl, "case is already solved or closed");
312
313 cp->ci_state = FMD_CASE_SOLVED;
314
315 fmd_hdl_debug(hdl, "case solved (%s)", cp->ci_uuid);
316 mp->mod_stats.ms_casesolved.fmds_value.ui64++;
317 }
318
319 void
320 fmd_case_close(fmd_hdl_t *hdl, fmd_case_t *cp)
321 {
322 fmd_module_t *mp = (fmd_module_t *)hdl;
323 const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;
324
325 fmd_hdl_debug(hdl, "case closed (%s)", cp->ci_uuid);
326
327 if (ops->fmdo_close != NULL)
328 ops->fmdo_close(hdl, cp);
329
330 mp->mod_stats.ms_caseopen.fmds_value.ui64--;
331 mp->mod_stats.ms_caseclosed.fmds_value.ui64++;
332
333 if (cp->ci_bufptr != NULL && cp->ci_bufsiz > 0)
334 fmd_hdl_free(hdl, cp->ci_bufptr, cp->ci_bufsiz);
335
336 fmd_hdl_free(hdl, cp, sizeof (fmd_case_t));
337 }
338
339 void
340 fmd_case_uuresolved(fmd_hdl_t *hdl, const char *uuid)
341 {
342 fmd_hdl_debug(hdl, "case resolved by uuid (%s)", uuid);
343 }
344
345 boolean_t
346 fmd_case_solved(fmd_hdl_t *hdl, fmd_case_t *cp)
347 {
348 (void) hdl;
349 return (cp->ci_state >= FMD_CASE_SOLVED);
350 }
351
352 void
353 fmd_case_add_ereport(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep)
354 {
355 (void) hdl, (void) cp, (void) ep;
356 }
357
358 static void
359 zed_log_fault(nvlist_t *nvl, const char *uuid, const char *code)
360 {
361 nvlist_t *rsrc;
362 char *strval;
363 uint64_t guid;
364 uint8_t byte;
365
366 zed_log_msg(LOG_INFO, "\nzed_fault_event:");
367
368 if (uuid != NULL)
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);
372 if (code != NULL)
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,
379 strval);
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,
382 guid);
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,
385 guid);
386 }
387 }
388
389 static const char *
390 fmd_fault_mkcode(nvlist_t *fault)
391 {
392 char *class;
393 const char *code = "-";
394
395 /*
396 * Note: message codes come from: openzfs/usr/src/cmd/fm/dicts/ZFS.po
397 */
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";
413
414 }
415 return (code);
416 }
417
418 void
419 fmd_case_add_suspect(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *fault)
420 {
421 nvlist_t *nvl;
422 const char *code = fmd_fault_mkcode(fault);
423 int64_t tod[2];
424 int err = 0;
425
426 /*
427 * payload derived from fmd_protocol_list()
428 */
429
430 (void) gettimeofday(&cp->ci_tv, NULL);
431 tod[0] = cp->ci_tv.tv_sec;
432 tod[1] = cp->ci_tv.tv_usec;
433
434 nvl = fmd_nvl_alloc(hdl, FMD_SLEEP);
435
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);
444
445 if (err)
446 zed_log_die("failed to populate nvlist");
447
448 zed_log_fault(fault, cp->ci_uuid, code);
449 zfs_agent_post_event(FM_LIST_SUSPECT_CLASS, NULL, nvl);
450
451 nvlist_free(nvl);
452 nvlist_free(fault);
453 }
454
455 void
456 fmd_case_setspecific(fmd_hdl_t *hdl, fmd_case_t *cp, void *data)
457 {
458 (void) hdl;
459 cp->ci_data = data;
460 }
461
462 void *
463 fmd_case_getspecific(fmd_hdl_t *hdl, fmd_case_t *cp)
464 {
465 (void) hdl;
466 return (cp->ci_data);
467 }
468
469 void
470 fmd_buf_create(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, size_t size)
471 {
472 assert(strcmp(name, "data") == 0), (void) name;
473 assert(cp->ci_bufptr == NULL);
474 assert(size < (1024 * 1024));
475
476 cp->ci_bufptr = fmd_hdl_alloc(hdl, size, FMD_SLEEP);
477 cp->ci_bufsiz = size;
478 }
479
480 void
481 fmd_buf_read(fmd_hdl_t *hdl, fmd_case_t *cp,
482 const char *name, void *buf, size_t size)
483 {
484 (void) hdl;
485 assert(strcmp(name, "data") == 0), (void) name;
486 assert(cp->ci_bufptr != NULL);
487 assert(size <= cp->ci_bufsiz);
488
489 memcpy(buf, cp->ci_bufptr, size);
490 }
491
492 void
493 fmd_buf_write(fmd_hdl_t *hdl, fmd_case_t *cp,
494 const char *name, const void *buf, size_t size)
495 {
496 (void) hdl;
497 assert(strcmp(name, "data") == 0), (void) name;
498 assert(cp->ci_bufptr != NULL);
499 assert(cp->ci_bufsiz >= size);
500
501 memcpy(cp->ci_bufptr, buf, size);
502 }
503
504 /* SERD Engines */
505
506 void
507 fmd_serd_create(fmd_hdl_t *hdl, const char *name, uint_t n, hrtime_t t)
508 {
509 fmd_module_t *mp = (fmd_module_t *)hdl;
510
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);
514 return;
515 }
516
517 (void) fmd_serd_eng_insert(&mp->mod_serds, name, n, t);
518 }
519
520 void
521 fmd_serd_destroy(fmd_hdl_t *hdl, const char *name)
522 {
523 fmd_module_t *mp = (fmd_module_t *)hdl;
524
525 fmd_serd_eng_delete(&mp->mod_serds, name);
526
527 fmd_hdl_debug(hdl, "serd_destroy %s", name);
528 }
529
530 int
531 fmd_serd_exists(fmd_hdl_t *hdl, const char *name)
532 {
533 fmd_module_t *mp = (fmd_module_t *)hdl;
534
535 return (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL);
536 }
537
538 void
539 fmd_serd_reset(fmd_hdl_t *hdl, const char *name)
540 {
541 fmd_module_t *mp = (fmd_module_t *)hdl;
542 fmd_serd_eng_t *sgp;
543
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);
546 return;
547 }
548
549 fmd_serd_eng_reset(sgp);
550
551 fmd_hdl_debug(hdl, "serd_reset %s", name);
552 }
553
554 int
555 fmd_serd_record(fmd_hdl_t *hdl, const char *name, fmd_event_t *ep)
556 {
557 fmd_module_t *mp = (fmd_module_t *)hdl;
558 fmd_serd_eng_t *sgp;
559 int err;
560
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'",
563 name);
564 return (0);
565 }
566 err = fmd_serd_eng_record(sgp, ep->ev_hrt);
567
568 return (err);
569 }
570
571 /* FMD Timers */
572
573 static void
574 _timer_notify(union sigval sv)
575 {
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;
581
582 fmd_hdl_debug(hdl, "timer fired (%p)", ftp->ft_tid);
583
584 /* disarm the timer */
585 memset(&its, 0, sizeof (struct itimerspec));
586 timer_settime(ftp->ft_tid, 0, &its, NULL);
587
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);
591 }
592
593 /*
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.
597 */
598 fmd_timer_t *
599 fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta)
600 {
601 (void) ep;
602 struct sigevent sev;
603 struct itimerspec its;
604 fmd_timer_t *ftp;
605
606 ftp = fmd_hdl_alloc(hdl, sizeof (fmd_timer_t), FMD_SLEEP);
607 ftp->ft_arg = arg;
608 ftp->ft_hdl = hdl;
609
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;
614
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;
619 sev.sigev_signo = 0;
620
621 timer_create(CLOCK_REALTIME, &sev, &ftp->ft_tid);
622 timer_settime(ftp->ft_tid, 0, &its, NULL);
623
624 fmd_hdl_debug(hdl, "installing timer for %d secs (%p)",
625 (int)its.it_value.tv_sec, ftp->ft_tid);
626
627 return (ftp);
628 }
629
630 void
631 fmd_timer_remove(fmd_hdl_t *hdl, fmd_timer_t *ftp)
632 {
633 fmd_hdl_debug(hdl, "removing timer (%p)", ftp->ft_tid);
634
635 timer_delete(ftp->ft_tid);
636
637 fmd_hdl_free(hdl, ftp, sizeof (fmd_timer_t));
638 }
639
640 /* Name-Value Pair Lists */
641
642 nvlist_t *
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)
645 {
646 (void) hdl;
647 nvlist_t *nvl;
648 int err = 0;
649
650 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
651 zed_log_die("failed to xalloc fault nvlist");
652
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);
656
657 if (asru != NULL)
658 err |= nvlist_add_nvlist(nvl, FM_FAULT_ASRU, asru);
659 if (fru != NULL)
660 err |= nvlist_add_nvlist(nvl, FM_FAULT_FRU, fru);
661 if (resource != NULL)
662 err |= nvlist_add_nvlist(nvl, FM_FAULT_RESOURCE, resource);
663
664 if (err)
665 zed_log_die("failed to populate nvlist: %s\n", strerror(err));
666
667 return (nvl);
668 }
669
670 /*
671 * sourced from fmd_string.c
672 */
673 static int
674 fmd_strmatch(const char *s, const char *p)
675 {
676 char c;
677
678 if (p == NULL)
679 return (0);
680
681 if (s == NULL)
682 s = ""; /* treat NULL string as the empty string */
683
684 do {
685 if ((c = *p++) == '\0')
686 return (*s == '\0');
687
688 if (c == '*') {
689 while (*p == '*')
690 p++; /* consecutive *'s can be collapsed */
691
692 if (*p == '\0')
693 return (1);
694
695 while (*s != '\0') {
696 if (fmd_strmatch(s++, p) != 0)
697 return (1);
698 }
699
700 return (0);
701 }
702 } while (c == *s++);
703
704 return (0);
705 }
706
707 int
708 fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern)
709 {
710 (void) hdl;
711 char *class;
712
713 return (nvl != NULL &&
714 nvlist_lookup_string(nvl, FM_CLASS, &class) == 0 &&
715 fmd_strmatch(class, pattern));
716 }
717
718 nvlist_t *
719 fmd_nvl_alloc(fmd_hdl_t *hdl, int flags)
720 {
721 (void) hdl, (void) flags;
722 nvlist_t *nvl = NULL;
723
724 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
725 return (NULL);
726
727 return (nvl);
728 }
729
730
731 /*
732 * ZED Agent specific APIs
733 */
734
735 fmd_hdl_t *
736 fmd_module_hdl(const char *name)
737 {
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);
742
743 return (NULL);
744 }
745
746 boolean_t
747 fmd_module_initialized(fmd_hdl_t *hdl)
748 {
749 fmd_module_t *mp = (fmd_module_t *)hdl;
750
751 return (mp->mod_info != NULL);
752 }
753
754 /*
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.
758 */
759 void
760 fmd_module_recv(fmd_hdl_t *hdl, nvlist_t *nvl, const char *class)
761 {
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};
765 int64_t *tv;
766 uint_t n;
767
768 /*
769 * Will need to normalized this if we persistently store the case data
770 */
771 if (nvlist_lookup_int64_array(nvl, FM_EREPORT_TIME, &tv, &n) == 0)
772 faux_event.ev_hrt = tv[0] * NANOSEC + tv[1];
773 else
774 faux_event.ev_hrt = 0;
775
776 ops->fmdo_recv(hdl, &faux_event, nvl, class);
777
778 mp->mod_stats.ms_accepted.fmds_value.ui64++;
779
780 /* TBD - should we initiate fm_module_gc() periodically? */
781 }
Cache object: 1ce8e943141abd732f08dcdcebd392f4
|