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 <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: %llu", 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, *code = "-"; 393 394 /* 395 * Note: message codes come from: openzfs/usr/src/cmd/fm/dicts/ZFS.po 396 */ 397 if (nvlist_lookup_string(fault, FM_CLASS, &class) == 0) { 398 if (strcmp(class, "fault.fs.zfs.vdev.io") == 0) 399 code = "ZFS-8000-FD"; 400 else if (strcmp(class, "fault.fs.zfs.vdev.checksum") == 0) 401 code = "ZFS-8000-GH"; 402 else if (strcmp(class, "fault.fs.zfs.io_failure_wait") == 0) 403 code = "ZFS-8000-HC"; 404 else if (strcmp(class, "fault.fs.zfs.io_failure_continue") == 0) 405 code = "ZFS-8000-JQ"; 406 else if (strcmp(class, "fault.fs.zfs.log_replay") == 0) 407 code = "ZFS-8000-K4"; 408 else if (strcmp(class, "fault.fs.zfs.pool") == 0) 409 code = "ZFS-8000-CS"; 410 else if (strcmp(class, "fault.fs.zfs.device") == 0) 411 code = "ZFS-8000-D3"; 412 413 } 414 return (code); 415 } 416 417 void 418 fmd_case_add_suspect(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *fault) 419 { 420 nvlist_t *nvl; 421 const char *code = fmd_fault_mkcode(fault); 422 int64_t tod[2]; 423 int err = 0; 424 425 /* 426 * payload derived from fmd_protocol_list() 427 */ 428 429 (void) gettimeofday(&cp->ci_tv, NULL); 430 tod[0] = cp->ci_tv.tv_sec; 431 tod[1] = cp->ci_tv.tv_usec; 432 433 nvl = fmd_nvl_alloc(hdl, FMD_SLEEP); 434 435 err |= nvlist_add_uint8(nvl, FM_VERSION, FM_SUSPECT_VERSION); 436 err |= nvlist_add_string(nvl, FM_CLASS, FM_LIST_SUSPECT_CLASS); 437 err |= nvlist_add_string(nvl, FM_SUSPECT_UUID, cp->ci_uuid); 438 err |= nvlist_add_string(nvl, FM_SUSPECT_DIAG_CODE, code); 439 err |= nvlist_add_int64_array(nvl, FM_SUSPECT_DIAG_TIME, tod, 2); 440 err |= nvlist_add_uint32(nvl, FM_SUSPECT_FAULT_SZ, 1); 441 err |= nvlist_add_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, 442 (const nvlist_t **)&fault, 1); 443 444 if (err) 445 zed_log_die("failed to populate nvlist"); 446 447 zed_log_fault(fault, cp->ci_uuid, code); 448 zfs_agent_post_event(FM_LIST_SUSPECT_CLASS, NULL, nvl); 449 450 nvlist_free(nvl); 451 nvlist_free(fault); 452 } 453 454 void 455 fmd_case_setspecific(fmd_hdl_t *hdl, fmd_case_t *cp, void *data) 456 { 457 (void) hdl; 458 cp->ci_data = data; 459 } 460 461 void * 462 fmd_case_getspecific(fmd_hdl_t *hdl, fmd_case_t *cp) 463 { 464 (void) hdl; 465 return (cp->ci_data); 466 } 467 468 void 469 fmd_buf_create(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, size_t size) 470 { 471 assert(strcmp(name, "data") == 0), (void) name; 472 assert(cp->ci_bufptr == NULL); 473 assert(size < (1024 * 1024)); 474 475 cp->ci_bufptr = fmd_hdl_alloc(hdl, size, FMD_SLEEP); 476 cp->ci_bufsiz = size; 477 } 478 479 void 480 fmd_buf_read(fmd_hdl_t *hdl, fmd_case_t *cp, 481 const char *name, void *buf, size_t size) 482 { 483 (void) hdl; 484 assert(strcmp(name, "data") == 0), (void) name; 485 assert(cp->ci_bufptr != NULL); 486 assert(size <= cp->ci_bufsiz); 487 488 memcpy(buf, cp->ci_bufptr, size); 489 } 490 491 void 492 fmd_buf_write(fmd_hdl_t *hdl, fmd_case_t *cp, 493 const char *name, const void *buf, size_t size) 494 { 495 (void) hdl; 496 assert(strcmp(name, "data") == 0), (void) name; 497 assert(cp->ci_bufptr != NULL); 498 assert(cp->ci_bufsiz >= size); 499 500 memcpy(cp->ci_bufptr, buf, size); 501 } 502 503 /* SERD Engines */ 504 505 void 506 fmd_serd_create(fmd_hdl_t *hdl, const char *name, uint_t n, hrtime_t t) 507 { 508 fmd_module_t *mp = (fmd_module_t *)hdl; 509 510 if (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL) { 511 zed_log_msg(LOG_ERR, "failed to create SERD engine '%s': " 512 " name already exists", name); 513 return; 514 } 515 516 (void) fmd_serd_eng_insert(&mp->mod_serds, name, n, t); 517 } 518 519 void 520 fmd_serd_destroy(fmd_hdl_t *hdl, const char *name) 521 { 522 fmd_module_t *mp = (fmd_module_t *)hdl; 523 524 fmd_serd_eng_delete(&mp->mod_serds, name); 525 526 fmd_hdl_debug(hdl, "serd_destroy %s", name); 527 } 528 529 int 530 fmd_serd_exists(fmd_hdl_t *hdl, const char *name) 531 { 532 fmd_module_t *mp = (fmd_module_t *)hdl; 533 534 return (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL); 535 } 536 537 void 538 fmd_serd_reset(fmd_hdl_t *hdl, const char *name) 539 { 540 fmd_module_t *mp = (fmd_module_t *)hdl; 541 fmd_serd_eng_t *sgp; 542 543 if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) { 544 zed_log_msg(LOG_ERR, "serd engine '%s' does not exist", name); 545 return; 546 } 547 548 fmd_serd_eng_reset(sgp); 549 550 fmd_hdl_debug(hdl, "serd_reset %s", name); 551 } 552 553 int 554 fmd_serd_record(fmd_hdl_t *hdl, const char *name, fmd_event_t *ep) 555 { 556 fmd_module_t *mp = (fmd_module_t *)hdl; 557 fmd_serd_eng_t *sgp; 558 int err; 559 560 if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) { 561 zed_log_msg(LOG_ERR, "failed to add record to SERD engine '%s'", 562 name); 563 return (0); 564 } 565 err = fmd_serd_eng_record(sgp, ep->ev_hrt); 566 567 return (err); 568 } 569 570 /* FMD Timers */ 571 572 static void 573 _timer_notify(union sigval sv) 574 { 575 fmd_timer_t *ftp = sv.sival_ptr; 576 fmd_hdl_t *hdl = ftp->ft_hdl; 577 fmd_module_t *mp = (fmd_module_t *)hdl; 578 const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops; 579 struct itimerspec its; 580 581 fmd_hdl_debug(hdl, "timer fired (%p)", ftp->ft_tid); 582 583 /* disarm the timer */ 584 memset(&its, 0, sizeof (struct itimerspec)); 585 timer_settime(ftp->ft_tid, 0, &its, NULL); 586 587 /* Note that the fmdo_timeout can remove this timer */ 588 if (ops->fmdo_timeout != NULL) 589 ops->fmdo_timeout(hdl, ftp, ftp->ft_arg); 590 } 591 592 /* 593 * Install a new timer which will fire at least delta nanoseconds after the 594 * current time. After the timeout has expired, the module's fmdo_timeout 595 * entry point is called. 596 */ 597 fmd_timer_t * 598 fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta) 599 { 600 (void) ep; 601 struct sigevent sev; 602 struct itimerspec its; 603 fmd_timer_t *ftp; 604 605 ftp = fmd_hdl_alloc(hdl, sizeof (fmd_timer_t), FMD_SLEEP); 606 ftp->ft_arg = arg; 607 ftp->ft_hdl = hdl; 608 609 its.it_value.tv_sec = delta / 1000000000; 610 its.it_value.tv_nsec = delta % 1000000000; 611 its.it_interval.tv_sec = its.it_value.tv_sec; 612 its.it_interval.tv_nsec = its.it_value.tv_nsec; 613 614 sev.sigev_notify = SIGEV_THREAD; 615 sev.sigev_notify_function = _timer_notify; 616 sev.sigev_notify_attributes = NULL; 617 sev.sigev_value.sival_ptr = ftp; 618 619 timer_create(CLOCK_REALTIME, &sev, &ftp->ft_tid); 620 timer_settime(ftp->ft_tid, 0, &its, NULL); 621 622 fmd_hdl_debug(hdl, "installing timer for %d secs (%p)", 623 (int)its.it_value.tv_sec, ftp->ft_tid); 624 625 return (ftp); 626 } 627 628 void 629 fmd_timer_remove(fmd_hdl_t *hdl, fmd_timer_t *ftp) 630 { 631 fmd_hdl_debug(hdl, "removing timer (%p)", ftp->ft_tid); 632 633 timer_delete(ftp->ft_tid); 634 635 fmd_hdl_free(hdl, ftp, sizeof (fmd_timer_t)); 636 } 637 638 /* Name-Value Pair Lists */ 639 640 nvlist_t * 641 fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class, uint8_t certainty, 642 nvlist_t *asru, nvlist_t *fru, nvlist_t *resource) 643 { 644 (void) hdl; 645 nvlist_t *nvl; 646 int err = 0; 647 648 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 649 zed_log_die("failed to xalloc fault nvlist"); 650 651 err |= nvlist_add_uint8(nvl, FM_VERSION, FM_FAULT_VERSION); 652 err |= nvlist_add_string(nvl, FM_CLASS, class); 653 err |= nvlist_add_uint8(nvl, FM_FAULT_CERTAINTY, certainty); 654 655 if (asru != NULL) 656 err |= nvlist_add_nvlist(nvl, FM_FAULT_ASRU, asru); 657 if (fru != NULL) 658 err |= nvlist_add_nvlist(nvl, FM_FAULT_FRU, fru); 659 if (resource != NULL) 660 err |= nvlist_add_nvlist(nvl, FM_FAULT_RESOURCE, resource); 661 662 if (err) 663 zed_log_die("failed to populate nvlist: %s\n", strerror(err)); 664 665 return (nvl); 666 } 667 668 /* 669 * sourced from fmd_string.c 670 */ 671 static int 672 fmd_strmatch(const char *s, const char *p) 673 { 674 char c; 675 676 if (p == NULL) 677 return (0); 678 679 if (s == NULL) 680 s = ""; /* treat NULL string as the empty string */ 681 682 do { 683 if ((c = *p++) == '\0') 684 return (*s == '\0'); 685 686 if (c == '*') { 687 while (*p == '*') 688 p++; /* consecutive *'s can be collapsed */ 689 690 if (*p == '\0') 691 return (1); 692 693 while (*s != '\0') { 694 if (fmd_strmatch(s++, p) != 0) 695 return (1); 696 } 697 698 return (0); 699 } 700 } while (c == *s++); 701 702 return (0); 703 } 704 705 int 706 fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern) 707 { 708 (void) hdl; 709 char *class; 710 711 return (nvl != NULL && 712 nvlist_lookup_string(nvl, FM_CLASS, &class) == 0 && 713 fmd_strmatch(class, pattern)); 714 } 715 716 nvlist_t * 717 fmd_nvl_alloc(fmd_hdl_t *hdl, int flags) 718 { 719 (void) hdl, (void) flags; 720 nvlist_t *nvl = NULL; 721 722 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 723 return (NULL); 724 725 return (nvl); 726 } 727 728 729 /* 730 * ZED Agent specific APIs 731 */ 732 733 fmd_hdl_t * 734 fmd_module_hdl(const char *name) 735 { 736 if (strcmp(name, "zfs-retire") == 0) 737 return ((fmd_hdl_t *)&zfs_retire_module); 738 if (strcmp(name, "zfs-diagnosis") == 0) 739 return ((fmd_hdl_t *)&zfs_diagnosis_module); 740 741 return (NULL); 742 } 743 744 boolean_t 745 fmd_module_initialized(fmd_hdl_t *hdl) 746 { 747 fmd_module_t *mp = (fmd_module_t *)hdl; 748 749 return (mp->mod_info != NULL); 750 } 751 752 /* 753 * fmd_module_recv is called for each event that is received by 754 * the fault manager that has a class that matches one of the 755 * module's subscriptions. 756 */ 757 void 758 fmd_module_recv(fmd_hdl_t *hdl, nvlist_t *nvl, const char *class) 759 { 760 fmd_module_t *mp = (fmd_module_t *)hdl; 761 const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops; 762 fmd_event_t faux_event = {0}; 763 int64_t *tv; 764 uint_t n; 765 766 /* 767 * Will need to normalized this if we persistently store the case data 768 */ 769 if (nvlist_lookup_int64_array(nvl, FM_EREPORT_TIME, &tv, &n) == 0) 770 faux_event.ev_hrt = tv[0] * NANOSEC + tv[1]; 771 else 772 faux_event.ev_hrt = 0; 773 774 ops->fmdo_recv(hdl, &faux_event, nvl, class); 775 776 mp->mod_stats.ms_accepted.fmds_value.ui64++; 777 778 /* TBD - should we initiate fm_module_gc() periodically? */ 779 } 780