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 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/fm/protocol.h> 30 #include <uuid/uuid.h> 31 32 #include <dirent.h> 33 #include <limits.h> 34 #include <unistd.h> 35 #include <alloca.h> 36 37 #include <fmd_alloc.h> 38 #include <fmd_string.h> 39 #include <fmd_error.h> 40 #include <fmd_subr.h> 41 #include <fmd_protocol.h> 42 #include <fmd_event.h> 43 #include <fmd_conf.h> 44 #include <fmd_fmri.h> 45 #include <fmd_dispq.h> 46 #include <fmd_case.h> 47 #include <fmd_module.h> 48 #include <fmd_asru.h> 49 50 #include <fmd.h> 51 52 static const char *const _fmd_asru_events[] = { 53 FMD_RSRC_CLASS "asru.ok", /* UNUSABLE=0 FAULTED=0 */ 54 FMD_RSRC_CLASS "asru.degraded", /* UNUSABLE=0 FAULTED=1 */ 55 FMD_RSRC_CLASS "asru.unknown", /* UNUSABLE=1 FAULTED=0 */ 56 FMD_RSRC_CLASS "asru.faulted" /* UNUSABLE=1 FAULTED=1 */ 57 }; 58 59 static const char *const _fmd_asru_snames[] = { 60 "uf", "uF", "Uf", "UF" /* same order as above */ 61 }; 62 63 volatile uint32_t fmd_asru_fake_not_present = 0; 64 65 static fmd_asru_t * 66 fmd_asru_create(fmd_asru_hash_t *ahp, const char *uuid, 67 const char *name, nvlist_t *fmri) 68 { 69 fmd_asru_t *ap = fmd_alloc(sizeof (fmd_asru_t), FMD_SLEEP); 70 char *s; 71 72 (void) pthread_mutex_init(&ap->asru_lock, NULL); 73 (void) pthread_cond_init(&ap->asru_cv, NULL); 74 75 ap->asru_next = NULL; 76 ap->asru_name = fmd_strdup(name, FMD_SLEEP); 77 (void) nvlist_xdup(fmri, &ap->asru_fmri, &fmd.d_nva); 78 ap->asru_root = fmd_strdup(ahp->ah_dirpath, FMD_SLEEP); 79 ap->asru_uuid = fmd_strdup(uuid, FMD_SLEEP); 80 ap->asru_uuidlen = ap->asru_uuid ? strlen(ap->asru_uuid) : 0; 81 ap->asru_log = NULL; 82 ap->asru_refs = 1; 83 ap->asru_flags = 0; 84 ap->asru_case = NULL; 85 ap->asru_event = NULL; 86 87 if (nvlist_lookup_string(ap->asru_fmri, FM_FMRI_SCHEME, &s) == 0 && 88 strcmp(s, FM_FMRI_SCHEME_FMD) == 0) 89 ap->asru_flags |= FMD_ASRU_INTERNAL; 90 91 return (ap); 92 } 93 94 static void 95 fmd_asru_destroy(fmd_asru_t *ap) 96 { 97 ASSERT(MUTEX_HELD(&ap->asru_lock)); 98 ASSERT(ap->asru_refs == 0); 99 100 if (ap->asru_log != NULL) 101 fmd_log_rele(ap->asru_log); 102 103 if (ap->asru_case != NULL) 104 fmd_case_rele(ap->asru_case); 105 106 nvlist_free(ap->asru_event); 107 fmd_strfree(ap->asru_name); 108 nvlist_free(ap->asru_fmri); 109 fmd_strfree(ap->asru_root); 110 fmd_free(ap->asru_uuid, ap->asru_uuidlen + 1); 111 fmd_free(ap, sizeof (fmd_asru_t)); 112 } 113 114 static void 115 fmd_asru_hash_insert(fmd_asru_hash_t *ahp, fmd_asru_t *ap) 116 { 117 uint_t h = fmd_strhash(ap->asru_name) % ahp->ah_hashlen; 118 119 ASSERT(RW_WRITE_HELD(&ahp->ah_lock)); 120 ap->asru_next = ahp->ah_hash[h]; 121 ahp->ah_hash[h] = ap; 122 ahp->ah_count++; 123 } 124 125 static fmd_asru_t * 126 fmd_asru_hold(fmd_asru_t *ap) 127 { 128 (void) pthread_mutex_lock(&ap->asru_lock); 129 ap->asru_refs++; 130 ASSERT(ap->asru_refs != 0); 131 (void) pthread_mutex_unlock(&ap->asru_lock); 132 return (ap); 133 } 134 135 /* 136 * Lookup an asru in the hash by name and place a hold on it. If the asru is 137 * not found, no entry is created and NULL is returned. This internal function 138 * is for callers who have the ah_lock held and is used by lookup_name below. 139 */ 140 fmd_asru_t * 141 fmd_asru_hash_lookup(fmd_asru_hash_t *ahp, const char *name) 142 { 143 fmd_asru_t *ap; 144 uint_t h; 145 146 ASSERT(RW_LOCK_HELD(&ahp->ah_lock)); 147 h = fmd_strhash(name) % ahp->ah_hashlen; 148 149 for (ap = ahp->ah_hash[h]; ap != NULL; ap = ap->asru_next) { 150 if (strcmp(ap->asru_name, name) == 0) 151 break; 152 } 153 154 if (ap != NULL) 155 (void) fmd_asru_hold(ap); 156 else 157 (void) fmd_set_errno(EFMD_ASRU_NOENT); 158 159 return (ap); 160 } 161 162 static void 163 fmd_asru_hash_recreate(fmd_log_t *lp, fmd_event_t *ep, fmd_asru_hash_t *ahp) 164 { 165 nvlist_t *nvl = FMD_EVENT_NVL(ep); 166 char *case_uuid = NULL, *case_code = NULL; 167 char *name = NULL; 168 ssize_t namelen; 169 170 nvlist_t *fmri, *flt, *flt_copy; 171 boolean_t f, u, m; 172 fmd_asru_t *ap; 173 int ps, us; 174 int64_t *diag_time; 175 uint_t nelem; 176 177 /* 178 * Extract the resource FMRI and most recent values of 'faulty' and 179 * 'unusable' from the event log. If the event is malformed, return. 180 */ 181 if (nvlist_lookup_nvlist(nvl, FM_RSRC_RESOURCE, &fmri) != 0 || 182 nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_FAULTY, &f) != 0 || 183 nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_UNUSABLE, &u) != 0) { 184 fmd_error(EFMD_ASRU_EVENT, "failed to reload asru %s: " 185 "invalid event log record\n", lp->log_name); 186 ahp->ah_error = EFMD_ASRU_EVENT; 187 return; 188 } 189 190 /* 191 * If this resource has been explicitly repaired, then return and 192 * discard the log. This is consistant with the behaviour when rsrc.age 193 * expires below. 194 */ 195 if (!f) 196 return; 197 198 /* 199 * Check to see if the resource is still present in the system. If 200 * so, then update the value of the unusable bit based on the current 201 * system configuration. If not, then either keep the entry in our 202 * cache if it is recent, or return and discard it if it is too old. 203 */ 204 if (fmd_asru_fake_not_present) 205 ps = 0; 206 else if ((ps = fmd_fmri_present(fmri)) == -1) { 207 fmd_error(EFMD_ASRU_FMRI, "failed to locate %s", lp->log_name); 208 ahp->ah_error = EFMD_ASRU_FMRI; 209 return; 210 } 211 212 if (ps) { 213 if ((us = fmd_fmri_unusable(fmri)) == -1) { 214 fmd_error(EFMD_ASRU_FMRI, "failed to update " 215 "status of asru %s", lp->log_name); 216 u = FMD_B_FALSE; 217 } else 218 u = us != 0; 219 220 } else { 221 struct timeval tv; 222 223 fmd_time_gettimeofday(&tv); 224 if ((hrtime_t)(tv.tv_sec - 225 lp->log_stat.st_mtime) * NANOSEC < ahp->ah_lifetime) { 226 u = FMD_B_TRUE; /* not present; set unusable */ 227 } else 228 return; /* too old; discard this log */ 229 } 230 231 /* 232 * In order to insert the ASRU into our hash, convert the FMRI from 233 * nvlist form into a string form and assign this name to the ASRU. 234 */ 235 if ((namelen = fmd_fmri_nvl2str(fmri, NULL, 0)) == -1 || 236 (name = fmd_alloc(namelen + 1, FMD_NOSLEEP)) == NULL || 237 fmd_fmri_nvl2str(fmri, name, namelen + 1) == -1) { 238 fmd_error(EFMD_ASRU_FMRI, 239 "failed to reload asru %s", lp->log_name); 240 if (name != NULL) 241 fmd_free(name, namelen + 1); 242 ahp->ah_error = EFMD_ASRU_FMRI; 243 return; 244 } 245 246 /* 247 * Look to see if the ASRU already exists in the hash: if it does and 248 * the existing ASRU entry is unusable but the duplicate is not, then 249 * delete the existing entry and continue on using the new entry; if 250 * the new entry is no "better", return an error and ignore it. 251 */ 252 if ((ap = fmd_asru_hash_lookup(ahp, name)) != NULL) { 253 if (!u && (ap->asru_flags & FMD_ASRU_UNUSABLE)) { 254 (void) fmd_asru_hash_delete_name(ahp, name); 255 fmd_asru_hash_release(ahp, ap); 256 } else { 257 fmd_error(EFMD_ASRU_DUP, "removing duplicate asru " 258 "log %s for %s\n", lp->log_name, name); 259 fmd_free(name, namelen + 1); 260 fmd_asru_hash_release(ahp, ap); 261 ahp->ah_error = EFMD_ASRU_DUP; 262 return; 263 } 264 } 265 266 ap = fmd_asru_create(ahp, fmd_strbasename(lp->log_name), name, fmri); 267 fmd_free(name, namelen + 1); 268 ap->asru_flags |= FMD_ASRU_RECREATED; 269 270 if (ps) 271 ap->asru_flags |= FMD_ASRU_PRESENT; 272 if (f) 273 ap->asru_flags |= FMD_ASRU_FAULTY; 274 if (u) 275 ap->asru_flags |= FMD_ASRU_UNUSABLE; 276 277 if (nvlist_lookup_boolean_value(nvl, 278 FM_SUSPECT_MESSAGE, &m) == 0 && m == B_FALSE) 279 ap->asru_flags |= FMD_ASRU_INVISIBLE; 280 281 /* 282 * Recreate the case in the CLOSED state. If the case is not closed, 283 * fmd_case_transition_update() will set it to the correct state later. 284 * If the case is already present, fmd_case_recreate() will return 285 * as an orphaned case. If not, it will create a new orphaned case. 286 * Either way we use the ASRU event to insert a suspect into the 287 * restored case. 288 */ 289 (void) nvlist_lookup_string(nvl, FM_RSRC_ASRU_UUID, &case_uuid); 290 (void) nvlist_lookup_string(nvl, FM_RSRC_ASRU_CODE, &case_code); 291 (void) nvlist_lookup_nvlist(nvl, FM_RSRC_ASRU_EVENT, &flt); 292 293 fmd_module_lock(fmd.d_rmod); 294 295 ap->asru_case = fmd_case_recreate(fmd.d_rmod, NULL, 296 FMD_CASE_CLOSED, case_uuid, case_code); 297 ASSERT(ap->asru_case != NULL); 298 299 ASSERT(fmd_case_orphaned(ap->asru_case)); 300 301 fmd_case_hold(ap->asru_case); 302 fmd_module_unlock(fmd.d_rmod); 303 304 if (nvlist_lookup_int64_array(nvl, FM_SUSPECT_DIAG_TIME, &diag_time, 305 &nelem) == 0 && nelem >= 2) 306 fmd_case_settime(ap->asru_case, diag_time[0], diag_time[1]); 307 else 308 fmd_case_settime(ap->asru_case, lp->log_stat.st_ctime, 0); 309 310 (void) nvlist_xdup(flt, &ap->asru_event, &fmd.d_nva); 311 (void) nvlist_xdup(flt, &flt_copy, &fmd.d_nva); 312 fmd_case_recreate_suspect(ap->asru_case, flt_copy); 313 314 ASSERT(!(ap->asru_flags & FMD_ASRU_VALID)); 315 ap->asru_flags |= FMD_ASRU_VALID; 316 fmd_asru_hash_insert(ahp, ap); 317 318 TRACE((FMD_DBG_ASRU, "asru %s recreated as %p (%s)", ap->asru_uuid, 319 (void *)ap, _fmd_asru_snames[ap->asru_flags & FMD_ASRU_STATE])); 320 } 321 322 static void 323 fmd_asru_hash_discard(fmd_asru_hash_t *ahp, const char *uuid, int err) 324 { 325 char src[PATH_MAX], dst[PATH_MAX]; 326 327 (void) snprintf(src, PATH_MAX, "%s/%s", ahp->ah_dirpath, uuid); 328 (void) snprintf(dst, PATH_MAX, "%s/%s-", ahp->ah_dirpath, uuid); 329 330 if (err != 0) 331 err = rename(src, dst); 332 else 333 err = unlink(src); 334 335 if (err != 0 && errno != ENOENT) 336 fmd_error(EFMD_ASRU_EVENT, "failed to rename log %s", src); 337 } 338 339 /* 340 * Open a saved log file and restore it into the ASRU hash. If we can't even 341 * open the log, rename the log file to <uuid>- to indicate it is corrupt. If 342 * fmd_log_replay() fails, we either delete the file (if it has reached the 343 * upper limit on cache age) or rename it for debugging if it was corrupted. 344 */ 345 static void 346 fmd_asru_hash_logopen(fmd_asru_hash_t *ahp, const char *uuid) 347 { 348 fmd_log_t *lp = fmd_log_tryopen(ahp->ah_dirpath, uuid, FMD_LOG_ASRU); 349 uint_t n; 350 351 if (lp == NULL) { 352 fmd_asru_hash_discard(ahp, uuid, errno); 353 return; 354 } 355 356 ahp->ah_error = 0; 357 n = ahp->ah_count; 358 359 fmd_log_replay(lp, (fmd_log_f *)fmd_asru_hash_recreate, ahp); 360 fmd_log_rele(lp); 361 362 if (ahp->ah_count == n) 363 fmd_asru_hash_discard(ahp, uuid, ahp->ah_error); 364 } 365 366 void 367 fmd_asru_hash_refresh(fmd_asru_hash_t *ahp) 368 { 369 struct dirent *dp; 370 DIR *dirp; 371 int zero; 372 373 if ((dirp = opendir(ahp->ah_dirpath)) == NULL) { 374 fmd_error(EFMD_ASRU_NODIR, 375 "failed to open asru cache directory %s", ahp->ah_dirpath); 376 return; 377 } 378 379 (void) fmd_conf_getprop(fmd.d_conf, "rsrc.zero", &zero); 380 381 (void) pthread_rwlock_wrlock(&ahp->ah_lock); 382 383 while ((dp = readdir(dirp)) != NULL) { 384 if (dp->d_name[0] == '.') 385 continue; /* skip "." and ".." */ 386 387 if (zero) 388 fmd_asru_hash_discard(ahp, dp->d_name, 0); 389 else if (!fmd_strmatch(dp->d_name, "*-")) 390 fmd_asru_hash_logopen(ahp, dp->d_name); 391 } 392 393 (void) pthread_rwlock_unlock(&ahp->ah_lock); 394 (void) closedir(dirp); 395 } 396 397 /* 398 * If the resource is present and faulty but not unusable, replay the fault 399 * event that caused it be marked faulty. This will cause the agent 400 * subscribing to this fault class to again disable the resource. 401 */ 402 /*ARGSUSED*/ 403 static void 404 fmd_asru_hash_replay_asru(fmd_asru_t *ap, void *data) 405 { 406 fmd_event_t *e; 407 nvlist_t *nvl; 408 char *class; 409 410 if (ap->asru_event != NULL && (ap->asru_flags & (FMD_ASRU_STATE | 411 FMD_ASRU_PRESENT)) == (FMD_ASRU_FAULTY | FMD_ASRU_PRESENT)) { 412 413 fmd_dprintf(FMD_DBG_ASRU, 414 "replaying fault event for %s", ap->asru_name); 415 416 (void) nvlist_xdup(ap->asru_event, &nvl, &fmd.d_nva); 417 (void) nvlist_lookup_string(nvl, FM_CLASS, &class); 418 419 (void) nvlist_add_string(nvl, FMD_EVN_UUID, 420 ((fmd_case_impl_t *)ap->asru_case)->ci_uuid); 421 422 e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); 423 fmd_dispq_dispatch(fmd.d_disp, e, class); 424 } 425 } 426 427 void 428 fmd_asru_hash_replay(fmd_asru_hash_t *ahp) 429 { 430 fmd_asru_hash_apply(ahp, fmd_asru_hash_replay_asru, NULL); 431 } 432 433 fmd_asru_hash_t * 434 fmd_asru_hash_create(const char *root, const char *dir) 435 { 436 fmd_asru_hash_t *ahp; 437 char path[PATH_MAX]; 438 439 ahp = fmd_alloc(sizeof (fmd_asru_hash_t), FMD_SLEEP); 440 (void) pthread_rwlock_init(&ahp->ah_lock, NULL); 441 ahp->ah_hashlen = fmd.d_str_buckets; 442 ahp->ah_hash = fmd_zalloc(sizeof (void *) * ahp->ah_hashlen, FMD_SLEEP); 443 (void) snprintf(path, sizeof (path), "%s/%s", root, dir); 444 ahp->ah_dirpath = fmd_strdup(path, FMD_SLEEP); 445 (void) fmd_conf_getprop(fmd.d_conf, "rsrc.age", &ahp->ah_lifetime); 446 (void) fmd_conf_getprop(fmd.d_conf, "fakenotpresent", 447 (uint32_t *)&fmd_asru_fake_not_present); 448 ahp->ah_count = 0; 449 ahp->ah_error = 0; 450 451 return (ahp); 452 } 453 454 void 455 fmd_asru_hash_destroy(fmd_asru_hash_t *ahp) 456 { 457 fmd_asru_t *ap, *np; 458 uint_t i; 459 460 for (i = 0; i < ahp->ah_hashlen; i++) { 461 for (ap = ahp->ah_hash[i]; ap != NULL; ap = np) { 462 np = ap->asru_next; 463 ap->asru_next = NULL; 464 fmd_asru_hash_release(ahp, ap); 465 } 466 } 467 468 fmd_strfree(ahp->ah_dirpath); 469 fmd_free(ahp->ah_hash, sizeof (void *) * ahp->ah_hashlen); 470 fmd_free(ahp, sizeof (fmd_asru_hash_t)); 471 } 472 473 /* 474 * Take a snapshot of the ASRU database by placing an additional hold on each 475 * member in an auxiliary array, and then call 'func' for each ASRU. 476 */ 477 void 478 fmd_asru_hash_apply(fmd_asru_hash_t *ahp, 479 void (*func)(fmd_asru_t *, void *), void *arg) 480 { 481 fmd_asru_t *ap, **aps, **app; 482 uint_t apc, i; 483 484 (void) pthread_rwlock_rdlock(&ahp->ah_lock); 485 486 aps = app = fmd_alloc(ahp->ah_count * sizeof (fmd_asru_t *), FMD_SLEEP); 487 apc = ahp->ah_count; 488 489 for (i = 0; i < ahp->ah_hashlen; i++) { 490 for (ap = ahp->ah_hash[i]; ap != NULL; ap = ap->asru_next) 491 *app++ = fmd_asru_hold(ap); 492 } 493 494 ASSERT(app == aps + apc); 495 (void) pthread_rwlock_unlock(&ahp->ah_lock); 496 497 for (i = 0; i < apc; i++) { 498 func(aps[i], arg); 499 fmd_asru_hash_release(ahp, aps[i]); 500 } 501 502 fmd_free(aps, apc * sizeof (fmd_asru_t *)); 503 } 504 505 /* 506 * Lookup an asru in the hash by name and place a hold on it. If the asru is 507 * not found, no entry is created and NULL is returned. 508 */ 509 fmd_asru_t * 510 fmd_asru_hash_lookup_name(fmd_asru_hash_t *ahp, const char *name) 511 { 512 fmd_asru_t *ap; 513 514 (void) pthread_rwlock_rdlock(&ahp->ah_lock); 515 ap = fmd_asru_hash_lookup(ahp, name); 516 (void) pthread_rwlock_unlock(&ahp->ah_lock); 517 518 return (ap); 519 } 520 521 /* 522 * Lookup an asru in the hash and place a hold on it. If 'create' is true, an 523 * absent entry will be created for the caller; otherwise NULL is returned. 524 */ 525 fmd_asru_t * 526 fmd_asru_hash_lookup_nvl(fmd_asru_hash_t *ahp, nvlist_t *fmri, int create) 527 { 528 fmd_asru_t *ap; 529 char *name = NULL; 530 ssize_t namelen; 531 uint_t h; 532 533 /* 534 * In order to lookup the ASRU in our hash, convert the FMRI from 535 * nvlist form into a string form using the scheme module. 536 */ 537 if ((namelen = fmd_fmri_nvl2str(fmri, NULL, 0)) == -1 || 538 (name = fmd_alloc(namelen + 1, FMD_NOSLEEP)) == NULL || 539 fmd_fmri_nvl2str(fmri, name, namelen + 1) == -1) { 540 if (name != NULL) 541 fmd_free(name, namelen + 1); 542 return (NULL); 543 } 544 545 /* 546 * If we must create the asru, grab the rwlock as a writer; otherwise 547 * reader is sufficient. Then search the hash for the given asru name. 548 * If we didn't find the asru in the hash and we need to create it, 549 * create and insert the asru with ahp->ah_lock held and hash it in. 550 * We'll then drop the rwlock and proceed to initializing the asru. 551 */ 552 if (create) 553 (void) pthread_rwlock_wrlock(&ahp->ah_lock); 554 else 555 (void) pthread_rwlock_rdlock(&ahp->ah_lock); 556 557 h = fmd_strhash(name) % ahp->ah_hashlen; 558 559 for (ap = ahp->ah_hash[h]; ap != NULL; ap = ap->asru_next) { 560 if (strcmp(ap->asru_name, name) == 0) 561 break; 562 } 563 564 if (ap == NULL && create == FMD_B_TRUE) { 565 ap = fmd_asru_create(ahp, NULL, name, fmri); 566 fmd_asru_hash_insert(ahp, ap); 567 (void) pthread_mutex_lock(&ap->asru_lock); 568 } else 569 create = FMD_B_FALSE; 570 571 (void) pthread_rwlock_unlock(&ahp->ah_lock); 572 fmd_free(name, namelen + 1); 573 574 /* 575 * If 'create' is still true, then we need to initialize the asru log; 576 * If 'create' is false and an asru was found, we must cond_wait for 577 * the FMD_ASRU_VALID bit to be set before returning. In both cases, 578 * we increment asru_refs for the caller. 579 */ 580 if (create == FMD_B_TRUE) { 581 uuid_t uuid; 582 583 ASSERT(MUTEX_HELD(&ap->asru_lock)); 584 ASSERT(ap->asru_uuid == NULL && ap->asru_log == NULL); 585 586 /* 587 * Generate a UUID for the ASRU. libuuid cleverly gives us no 588 * interface for specifying or learning the buffer size. Sigh. 589 * The spec says 36 bytes but we use a tunable just to be safe. 590 */ 591 (void) fmd_conf_getprop(fmd.d_conf, 592 "uuidlen", &ap->asru_uuidlen); 593 594 ap->asru_uuid = fmd_zalloc(ap->asru_uuidlen + 1, FMD_SLEEP); 595 uuid_generate(uuid); 596 uuid_unparse(uuid, ap->asru_uuid); 597 598 ASSERT(!(ap->asru_flags & FMD_ASRU_VALID)); 599 ap->asru_flags |= FMD_ASRU_VALID; 600 601 ap->asru_refs++; 602 ASSERT(ap->asru_refs != 0); 603 (void) pthread_cond_broadcast(&ap->asru_cv); 604 (void) pthread_mutex_unlock(&ap->asru_lock); 605 606 TRACE((FMD_DBG_ASRU, "asru %s created as %p", 607 ap->asru_uuid, (void *)ap)); 608 609 } else if (ap != NULL) { 610 (void) pthread_mutex_lock(&ap->asru_lock); 611 612 while (!(ap->asru_flags & FMD_ASRU_VALID)) 613 (void) pthread_cond_wait(&ap->asru_cv, &ap->asru_lock); 614 615 ap->asru_refs++; 616 ASSERT(ap->asru_refs != 0); 617 (void) pthread_mutex_unlock(&ap->asru_lock); 618 } 619 620 return (ap); 621 } 622 623 /* 624 * Release the reference count on an asru obtained using fmd_asru_hash_lookup. 625 * We take 'ahp' for symmetry and in case we need to use it in future work. 626 */ 627 /*ARGSUSED*/ 628 void 629 fmd_asru_hash_release(fmd_asru_hash_t *ahp, fmd_asru_t *ap) 630 { 631 (void) pthread_mutex_lock(&ap->asru_lock); 632 633 ASSERT(ap->asru_refs != 0); 634 if (--ap->asru_refs == 0) 635 fmd_asru_destroy(ap); 636 else 637 (void) pthread_mutex_unlock(&ap->asru_lock); 638 } 639 640 int 641 fmd_asru_hash_delete_name(fmd_asru_hash_t *ahp, const char *name) 642 { 643 fmd_asru_t *ap, **pp; 644 char path[PATH_MAX]; 645 uint_t h; 646 647 (void) pthread_rwlock_wrlock(&ahp->ah_lock); 648 649 h = fmd_strhash(name) % ahp->ah_hashlen; 650 pp = &ahp->ah_hash[h]; 651 652 for (ap = *pp; ap != NULL; ap = ap->asru_next) { 653 if (strcmp(ap->asru_name, name) == 0) 654 break; 655 else 656 pp = &ap->asru_next; 657 } 658 659 if (ap != NULL) { 660 *pp = ap->asru_next; 661 ap->asru_next = NULL; 662 ASSERT(ahp->ah_count != 0); 663 ahp->ah_count--; 664 } 665 666 (void) pthread_rwlock_unlock(&ahp->ah_lock); 667 668 if (ap == NULL) 669 return (fmd_set_errno(EFMD_ASRU_NOENT)); 670 671 /* 672 * If we found a matching ASRU, unlink its log file and then release 673 * the hash entry. Note that it may still be referenced if another 674 * thread is manipulating it; this is ok because once we unlink, the 675 * log file will not be restored, and the log data will be freed when 676 * all of the referencing threads release their respective references. 677 */ 678 (void) snprintf(path, sizeof (path), 679 "%s/%s", ahp->ah_dirpath, ap->asru_uuid); 680 681 if (unlink(path) != 0) 682 fmd_error(EFMD_ASRU_UNLINK, "failed to unlink asru %s", path); 683 684 fmd_asru_hash_release(ahp, ap); 685 return (0); 686 } 687 688 static void 689 fmd_asru_logevent(fmd_asru_t *ap) 690 { 691 boolean_t f = (ap->asru_flags & FMD_ASRU_FAULTY) != 0; 692 boolean_t u = (ap->asru_flags & FMD_ASRU_UNUSABLE) != 0; 693 boolean_t m = (ap->asru_flags & FMD_ASRU_INVISIBLE) == 0; 694 695 fmd_case_impl_t *cip; 696 fmd_event_t *e; 697 fmd_log_t *lp; 698 nvlist_t *nvl; 699 char *class; 700 701 ASSERT(MUTEX_HELD(&ap->asru_lock)); 702 cip = (fmd_case_impl_t *)ap->asru_case; 703 ASSERT(cip != NULL); 704 705 if ((lp = ap->asru_log) == NULL) 706 lp = fmd_log_open(ap->asru_root, ap->asru_uuid, FMD_LOG_ASRU); 707 708 if (lp == NULL) 709 return; /* can't log events if we can't open the log */ 710 711 nvl = fmd_protocol_rsrc_asru(_fmd_asru_events[f | (u << 1)], 712 ap->asru_fmri, cip->ci_uuid, cip->ci_code, f, u, m, ap->asru_event, 713 &cip->ci_tv); 714 715 (void) nvlist_lookup_string(nvl, FM_CLASS, &class); 716 e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); 717 718 fmd_event_hold(e); 719 fmd_log_append(lp, e, NULL); 720 fmd_event_rele(e); 721 722 /* 723 * For now, we close the log file after every update to conserve file 724 * descriptors and daemon overhead. If this becomes a performance 725 * issue this code can change to keep a fixed-size LRU cache of logs. 726 */ 727 fmd_log_rele(lp); 728 ap->asru_log = NULL; 729 } 730 731 int 732 fmd_asru_setflags(fmd_asru_t *ap, uint_t sflag, fmd_case_t *cp, nvlist_t *nvl) 733 { 734 fmd_case_t *old_case = NULL; 735 nvlist_t *old_nvl = NULL; 736 uint_t nstate, ostate; 737 boolean_t msg; 738 739 ASSERT(!(sflag & ~FMD_ASRU_STATE)); 740 ASSERT(sflag != FMD_ASRU_STATE); 741 742 (void) pthread_mutex_lock(&ap->asru_lock); 743 744 ostate = ap->asru_flags & FMD_ASRU_STATE; 745 ap->asru_flags |= sflag; 746 nstate = ap->asru_flags & FMD_ASRU_STATE; 747 748 if (nstate == ostate) { 749 (void) pthread_mutex_unlock(&ap->asru_lock); 750 return (0); 751 } 752 753 if (cp != NULL && cp != ap->asru_case) { 754 old_case = ap->asru_case; 755 fmd_case_hold_locked(cp); 756 ap->asru_case = cp; 757 old_nvl = ap->asru_event; 758 (void) nvlist_xdup(nvl, &ap->asru_event, &fmd.d_nva); 759 } 760 761 if (nvl != NULL && nvlist_lookup_boolean_value(nvl, 762 FM_SUSPECT_MESSAGE, &msg) == 0 && msg == B_FALSE) 763 ap->asru_flags |= FMD_ASRU_INVISIBLE; 764 765 TRACE((FMD_DBG_ASRU, "asru %s %s->%s", ap->asru_uuid, 766 _fmd_asru_snames[ostate], _fmd_asru_snames[nstate])); 767 768 fmd_asru_logevent(ap); 769 770 (void) pthread_cond_broadcast(&ap->asru_cv); 771 (void) pthread_mutex_unlock(&ap->asru_lock); 772 773 if (old_case != NULL) 774 fmd_case_rele(old_case); 775 776 if (old_nvl != NULL) 777 nvlist_free(old_nvl); 778 779 return (1); 780 } 781 782 int 783 fmd_asru_clrflags(fmd_asru_t *ap, uint_t sflag, fmd_case_t *cp, nvlist_t *nvl) 784 { 785 fmd_case_t *old_case = NULL; 786 nvlist_t *old_nvl = NULL; 787 uint_t nstate, ostate; 788 789 ASSERT(!(sflag & ~FMD_ASRU_STATE)); 790 ASSERT(sflag != FMD_ASRU_STATE); 791 792 (void) pthread_mutex_lock(&ap->asru_lock); 793 794 ostate = ap->asru_flags & FMD_ASRU_STATE; 795 ap->asru_flags &= ~sflag; 796 nstate = ap->asru_flags & FMD_ASRU_STATE; 797 798 if (nstate == ostate) { 799 (void) pthread_mutex_unlock(&ap->asru_lock); 800 return (0); 801 } 802 803 if (cp != NULL && cp != ap->asru_case) { 804 old_case = ap->asru_case; 805 fmd_case_hold_locked(cp); 806 ap->asru_case = cp; 807 old_nvl = ap->asru_event; 808 (void) nvlist_xdup(nvl, &ap->asru_event, &fmd.d_nva); 809 } 810 811 TRACE((FMD_DBG_ASRU, "asru %s %s->%s", ap->asru_uuid, 812 _fmd_asru_snames[ostate], _fmd_asru_snames[nstate])); 813 814 fmd_asru_logevent(ap); 815 816 if (cp == NULL && (sflag & FMD_ASRU_FAULTY)) { 817 old_case = ap->asru_case; 818 ap->asru_case = NULL; 819 old_nvl = ap->asru_event; 820 ap->asru_event = NULL; 821 } 822 823 (void) pthread_cond_broadcast(&ap->asru_cv); 824 (void) pthread_mutex_unlock(&ap->asru_lock); 825 826 if (old_case != NULL) { 827 if (cp == NULL && (sflag & FMD_ASRU_FAULTY)) 828 fmd_case_update(old_case); 829 fmd_case_rele(old_case); 830 } 831 832 if (old_nvl != NULL) 833 nvlist_free(old_nvl); 834 835 return (1); 836 } 837 838 /* 839 * Report the current known state of the ASRU by refreshing its unusable status 840 * based upon the routines provided by the scheme module. If the unusable bit 841 * is different, we do *not* generate a state change here because that change 842 * may be unrelated to fmd activities and therefore we have no case or event. 843 * The absence of the transition is harmless as this function is only provided 844 * for RPC observability and fmd's clients are only concerned with ASRU_FAULTY. 845 */ 846 int 847 fmd_asru_getstate(fmd_asru_t *ap) 848 { 849 int us, st; 850 851 if (!(ap->asru_flags & FMD_ASRU_INTERNAL) && 852 (fmd_asru_fake_not_present || fmd_fmri_present(ap->asru_fmri) <= 0)) 853 return (0); /* do not report non-fmd non-present resources */ 854 855 us = fmd_fmri_unusable(ap->asru_fmri); 856 st = ap->asru_flags & FMD_ASRU_STATE; 857 858 if (us > 0) 859 st |= FMD_ASRU_UNUSABLE; 860 else if (us == 0) 861 st &= ~FMD_ASRU_UNUSABLE; 862 863 return (st); 864 } 865