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 } 782