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