1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <strings.h>
27 #include <assert.h>
28 #include <fm/libtopo.h>
29 #include <topo_prop.h>
30 #include <topo_string.h>
31 #include <topo_alloc.h>
32 #include <topo_error.h>
33 #include <topo_method.h>
34 
35 /*
36  * Topology nodes are permitted to contain property information.
37  * Property information is organized according to property grouping.
38  * Each property group defines a name, a stability level for that name,
39  * a stability level for all underlying property data (name, type, values),
40  * a version for the property group definition and and a list of uniquely
41  * defined properties.  Property group versions are incremented when one of
42  * the following changes occurs:
43  *	- a property name changes
44  *	- a property type changes
45  *	- a property definition is removed from the group
46  * Compatible changes such as new property definitions in the group do
47  * not require version changes.
48  *
49  * Each property defines a unique (within the group) name, a type and
50  * a value.  Properties may be statically defined as int32, uint32, int64,
51  * uint64, fmri, string or arrays of each type.  Properties may also be
52  * dynamically exported via module registered methods.  For example, a module
53  * may register a method to export an ASRU property that is dynamically
54  * contructed when a call to topo_node_fmri() is invoked for a particular
55  * topology node.
56  *
57  * Static properties are persistently attached to topology nodes during
58  * enumeration by an enumeration module or as part of XML statements in a
59  * toplogy map file using the topo_prop_set* family of routines.  Similarly,
60  * property methods are registered during enumeration or as part of
61  * statements in topololgy map files.  Set-up of property methods is performed
62  * by calling topo_prop_method_register().
63  *
64  * All properties, whether statically persisted in a snapshot or dynamically
65  * obtained, may be read via the topo_prop_get* family of interfaces.
66  * Callers wishing to receive all property groups and properties for a given
67  * node may use topo_prop_getall().  This routine returns a nested nvlist
68  * of all groupings and property (name, type, value) sets.  Groupings
69  * are defined by TOPO_PROP_GROUP (name, data stability, name stability and
70  * version) and a nested nvlist of properties (TOPO_PROP_VAL).  Each property
71  * value is defined by its name, type and value.
72  */
73 static void topo_propval_destroy(topo_propval_t *);
74 
75 static topo_pgroup_t *
76 pgroup_get(tnode_t *node, const char *pgname)
77 {
78 	topo_pgroup_t *pg;
79 	/*
80 	 * Check for an existing pgroup
81 	 */
82 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
83 	    pg = topo_list_next(pg)) {
84 		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
85 			return (pg);
86 		}
87 	}
88 
89 	return (NULL);
90 }
91 
92 static topo_propval_t *
93 propval_get(topo_pgroup_t *pg, const char *pname)
94 {
95 	topo_proplist_t *pvl;
96 
97 	if (pg == NULL)
98 		return (NULL);
99 
100 	for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
101 	    pvl = topo_list_next(pvl)) {
102 		if (strcmp(pvl->tp_pval->tp_name, pname) == 0)
103 			return (pvl->tp_pval);
104 	}
105 
106 	return (NULL);
107 }
108 
109 static int
110 method_geterror(nvlist_t *nvl, int err, int *errp)
111 {
112 	nvlist_free(nvl);
113 
114 	*errp = err;
115 
116 	return (-1);
117 }
118 
119 static int
120 prop_method_get(tnode_t *node, topo_propval_t *pv, topo_propmethod_t *pm,
121     nvlist_t *pargs, int *err)
122 {
123 	int ret;
124 	nvlist_t *args, *nvl;
125 	char *name;
126 	topo_type_t type;
127 
128 	if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0 ||
129 	    nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args) != 0)
130 		return (method_geterror(NULL, ETOPO_PROP_NVL, err));
131 
132 	if (pargs != NULL)
133 		if (nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs) != 0)
134 			return (method_geterror(args, ETOPO_PROP_NVL, err));
135 
136 	/*
137 	 * Now, get the latest value
138 	 *
139 	 * Grab a reference to the property and then unlock the node.  This will
140 	 * allow property methods to safely re-enter the prop_get codepath,
141 	 * making it possible for property methods to access other property
142 	 * values on the same node w\o causing a deadlock.
143 	 */
144 	topo_prop_hold(pv);
145 	topo_node_unlock(node);
146 	if (topo_method_call(node, pm->tpm_name, pm->tpm_version,
147 	    args, &nvl, err) < 0) {
148 		topo_node_lock(node);
149 		topo_prop_rele(pv);
150 		return (method_geterror(args, *err, err));
151 	}
152 	topo_node_lock(node);
153 	topo_prop_rele(pv);
154 
155 	nvlist_free(args);
156 
157 	/* Verify the property contents */
158 	ret = nvlist_lookup_string(nvl, TOPO_PROP_VAL_NAME, &name);
159 	if (ret != 0 || strcmp(name, pv->tp_name) != 0)
160 		return (method_geterror(nvl, ETOPO_PROP_NAME, err));
161 
162 	ret = nvlist_lookup_uint32(nvl, TOPO_PROP_VAL_TYPE, (uint32_t *)&type);
163 	if (ret != 0 || type != pv->tp_type)
164 		return (method_geterror(nvl, ETOPO_PROP_TYPE, err));
165 
166 	/* Release the last value and re-assign to the new value */
167 	nvlist_free(pv->tp_val);
168 	pv->tp_val = nvl;
169 
170 	return (0);
171 }
172 
173 static topo_propval_t *
174 prop_get(tnode_t *node, const char *pgname, const char *pname, nvlist_t *pargs,
175     int *err)
176 {
177 	topo_propval_t *pv = NULL;
178 
179 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
180 		*err = ETOPO_PROP_NOENT;
181 		return (NULL);
182 	}
183 
184 	if (pv->tp_flag & TOPO_PROP_NONVOLATILE && pv->tp_val != NULL)
185 		return (pv);
186 
187 	if (pv->tp_method != NULL) {
188 		if (prop_method_get(node, pv, pv->tp_method, pargs, err) < 0)
189 			return (NULL);
190 	}
191 
192 	return (pv);
193 }
194 
195 static int
196 get_properror(tnode_t *node, int *errp, int err)
197 {
198 	topo_node_unlock(node);
199 	*errp = err;
200 	return (-1);
201 }
202 
203 static int
204 prop_getval(tnode_t *node, const char *pgname, const char *pname, void *val,
205     topo_type_t type, uint_t *nelems, int *err)
206 {
207 	int i, j, ret = 0;
208 	topo_hdl_t *thp = node->tn_hdl;
209 	topo_propval_t *pv;
210 
211 	topo_node_lock(node);
212 	if ((pv = prop_get(node, pgname, pname, NULL, err))
213 	    == NULL)
214 		return (get_properror(node, err, *err));
215 
216 	if (pv->tp_type != type)
217 		return (get_properror(node, err, ETOPO_PROP_TYPE));
218 
219 	switch (type) {
220 		case TOPO_TYPE_INT32:
221 			ret = nvlist_lookup_int32(pv->tp_val, TOPO_PROP_VAL_VAL,
222 			    (int32_t *)val);
223 			break;
224 		case TOPO_TYPE_UINT32:
225 			ret = nvlist_lookup_uint32(pv->tp_val,
226 			    TOPO_PROP_VAL_VAL, (uint32_t *)val);
227 			break;
228 		case TOPO_TYPE_INT64:
229 			ret = nvlist_lookup_int64(pv->tp_val, TOPO_PROP_VAL_VAL,
230 			    (int64_t *)val);
231 			break;
232 		case TOPO_TYPE_UINT64:
233 			ret = nvlist_lookup_uint64(pv->tp_val,
234 			    TOPO_PROP_VAL_VAL, (uint64_t *)val);
235 			break;
236 		case TOPO_TYPE_DOUBLE:
237 			ret = nvlist_lookup_double(pv->tp_val,
238 			    TOPO_PROP_VAL_VAL, (double *)val);
239 			break;
240 		case TOPO_TYPE_STRING: {
241 			char *str;
242 
243 			ret = nvlist_lookup_string(pv->tp_val,
244 			    TOPO_PROP_VAL_VAL, &str);
245 			if (ret == 0) {
246 				char *s2;
247 				if ((s2 = topo_hdl_strdup(thp, str)) == NULL)
248 					ret = -1;
249 				else
250 					*(char **)val = s2;
251 			}
252 			break;
253 		}
254 		case TOPO_TYPE_FMRI: {
255 			nvlist_t *nvl;
256 
257 			ret = nvlist_lookup_nvlist(pv->tp_val,
258 			    TOPO_PROP_VAL_VAL, &nvl);
259 			if (ret == 0)
260 				ret = topo_hdl_nvdup(thp, nvl,
261 				    (nvlist_t **)val);
262 			break;
263 		}
264 		case TOPO_TYPE_INT32_ARRAY: {
265 			int32_t *a1, *a2;
266 
267 			if ((ret = nvlist_lookup_int32_array(pv->tp_val,
268 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
269 				break;
270 			if ((a1 = topo_hdl_alloc(thp, sizeof (int32_t) *
271 			    *nelems)) == NULL) {
272 				ret = ETOPO_NOMEM;
273 				break;
274 			}
275 			for (i = 0; i < *nelems; ++i)
276 				a1[i] = a2[i];
277 			*(int32_t **)val = a1;
278 			break;
279 		}
280 		case TOPO_TYPE_UINT32_ARRAY: {
281 			uint32_t *a1, *a2;
282 
283 			if ((ret = nvlist_lookup_uint32_array(pv->tp_val,
284 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
285 				break;
286 			if ((a1 = topo_hdl_alloc(thp, sizeof (uint32_t) *
287 			    *nelems)) == NULL) {
288 				ret = ETOPO_NOMEM;
289 				break;
290 			}
291 			for (i = 0; i < *nelems; ++i)
292 				a1[i] = a2[i];
293 			*(uint32_t **)val = a1;
294 			break;
295 		}
296 		case TOPO_TYPE_INT64_ARRAY: {
297 			int64_t *a1, *a2;
298 
299 			if ((ret = nvlist_lookup_int64_array(pv->tp_val,
300 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
301 				break;
302 			if ((a1 = topo_hdl_alloc(thp, sizeof (int64_t) *
303 			    *nelems)) == NULL) {
304 				ret = ETOPO_NOMEM;
305 				break;
306 			}
307 			for (i = 0; i < *nelems; ++i)
308 				a1[i] = a2[i];
309 			*(int64_t **)val = a1;
310 			break;
311 		}
312 		case TOPO_TYPE_UINT64_ARRAY: {
313 			uint64_t *a1, *a2;
314 
315 			if ((ret = nvlist_lookup_uint64_array(pv->tp_val,
316 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
317 				break;
318 			if ((a1 = topo_hdl_alloc(thp, sizeof (uint64_t) *
319 			    *nelems)) == NULL) {
320 				ret = ETOPO_NOMEM;
321 				break;
322 			}
323 			for (i = 0; i < *nelems; ++i)
324 				a1[i] = a2[i];
325 			*(uint64_t **)val = a1;
326 			break;
327 		}
328 		case TOPO_TYPE_STRING_ARRAY: {
329 			char **a1, **a2;
330 
331 			if ((ret = nvlist_lookup_string_array(pv->tp_val,
332 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
333 				break;
334 			if ((a1 = topo_hdl_alloc(thp, sizeof (char *) *
335 			    *nelems)) == NULL) {
336 				ret = ETOPO_NOMEM;
337 				break;
338 			}
339 			for (i = 0; i < *nelems; ++i) {
340 				if ((a1[i] = topo_hdl_strdup(thp, a2[i]))
341 				    == NULL) {
342 					for (j = 0; j < i; ++j)
343 						topo_hdl_free(thp, a1[j],
344 						    sizeof (char *));
345 					topo_hdl_free(thp, a1,
346 					    sizeof (char *) * *nelems);
347 					break;
348 				}
349 			}
350 			*(char ***)val = a1;
351 			break;
352 		}
353 		case TOPO_TYPE_FMRI_ARRAY: {
354 			nvlist_t **a1, **a2;
355 
356 			if ((ret = nvlist_lookup_nvlist_array(pv->tp_val,
357 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
358 				break;
359 			if ((a1 = topo_hdl_alloc(thp, sizeof (nvlist_t *) *
360 			    *nelems)) == NULL) {
361 				ret = ETOPO_NOMEM;
362 				break;
363 			}
364 			for (i = 0; i < *nelems; ++i) {
365 				if (topo_hdl_nvdup(thp, a2[i], &a1[i]) < 0) {
366 					for (j = 0; j < i; ++j)
367 						nvlist_free(a1[j]);
368 					topo_hdl_free(thp, a1,
369 					    sizeof (nvlist_t *) * *nelems);
370 					break;
371 				}
372 			}
373 			*(nvlist_t ***)val = a1;
374 			break;
375 		}
376 		default:
377 			ret = ETOPO_PROP_NOENT;
378 	}
379 
380 	if (ret != 0) {
381 		if (ret == ENOENT)
382 			return (get_properror(node, err, ETOPO_PROP_NOENT));
383 		else if (ret < ETOPO_UNKNOWN)
384 			return (get_properror(node, err, ETOPO_PROP_NVL));
385 		else
386 			return (get_properror(node, err, ret));
387 	}
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_MUTABLE))
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 		nvlist_free(pm->tpm_args);
858 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
859 	}
860 
861 	*errp = err;
862 
863 	if (l != 0)
864 		topo_node_unlock(node);
865 
866 	return (-1);
867 }
868 
869 int
870 prop_method_register(tnode_t *node, const char *pgname, const char *pname,
871     topo_type_t ptype, const char *mname, topo_version_t version,
872     const nvlist_t *args, int *err)
873 {
874 	topo_hdl_t *thp = node->tn_hdl;
875 	topo_propmethod_t *pm = NULL;
876 	topo_propval_t *pv = NULL;
877 
878 	if ((pm = topo_hdl_zalloc(thp, sizeof (topo_propmethod_t))) == NULL)
879 		return (register_methoderror(node, pm, err, 1,
880 		    ETOPO_PROP_NOMEM));
881 
882 	if ((pm->tpm_name = topo_hdl_strdup(thp, mname)) == NULL)
883 		return (register_methoderror(node, pm, err, 1,
884 		    ETOPO_PROP_NOMEM));
885 
886 	pm->tpm_version = version;
887 
888 	if (topo_hdl_nvdup(thp, (nvlist_t *)args, &pm->tpm_args) != 0)
889 		return (register_methoderror(node, pm, err, 1,
890 		    ETOPO_PROP_NOMEM));
891 
892 	/*
893 	 * It's possible the property may already exist.  However we still want
894 	 * to allow the method to be registered.  This is to handle the case
895 	 * where we specify a prop method in an xml map to override the value
896 	 * that was set by the enumerator.
897 	 *
898 	 * By default, propmethod-backed properties are not MUTABLE.  This is
899 	 * done to simplify the programming model for modules that implement
900 	 * property methods as most propmethods tend to only support get
901 	 * operations.  Enumerator modules can override this by calling
902 	 * topo_prop_setmutable().  Propmethods that are registered via XML can
903 	 * be set as mutable via the optional "mutable" attribute, which will
904 	 * result in the xml parser calling topo_prop_setflags() after
905 	 * 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 int
1040 topo_prop_setnonvolatile(tnode_t *node, const char *pgname, const char *pname,
1041     int *err)
1042 {
1043 	topo_propval_t *pv = NULL;
1044 
1045 	topo_node_lock(node);
1046 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
1047 		topo_node_unlock(node);
1048 		*err = ETOPO_PROP_NOENT;
1049 		return (-1);
1050 	}
1051 
1052 	pv->tp_flag |= TOPO_PROP_NONVOLATILE;
1053 
1054 	topo_node_unlock(node);
1055 
1056 	return (0);
1057 }
1058 
1059 static int
1060 inherit_seterror(tnode_t *node, int *errp, int err)
1061 {
1062 	topo_node_unlock(node);
1063 	topo_node_unlock(node->tn_parent);
1064 
1065 	*errp = err;
1066 
1067 	return (-1);
1068 }
1069 
1070 int
1071 topo_prop_inherit(tnode_t *node, const char *pgname, const char *name, int *err)
1072 {
1073 	topo_hdl_t *thp = node->tn_hdl;
1074 	tnode_t *pnode = node->tn_parent;
1075 	topo_pgroup_t *pg;
1076 	topo_propval_t *pv;
1077 	topo_proplist_t *pvl;
1078 
1079 	topo_node_lock(pnode);
1080 	topo_node_lock(node);
1081 
1082 	/*
1083 	 * Check if the requested property group and prop val are already set
1084 	 * on the node.
1085 	 */
1086 	if (propval_get(pgroup_get(node, pgname), name) != NULL)
1087 		return (inherit_seterror(node, err, ETOPO_PROP_DEFD));
1088 
1089 	/*
1090 	 * Check if the requested property group and prop val exists on the
1091 	 * parent node
1092 	 */
1093 	if ((pv = propval_get(pgroup_get(pnode, pgname), name)) == NULL)
1094 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1095 
1096 	/*
1097 	 * Can this propval be inherited?
1098 	 */
1099 	if (pv->tp_flag & TOPO_PROP_MUTABLE)
1100 		return (inherit_seterror(node, err, ETOPO_PROP_NOINHERIT));
1101 
1102 	/*
1103 	 * Property group should already exist: bump the ref count for this
1104 	 * propval and add it to the node's property group
1105 	 */
1106 	if ((pg = pgroup_get(node, pgname)) == NULL)
1107 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1108 
1109 	if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
1110 	    == NULL)
1111 		return (inherit_seterror(node, err, ETOPO_NOMEM));
1112 
1113 	topo_prop_hold(pv);
1114 	pvl->tp_pval = pv;
1115 	topo_list_append(&pg->tpg_pvals, pvl);
1116 
1117 	topo_node_unlock(node);
1118 	topo_node_unlock(pnode);
1119 
1120 	return (0);
1121 }
1122 
1123 topo_pgroup_info_t *
1124 topo_pgroup_info(tnode_t *node, const char *pgname, int *err)
1125 {
1126 	topo_hdl_t *thp = node->tn_hdl;
1127 	topo_pgroup_t *pg;
1128 	topo_ipgroup_info_t *pip;
1129 	topo_pgroup_info_t *info;
1130 
1131 	topo_node_lock(node);
1132 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1133 	    pg = topo_list_next(pg)) {
1134 		if (strcmp(pgname, pg->tpg_info->tpi_name) == 0) {
1135 			if ((info = topo_hdl_alloc(thp,
1136 			    sizeof (topo_pgroup_info_t))) == NULL)
1137 				return (NULL);
1138 
1139 			pip = pg->tpg_info;
1140 			if ((info->tpi_name =
1141 			    topo_hdl_strdup(thp, pip->tpi_name)) == NULL) {
1142 				*err = ETOPO_PROP_NOMEM;
1143 				topo_hdl_free(thp, info,
1144 				    sizeof (topo_pgroup_info_t));
1145 				topo_node_unlock(node);
1146 				return (NULL);
1147 			}
1148 			info->tpi_namestab = pip->tpi_namestab;
1149 			info->tpi_datastab = pip->tpi_datastab;
1150 			info->tpi_version = pip->tpi_version;
1151 			topo_node_unlock(node);
1152 			return (info);
1153 		}
1154 	}
1155 
1156 	*err = ETOPO_PROP_NOENT;
1157 	topo_node_unlock(node);
1158 	return (NULL);
1159 }
1160 
1161 static int
1162 pgroup_seterr(tnode_t *node, topo_pgroup_t *pg, topo_ipgroup_info_t *pip,
1163     int *err)
1164 {
1165 	topo_hdl_t *thp = node->tn_hdl;
1166 
1167 	if (pip != NULL) {
1168 		if (pip->tpi_name != NULL)
1169 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1170 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1171 	}
1172 
1173 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1174 	*err = ETOPO_NOMEM;
1175 
1176 	topo_node_unlock(node);
1177 
1178 	return (-1);
1179 }
1180 
1181 int
1182 topo_pgroup_create(tnode_t *node, const topo_pgroup_info_t *pinfo, int *err)
1183 {
1184 	topo_pgroup_t *pg;
1185 	topo_ipgroup_info_t *pip;
1186 	topo_hdl_t *thp = node->tn_hdl;
1187 
1188 	*err = 0;
1189 
1190 	topo_node_lock(node);
1191 	/*
1192 	 * Check for an existing pgroup
1193 	 */
1194 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1195 	    pg = topo_list_next(pg)) {
1196 		if (strcmp(pg->tpg_info->tpi_name, pinfo->tpi_name) == 0) {
1197 			*err = ETOPO_PROP_DEFD;
1198 			topo_node_unlock(node);
1199 			return (-1);
1200 		}
1201 	}
1202 
1203 	if ((pg = topo_hdl_zalloc(thp, sizeof (topo_pgroup_t))) == NULL) {
1204 		*err = ETOPO_NOMEM;
1205 		topo_node_unlock(node);
1206 		return (-1);
1207 	}
1208 
1209 	if ((pip = topo_hdl_zalloc(thp, sizeof (topo_ipgroup_info_t)))
1210 	    == NULL)
1211 		return (pgroup_seterr(node, pg, pip, err));
1212 
1213 	if ((pip->tpi_name = topo_hdl_strdup(thp, pinfo->tpi_name))
1214 	    == NULL)
1215 		return (pgroup_seterr(node, pg, pip, err));
1216 
1217 	pip->tpi_namestab = pinfo->tpi_namestab;
1218 	pip->tpi_datastab = pinfo->tpi_datastab;
1219 	pip->tpi_version = pinfo->tpi_version;
1220 
1221 	pg->tpg_info = pip;
1222 
1223 	topo_list_append(&node->tn_pgroups, pg);
1224 	topo_node_unlock(node);
1225 
1226 	return (0);
1227 }
1228 
1229 void
1230 topo_pgroup_destroy(tnode_t *node, const char *pname)
1231 {
1232 	topo_hdl_t *thp = node->tn_hdl;
1233 	topo_pgroup_t *pg;
1234 	topo_proplist_t *pvl;
1235 	topo_ipgroup_info_t *pip;
1236 
1237 	topo_node_lock(node);
1238 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1239 	    pg = topo_list_next(pg)) {
1240 		if (strcmp(pg->tpg_info->tpi_name, pname) == 0) {
1241 			break;
1242 		}
1243 	}
1244 
1245 	if (pg == NULL) {
1246 		topo_node_unlock(node);
1247 		return;
1248 	}
1249 
1250 	while ((pvl = topo_list_next(&pg->tpg_list)) != NULL) {
1251 		topo_list_delete(&pg->tpg_pvals, pvl);
1252 		topo_prop_rele(pvl->tp_pval);
1253 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1254 	}
1255 
1256 	topo_list_delete(&node->tn_pgroups, pg);
1257 	topo_node_unlock(node);
1258 
1259 	pip = pg->tpg_info;
1260 	if (pip != NULL) {
1261 		if (pip->tpi_name != NULL)
1262 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1263 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1264 	}
1265 
1266 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1267 }
1268 
1269 void
1270 topo_pgroup_destroy_all(tnode_t *node)
1271 {
1272 	topo_hdl_t *thp = node->tn_hdl;
1273 	topo_pgroup_t *pg;
1274 	topo_proplist_t *pvl;
1275 	topo_ipgroup_info_t *pip;
1276 
1277 	topo_node_lock(node);
1278 	while ((pg = topo_list_next(&node->tn_pgroups)) != NULL) {
1279 		while ((pvl = topo_list_next(&pg->tpg_pvals)) != NULL) {
1280 			topo_list_delete(&pg->tpg_pvals, pvl);
1281 			topo_prop_rele(pvl->tp_pval);
1282 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1283 		}
1284 
1285 		topo_list_delete(&node->tn_pgroups, pg);
1286 
1287 		pip = pg->tpg_info;
1288 		if (pip != NULL) {
1289 			if (pip->tpi_name != NULL)
1290 				topo_hdl_strfree(thp, (char *)pip->tpi_name);
1291 			topo_hdl_free(thp, pip, sizeof (topo_pgroup_info_t));
1292 		}
1293 
1294 		topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1295 	}
1296 	topo_node_unlock(node);
1297 }
1298 
1299 static void
1300 propmethod_destroy(topo_hdl_t *thp, topo_propval_t *pv)
1301 {
1302 	topo_propmethod_t *pm;
1303 
1304 	pm = pv->tp_method;
1305 	if (pm != NULL) {
1306 		if (pm->tpm_name != NULL)
1307 			topo_hdl_strfree(thp, pm->tpm_name);
1308 		nvlist_free(pm->tpm_args);
1309 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
1310 		pv->tp_method = NULL;
1311 	}
1312 }
1313 
1314 static void
1315 topo_propval_destroy(topo_propval_t *pv)
1316 {
1317 	topo_hdl_t *thp;
1318 
1319 	if (pv == NULL)
1320 		return;
1321 
1322 	thp = pv->tp_hdl;
1323 
1324 	if (pv->tp_name != NULL)
1325 		topo_hdl_strfree(thp, pv->tp_name);
1326 
1327 	nvlist_free(pv->tp_val);
1328 
1329 	propmethod_destroy(thp, pv);
1330 
1331 	topo_hdl_free(thp, pv, sizeof (topo_propval_t));
1332 }
1333 
1334 void
1335 topo_prop_hold(topo_propval_t *pv)
1336 {
1337 	pv->tp_refs++;
1338 }
1339 
1340 void
1341 topo_prop_rele(topo_propval_t *pv)
1342 {
1343 	pv->tp_refs--;
1344 
1345 	assert(pv->tp_refs >= 0);
1346 
1347 	if (pv->tp_refs == 0)
1348 		topo_propval_destroy(pv);
1349 }
1350 
1351 /*
1352  * topo_prop_getprop() and topo_prop_getprops() are private project functions
1353  * for fmtopo
1354  */
1355 int
1356 topo_prop_getprop(tnode_t *node, const char *pgname, const char *pname,
1357     nvlist_t *args, nvlist_t **prop, int *err)
1358 {
1359 	topo_hdl_t *thp = node->tn_hdl;
1360 	topo_propval_t *pv;
1361 
1362 	topo_node_lock(node);
1363 	if ((pv = prop_get(node, pgname, pname, args, err)) == NULL) {
1364 		(void) get_properror(node, err, *err);
1365 		return (-1);
1366 	}
1367 
1368 	if (topo_hdl_nvdup(thp, pv->tp_val, prop) != 0) {
1369 		(void) get_properror(node, err, ETOPO_NOMEM);
1370 		return (-1);
1371 	}
1372 	topo_node_unlock(node);
1373 
1374 	return (0);
1375 }
1376 
1377 static int
1378 prop_val_add(tnode_t *node, nvlist_t **nvl, topo_propval_t *pv, int *err)
1379 {
1380 	if (pv->tp_method != NULL)
1381 		if (prop_method_get(node, pv, pv->tp_method, NULL, err) < 0)
1382 			return (-1);
1383 
1384 	if (pv->tp_val == NULL) {
1385 		*err = ETOPO_PROP_NOENT;
1386 		return (-1);
1387 	}
1388 
1389 	if (topo_hdl_nvdup(pv->tp_hdl, pv->tp_val, nvl) != 0) {
1390 		*err = ETOPO_PROP_NOMEM;
1391 		return (-1);
1392 	}
1393 
1394 	return (0);
1395 }
1396 
1397 static int
1398 get_pgrp_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1399 {
1400 	topo_node_unlock(node);
1401 
1402 	nvlist_free(nvl);
1403 
1404 	*errp = err;
1405 
1406 	return (-1);
1407 }
1408 
1409 int
1410 topo_prop_getpgrp(tnode_t *node, const char *pgname, nvlist_t **pgrp,
1411     int *err)
1412 {
1413 	int ret;
1414 	topo_hdl_t *thp = node->tn_hdl;
1415 	nvlist_t *nvl, *pvnvl;
1416 	topo_pgroup_t *pg;
1417 	topo_propval_t *pv;
1418 	topo_proplist_t *pvl;
1419 
1420 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1421 		*err = ETOPO_NOMEM;
1422 		return (-1);
1423 	}
1424 
1425 	topo_node_lock(node);
1426 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1427 	    pg = topo_list_next(pg)) {
1428 
1429 		if (strcmp(pgname, pg->tpg_info->tpi_name) != 0)
1430 			continue;
1431 
1432 		if (nvlist_add_string(nvl, TOPO_PROP_GROUP_NAME,
1433 		    pg->tpg_info->tpi_name) != 0 ||
1434 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_NSTAB,
1435 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1436 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_DSTAB,
1437 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1438 		    nvlist_add_int32(nvl, TOPO_PROP_GROUP_VERSION,
1439 		    pg->tpg_info->tpi_version) != 0)
1440 			return (get_pgrp_seterror(node, nvl, err,
1441 			    ETOPO_PROP_NVL));
1442 
1443 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1444 		    pvl = topo_list_next(pvl)) {
1445 
1446 			pv = pvl->tp_pval;
1447 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1448 				return (get_pgrp_seterror(node, nvl, err,
1449 				    *err));
1450 			}
1451 			if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_VAL,
1452 			    pvnvl)) != 0) {
1453 				nvlist_free(pvnvl);
1454 				return (get_pgrp_seterror(node, nvl, err, ret));
1455 			}
1456 
1457 			nvlist_free(pvnvl);
1458 		}
1459 		topo_node_unlock(node);
1460 		*pgrp = nvl;
1461 		return (0);
1462 	}
1463 
1464 	topo_node_unlock(node);
1465 	*err = ETOPO_PROP_NOENT;
1466 	return (-1);
1467 }
1468 
1469 static nvlist_t *
1470 get_all_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1471 {
1472 	topo_node_unlock(node);
1473 
1474 	nvlist_free(nvl);
1475 
1476 	*errp = err;
1477 
1478 	return (NULL);
1479 }
1480 
1481 nvlist_t *
1482 topo_prop_getprops(tnode_t *node, int *err)
1483 {
1484 	int ret;
1485 	topo_hdl_t *thp = node->tn_hdl;
1486 	nvlist_t *nvl, *pgnvl, *pvnvl;
1487 	topo_pgroup_t *pg;
1488 	topo_propval_t *pv;
1489 	topo_proplist_t *pvl;
1490 
1491 	topo_node_lock(node);
1492 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1493 		return (get_all_seterror(node, NULL, err, ETOPO_NOMEM));
1494 	}
1495 
1496 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1497 	    pg = topo_list_next(pg)) {
1498 		if (topo_hdl_nvalloc(thp, &pgnvl, 0) != 0)
1499 			return (get_all_seterror(node, nvl, err, ETOPO_NOMEM));
1500 
1501 		if (nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NAME,
1502 		    pg->tpg_info->tpi_name) != 0 ||
1503 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NSTAB,
1504 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1505 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_DSTAB,
1506 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1507 		    nvlist_add_int32(pgnvl, TOPO_PROP_GROUP_VERSION,
1508 		    pg->tpg_info->tpi_version) != 0)
1509 			return (get_all_seterror(node, nvl, err,
1510 			    ETOPO_PROP_NVL));
1511 
1512 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1513 		    pvl = topo_list_next(pvl)) {
1514 
1515 			pv = pvl->tp_pval;
1516 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1517 				nvlist_free(pgnvl);
1518 				return (get_all_seterror(node, nvl, err, *err));
1519 			}
1520 			if ((ret = nvlist_add_nvlist(pgnvl, TOPO_PROP_VAL,
1521 			    pvnvl)) != 0) {
1522 				nvlist_free(pgnvl);
1523 				nvlist_free(pvnvl);
1524 				return (get_all_seterror(node, nvl, err, ret));
1525 			}
1526 
1527 			nvlist_free(pvnvl);
1528 		}
1529 		if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_GROUP, pgnvl))
1530 		    != 0) {
1531 			nvlist_free(pgnvl);
1532 			return (get_all_seterror(node, nvl, err, ret));
1533 		}
1534 
1535 		nvlist_free(pgnvl);
1536 	}
1537 
1538 	topo_node_unlock(node);
1539 
1540 	return (nvl);
1541 }
1542