xref: /illumos-gate/usr/src/cmd/fm/modules/common/disk-monitor/topo_gather.c (revision 4764d912222e53f8386bae7bf491f5780fd102ec)
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 2008 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 /*
30  * Gathers properties exported by libtopo and uses them to construct diskmon
31  * data structures, which hold the configuration information for the
32  * DE.
33  */
34 
35 #include <limits.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <ctype.h>
41 #include <pthread.h>
42 #include <libnvpair.h>
43 #include <config_admin.h>
44 #include <sys/fm/protocol.h>
45 #include <fm/libtopo.h>
46 #include <fm/topo_hc.h>
47 
48 #include "disk.h"
49 #include "disk_monitor.h"
50 #include "hotplug_mgr.h"
51 #include "topo_gather.h"
52 
53 #define	TOPO_PGROUP_IO		"io"	/* duplicated from did_props.h */
54 #define	MAX_CONF_MSG_LEN	256
55 
56 static nvlist_t *g_topo2diskmon = NULL;
57 
58 /*
59  * The following function template is required for nvlists that were
60  * create with no flags (so there can be multiple identical name or name-value
61  * pairs).  The function defined below returns the first match for the name
62  * provided.
63  */
64 #define	NONUNIQUE_NVLIST_FN(suffix, type, atype) \
65 static int								\
66 nonunique_nvlist_lookup_##suffix(nvlist_t *nvlp, const char *n, atype *rpp) \
67 {									\
68 	nvpair_t *nvp = NULL;						\
69 	while ((nvp = nvlist_next_nvpair(nvlp, nvp)) != NULL) {		\
70 		if (nvpair_type(nvp) != type)				\
71 			continue;					\
72 		if (strcmp(nvpair_name(nvp), n) == 0)			\
73 			return (nvpair_value_##suffix(nvp, rpp));	\
74 	}								\
75 	return (ENOENT);						\
76 }
77 
78 NONUNIQUE_NVLIST_FN(string, DATA_TYPE_STRING, char *)
79 
80 static diskmon_t *
81 dm_fmristring_to_diskmon(char *str)
82 {
83 	diskmon_t *p = NULL;
84 	uint64_t u64val;
85 	char ch;
86 	char *lastsl = strrchr(str, '/');
87 
88 	ch = *lastsl;
89 	*lastsl = 0;
90 
91 	if (nvlist_lookup_uint64(g_topo2diskmon, str, &u64val) == 0) {
92 
93 		p = (diskmon_t *)(uintptr_t)u64val;
94 	}
95 
96 	*lastsl = ch;
97 
98 	return (p);
99 }
100 
101 diskmon_t *
102 dm_fmri_to_diskmon(fmd_hdl_t *hdl, nvlist_t *fmri)
103 {
104 	topo_hdl_t *thdl;
105 	nvlist_t *dupfmri;
106 	diskmon_t *diskp;
107 	char *buf;
108 	int err;
109 
110 	if (nvlist_dup(fmri, &dupfmri, 0) != 0)
111 		return (NULL);
112 
113 	(void) nvlist_remove(dupfmri, FM_FMRI_HC_REVISION, DATA_TYPE_STRING);
114 	(void) nvlist_remove(dupfmri, FM_FMRI_HC_SERIAL_ID, DATA_TYPE_STRING);
115 	(void) nvlist_remove(dupfmri, FM_FMRI_HC_PART, DATA_TYPE_STRING);
116 
117 	thdl = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
118 	if (topo_fmri_nvl2str(thdl, dupfmri, &buf, &err) != 0) {
119 		fmd_hdl_topo_rele(hdl, thdl);
120 		nvlist_free(dupfmri);
121 		return (NULL);
122 	}
123 	fmd_hdl_topo_rele(hdl, thdl);
124 
125 	diskp = dm_fmristring_to_diskmon(buf);
126 
127 	nvlist_free(dupfmri);
128 	topo_hdl_strfree(thdl, buf);
129 
130 	return (diskp);
131 }
132 
133 static nvlist_t *
134 find_disk_monitor_private_pgroup(tnode_t *node)
135 {
136 	int err;
137 	nvlist_t *list_of_lists, *nvlp, *dupnvlp;
138 	nvlist_t *disk_monitor_pgrp = NULL;
139 	nvpair_t *nvp = NULL;
140 	char *pgroup_name;
141 
142 	/*
143 	 * topo_prop_get_all() returns an nvlist that contains other
144 	 * nvlists (some of which are property groups).  Since the private
145 	 * property group we need will be among the list of property
146 	 * groups returned (hopefully), we need to walk the list of nvlists
147 	 * in the topo node's properties to find the property groups, then
148 	 * check inside each embedded nvlist to see if it's the pgroup we're
149 	 * looking for.
150 	 */
151 	if ((list_of_lists = topo_prop_getprops(node, &err)) != NULL) {
152 		/*
153 		 * Go through the list of nvlists, looking for the
154 		 * property group we need.
155 		 */
156 		while ((nvp = nvlist_next_nvpair(list_of_lists, nvp)) != NULL) {
157 
158 			if (nvpair_type(nvp) != DATA_TYPE_NVLIST ||
159 			    strcmp(nvpair_name(nvp), TOPO_PROP_GROUP) != 0 ||
160 			    nvpair_value_nvlist(nvp, &nvlp) != 0)
161 				continue;
162 
163 			dm_assert(nvlp != NULL);
164 			pgroup_name = NULL;
165 
166 			if (nonunique_nvlist_lookup_string(nvlp,
167 			    TOPO_PROP_GROUP_NAME, &pgroup_name) != 0 ||
168 			    strcmp(pgroup_name, DISK_MONITOR_PROPERTIES) != 0)
169 				continue;
170 			else {
171 				/*
172 				 * Duplicate the nvlist so that when the
173 				 * master nvlist is freed (below), we will
174 				 * still refer to allocated memory.
175 				 */
176 				if (nvlist_dup(nvlp, &dupnvlp, 0) == 0)
177 					disk_monitor_pgrp = dupnvlp;
178 				else
179 					disk_monitor_pgrp = NULL;
180 				break;
181 			}
182 		}
183 
184 		nvlist_free(list_of_lists);
185 	}
186 
187 	return (disk_monitor_pgrp);
188 }
189 
190 /*
191  * Look up the FMRI corresponding to the node in the global
192  * hash table and return the pointer stored (if any).  Save the
193  * FMRI string in *str if str is non-NULL.
194  */
195 static void *
196 fmri2ptr(topo_hdl_t *thp, tnode_t *node, char **str, int *err)
197 {
198 	nvlist_t	*fmri = NULL;
199 	char		*cstr = NULL;
200 	uint64_t	u64val;
201 	void		*p = NULL;
202 
203 	if (topo_node_resource(node, &fmri, err) != 0)
204 		return (NULL);
205 
206 	if (topo_fmri_nvl2str(thp, fmri, &cstr, err) != 0) {
207 		nvlist_free(fmri);
208 		return (NULL);
209 	}
210 
211 	if (nvlist_lookup_uint64(g_topo2diskmon, cstr, &u64val) == 0) {
212 
213 		p = (void *)(uintptr_t)u64val;
214 	}
215 
216 	nvlist_free(fmri);
217 	if (str != NULL)
218 		*str = dstrdup(cstr);
219 	topo_hdl_strfree(thp, cstr);
220 	return (p);
221 }
222 
223 typedef struct walk_diskmon {
224 	diskmon_t *target;
225 	char *pfmri;
226 } walk_diskmon_t;
227 
228 static int
229 topo_add_disk(topo_hdl_t *thp, tnode_t *node, walk_diskmon_t *wdp)
230 {
231 	diskmon_t *target_diskp = wdp->target;
232 	char		*devpath = NULL;
233 	char		*capacity = NULL;
234 	char		*firmrev = NULL;
235 	char		*serial = NULL;
236 	char		*manuf = NULL;
237 	char		*model = NULL;
238 	char		*label;
239 	uint64_t	ptr = 0;
240 	int		err;
241 	dm_fru_t	*frup;
242 	diskmon_t	*diskp;
243 
244 	if (wdp->pfmri == NULL) {
245 		log_msg(MM_TOPO, "No diskmon for parent of node %p.\n", node);
246 		return (0);
247 	}
248 
249 	if (nvlist_lookup_uint64(g_topo2diskmon, wdp->pfmri, &ptr) != 0) {
250 		log_msg(MM_TOPO, "No diskmon for %s: parent of node %p.\n",
251 		    wdp->pfmri, node);
252 		dstrfree(wdp->pfmri);
253 		/* Skip this disk: */
254 		return (0);
255 	}
256 
257 	dstrfree(wdp->pfmri);
258 	wdp->pfmri = NULL;
259 
260 	diskp = (diskmon_t *)(uintptr_t)ptr;
261 
262 	/* If we were called upon to update a particular disk, do it */
263 	if (target_diskp != NULL && diskp != target_diskp) {
264 		return (0);
265 	}
266 
267 	/*
268 	 * Update the diskmon's location field with the disk's label
269 	 */
270 	if (diskp->location)
271 		dstrfree(diskp->location);
272 	if (topo_node_label(node, &label, &err) == 0) {
273 		diskp->location = dstrdup(label);
274 		topo_hdl_strfree(thp, label);
275 	} else
276 		diskp->location = dstrdup("unknown location");
277 
278 	/*
279 	 * Check for a device path property (if the disk is configured,
280 	 * it will be present) and add it to the diskmon's properties)
281 	 */
282 	if (topo_prop_get_string(node, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH,
283 	    &devpath, &err) == 0) {
284 		char devp[PATH_MAX];
285 		/*
286 		 * Consumers of the DISK_PROP_DEVPATH property expect a raw
287 		 * minor device node
288 		 */
289 		(void) snprintf(devp, PATH_MAX, "%s:q,raw", devpath);
290 		(void) nvlist_add_string(diskp->props, DISK_PROP_DEVPATH,
291 		    devp);
292 		topo_hdl_strfree(thp, devpath);
293 	}
294 
295 	/*
296 	 * Add the logical disk node, if it exists
297 	 */
298 	if (topo_prop_get_string(node, TOPO_PGROUP_STORAGE,
299 	    TOPO_STORAGE_LOGICAL_DISK_NAME, &devpath, &err) == 0) {
300 		(void) nvlist_add_string(diskp->props, DISK_PROP_LOGNAME,
301 		    devpath);
302 		topo_hdl_strfree(thp, devpath);
303 	}
304 
305 	/*
306 	 * Add the FRU information (if present in the node) to the diskmon's
307 	 * fru data structure:
308 	 */
309 	(void) topo_prop_get_string(node, TOPO_PGROUP_STORAGE,
310 	    TOPO_STORAGE_MODEL, &model, &err);
311 
312 	(void) topo_prop_get_string(node, TOPO_PGROUP_STORAGE,
313 	    TOPO_STORAGE_MANUFACTURER, &manuf, &err);
314 
315 	(void) topo_prop_get_string(node, TOPO_PGROUP_STORAGE,
316 	    TOPO_STORAGE_SERIAL_NUM, &serial, &err);
317 
318 	(void) topo_prop_get_string(node, TOPO_PGROUP_STORAGE,
319 	    TOPO_STORAGE_FIRMWARE_REV, &firmrev, &err);
320 
321 	(void) topo_prop_get_string(node, TOPO_PGROUP_STORAGE,
322 	    TOPO_STORAGE_CAPACITY, &capacity, &err);
323 
324 	frup = new_dmfru(manuf, model, firmrev, serial,
325 	    capacity == NULL ? 0 : strtoull(capacity, 0, 0));
326 
327 	if (model)
328 		topo_hdl_strfree(thp, model);
329 	if (manuf)
330 		topo_hdl_strfree(thp, manuf);
331 	if (serial)
332 		topo_hdl_strfree(thp, serial);
333 	if (firmrev)
334 		topo_hdl_strfree(thp, firmrev);
335 	if (capacity)
336 		topo_hdl_strfree(thp, capacity);
337 
338 	/* Add the fru information to the diskmon: */
339 	dm_assert(pthread_mutex_lock(&diskp->fru_mutex) == 0);
340 	dm_assert(diskp->frup == NULL);
341 	diskp->frup = frup;
342 	dm_assert(pthread_mutex_unlock(&diskp->fru_mutex) == 0);
343 
344 	return (0);
345 }
346 
347 static int
348 indicator_breakup(char *identifier, ind_state_t *state, char **name)
349 {
350 	if (identifier[0] != '+' && identifier[0] != '-') {
351 		log_msg(MM_CONF, "Invalid indicator name `%s'\n", identifier);
352 		return (-1);
353 	}
354 
355 	*state = (identifier[0] == '+') ? INDICATOR_ON : INDICATOR_OFF;
356 	*name = &identifier[1];
357 	return (0);
358 }
359 
360 static int
361 topoprop_indicator_add(indicator_t **indp, char *ind_name, char *ind_action)
362 {
363 	/* The Indicator name is of the form: "[+-][A-Za-z][A-Za-z0-9]+" */
364 	indicator_t *newindp;
365 	ind_state_t state;
366 	char *name;
367 
368 	if (indicator_breakup(ind_name, &state, &name) != 0)
369 		return (-1);
370 	newindp = new_indicator(state, name, ind_action);
371 
372 	link_indicator(indp, newindp);
373 
374 	return (0);
375 }
376 
377 static hotplug_state_t
378 str2dmstate(char *str)
379 {
380 	if (strcasecmp("configured", str) == 0) {
381 		return (HPS_CONFIGURED);
382 	} else if (strcasecmp("unconfigured", str) == 0) {
383 		return (HPS_UNCONFIGURED);
384 	} else if (strcasecmp("absent", str) == 0) {
385 		return (HPS_ABSENT);
386 	} else if (strcasecmp("present", str) == 0) {
387 		return (HPS_PRESENT);
388 	} else
389 		return (HPS_UNKNOWN);
390 }
391 
392 static int
393 topoprop_indrule_add(indrule_t **indrp, char *sts, char *acts)
394 {
395 	ind_action_t		*indactp = NULL;
396 	ind_state_t		state;
397 	char			*name, *lasts, *p;
398 	int			stateslen = strlen(sts) + 1;
399 	int			actionslen = strlen(acts) + 1;
400 	char			*states = dstrdup(sts);
401 	char			*actions = dstrdup(acts);
402 	state_transition_t	strans;
403 	boolean_t		failed = B_FALSE;
404 	conf_err_t		err;
405 	char			msgbuf[MAX_CONF_MSG_LEN];
406 
407 	/* The state string is of the form "{STATE}>{STATE}" */
408 	p = strchr(states, '>');
409 	dm_assert(p != NULL);
410 	*p = 0;
411 	strans.begin = str2dmstate(states);
412 	*p = '>';
413 	strans.end = str2dmstate(p + 1);
414 
415 	if (strans.begin == HPS_UNKNOWN || strans.end == HPS_UNKNOWN) {
416 		log_msg(MM_CONF, "Invalid states property `%s'\n", sts);
417 		failed = B_TRUE;
418 	} else if ((err = check_state_transition(strans.begin, strans.end))
419 	    != E_NO_ERROR) {
420 		conf_error_msg(err, msgbuf, MAX_CONF_MSG_LEN, &strans);
421 		log_msg(MM_CONF, "%s: Not adding disk to list!\n", msgbuf);
422 		failed = B_TRUE;
423 	}
424 
425 	/* Actions are of the form "{ACTION}[&{ACTION}]" */
426 	if (!failed && (p = strtok_r(actions, "&", &lasts)) != NULL) {
427 		/* At least 2 tokens */
428 		do {
429 			if (indicator_breakup(p, &state, &name) != 0) {
430 				failed = B_TRUE;
431 				break;
432 			}
433 
434 			link_indaction(&indactp, new_indaction(state, name));
435 
436 		} while ((p = strtok_r(NULL, "&", &lasts)) != NULL);
437 	} else if (!failed) {
438 		/* One token */
439 		if (indicator_breakup(actions, &state, &name) != 0)
440 			return (-1);
441 		indactp = new_indaction(state, name);
442 	}
443 
444 	dfree(states, stateslen);
445 	dfree(actions, actionslen);
446 
447 	if (!failed && (err = check_indactions(indactp)) != E_NO_ERROR) {
448 		conf_error_msg(err, msgbuf, MAX_CONF_MSG_LEN, NULL);
449 		log_msg(MM_CONF, "%s: Not adding disk to list!\n", msgbuf);
450 		failed = B_TRUE;
451 	}
452 
453 	if (failed) {
454 		indaction_free(indactp);
455 		return (-1);
456 	} else
457 		link_indrule(indrp, new_indrule(&strans, indactp));
458 	return (0);
459 }
460 
461 
462 static int
463 topo_add_bay(topo_hdl_t *thp, tnode_t *node, walk_diskmon_t *wdp)
464 {
465 	diskmon_t *target_diskp = wdp->target;
466 	nvlist_t	*nvlp = find_disk_monitor_private_pgroup(node);
467 	nvlist_t	*prop_nvlp;
468 	nvpair_t	*nvp = NULL;
469 	char		*prop_name, *prop_value;
470 #define	PNAME_MAX 128
471 	char		pname[PNAME_MAX];
472 	char		msgbuf[MAX_CONF_MSG_LEN];
473 	char		*indicator_name, *indicator_action;
474 	char		*indrule_states, *indrule_actions;
475 	int		err = 0, i;
476 	conf_err_t	conferr;
477 	boolean_t	conf_failure = B_FALSE;
478 	char		*unadj_physid = NULL;
479 	char		physid[MAXPATHLEN];
480 	char		*label;
481 	nvlist_t	*diskprops = NULL;
482 	char		*cstr = NULL;
483 	indicator_t	*indp = NULL;
484 	indrule_t	*indrp = NULL;
485 	void		*p;
486 	diskmon_t	*diskp;
487 	void		*ptr;
488 
489 	/* No private properties -- just ignore the port */
490 	if (nvlp == NULL)
491 		return (0);
492 
493 	/*
494 	 * Look for a diskmon based on this node's FMRI string.
495 	 * Once a diskmon has been created, it's not re-created.  This is
496 	 * essential for the times when the tree-walk is called after a
497 	 * disk is inserted (or removed) -- in that case, the disk node
498 	 * handler simply updates the FRU information in the diskmon.
499 	 */
500 	if ((p = fmri2ptr(thp, node, &cstr, &err)) != NULL) {
501 
502 		diskp = (diskmon_t *)p;
503 
504 		/*
505 		 * Delete the FRU information from the diskmon.  If a disk
506 		 * is connected, its FRU information will be refreshed by
507 		 * the disk node code.
508 		 */
509 		if (diskp->frup && (target_diskp == NULL ||
510 		    diskp == target_diskp)) {
511 			dm_assert(pthread_mutex_lock(&diskp->fru_mutex) == 0);
512 			dmfru_free(diskp->frup);
513 			diskp->frup = NULL;
514 			dm_assert(pthread_mutex_unlock(&diskp->fru_mutex) == 0);
515 		}
516 
517 		wdp->pfmri = cstr;
518 		nvlist_free(nvlp);
519 		return (0);
520 	}
521 
522 	/*
523 	 * Determine the physical path to the attachment point
524 	 */
525 	if (topo_prop_get_string(node, TOPO_PGROUP_IO,
526 	    TOPO_IO_AP_PATH, &unadj_physid, &err) == 0) {
527 
528 		adjust_dynamic_ap(unadj_physid, physid);
529 		topo_hdl_strfree(thp, unadj_physid);
530 	} else {
531 
532 		/* unadj_physid cannot have been allocated */
533 		if (cstr)
534 			dstrfree(cstr);
535 		nvlist_free(nvlp);
536 		return (-1);
537 	}
538 
539 	/*
540 	 */
541 
542 	/*
543 	 * Process the properties.  If we encounter a property that
544 	 * is not an indicator name, action, or rule, add it to the
545 	 * disk's props list.
546 	 */
547 
548 	/* Process indicators */
549 	i = 0;
550 	indicator_name = NULL;
551 	indicator_action = NULL;
552 	do {
553 		if (indicator_name != NULL && indicator_action != NULL) {
554 
555 			if (topoprop_indicator_add(&indp, indicator_name,
556 			    indicator_action) != 0) {
557 
558 				conf_failure = B_TRUE;
559 			}
560 
561 			topo_hdl_strfree(thp, indicator_name);
562 			topo_hdl_strfree(thp, indicator_action);
563 		}
564 
565 		(void) snprintf(pname, PNAME_MAX, BAY_IND_NAME "-%d", i);
566 		if (topo_prop_get_string(node, DISK_MONITOR_PROPERTIES,
567 		    pname, &indicator_name, &err) != 0)
568 			break;
569 
570 		(void) snprintf(pname, PNAME_MAX, BAY_IND_ACTION "-%d", i);
571 		if (topo_prop_get_string(node, DISK_MONITOR_PROPERTIES,
572 		    pname, &indicator_action, &err) != 0)
573 			break;
574 
575 		i++;
576 	} while (!conf_failure && indicator_name != NULL &&
577 	    indicator_action != NULL);
578 
579 	if (!conf_failure && indp != NULL &&
580 	    (conferr = check_inds(indp)) != E_NO_ERROR) {
581 		conf_error_msg(conferr, msgbuf, MAX_CONF_MSG_LEN, NULL);
582 		log_msg(MM_CONF, "%s: Not adding disk to list\n", msgbuf);
583 		conf_failure = B_TRUE;
584 	}
585 
586 	/* Process state rules and indicator actions */
587 	i = 0;
588 	indrule_states = NULL;
589 	indrule_actions = NULL;
590 	do {
591 		if (indrule_states != NULL && indrule_actions != NULL) {
592 
593 			if (topoprop_indrule_add(&indrp, indrule_states,
594 			    indrule_actions) != 0) {
595 
596 				conf_failure = B_TRUE;
597 			}
598 
599 			topo_hdl_strfree(thp, indrule_states);
600 			topo_hdl_strfree(thp, indrule_actions);
601 		}
602 
603 		(void) snprintf(pname, PNAME_MAX, BAY_INDRULE_STATES "-%d", i);
604 		if (topo_prop_get_string(node, DISK_MONITOR_PROPERTIES,
605 		    pname, &indrule_states, &err) != 0)
606 			break;
607 
608 		(void) snprintf(pname, PNAME_MAX, BAY_INDRULE_ACTIONS "-%d",
609 		    i);
610 		if (topo_prop_get_string(node, DISK_MONITOR_PROPERTIES,
611 		    pname, &indrule_actions, &err) != 0)
612 			break;
613 
614 		i++;
615 	} while (!conf_failure && indrule_states != NULL &&
616 	    indrule_actions != NULL);
617 
618 	if (!conf_failure && indrp != NULL && indp != NULL &&
619 	    ((conferr = check_indrules(indrp, (state_transition_t **)&ptr))
620 	    != E_NO_ERROR ||
621 	    (conferr = check_consistent_ind_indrules(indp, indrp,
622 	    (ind_action_t **)&ptr)) != E_NO_ERROR)) {
623 
624 		conf_error_msg(conferr, msgbuf, MAX_CONF_MSG_LEN, ptr);
625 		log_msg(MM_CONF, "%s: Not adding disk to list\n", msgbuf);
626 		conf_failure = B_TRUE;
627 
628 	}
629 
630 	/*
631 	 * Now collect miscellaneous properties.
632 	 * Each property is stored as an embedded nvlist named
633 	 * TOPO_PROP_VAL.  The property name is stored in the value for
634 	 * key=TOPO_PROP_VAL_NAME and the property's value is
635 	 * stored in the value for key=TOPO_PROP_VAL_VAL.  This is all
636 	 * necessary so we can subtractively decode the properties that
637 	 * we do not directly handle (so that these properties are added to
638 	 * the per-disk properties nvlist), increasing flexibility.
639 	 */
640 	(void) nvlist_alloc(&diskprops, NV_UNIQUE_NAME, 0);
641 	while ((nvp = nvlist_next_nvpair(nvlp, nvp)) != NULL) {
642 		/* Only care about embedded nvlists named TOPO_PROP_VAL */
643 		if (nvpair_type(nvp) != DATA_TYPE_NVLIST ||
644 		    strcmp(nvpair_name(nvp), TOPO_PROP_VAL) != 0 ||
645 		    nvpair_value_nvlist(nvp, &prop_nvlp) != 0)
646 			continue;
647 
648 		if (nonunique_nvlist_lookup_string(prop_nvlp,
649 		    TOPO_PROP_VAL_NAME, &prop_name) != 0)
650 			continue;
651 
652 		/* Filter out indicator properties */
653 		if (strstr(prop_name, BAY_IND_NAME) != NULL ||
654 		    strstr(prop_name, BAY_IND_ACTION) != NULL ||
655 		    strstr(prop_name, BAY_INDRULE_STATES) != NULL ||
656 		    strstr(prop_name, BAY_INDRULE_ACTIONS) != NULL)
657 			continue;
658 
659 		if (nonunique_nvlist_lookup_string(prop_nvlp, TOPO_PROP_VAL_VAL,
660 		    &prop_value) != 0)
661 			continue;
662 
663 		/* Add the property to the disk's prop list: */
664 		if (nvlist_add_string(diskprops, prop_name, prop_value) != 0)
665 			log_msg(MM_TOPO,
666 			    "Could not add disk property `%s' with "
667 			    "value `%s'\n", prop_name, prop_value);
668 	}
669 
670 	nvlist_free(nvlp);
671 
672 	if (cstr != NULL) {
673 		namevalpr_t nvpr;
674 		nvlist_t *dmap_nvl;
675 
676 		nvpr.name = DISK_AP_PROP_APID;
677 		nvpr.value = strncmp(physid, "/devices", 8) == 0 ?
678 		    (physid + 8) : physid;
679 
680 		/*
681 		 * Set the diskmon's location to the value in this port's label.
682 		 * If there's a disk plugged in, the location will be updated
683 		 * to be the disk label (e.g. HD_ID_00).  Until a disk is
684 		 * inserted, though, there won't be a disk libtopo node
685 		 * created.
686 		 */
687 
688 		/* Pass physid without the leading "/devices": */
689 		dmap_nvl = namevalpr_to_nvlist(&nvpr);
690 
691 		diskp = new_diskmon(dmap_nvl, indp, indrp, diskprops);
692 
693 		if (topo_node_label(node, &label, &err) == 0) {
694 			diskp->location = dstrdup(label);
695 			topo_hdl_strfree(thp, label);
696 		} else
697 			diskp->location = dstrdup("unknown location");
698 
699 		if (!conf_failure && diskp != NULL) {
700 			/* Add this diskmon to the disk list */
701 			cfgdata_add_diskmon(config_data, diskp);
702 			if (nvlist_add_uint64(g_topo2diskmon, cstr,
703 			    (uint64_t)(uintptr_t)diskp) != 0) {
704 				log_msg(MM_TOPO,
705 				    "Could not add pointer to nvlist "
706 				    "for `%s'!\n", cstr);
707 			}
708 		} else if (diskp != NULL) {
709 			diskmon_free(diskp);
710 		} else {
711 			if (dmap_nvl)
712 				nvlist_free(dmap_nvl);
713 			if (indp)
714 				ind_free(indp);
715 			if (indrp)
716 				indrule_free(indrp);
717 			if (diskprops)
718 				nvlist_free(diskprops);
719 		}
720 
721 		wdp->pfmri = cstr;
722 	}
723 
724 
725 	return (0);
726 }
727 
728 /*ARGSUSED*/
729 static int
730 gather_topo_cfg(topo_hdl_t *thp, tnode_t *node, void *arg)
731 {
732 	char *nodename = topo_node_name(node);
733 	if (strcmp(DISK, nodename) == 0)
734 		return (topo_add_disk(thp, node, (walk_diskmon_t *)arg)
735 		    ? TOPO_WALK_ERR : TOPO_WALK_NEXT);
736 	else if (strcmp(BAY, nodename) == 0)
737 		return (topo_add_bay(thp, node, (walk_diskmon_t *)arg)
738 		    ? TOPO_WALK_ERR : TOPO_WALK_NEXT);
739 
740 	return (TOPO_WALK_NEXT);
741 }
742 
743 
744 /*ARGSUSED*/
745 int
746 update_configuration_from_topo(fmd_hdl_t *hdl, diskmon_t *diskp)
747 {
748 	int err;
749 	topo_hdl_t *thp;
750 	topo_walk_t *twp;
751 	walk_diskmon_t wd;
752 
753 	if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL) {
754 		return (TOPO_OPEN_ERROR);
755 	}
756 
757 	wd.target = diskp;
758 	wd.pfmri = NULL;
759 	if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, gather_topo_cfg,
760 	    &wd, &err)) == NULL) {
761 		fmd_hdl_topo_rele(hdl, thp);
762 		return (err ? TOPO_WALK_INIT_ERROR : TOPO_SUCCESS);
763 	}
764 
765 	if (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) {
766 
767 		topo_walk_fini(twp);
768 		if (wd.pfmri != NULL)
769 			dstrfree(wd.pfmri);
770 
771 		fmd_hdl_topo_rele(hdl, thp);
772 		return (TOPO_WALK_ERROR);
773 	}
774 
775 	topo_walk_fini(twp);
776 	fmd_hdl_topo_rele(hdl, thp);
777 	if (wd.pfmri != NULL)
778 		dstrfree(wd.pfmri);
779 
780 	return (TOPO_SUCCESS);
781 }
782 
783 int
784 init_configuration_from_topo(void)
785 {
786 	return (nvlist_alloc(&g_topo2diskmon, NV_UNIQUE_NAME, 0));
787 }
788 
789 void
790 fini_configuration_from_topo(void)
791 {
792 	if (g_topo2diskmon) {
793 		nvlist_free(g_topo2diskmon);
794 	}
795 }
796