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