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