xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_prop.c (revision e5dcf7beb7c949f9234713d5818b581ec3825443)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <strings.h>
27 #include <assert.h>
28 #include <fm/libtopo.h>
29 #include <topo_prop.h>
30 #include <topo_string.h>
31 #include <topo_alloc.h>
32 #include <topo_error.h>
33 #include <topo_method.h>
34 
35 /*
36  * Topology nodes are permitted to contain property information.
37  * Property information is organized according to property grouping.
38  * Each property group defines a name, a stability level for that name,
39  * a stability level for all underlying property data (name, type, values),
40  * a version for the property group definition and and a list of uniquely
41  * defined properties.  Property group versions are incremented when one of
42  * the following changes occurs:
43  *	- a property name changes
44  *	- a property type changes
45  *	- a property definition is removed from the group
46  * Compatible changes such as new property definitions in the group do
47  * not require version changes.
48  *
49  * Each property defines a unique (within the group) name, a type and
50  * a value.  Properties may be statically defined as int32, uint32, int64,
51  * uint64, fmri, string or arrays of each type.  Properties may also be
52  * dynamically exported via module registered methods.  For example, a module
53  * may register a method to export an ASRU property that is dynamically
54  * contructed when a call to topo_node_fmri() is invoked for a particular
55  * topology node.
56  *
57  * Static properties are persistently attached to topology nodes during
58  * enumeration by an enumeration module or as part of XML statements in a
59  * toplogy map file using the topo_prop_set* family of routines.  Similarly,
60  * property methods are registered during enumeration or as part of
61  * statements in topololgy map files.  Set-up of property methods is performed
62  * by calling topo_prop_method_register().
63  *
64  * All properties, whether statically persisted in a snapshot or dynamically
65  * obtained, may be read via the topo_prop_get* family of interfaces.
66  * Callers wishing to receive all property groups and properties for a given
67  * node may use topo_prop_getall().  This routine returns a nested nvlist
68  * of all groupings and property (name, type, value) sets.  Groupings
69  * are defined by TOPO_PROP_GROUP (name, data stability, name stability and
70  * version) and a nested nvlist of properties (TOPO_PROP_VAL).  Each property
71  * value is defined by its name, type and value.
72  */
73 static void topo_propval_destroy(topo_propval_t *);
74 
75 static topo_pgroup_t *
76 pgroup_get(tnode_t *node, const char *pgname)
77 {
78 	topo_pgroup_t *pg;
79 	/*
80 	 * Check for an existing pgroup
81 	 */
82 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
83 	    pg = topo_list_next(pg)) {
84 		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
85 			return (pg);
86 		}
87 	}
88 
89 	return (NULL);
90 }
91 
92 static topo_propval_t *
93 propval_get(topo_pgroup_t *pg, const char *pname)
94 {
95 	topo_proplist_t *pvl;
96 
97 	if (pg == NULL)
98 		return (NULL);
99 
100 	for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
101 	    pvl = topo_list_next(pvl)) {
102 		if (strcmp(pvl->tp_pval->tp_name, pname) == 0)
103 			return (pvl->tp_pval);
104 	}
105 
106 	return (NULL);
107 }
108 
109 static int
110 method_geterror(nvlist_t *nvl, int err, int *errp)
111 {
112 	if (nvl != NULL)
113 		nvlist_free(nvl);
114 
115 	*errp = err;
116 
117 	return (-1);
118 }
119 
120 static int
121 prop_method_get(tnode_t *node, topo_propval_t *pv, topo_propmethod_t *pm,
122     nvlist_t *pargs, int *err)
123 {
124 	int ret;
125 	nvlist_t *args, *nvl;
126 	char *name;
127 	topo_type_t type;
128 
129 	if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0 ||
130 	    nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args) != 0)
131 		return (method_geterror(NULL, ETOPO_PROP_NVL, err));
132 
133 	if (pargs != NULL)
134 		if (nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs) != 0)
135 			return (method_geterror(args, ETOPO_PROP_NVL, err));
136 
137 	/*
138 	 * Now, get the latest value
139 	 *
140 	 * Grab a reference to the property and then unlock the node.  This will
141 	 * allow property methods to safely re-enter the prop_get codepath,
142 	 * making it possible for property methods to access other property
143 	 * values on the same node w\o causing a deadlock.
144 	 */
145 	topo_prop_hold(pv);
146 	topo_node_unlock(node);
147 	if (topo_method_call(node, pm->tpm_name, pm->tpm_version,
148 	    args, &nvl, err) < 0) {
149 		topo_node_lock(node);
150 		topo_prop_rele(pv);
151 		return (method_geterror(args, *err, err));
152 	}
153 	topo_node_lock(node);
154 	topo_prop_rele(pv);
155 
156 	nvlist_free(args);
157 
158 	/* Verify the property contents */
159 	ret = nvlist_lookup_string(nvl, TOPO_PROP_VAL_NAME, &name);
160 	if (ret != 0 || strcmp(name, pv->tp_name) != 0)
161 		return (method_geterror(nvl, ETOPO_PROP_NAME, err));
162 
163 	ret = nvlist_lookup_uint32(nvl, TOPO_PROP_VAL_TYPE, (uint32_t *)&type);
164 	if (ret != 0 || type != pv->tp_type)
165 		return (method_geterror(nvl, ETOPO_PROP_TYPE, err));
166 
167 	/* Release the last value and re-assign to the new value */
168 	if (pv->tp_val != NULL)
169 		nvlist_free(pv->tp_val);
170 	pv->tp_val = nvl;
171 
172 	return (0);
173 }
174 
175 static topo_propval_t *
176 prop_get(tnode_t *node, const char *pgname, const char *pname, nvlist_t *pargs,
177     int *err)
178 {
179 	topo_propval_t *pv = NULL;
180 
181 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
182 		*err = ETOPO_PROP_NOENT;
183 		return (NULL);
184 	}
185 
186 	if (pv->tp_flag & TOPO_PROP_NONVOLATILE && pv->tp_val != NULL)
187 		return (pv);
188 
189 	if (pv->tp_method != NULL) {
190 		if (prop_method_get(node, pv, pv->tp_method, pargs, err) < 0)
191 			return (NULL);
192 	}
193 
194 	return (pv);
195 }
196 
197 static int
198 get_properror(tnode_t *node, int *errp, int err)
199 {
200 	topo_node_unlock(node);
201 	*errp = err;
202 	return (-1);
203 }
204 
205 static int
206 prop_getval(tnode_t *node, const char *pgname, const char *pname, void *val,
207     topo_type_t type, uint_t *nelems, int *err)
208 {
209 	int i, j, ret = 0;
210 	topo_hdl_t *thp = node->tn_hdl;
211 	topo_propval_t *pv;
212 
213 	topo_node_lock(node);
214 	if ((pv = prop_get(node, pgname, pname, NULL, err))
215 	    == NULL)
216 		return (get_properror(node, err, *err));
217 
218 	if (pv->tp_type != type)
219 		return (get_properror(node, err, ETOPO_PROP_TYPE));
220 
221 	switch (type) {
222 		case TOPO_TYPE_INT32:
223 			ret = nvlist_lookup_int32(pv->tp_val, TOPO_PROP_VAL_VAL,
224 			    (int32_t *)val);
225 			break;
226 		case TOPO_TYPE_UINT32:
227 			ret = nvlist_lookup_uint32(pv->tp_val,
228 			    TOPO_PROP_VAL_VAL, (uint32_t *)val);
229 			break;
230 		case TOPO_TYPE_INT64:
231 			ret = nvlist_lookup_int64(pv->tp_val, TOPO_PROP_VAL_VAL,
232 			    (int64_t *)val);
233 			break;
234 		case TOPO_TYPE_UINT64:
235 			ret = nvlist_lookup_uint64(pv->tp_val,
236 			    TOPO_PROP_VAL_VAL, (uint64_t *)val);
237 			break;
238 		case TOPO_TYPE_DOUBLE:
239 			ret = nvlist_lookup_double(pv->tp_val,
240 			    TOPO_PROP_VAL_VAL, (double *)val);
241 			break;
242 		case TOPO_TYPE_STRING: {
243 			char *str;
244 
245 			ret = nvlist_lookup_string(pv->tp_val,
246 			    TOPO_PROP_VAL_VAL, &str);
247 			if (ret == 0) {
248 				char *s2;
249 				if ((s2 = topo_hdl_strdup(thp, str)) == NULL)
250 					ret = -1;
251 				else
252 					*(char **)val = s2;
253 			}
254 			break;
255 		}
256 		case TOPO_TYPE_FMRI: {
257 			nvlist_t *nvl;
258 
259 			ret = nvlist_lookup_nvlist(pv->tp_val,
260 			    TOPO_PROP_VAL_VAL, &nvl);
261 			if (ret == 0)
262 				ret = topo_hdl_nvdup(thp, nvl,
263 				    (nvlist_t **)val);
264 			break;
265 		}
266 		case TOPO_TYPE_INT32_ARRAY: {
267 			int32_t *a1, *a2;
268 
269 			if ((ret = nvlist_lookup_int32_array(pv->tp_val,
270 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
271 				break;
272 			if ((a1 = topo_hdl_alloc(thp, sizeof (int32_t) *
273 			    *nelems)) == NULL) {
274 				ret = ETOPO_NOMEM;
275 				break;
276 			}
277 			for (i = 0; i < *nelems; ++i)
278 				a1[i] = a2[i];
279 			*(int32_t **)val = a1;
280 			break;
281 		}
282 		case TOPO_TYPE_UINT32_ARRAY: {
283 			uint32_t *a1, *a2;
284 
285 			if ((ret = nvlist_lookup_uint32_array(pv->tp_val,
286 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
287 				break;
288 			if ((a1 = topo_hdl_alloc(thp, sizeof (uint32_t) *
289 			    *nelems)) == NULL) {
290 				ret = ETOPO_NOMEM;
291 				break;
292 			}
293 			for (i = 0; i < *nelems; ++i)
294 				a1[i] = a2[i];
295 			*(uint32_t **)val = a1;
296 			break;
297 		}
298 		case TOPO_TYPE_INT64_ARRAY: {
299 			int64_t *a1, *a2;
300 
301 			if ((ret = nvlist_lookup_int64_array(pv->tp_val,
302 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
303 				break;
304 			if ((a1 = topo_hdl_alloc(thp, sizeof (int64_t) *
305 			    *nelems)) == NULL) {
306 				ret = ETOPO_NOMEM;
307 				break;
308 			}
309 			for (i = 0; i < *nelems; ++i)
310 				a1[i] = a2[i];
311 			*(int64_t **)val = a1;
312 			break;
313 		}
314 		case TOPO_TYPE_UINT64_ARRAY: {
315 			uint64_t *a1, *a2;
316 
317 			if ((ret = nvlist_lookup_uint64_array(pv->tp_val,
318 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
319 				break;
320 			if ((a1 = topo_hdl_alloc(thp, sizeof (uint64_t) *
321 			    *nelems)) == NULL) {
322 				ret = ETOPO_NOMEM;
323 				break;
324 			}
325 			for (i = 0; i < *nelems; ++i)
326 				a1[i] = a2[i];
327 			*(uint64_t **)val = a1;
328 			break;
329 		}
330 		case TOPO_TYPE_STRING_ARRAY: {
331 			char **a1, **a2;
332 
333 			if ((ret = nvlist_lookup_string_array(pv->tp_val,
334 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
335 				break;
336 			if ((a1 = topo_hdl_alloc(thp, sizeof (char *) *
337 			    *nelems)) == NULL) {
338 				ret = ETOPO_NOMEM;
339 				break;
340 			}
341 			for (i = 0; i < *nelems; ++i) {
342 				if ((a1[i] = topo_hdl_strdup(thp, a2[i]))
343 				    == NULL) {
344 					for (j = 0; j < i; ++j)
345 						topo_hdl_free(thp, a1[j],
346 						    sizeof (char *));
347 					topo_hdl_free(thp, a1,
348 					    sizeof (char *) * *nelems);
349 					break;
350 				}
351 			}
352 			*(char ***)val = a1;
353 			break;
354 		}
355 		case TOPO_TYPE_FMRI_ARRAY: {
356 			nvlist_t **a1, **a2;
357 
358 			if ((ret = nvlist_lookup_nvlist_array(pv->tp_val,
359 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
360 				break;
361 			if ((a1 = topo_hdl_alloc(thp, sizeof (nvlist_t *) *
362 			    *nelems)) == NULL) {
363 				ret = ETOPO_NOMEM;
364 				break;
365 			}
366 			for (i = 0; i < *nelems; ++i) {
367 				if (topo_hdl_nvdup(thp, a2[i], &a1[i]) < 0) {
368 					for (j = 0; j < i; ++j)
369 						nvlist_free(a1[j]);
370 					topo_hdl_free(thp, a1,
371 					    sizeof (nvlist_t *) * *nelems);
372 					break;
373 				}
374 			}
375 			*(nvlist_t ***)val = a1;
376 			break;
377 		}
378 		default:
379 			ret = ETOPO_PROP_NOENT;
380 	}
381 
382 	if (ret != 0)
383 		if (ret == ENOENT)
384 			return (get_properror(node, err, ETOPO_PROP_NOENT));
385 		else if (ret < ETOPO_UNKNOWN)
386 			return (get_properror(node, err, ETOPO_PROP_NVL));
387 		else
388 			return (get_properror(node, err, ret));
389 
390 	topo_node_unlock(node);
391 	return (0);
392 }
393 
394 int
395 topo_prop_get_int32(tnode_t *node, const char *pgname, const char *pname,
396     int32_t *val, int *err)
397 {
398 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT32,
399 	    NULL, err));
400 }
401 
402 int
403 topo_prop_get_uint32(tnode_t *node, const char *pgname, const char *pname,
404     uint32_t *val, int *err)
405 {
406 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT32,
407 	    NULL, err));
408 }
409 
410 int
411 topo_prop_get_int64(tnode_t *node, const char *pgname, const char *pname,
412     int64_t *val, int *err)
413 {
414 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT64,
415 	    NULL, err));
416 }
417 
418 int
419 topo_prop_get_uint64(tnode_t *node, const char *pgname, const char *pname,
420     uint64_t *val, int *err)
421 {
422 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT64,
423 	    NULL, err));
424 }
425 
426 int
427 topo_prop_get_double(tnode_t *node, const char *pgname, const char *pname,
428     double *val, int *err)
429 {
430 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_DOUBLE,
431 	    NULL, err));
432 }
433 
434 int
435 topo_prop_get_string(tnode_t *node, const char *pgname, const char *pname,
436     char **val, int *err)
437 {
438 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_STRING,
439 	    NULL, err));
440 }
441 
442 int
443 topo_prop_get_fmri(tnode_t *node, const char *pgname, const char *pname,
444     nvlist_t **val, int *err)
445 {
446 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_FMRI,
447 	    NULL, err));
448 }
449 
450 int
451 topo_prop_get_int32_array(tnode_t *node, const char *pgname, const char *pname,
452     int32_t **val, uint_t *nelem, int *err)
453 {
454 	return (prop_getval(node, pgname, pname, (void *)val,
455 	    TOPO_TYPE_INT32_ARRAY, nelem, err));
456 }
457 
458 int
459 topo_prop_get_uint32_array(tnode_t *node, const char *pgname, const char *pname,
460     uint32_t **val, uint_t *nelem, int *err)
461 {
462 	return (prop_getval(node, pgname, pname, (void *)val,
463 	    TOPO_TYPE_UINT32_ARRAY, nelem, err));
464 }
465 
466 int
467 topo_prop_get_int64_array(tnode_t *node, const char *pgname, const char *pname,
468     int64_t **val, uint_t *nelem, int *err)
469 {
470 	return (prop_getval(node, pgname, pname, (void *)val,
471 	    TOPO_TYPE_INT64_ARRAY, nelem, err));
472 }
473 
474 int
475 topo_prop_get_uint64_array(tnode_t *node, const char *pgname, const char *pname,
476     uint64_t **val, uint_t *nelem, int *err)
477 {
478 	return (prop_getval(node, pgname, pname, (void *)val,
479 	    TOPO_TYPE_UINT64_ARRAY, nelem, err));
480 }
481 
482 int
483 topo_prop_get_string_array(tnode_t *node, const char *pgname, const char *pname,
484     char ***val, uint_t *nelem, int *err)
485 {
486 	return (prop_getval(node, pgname, pname, (void *)val,
487 	    TOPO_TYPE_STRING_ARRAY, nelem, err));
488 }
489 
490 int
491 topo_prop_get_fmri_array(tnode_t *node, const char *pgname, const char *pname,
492     nvlist_t ***val, uint_t *nelem, int *err)
493 {
494 	return (prop_getval(node, pgname, pname, (void *)val,
495 	    TOPO_TYPE_FMRI_ARRAY, nelem, err));
496 }
497 
498 static topo_propval_t *
499 set_seterror(tnode_t *node, topo_proplist_t *pvl, int *errp, int err)
500 {
501 	topo_hdl_t *thp = node->tn_hdl;
502 	topo_propval_t *pv;
503 
504 	if (pvl != NULL) {
505 		pv = pvl->tp_pval;
506 		topo_propval_destroy(pv);
507 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
508 	}
509 
510 	topo_node_unlock(node);
511 	*errp = err;
512 
513 	return (NULL);
514 }
515 
516 static topo_propval_t *
517 prop_create(tnode_t *node, const char *pgname, const char *pname,
518     topo_type_t type, int flag, int *err)
519 {
520 	topo_hdl_t *thp = node->tn_hdl;
521 	topo_pgroup_t *pg;
522 	topo_propval_t *pv;
523 	topo_proplist_t *pvl;
524 
525 	/*
526 	 * Replace existing prop value with new one
527 	 */
528 	if ((pg = pgroup_get(node, pgname)) == NULL) {
529 		topo_node_unlock(node);
530 		*err = ETOPO_PROP_NOENT;
531 		return (NULL);
532 	}
533 
534 	if ((pv = propval_get(pg, pname)) != NULL) {
535 		if (pv->tp_type != type)
536 			return (set_seterror(node, NULL, err, ETOPO_PROP_TYPE));
537 		else if (! (pv->tp_flag & TOPO_PROP_MUTABLE))
538 			return (set_seterror(node, NULL, err, ETOPO_PROP_DEFD));
539 
540 		nvlist_free(pv->tp_val);
541 		pv->tp_val = NULL;
542 	} else {
543 		if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
544 		    == NULL)
545 			return (set_seterror(node, NULL, err, ETOPO_NOMEM));
546 
547 		if ((pv = topo_hdl_zalloc(thp, sizeof (topo_propval_t)))
548 		    == NULL)
549 			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
550 
551 		pv->tp_hdl = thp;
552 		pvl->tp_pval = pv;
553 
554 		if ((pv->tp_name = topo_hdl_strdup(thp, pname))
555 		    == NULL)
556 			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
557 		pv->tp_flag = flag;
558 		pv->tp_type = type;
559 		topo_prop_hold(pv);
560 		topo_list_append(&pg->tpg_pvals, pvl);
561 	}
562 
563 	return (pv);
564 }
565 
566 static int
567 topo_prop_set(tnode_t *node, const char *pgname, const char *pname,
568     topo_type_t type, int flag, void *val, int nelems, int *err)
569 {
570 	int ret;
571 	topo_hdl_t *thp = node->tn_hdl;
572 	nvlist_t *nvl;
573 
574 	if (topo_hdl_nvalloc(thp, &nvl, NV_UNIQUE_NAME) < 0) {
575 		*err = ETOPO_PROP_NVL;
576 		return (-1);
577 	}
578 
579 	ret = nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, pname);
580 	ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, type);
581 	switch (type) {
582 		case TOPO_TYPE_INT32:
583 			ret |= nvlist_add_int32(nvl, TOPO_PROP_VAL_VAL,
584 			    *(int32_t *)val);
585 			break;
586 		case TOPO_TYPE_UINT32:
587 			ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL,
588 			    *(uint32_t *)val);
589 			break;
590 		case TOPO_TYPE_INT64:
591 			ret |= nvlist_add_int64(nvl, TOPO_PROP_VAL_VAL,
592 			    *(int64_t *)val);
593 			break;
594 		case TOPO_TYPE_UINT64:
595 			ret |= nvlist_add_uint64(nvl, TOPO_PROP_VAL_VAL,
596 			    *(uint64_t *)val);
597 			break;
598 		case TOPO_TYPE_DOUBLE:
599 			ret |= nvlist_add_double(nvl, TOPO_PROP_VAL_VAL,
600 			    *(double *)val);
601 			break;
602 		case TOPO_TYPE_STRING:
603 			ret |= nvlist_add_string(nvl, TOPO_PROP_VAL_VAL,
604 			    (char *)val);
605 			break;
606 		case TOPO_TYPE_FMRI:
607 			ret |= nvlist_add_nvlist(nvl, TOPO_PROP_VAL_VAL,
608 			    (nvlist_t *)val);
609 			break;
610 		case TOPO_TYPE_INT32_ARRAY:
611 			ret |= nvlist_add_int32_array(nvl,
612 			    TOPO_PROP_VAL_VAL, (int32_t *)val, nelems);
613 			break;
614 		case TOPO_TYPE_UINT32_ARRAY:
615 			ret |= nvlist_add_uint32_array(nvl,
616 			    TOPO_PROP_VAL_VAL, (uint32_t *)val, nelems);
617 			break;
618 		case TOPO_TYPE_INT64_ARRAY:
619 			ret |= nvlist_add_int64_array(nvl,
620 			    TOPO_PROP_VAL_VAL, (int64_t *)val, nelems);
621 			break;
622 		case TOPO_TYPE_UINT64_ARRAY:
623 			ret |= nvlist_add_uint64_array(nvl,
624 			    TOPO_PROP_VAL_VAL, (uint64_t *)val, nelems);
625 			break;
626 		case TOPO_TYPE_STRING_ARRAY:
627 			ret |= nvlist_add_string_array(nvl,
628 			    TOPO_PROP_VAL_VAL, (char **)val, nelems);
629 			break;
630 		case TOPO_TYPE_FMRI_ARRAY:
631 			ret |= nvlist_add_nvlist_array(nvl,
632 			    TOPO_PROP_VAL_VAL, (nvlist_t **)val, nelems);
633 			break;
634 		default:
635 			*err = ETOPO_PROP_TYPE;
636 			return (-1);
637 	}
638 
639 	if (ret != 0) {
640 		nvlist_free(nvl);
641 		if (ret == ENOMEM) {
642 			*err = ETOPO_PROP_NOMEM;
643 			return (-1);
644 		} else {
645 			*err = ETOPO_PROP_NVL;
646 			return (-1);
647 		}
648 	}
649 
650 	if (topo_prop_setprop(node, pgname, nvl, flag, nvl, err) != 0) {
651 		nvlist_free(nvl);
652 		return (-1); /* err set */
653 	}
654 	nvlist_free(nvl);
655 	return (ret);
656 }
657 
658 int
659 topo_prop_set_int32(tnode_t *node, const char *pgname, const char *pname,
660     int flag, int32_t val, int *err)
661 {
662 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32, flag,
663 	    &val, 1, err));
664 }
665 
666 int
667 topo_prop_set_uint32(tnode_t *node, const char *pgname, const char *pname,
668     int flag, uint32_t val, int *err)
669 {
670 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32, flag,
671 	    &val, 1, err));
672 }
673 
674 int
675 topo_prop_set_int64(tnode_t *node, const char *pgname, const char *pname,
676     int flag, int64_t val, int *err)
677 {
678 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64, flag,
679 	    &val, 1, err));
680 }
681 
682 int
683 topo_prop_set_uint64(tnode_t *node, const char *pgname, const char *pname,
684     int flag, uint64_t val, int *err)
685 {
686 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64, flag,
687 	    &val, 1, err));
688 }
689 
690 int
691 topo_prop_set_double(tnode_t *node, const char *pgname, const char *pname,
692     int flag, double val, int *err)
693 {
694 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_DOUBLE, flag,
695 	    &val, 1, err));
696 }
697 
698 int
699 topo_prop_set_string(tnode_t *node, const char *pgname, const char *pname,
700     int flag, const char *val, int *err)
701 {
702 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING, flag,
703 	    (void *)val, 1, err));
704 }
705 
706 int
707 topo_prop_set_fmri(tnode_t *node, const char *pgname, const char *pname,
708     int flag, const nvlist_t *fmri, int *err)
709 {
710 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI, flag,
711 	    (void *)fmri, 1, err));
712 }
713 
714 int
715 topo_prop_set_int32_array(tnode_t *node, const char *pgname, const char *pname,
716     int flag, int32_t *val, uint_t nelems, int *err)
717 {
718 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32_ARRAY, flag,
719 	    val, nelems, err));
720 }
721 
722 int
723 topo_prop_set_uint32_array(tnode_t *node, const char *pgname, const char *pname,
724     int flag, uint32_t *val, uint_t nelems, int *err)
725 {
726 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32_ARRAY, flag,
727 	    val, nelems, err));
728 }
729 
730 int
731 topo_prop_set_int64_array(tnode_t *node, const char *pgname, const char *pname,
732     int flag, int64_t *val, uint_t nelems, int *err)
733 {
734 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64_ARRAY, flag,
735 	    val, nelems, err));
736 }
737 
738 int
739 topo_prop_set_uint64_array(tnode_t *node, const char *pgname, const char *pname,
740     int flag, uint64_t *val, uint_t nelems, int *err)
741 {
742 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64_ARRAY, flag,
743 	    val, nelems, err));
744 }
745 
746 int
747 topo_prop_set_string_array(tnode_t *node, const char *pgname, const char *pname,
748     int flag, const char **val, uint_t nelems, int *err)
749 {
750 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING_ARRAY, flag,
751 	    (void *)val, nelems, err));
752 }
753 
754 int
755 topo_prop_set_fmri_array(tnode_t *node, const char *pgname, const char *pname,
756     int flag, const nvlist_t **fmri, uint_t nelems, int *err)
757 {
758 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI_ARRAY, flag,
759 	    (void *)fmri, nelems, err));
760 }
761 
762 /*
763  * topo_prop_setprop() is a private project function for fmtopo
764  */
765 int
766 topo_prop_setprop(tnode_t *node, const char *pgname, nvlist_t *prop,
767     int flag, nvlist_t *pargs, int *err)
768 {
769 	int ret;
770 	topo_hdl_t *thp = node->tn_hdl;
771 	topo_propval_t *pv;
772 	nvlist_t *nvl, *args;
773 	char *name;
774 	topo_type_t type;
775 
776 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_NAME, &name) != 0) {
777 		*err = ETOPO_PROP_NAME;
778 		return (-1);
779 	}
780 	if (nvlist_lookup_uint32(prop, TOPO_PROP_VAL_TYPE, (uint32_t *)&type)
781 	    != 0) {
782 		*err = ETOPO_PROP_TYPE;
783 		return (-1);
784 	}
785 
786 	topo_node_lock(node);
787 	if ((pv = prop_create(node, pgname, name, type, flag, err)) == NULL)
788 		return (-1); /* unlocked and err set */
789 
790 	/*
791 	 * Set by method or set to new prop value.  If we fail, leave
792 	 * property in list with old value.
793 	 */
794 	if (pv->tp_method != NULL) {
795 		topo_propmethod_t *pm = pv->tp_method;
796 
797 		if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0) {
798 			topo_node_unlock(node);
799 			*err = ETOPO_PROP_NOMEM;
800 			return (-1);
801 		}
802 		ret = nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args);
803 		if (pargs != NULL)
804 			ret |= nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs);
805 
806 		if (ret != 0) {
807 			topo_node_unlock(node);
808 			nvlist_free(args);
809 			*err = ETOPO_PROP_NVL;
810 			return (-1);
811 		}
812 
813 		/*
814 		 *
815 		 * Grab a reference to the property and then unlock the node.
816 		 * This will allow property methods to safely re-enter the
817 		 * prop_get codepath, making it possible for property methods
818 		 * to access other property values on the same node w\o causing
819 		 * a deadlock.
820 		 *
821 		 * We don't technically need this now, since this interface is
822 		 * currently only used by fmtopo (which is single-threaded), but
823 		 * we may make this interface available to other parts of
824 		 * libtopo in the future, so best to make it MT-safe now.
825 		 */
826 		topo_prop_hold(pv);
827 		topo_node_unlock(node);
828 		ret = topo_method_call(node, pm->tpm_name, pm->tpm_version,
829 		    args, &nvl, err);
830 		topo_node_lock(node);
831 		topo_prop_rele(pv);
832 
833 		nvlist_free(args);
834 	} else {
835 		if ((ret = topo_hdl_nvdup(thp, prop, &nvl)) != 0)
836 			*err = ETOPO_PROP_NOMEM;
837 	}
838 
839 	if (ret != 0) {
840 		topo_node_unlock(node);
841 		return (-1);
842 	}
843 
844 	pv->tp_val = nvl;
845 	topo_node_unlock(node);
846 	return (0);
847 }
848 
849 static int
850 register_methoderror(tnode_t *node, topo_propmethod_t *pm, int *errp, int l,
851     int err)
852 {
853 	topo_hdl_t *thp = node->tn_hdl;
854 
855 	if (pm != NULL) {
856 		if (pm->tpm_name != NULL)
857 			topo_hdl_strfree(thp, pm->tpm_name);
858 		if (pm->tpm_args != NULL)
859 			nvlist_free(pm->tpm_args);
860 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
861 	}
862 
863 	*errp = err;
864 
865 	if (l != 0)
866 		topo_node_unlock(node);
867 
868 	return (-1);
869 }
870 
871 int
872 prop_method_register(tnode_t *node, const char *pgname, const char *pname,
873     topo_type_t ptype, const char *mname, topo_version_t version,
874     const nvlist_t *args, int *err)
875 {
876 	topo_hdl_t *thp = node->tn_hdl;
877 	topo_propmethod_t *pm = NULL;
878 	topo_propval_t *pv = NULL;
879 
880 	if ((pm = topo_hdl_zalloc(thp, sizeof (topo_propmethod_t))) == NULL)
881 		return (register_methoderror(node, pm, err, 1,
882 		    ETOPO_PROP_NOMEM));
883 
884 	if ((pm->tpm_name = topo_hdl_strdup(thp, mname)) == NULL)
885 		return (register_methoderror(node, pm, err, 1,
886 		    ETOPO_PROP_NOMEM));
887 
888 	pm->tpm_version = version;
889 
890 	if (topo_hdl_nvdup(thp, (nvlist_t *)args, &pm->tpm_args) != 0)
891 		return (register_methoderror(node, pm, err, 1,
892 		    ETOPO_PROP_NOMEM));
893 
894 	/*
895 	 * It's possible the property may already exist.  However we still want
896 	 * to allow the method to be registered.  This is to handle the case
897 	 * where we specify a prop method in an xml map to override the value
898 	 * that was set by the enumerator.
899 	 *
900 	 * By default, propmethod-backed properties are not MUTABLE.  This is
901 	 * done to simplify the programming model for modules that implement
902 	 * property methods as most propmethods tend to only support get
903 	 * operations.  Enumerator modules can override this by calling
904 	 * topo_prop_setmutable().  Propmethods that are registered via XML can
905 	 * be set as mutable via the optional "mutable" attribute, which will
906 	 * result in the xml parser calling topo_prop_setflags() after
907 	 * registering the propmethod.
908 	 */
909 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL)
910 		if ((pv = prop_create(node, pgname, pname, ptype,
911 		    TOPO_PROP_IMMUTABLE, err)) == NULL) {
912 			/* node unlocked */
913 			return (register_methoderror(node, pm, err, 0, *err));
914 		}
915 
916 	if (pv->tp_method != NULL)
917 		return (register_methoderror(node, pm, err, 1,
918 		    ETOPO_METHOD_DEFD));
919 
920 	if (pv->tp_val != NULL) {
921 		nvlist_free(pv->tp_val);
922 		pv->tp_val = NULL;
923 	}
924 	pv->tp_method = pm;
925 
926 	topo_node_unlock(node);
927 
928 	return (0);
929 }
930 
931 int
932 topo_prop_method_register(tnode_t *node, const char *pgname, const char *pname,
933     topo_type_t ptype, const char *mname, const nvlist_t *args, int *err)
934 {
935 	topo_imethod_t *mp;
936 
937 	topo_node_lock(node);
938 
939 	if ((mp = topo_method_lookup(node, mname)) == NULL)
940 		return (register_methoderror(node, NULL, err, 1,
941 		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
942 
943 	topo_node_lock(node);
944 
945 	return (prop_method_register(node, pgname, pname, ptype, mname,
946 	    mp->tim_version, args, err)); /* err set and node unlocked */
947 }
948 
949 int
950 topo_prop_method_version_register(tnode_t *node, const char *pgname,
951     const char *pname, topo_type_t ptype, const char *mname,
952     topo_version_t version, const nvlist_t *args, int *err)
953 {
954 	topo_imethod_t *mp;
955 
956 	topo_node_lock(node);
957 
958 	if ((mp = topo_method_lookup(node, mname)) == NULL)
959 		return (register_methoderror(node, NULL, err, 1,
960 		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
961 
962 	topo_node_lock(node);
963 
964 	if (version < mp->tim_version)
965 		return (register_methoderror(node, NULL, err, 1,
966 		    ETOPO_METHOD_VEROLD));
967 	if (version > mp->tim_version)
968 		return (register_methoderror(node, NULL, err, 1,
969 		    ETOPO_METHOD_VERNEW));
970 
971 	return (prop_method_register(node, pgname, pname, ptype, mname,
972 	    version, args, err)); /* err set and node unlocked */
973 }
974 
975 void
976 topo_prop_method_unregister(tnode_t *node, const char *pgname,
977     const char *pname)
978 {
979 	topo_propval_t *pv;
980 	topo_pgroup_t *pg;
981 	topo_proplist_t *pvl;
982 	topo_hdl_t *thp = node->tn_hdl;
983 
984 	topo_node_lock(node);
985 
986 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
987 	    pg = topo_list_next(pg)) {
988 		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
989 			break;
990 		}
991 	}
992 
993 	if (pg == NULL) {
994 		topo_node_unlock(node);
995 		return;
996 	}
997 
998 	for (pvl = topo_list_next(&pg->tpg_list); pvl != NULL;
999 	    pvl = topo_list_next(pvl)) {
1000 		pv = pvl->tp_pval;
1001 		if (strcmp(pv->tp_name, pname) == 0) {
1002 			topo_list_delete(&pg->tpg_pvals, pvl);
1003 			assert(pv->tp_refs == 1);
1004 			topo_prop_rele(pv);
1005 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1006 			break;
1007 		}
1008 	}
1009 
1010 	topo_node_unlock(node);
1011 }
1012 
1013 int
1014 topo_prop_setmutable(tnode_t *node, const char *pgname, const char *pname,
1015     int *err)
1016 {
1017 	topo_propval_t *pv = NULL;
1018 
1019 	topo_node_lock(node);
1020 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
1021 		topo_node_unlock(node);
1022 		*err = ETOPO_PROP_NOENT;
1023 		return (-1);
1024 	}
1025 
1026 	/*
1027 	 * If the property is being inherited then we don't want to allow a
1028 	 * change from IMMUTABLE to MUTABLE.
1029 	 */
1030 	if (pv->tp_refs > 1) {
1031 		topo_node_unlock(node);
1032 		*err = ETOPO_PROP_DEFD;
1033 		return (-1);
1034 	}
1035 	pv->tp_flag |= TOPO_PROP_MUTABLE;
1036 
1037 	topo_node_unlock(node);
1038 
1039 	return (0);
1040 }
1041 int
1042 topo_prop_setnonvolatile(tnode_t *node, const char *pgname, const char *pname,
1043     int *err)
1044 {
1045 	topo_propval_t *pv = NULL;
1046 
1047 	topo_node_lock(node);
1048 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
1049 		topo_node_unlock(node);
1050 		*err = ETOPO_PROP_NOENT;
1051 		return (-1);
1052 	}
1053 
1054 	pv->tp_flag |= TOPO_PROP_NONVOLATILE;
1055 
1056 	topo_node_unlock(node);
1057 
1058 	return (0);
1059 }
1060 
1061 static int
1062 inherit_seterror(tnode_t *node, int *errp, int err)
1063 {
1064 	topo_node_unlock(node);
1065 	topo_node_unlock(node->tn_parent);
1066 
1067 	*errp = err;
1068 
1069 	return (-1);
1070 }
1071 
1072 int
1073 topo_prop_inherit(tnode_t *node, const char *pgname, const char *name, int *err)
1074 {
1075 	topo_hdl_t *thp = node->tn_hdl;
1076 	tnode_t *pnode = node->tn_parent;
1077 	topo_pgroup_t *pg;
1078 	topo_propval_t *pv;
1079 	topo_proplist_t *pvl;
1080 
1081 	topo_node_lock(pnode);
1082 	topo_node_lock(node);
1083 
1084 	/*
1085 	 * Check if the requested property group and prop val are already set
1086 	 * on the node.
1087 	 */
1088 	if (propval_get(pgroup_get(node, pgname), name) != NULL)
1089 		return (inherit_seterror(node, err, ETOPO_PROP_DEFD));
1090 
1091 	/*
1092 	 * Check if the requested property group and prop val exists on the
1093 	 * parent node
1094 	 */
1095 	if ((pv = propval_get(pgroup_get(pnode, pgname), name)) == NULL)
1096 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1097 
1098 	/*
1099 	 * Can this propval be inherited?
1100 	 */
1101 	if (pv->tp_flag & TOPO_PROP_MUTABLE)
1102 		return (inherit_seterror(node, err, ETOPO_PROP_NOINHERIT));
1103 
1104 	/*
1105 	 * Property group should already exist: bump the ref count for this
1106 	 * propval and add it to the node's property group
1107 	 */
1108 	if ((pg = pgroup_get(node, pgname)) == NULL)
1109 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1110 
1111 	if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
1112 	    == NULL)
1113 		return (inherit_seterror(node, err, ETOPO_NOMEM));
1114 
1115 	topo_prop_hold(pv);
1116 	pvl->tp_pval = pv;
1117 	topo_list_append(&pg->tpg_pvals, pvl);
1118 
1119 	topo_node_unlock(node);
1120 	topo_node_unlock(pnode);
1121 
1122 	return (0);
1123 }
1124 
1125 topo_pgroup_info_t *
1126 topo_pgroup_info(tnode_t *node, const char *pgname, int *err)
1127 {
1128 	topo_hdl_t *thp = node->tn_hdl;
1129 	topo_pgroup_t *pg;
1130 	topo_ipgroup_info_t *pip;
1131 	topo_pgroup_info_t *info;
1132 
1133 	topo_node_lock(node);
1134 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1135 	    pg = topo_list_next(pg)) {
1136 		if (strcmp(pgname, pg->tpg_info->tpi_name) == 0) {
1137 			if ((info = topo_hdl_alloc(thp,
1138 			    sizeof (topo_pgroup_info_t))) == NULL)
1139 				return (NULL);
1140 
1141 			pip = pg->tpg_info;
1142 			if ((info->tpi_name =
1143 			    topo_hdl_strdup(thp, pip->tpi_name)) == NULL) {
1144 				*err = ETOPO_PROP_NOMEM;
1145 				topo_hdl_free(thp, info,
1146 				    sizeof (topo_pgroup_info_t));
1147 				topo_node_unlock(node);
1148 				return (NULL);
1149 			}
1150 			info->tpi_namestab = pip->tpi_namestab;
1151 			info->tpi_datastab = pip->tpi_datastab;
1152 			info->tpi_version = pip->tpi_version;
1153 			topo_node_unlock(node);
1154 			return (info);
1155 		}
1156 	}
1157 
1158 	*err = ETOPO_PROP_NOENT;
1159 	topo_node_unlock(node);
1160 	return (NULL);
1161 }
1162 
1163 static int
1164 pgroup_seterr(tnode_t *node, topo_pgroup_t *pg, topo_ipgroup_info_t *pip,
1165     int *err)
1166 {
1167 	topo_hdl_t *thp = node->tn_hdl;
1168 
1169 	if (pip != NULL) {
1170 		if (pip->tpi_name != NULL)
1171 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1172 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1173 	}
1174 
1175 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1176 	*err = ETOPO_NOMEM;
1177 
1178 	topo_node_unlock(node);
1179 
1180 	return (-1);
1181 }
1182 
1183 int
1184 topo_pgroup_create(tnode_t *node, const topo_pgroup_info_t *pinfo, int *err)
1185 {
1186 	topo_pgroup_t *pg;
1187 	topo_ipgroup_info_t *pip;
1188 	topo_hdl_t *thp = node->tn_hdl;
1189 
1190 	*err = 0;
1191 
1192 	topo_node_lock(node);
1193 	/*
1194 	 * Check for an existing pgroup
1195 	 */
1196 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1197 	    pg = topo_list_next(pg)) {
1198 		if (strcmp(pg->tpg_info->tpi_name, pinfo->tpi_name) == 0) {
1199 			*err = ETOPO_PROP_DEFD;
1200 			topo_node_unlock(node);
1201 			return (-1);
1202 		}
1203 	}
1204 
1205 	if ((pg = topo_hdl_zalloc(thp, sizeof (topo_pgroup_t))) == NULL) {
1206 		*err = ETOPO_NOMEM;
1207 		topo_node_unlock(node);
1208 		return (-1);
1209 	}
1210 
1211 	if ((pip = topo_hdl_zalloc(thp, sizeof (topo_ipgroup_info_t)))
1212 	    == NULL)
1213 		return (pgroup_seterr(node, pg, pip, err));
1214 
1215 	if ((pip->tpi_name = topo_hdl_strdup(thp, pinfo->tpi_name))
1216 	    == NULL)
1217 		return (pgroup_seterr(node, pg, pip, err));
1218 
1219 	pip->tpi_namestab = pinfo->tpi_namestab;
1220 	pip->tpi_datastab = pinfo->tpi_datastab;
1221 	pip->tpi_version = pinfo->tpi_version;
1222 
1223 	pg->tpg_info = pip;
1224 
1225 	topo_list_append(&node->tn_pgroups, pg);
1226 	topo_node_unlock(node);
1227 
1228 	return (0);
1229 }
1230 
1231 void
1232 topo_pgroup_destroy(tnode_t *node, const char *pname)
1233 {
1234 	topo_hdl_t *thp = node->tn_hdl;
1235 	topo_pgroup_t *pg;
1236 	topo_proplist_t *pvl;
1237 	topo_ipgroup_info_t *pip;
1238 
1239 	topo_node_lock(node);
1240 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1241 	    pg = topo_list_next(pg)) {
1242 		if (strcmp(pg->tpg_info->tpi_name, pname) == 0) {
1243 			break;
1244 		}
1245 	}
1246 
1247 	if (pg == NULL) {
1248 		topo_node_unlock(node);
1249 		return;
1250 	}
1251 
1252 	while ((pvl = topo_list_next(&pg->tpg_list)) != NULL) {
1253 		topo_list_delete(&pg->tpg_pvals, pvl);
1254 		topo_prop_rele(pvl->tp_pval);
1255 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1256 	}
1257 
1258 	topo_list_delete(&node->tn_pgroups, pg);
1259 	topo_node_unlock(node);
1260 
1261 	pip = pg->tpg_info;
1262 	if (pip != NULL) {
1263 		if (pip->tpi_name != NULL)
1264 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1265 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1266 	}
1267 
1268 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1269 }
1270 
1271 void
1272 topo_pgroup_destroy_all(tnode_t *node)
1273 {
1274 	topo_hdl_t *thp = node->tn_hdl;
1275 	topo_pgroup_t *pg;
1276 	topo_proplist_t *pvl;
1277 	topo_ipgroup_info_t *pip;
1278 
1279 	topo_node_lock(node);
1280 	while ((pg = topo_list_next(&node->tn_pgroups)) != NULL) {
1281 		while ((pvl = topo_list_next(&pg->tpg_pvals)) != NULL) {
1282 			topo_list_delete(&pg->tpg_pvals, pvl);
1283 			topo_prop_rele(pvl->tp_pval);
1284 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1285 		}
1286 
1287 		topo_list_delete(&node->tn_pgroups, pg);
1288 
1289 		pip = pg->tpg_info;
1290 		if (pip != NULL) {
1291 			if (pip->tpi_name != NULL)
1292 				topo_hdl_strfree(thp, (char *)pip->tpi_name);
1293 			topo_hdl_free(thp, pip, sizeof (topo_pgroup_info_t));
1294 		}
1295 
1296 		topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1297 	}
1298 	topo_node_unlock(node);
1299 }
1300 
1301 static void
1302 propmethod_destroy(topo_hdl_t *thp, topo_propval_t *pv)
1303 {
1304 	topo_propmethod_t *pm;
1305 
1306 	pm = pv->tp_method;
1307 	if (pm != NULL) {
1308 		if (pm->tpm_name != NULL)
1309 			topo_hdl_strfree(thp, pm->tpm_name);
1310 		if (pm->tpm_args != NULL)
1311 			nvlist_free(pm->tpm_args);
1312 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
1313 		pv->tp_method = NULL;
1314 	}
1315 }
1316 
1317 static void
1318 topo_propval_destroy(topo_propval_t *pv)
1319 {
1320 	topo_hdl_t *thp;
1321 
1322 	if (pv == NULL)
1323 		return;
1324 
1325 	thp = pv->tp_hdl;
1326 
1327 	if (pv->tp_name != NULL)
1328 		topo_hdl_strfree(thp, pv->tp_name);
1329 
1330 	if (pv->tp_val != NULL)
1331 		nvlist_free(pv->tp_val);
1332 
1333 	propmethod_destroy(thp, pv);
1334 
1335 	topo_hdl_free(thp, pv, sizeof (topo_propval_t));
1336 }
1337 
1338 void
1339 topo_prop_hold(topo_propval_t *pv)
1340 {
1341 	pv->tp_refs++;
1342 }
1343 
1344 void
1345 topo_prop_rele(topo_propval_t *pv)
1346 {
1347 	pv->tp_refs--;
1348 
1349 	assert(pv->tp_refs >= 0);
1350 
1351 	if (pv->tp_refs == 0)
1352 		topo_propval_destroy(pv);
1353 }
1354 
1355 /*
1356  * topo_prop_getprop() and topo_prop_getprops() are private project functions
1357  * for fmtopo
1358  */
1359 int
1360 topo_prop_getprop(tnode_t *node, const char *pgname, const char *pname,
1361     nvlist_t *args, nvlist_t **prop, int *err)
1362 {
1363 	topo_hdl_t *thp = node->tn_hdl;
1364 	topo_propval_t *pv;
1365 
1366 	topo_node_lock(node);
1367 	if ((pv = prop_get(node, pgname, pname, args, err)) == NULL) {
1368 		(void) get_properror(node, err, *err);
1369 		return (-1);
1370 	}
1371 
1372 	if (topo_hdl_nvdup(thp, pv->tp_val, prop) != 0) {
1373 		(void) get_properror(node, err, ETOPO_NOMEM);
1374 		return (-1);
1375 	}
1376 	topo_node_unlock(node);
1377 
1378 	return (0);
1379 }
1380 
1381 static int
1382 prop_val_add(tnode_t *node, nvlist_t **nvl, topo_propval_t *pv, int *err)
1383 {
1384 	if (pv->tp_method != NULL)
1385 		if (prop_method_get(node, pv, pv->tp_method, NULL, err) < 0)
1386 			return (-1);
1387 
1388 	if (pv->tp_val == NULL) {
1389 		*err = ETOPO_PROP_NOENT;
1390 		return (-1);
1391 	}
1392 
1393 	if (topo_hdl_nvdup(pv->tp_hdl, pv->tp_val, nvl) != 0) {
1394 		*err = ETOPO_PROP_NOMEM;
1395 		return (-1);
1396 	}
1397 
1398 	return (0);
1399 }
1400 
1401 static int
1402 get_pgrp_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1403 {
1404 	topo_node_unlock(node);
1405 
1406 	if (nvl != NULL)
1407 		nvlist_free(nvl);
1408 
1409 	*errp = err;
1410 
1411 	return (-1);
1412 }
1413 
1414 int
1415 topo_prop_getpgrp(tnode_t *node, const char *pgname, nvlist_t **pgrp,
1416     int *err)
1417 {
1418 	int ret;
1419 	topo_hdl_t *thp = node->tn_hdl;
1420 	nvlist_t *nvl, *pvnvl;
1421 	topo_pgroup_t *pg;
1422 	topo_propval_t *pv;
1423 	topo_proplist_t *pvl;
1424 
1425 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1426 		*err = ETOPO_NOMEM;
1427 		return (-1);
1428 	}
1429 
1430 	topo_node_lock(node);
1431 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1432 	    pg = topo_list_next(pg)) {
1433 
1434 		if (strcmp(pgname, pg->tpg_info->tpi_name) != 0)
1435 			continue;
1436 
1437 		if (nvlist_add_string(nvl, TOPO_PROP_GROUP_NAME,
1438 		    pg->tpg_info->tpi_name) != 0 ||
1439 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_NSTAB,
1440 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1441 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_DSTAB,
1442 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1443 		    nvlist_add_int32(nvl, TOPO_PROP_GROUP_VERSION,
1444 		    pg->tpg_info->tpi_version) != 0)
1445 			return (get_pgrp_seterror(node, nvl, err,
1446 			    ETOPO_PROP_NVL));
1447 
1448 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1449 		    pvl = topo_list_next(pvl)) {
1450 
1451 			pv = pvl->tp_pval;
1452 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1453 				return (get_pgrp_seterror(node, nvl, err,
1454 				    *err));
1455 			}
1456 			if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_VAL,
1457 			    pvnvl)) != 0) {
1458 				nvlist_free(pvnvl);
1459 				return (get_pgrp_seterror(node, nvl, err, ret));
1460 			}
1461 
1462 			nvlist_free(pvnvl);
1463 		}
1464 		topo_node_unlock(node);
1465 		*pgrp = nvl;
1466 		return (0);
1467 	}
1468 
1469 	topo_node_unlock(node);
1470 	*err = ETOPO_PROP_NOENT;
1471 	return (-1);
1472 }
1473 
1474 static nvlist_t *
1475 get_all_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1476 {
1477 	topo_node_unlock(node);
1478 
1479 	if (nvl != NULL)
1480 		nvlist_free(nvl);
1481 
1482 	*errp = err;
1483 
1484 	return (NULL);
1485 }
1486 
1487 nvlist_t *
1488 topo_prop_getprops(tnode_t *node, int *err)
1489 {
1490 	int ret;
1491 	topo_hdl_t *thp = node->tn_hdl;
1492 	nvlist_t *nvl, *pgnvl, *pvnvl;
1493 	topo_pgroup_t *pg;
1494 	topo_propval_t *pv;
1495 	topo_proplist_t *pvl;
1496 
1497 	topo_node_lock(node);
1498 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1499 		return (get_all_seterror(node, NULL, err, ETOPO_NOMEM));
1500 	}
1501 
1502 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1503 	    pg = topo_list_next(pg)) {
1504 		if (topo_hdl_nvalloc(thp, &pgnvl, 0) != 0)
1505 			return (get_all_seterror(node, nvl, err, ETOPO_NOMEM));
1506 
1507 		if (nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NAME,
1508 		    pg->tpg_info->tpi_name) != 0 ||
1509 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NSTAB,
1510 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1511 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_DSTAB,
1512 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1513 		    nvlist_add_int32(pgnvl, TOPO_PROP_GROUP_VERSION,
1514 		    pg->tpg_info->tpi_version) != 0)
1515 			return (get_all_seterror(node, nvl, err,
1516 			    ETOPO_PROP_NVL));
1517 
1518 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1519 		    pvl = topo_list_next(pvl)) {
1520 
1521 			pv = pvl->tp_pval;
1522 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1523 				nvlist_free(pgnvl);
1524 				return (get_all_seterror(node, nvl, err, *err));
1525 			}
1526 			if ((ret = nvlist_add_nvlist(pgnvl, TOPO_PROP_VAL,
1527 			    pvnvl)) != 0) {
1528 				nvlist_free(pgnvl);
1529 				nvlist_free(pvnvl);
1530 				return (get_all_seterror(node, nvl, err, ret));
1531 			}
1532 
1533 			nvlist_free(pvnvl);
1534 		}
1535 		if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_GROUP, pgnvl))
1536 		    != 0) {
1537 			nvlist_free(pgnvl);
1538 			return (get_all_seterror(node, nvl, err, ret));
1539 		}
1540 
1541 		nvlist_free(pgnvl);
1542 	}
1543 
1544 	topo_node_unlock(node);
1545 
1546 	return (nvl);
1547 }
1548