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