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