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 	topo_node_unlock(node);
389 	return (0);
390 }
391 
392 int
393 topo_prop_get_int32(tnode_t *node, const char *pgname, const char *pname,
394     int32_t *val, int *err)
395 {
396 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT32,
397 	    NULL, err));
398 }
399 
400 int
401 topo_prop_get_uint32(tnode_t *node, const char *pgname, const char *pname,
402     uint32_t *val, int *err)
403 {
404 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT32,
405 	    NULL, err));
406 }
407 
408 int
409 topo_prop_get_int64(tnode_t *node, const char *pgname, const char *pname,
410     int64_t *val, int *err)
411 {
412 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT64,
413 	    NULL, err));
414 }
415 
416 int
417 topo_prop_get_uint64(tnode_t *node, const char *pgname, const char *pname,
418     uint64_t *val, int *err)
419 {
420 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT64,
421 	    NULL, err));
422 }
423 
424 int
425 topo_prop_get_double(tnode_t *node, const char *pgname, const char *pname,
426     double *val, int *err)
427 {
428 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_DOUBLE,
429 	    NULL, err));
430 }
431 
432 int
433 topo_prop_get_string(tnode_t *node, const char *pgname, const char *pname,
434     char **val, int *err)
435 {
436 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_STRING,
437 	    NULL, err));
438 }
439 
440 int
441 topo_prop_get_fmri(tnode_t *node, const char *pgname, const char *pname,
442     nvlist_t **val, int *err)
443 {
444 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_FMRI,
445 	    NULL, err));
446 }
447 
448 int
449 topo_prop_get_int32_array(tnode_t *node, const char *pgname, const char *pname,
450     int32_t **val, uint_t *nelem, int *err)
451 {
452 	return (prop_getval(node, pgname, pname, (void *)val,
453 	    TOPO_TYPE_INT32_ARRAY, nelem, err));
454 }
455 
456 int
457 topo_prop_get_uint32_array(tnode_t *node, const char *pgname, const char *pname,
458     uint32_t **val, uint_t *nelem, int *err)
459 {
460 	return (prop_getval(node, pgname, pname, (void *)val,
461 	    TOPO_TYPE_UINT32_ARRAY, nelem, err));
462 }
463 
464 int
465 topo_prop_get_int64_array(tnode_t *node, const char *pgname, const char *pname,
466     int64_t **val, uint_t *nelem, int *err)
467 {
468 	return (prop_getval(node, pgname, pname, (void *)val,
469 	    TOPO_TYPE_INT64_ARRAY, nelem, err));
470 }
471 
472 int
473 topo_prop_get_uint64_array(tnode_t *node, const char *pgname, const char *pname,
474     uint64_t **val, uint_t *nelem, int *err)
475 {
476 	return (prop_getval(node, pgname, pname, (void *)val,
477 	    TOPO_TYPE_UINT64_ARRAY, nelem, err));
478 }
479 
480 int
481 topo_prop_get_string_array(tnode_t *node, const char *pgname, const char *pname,
482     char ***val, uint_t *nelem, int *err)
483 {
484 	return (prop_getval(node, pgname, pname, (void *)val,
485 	    TOPO_TYPE_STRING_ARRAY, nelem, err));
486 }
487 
488 int
489 topo_prop_get_fmri_array(tnode_t *node, const char *pgname, const char *pname,
490     nvlist_t ***val, uint_t *nelem, int *err)
491 {
492 	return (prop_getval(node, pgname, pname, (void *)val,
493 	    TOPO_TYPE_FMRI_ARRAY, nelem, err));
494 }
495 
496 static topo_propval_t *
497 set_seterror(tnode_t *node, topo_proplist_t *pvl, int *errp, int err)
498 {
499 	topo_hdl_t *thp = node->tn_hdl;
500 	topo_propval_t *pv;
501 
502 	if (pvl != NULL) {
503 		pv = pvl->tp_pval;
504 		topo_propval_destroy(pv);
505 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
506 	}
507 
508 	topo_node_unlock(node);
509 	*errp = err;
510 
511 	return (NULL);
512 }
513 
514 static topo_propval_t *
515 prop_create(tnode_t *node, const char *pgname, const char *pname,
516     topo_type_t type, int flag, int *err)
517 {
518 	topo_hdl_t *thp = node->tn_hdl;
519 	topo_pgroup_t *pg;
520 	topo_propval_t *pv;
521 	topo_proplist_t *pvl;
522 
523 	/*
524 	 * Replace existing prop value with new one
525 	 */
526 	if ((pg = pgroup_get(node, pgname)) == NULL) {
527 		topo_node_unlock(node);
528 		*err = ETOPO_PROP_NOENT;
529 		return (NULL);
530 	}
531 
532 	if ((pv = propval_get(pg, pname)) != NULL) {
533 		if (pv->tp_type != type)
534 			return (set_seterror(node, NULL, err, ETOPO_PROP_TYPE));
535 		else if (! (pv->tp_flag & TOPO_PROP_MUTABLE))
536 			return (set_seterror(node, NULL, err, ETOPO_PROP_DEFD));
537 
538 		nvlist_free(pv->tp_val);
539 		pv->tp_val = NULL;
540 	} else {
541 		if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
542 		    == NULL)
543 			return (set_seterror(node, NULL, err, ETOPO_NOMEM));
544 
545 		if ((pv = topo_hdl_zalloc(thp, sizeof (topo_propval_t)))
546 		    == NULL)
547 			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
548 
549 		pv->tp_hdl = thp;
550 		pvl->tp_pval = pv;
551 
552 		if ((pv->tp_name = topo_hdl_strdup(thp, pname))
553 		    == NULL)
554 			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
555 		pv->tp_flag = flag;
556 		pv->tp_type = type;
557 		topo_prop_hold(pv);
558 		topo_list_append(&pg->tpg_pvals, pvl);
559 	}
560 
561 	return (pv);
562 }
563 
564 static int
565 topo_prop_set(tnode_t *node, const char *pgname, const char *pname,
566     topo_type_t type, int flag, void *val, int nelems, int *err)
567 {
568 	int ret;
569 	topo_hdl_t *thp = node->tn_hdl;
570 	nvlist_t *nvl;
571 
572 	if (topo_hdl_nvalloc(thp, &nvl, NV_UNIQUE_NAME) < 0) {
573 		*err = ETOPO_PROP_NVL;
574 		return (-1);
575 	}
576 
577 	ret = nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, pname);
578 	ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, type);
579 	switch (type) {
580 		case TOPO_TYPE_INT32:
581 			ret |= nvlist_add_int32(nvl, TOPO_PROP_VAL_VAL,
582 			    *(int32_t *)val);
583 			break;
584 		case TOPO_TYPE_UINT32:
585 			ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL,
586 			    *(uint32_t *)val);
587 			break;
588 		case TOPO_TYPE_INT64:
589 			ret |= nvlist_add_int64(nvl, TOPO_PROP_VAL_VAL,
590 			    *(int64_t *)val);
591 			break;
592 		case TOPO_TYPE_UINT64:
593 			ret |= nvlist_add_uint64(nvl, TOPO_PROP_VAL_VAL,
594 			    *(uint64_t *)val);
595 			break;
596 		case TOPO_TYPE_DOUBLE:
597 			ret |= nvlist_add_double(nvl, TOPO_PROP_VAL_VAL,
598 			    *(double *)val);
599 			break;
600 		case TOPO_TYPE_STRING:
601 			ret |= nvlist_add_string(nvl, TOPO_PROP_VAL_VAL,
602 			    (char *)val);
603 			break;
604 		case TOPO_TYPE_FMRI:
605 			ret |= nvlist_add_nvlist(nvl, TOPO_PROP_VAL_VAL,
606 			    (nvlist_t *)val);
607 			break;
608 		case TOPO_TYPE_INT32_ARRAY:
609 			ret |= nvlist_add_int32_array(nvl,
610 			    TOPO_PROP_VAL_VAL, (int32_t *)val, nelems);
611 			break;
612 		case TOPO_TYPE_UINT32_ARRAY:
613 			ret |= nvlist_add_uint32_array(nvl,
614 			    TOPO_PROP_VAL_VAL, (uint32_t *)val, nelems);
615 			break;
616 		case TOPO_TYPE_INT64_ARRAY:
617 			ret |= nvlist_add_int64_array(nvl,
618 			    TOPO_PROP_VAL_VAL, (int64_t *)val, nelems);
619 			break;
620 		case TOPO_TYPE_UINT64_ARRAY:
621 			ret |= nvlist_add_uint64_array(nvl,
622 			    TOPO_PROP_VAL_VAL, (uint64_t *)val, nelems);
623 			break;
624 		case TOPO_TYPE_STRING_ARRAY:
625 			ret |= nvlist_add_string_array(nvl,
626 			    TOPO_PROP_VAL_VAL, (char **)val, nelems);
627 			break;
628 		case TOPO_TYPE_FMRI_ARRAY:
629 			ret |= nvlist_add_nvlist_array(nvl,
630 			    TOPO_PROP_VAL_VAL, (nvlist_t **)val, nelems);
631 			break;
632 		default:
633 			*err = ETOPO_PROP_TYPE;
634 			return (-1);
635 	}
636 
637 	if (ret != 0) {
638 		nvlist_free(nvl);
639 		if (ret == ENOMEM) {
640 			*err = ETOPO_PROP_NOMEM;
641 			return (-1);
642 		} else {
643 			*err = ETOPO_PROP_NVL;
644 			return (-1);
645 		}
646 	}
647 
648 	if (topo_prop_setprop(node, pgname, nvl, flag, nvl, err) != 0) {
649 		nvlist_free(nvl);
650 		return (-1); /* err set */
651 	}
652 	nvlist_free(nvl);
653 	return (ret);
654 }
655 
656 int
657 topo_prop_set_int32(tnode_t *node, const char *pgname, const char *pname,
658     int flag, int32_t val, int *err)
659 {
660 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32, flag,
661 	    &val, 1, err));
662 }
663 
664 int
665 topo_prop_set_uint32(tnode_t *node, const char *pgname, const char *pname,
666     int flag, uint32_t val, int *err)
667 {
668 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32, flag,
669 	    &val, 1, err));
670 }
671 
672 int
673 topo_prop_set_int64(tnode_t *node, const char *pgname, const char *pname,
674     int flag, int64_t val, int *err)
675 {
676 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64, flag,
677 	    &val, 1, err));
678 }
679 
680 int
681 topo_prop_set_uint64(tnode_t *node, const char *pgname, const char *pname,
682     int flag, uint64_t val, int *err)
683 {
684 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64, flag,
685 	    &val, 1, err));
686 }
687 
688 int
689 topo_prop_set_double(tnode_t *node, const char *pgname, const char *pname,
690     int flag, double val, int *err)
691 {
692 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_DOUBLE, flag,
693 	    &val, 1, err));
694 }
695 
696 int
697 topo_prop_set_string(tnode_t *node, const char *pgname, const char *pname,
698     int flag, const char *val, int *err)
699 {
700 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING, flag,
701 	    (void *)val, 1, err));
702 }
703 
704 int
705 topo_prop_set_fmri(tnode_t *node, const char *pgname, const char *pname,
706     int flag, const nvlist_t *fmri, int *err)
707 {
708 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI, flag,
709 	    (void *)fmri, 1, err));
710 }
711 
712 int
713 topo_prop_set_int32_array(tnode_t *node, const char *pgname, const char *pname,
714     int flag, int32_t *val, uint_t nelems, int *err)
715 {
716 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32_ARRAY, flag,
717 	    val, nelems, err));
718 }
719 
720 int
721 topo_prop_set_uint32_array(tnode_t *node, const char *pgname, const char *pname,
722     int flag, uint32_t *val, uint_t nelems, int *err)
723 {
724 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32_ARRAY, flag,
725 	    val, nelems, err));
726 }
727 
728 int
729 topo_prop_set_int64_array(tnode_t *node, const char *pgname, const char *pname,
730     int flag, int64_t *val, uint_t nelems, int *err)
731 {
732 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64_ARRAY, flag,
733 	    val, nelems, err));
734 }
735 
736 int
737 topo_prop_set_uint64_array(tnode_t *node, const char *pgname, const char *pname,
738     int flag, uint64_t *val, uint_t nelems, int *err)
739 {
740 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64_ARRAY, flag,
741 	    val, nelems, err));
742 }
743 
744 int
745 topo_prop_set_string_array(tnode_t *node, const char *pgname, const char *pname,
746     int flag, const char **val, uint_t nelems, int *err)
747 {
748 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING_ARRAY, flag,
749 	    (void *)val, nelems, err));
750 }
751 
752 int
753 topo_prop_set_fmri_array(tnode_t *node, const char *pgname, const char *pname,
754     int flag, const nvlist_t **fmri, uint_t nelems, int *err)
755 {
756 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI_ARRAY, flag,
757 	    (void *)fmri, nelems, err));
758 }
759 
760 /*
761  * topo_prop_setprop() is a private project function for fmtopo
762  */
763 int
764 topo_prop_setprop(tnode_t *node, const char *pgname, nvlist_t *prop,
765     int flag, nvlist_t *pargs, int *err)
766 {
767 	int ret;
768 	topo_hdl_t *thp = node->tn_hdl;
769 	topo_propval_t *pv;
770 	nvlist_t *nvl, *args;
771 	char *name;
772 	topo_type_t type;
773 
774 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_NAME, &name) != 0) {
775 		*err = ETOPO_PROP_NAME;
776 		return (-1);
777 	}
778 	if (nvlist_lookup_uint32(prop, TOPO_PROP_VAL_TYPE, (uint32_t *)&type)
779 	    != 0) {
780 		*err = ETOPO_PROP_TYPE;
781 		return (-1);
782 	}
783 
784 	topo_node_lock(node);
785 	if ((pv = prop_create(node, pgname, name, type, flag, err)) == NULL)
786 		return (-1); /* unlocked and err set */
787 
788 	/*
789 	 * Set by method or set to new prop value.  If we fail, leave
790 	 * property in list with old value.
791 	 */
792 	if (pv->tp_method != NULL) {
793 		topo_propmethod_t *pm = pv->tp_method;
794 
795 		if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0) {
796 			topo_node_unlock(node);
797 			*err = ETOPO_PROP_NOMEM;
798 			return (-1);
799 		}
800 		ret = nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args);
801 		if (pargs != NULL)
802 			ret |= nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs);
803 
804 		if (ret != 0) {
805 			topo_node_unlock(node);
806 			nvlist_free(args);
807 			*err = ETOPO_PROP_NVL;
808 			return (-1);
809 		}
810 
811 		/*
812 		 *
813 		 * Grab a reference to the property and then unlock the node.
814 		 * This will allow property methods to safely re-enter the
815 		 * prop_get codepath, making it possible for property methods
816 		 * to access other property values on the same node w\o causing
817 		 * a deadlock.
818 		 *
819 		 * We don't technically need this now, since this interface is
820 		 * currently only used by fmtopo (which is single-threaded), but
821 		 * we may make this interface available to other parts of
822 		 * libtopo in the future, so best to make it MT-safe now.
823 		 */
824 		topo_prop_hold(pv);
825 		topo_node_unlock(node);
826 		ret = topo_method_call(node, pm->tpm_name, pm->tpm_version,
827 		    args, &nvl, err);
828 		topo_node_lock(node);
829 		topo_prop_rele(pv);
830 
831 		nvlist_free(args);
832 	} else {
833 		if ((ret = topo_hdl_nvdup(thp, prop, &nvl)) != 0)
834 			*err = ETOPO_PROP_NOMEM;
835 	}
836 
837 	if (ret != 0) {
838 		topo_node_unlock(node);
839 		return (-1);
840 	}
841 
842 	pv->tp_val = nvl;
843 	topo_node_unlock(node);
844 	return (0);
845 }
846 
847 static int
848 register_methoderror(tnode_t *node, topo_propmethod_t *pm, int *errp, int l,
849     int err)
850 {
851 	topo_hdl_t *thp = node->tn_hdl;
852 
853 	if (pm != NULL) {
854 		if (pm->tpm_name != NULL)
855 			topo_hdl_strfree(thp, pm->tpm_name);
856 		nvlist_free(pm->tpm_args);
857 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
858 	}
859 
860 	*errp = err;
861 
862 	if (l != 0)
863 		topo_node_unlock(node);
864 
865 	return (-1);
866 }
867 
868 int
869 prop_method_register(tnode_t *node, const char *pgname, const char *pname,
870     topo_type_t ptype, const char *mname, topo_version_t version,
871     const nvlist_t *args, int *err)
872 {
873 	topo_hdl_t *thp = node->tn_hdl;
874 	topo_propmethod_t *pm = NULL;
875 	topo_propval_t *pv = NULL;
876 
877 	if ((pm = topo_hdl_zalloc(thp, sizeof (topo_propmethod_t))) == NULL)
878 		return (register_methoderror(node, pm, err, 1,
879 		    ETOPO_PROP_NOMEM));
880 
881 	if ((pm->tpm_name = topo_hdl_strdup(thp, mname)) == NULL)
882 		return (register_methoderror(node, pm, err, 1,
883 		    ETOPO_PROP_NOMEM));
884 
885 	pm->tpm_version = version;
886 
887 	if (topo_hdl_nvdup(thp, (nvlist_t *)args, &pm->tpm_args) != 0)
888 		return (register_methoderror(node, pm, err, 1,
889 		    ETOPO_PROP_NOMEM));
890 
891 	/*
892 	 * It's possible the property may already exist.  However we still want
893 	 * to allow the method to be registered.  This is to handle the case
894 	 * where we specify a prop method in an xml map to override the value
895 	 * that was set by the enumerator.
896 	 *
897 	 * By default, propmethod-backed properties are not MUTABLE.  This is
898 	 * done to simplify the programming model for modules that implement
899 	 * property methods as most propmethods tend to only support get
900 	 * operations.  Enumerator modules can override this by calling
901 	 * topo_prop_setmutable().  Propmethods that are registered via XML can
902 	 * be set as mutable via the optional "mutable" attribute, which will
903 	 * result in the xml parser calling topo_prop_setflags() after
904 	 * registering the propmethod.
905 	 */
906 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL)
907 		if ((pv = prop_create(node, pgname, pname, ptype,
908 		    TOPO_PROP_IMMUTABLE, err)) == NULL) {
909 			/* node unlocked */
910 			return (register_methoderror(node, pm, err, 0, *err));
911 		}
912 
913 	if (pv->tp_method != NULL)
914 		return (register_methoderror(node, pm, err, 1,
915 		    ETOPO_METHOD_DEFD));
916 
917 	if (pv->tp_val != NULL) {
918 		nvlist_free(pv->tp_val);
919 		pv->tp_val = NULL;
920 	}
921 	pv->tp_method = pm;
922 
923 	topo_node_unlock(node);
924 
925 	return (0);
926 }
927 
928 int
929 topo_prop_method_register(tnode_t *node, const char *pgname, const char *pname,
930     topo_type_t ptype, const char *mname, const nvlist_t *args, int *err)
931 {
932 	topo_imethod_t *mp;
933 
934 	topo_node_lock(node);
935 
936 	if ((mp = topo_method_lookup(node, mname)) == NULL)
937 		return (register_methoderror(node, NULL, err, 1,
938 		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
939 
940 	topo_node_lock(node);
941 
942 	return (prop_method_register(node, pgname, pname, ptype, mname,
943 	    mp->tim_version, args, err)); /* err set and node unlocked */
944 }
945 
946 int
947 topo_prop_method_version_register(tnode_t *node, const char *pgname,
948     const char *pname, topo_type_t ptype, const char *mname,
949     topo_version_t version, const nvlist_t *args, int *err)
950 {
951 	topo_imethod_t *mp;
952 
953 	topo_node_lock(node);
954 
955 	if ((mp = topo_method_lookup(node, mname)) == NULL)
956 		return (register_methoderror(node, NULL, err, 1,
957 		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
958 
959 	topo_node_lock(node);
960 
961 	if (version < mp->tim_version)
962 		return (register_methoderror(node, NULL, err, 1,
963 		    ETOPO_METHOD_VEROLD));
964 	if (version > mp->tim_version)
965 		return (register_methoderror(node, NULL, err, 1,
966 		    ETOPO_METHOD_VERNEW));
967 
968 	return (prop_method_register(node, pgname, pname, ptype, mname,
969 	    version, args, err)); /* err set and node unlocked */
970 }
971 
972 void
973 topo_prop_method_unregister(tnode_t *node, const char *pgname,
974     const char *pname)
975 {
976 	topo_propval_t *pv;
977 	topo_pgroup_t *pg;
978 	topo_proplist_t *pvl;
979 	topo_hdl_t *thp = node->tn_hdl;
980 
981 	topo_node_lock(node);
982 
983 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
984 	    pg = topo_list_next(pg)) {
985 		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
986 			break;
987 		}
988 	}
989 
990 	if (pg == NULL) {
991 		topo_node_unlock(node);
992 		return;
993 	}
994 
995 	for (pvl = topo_list_next(&pg->tpg_list); pvl != NULL;
996 	    pvl = topo_list_next(pvl)) {
997 		pv = pvl->tp_pval;
998 		if (strcmp(pv->tp_name, pname) == 0) {
999 			topo_list_delete(&pg->tpg_pvals, pvl);
1000 			assert(pv->tp_refs == 1);
1001 			topo_prop_rele(pv);
1002 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1003 			break;
1004 		}
1005 	}
1006 
1007 	topo_node_unlock(node);
1008 }
1009 
1010 int
1011 topo_prop_setmutable(tnode_t *node, const char *pgname, const char *pname,
1012     int *err)
1013 {
1014 	topo_propval_t *pv = NULL;
1015 
1016 	topo_node_lock(node);
1017 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
1018 		topo_node_unlock(node);
1019 		*err = ETOPO_PROP_NOENT;
1020 		return (-1);
1021 	}
1022 
1023 	/*
1024 	 * If the property is being inherited then we don't want to allow a
1025 	 * change from IMMUTABLE to MUTABLE.
1026 	 */
1027 	if (pv->tp_refs > 1) {
1028 		topo_node_unlock(node);
1029 		*err = ETOPO_PROP_DEFD;
1030 		return (-1);
1031 	}
1032 	pv->tp_flag |= TOPO_PROP_MUTABLE;
1033 
1034 	topo_node_unlock(node);
1035 
1036 	return (0);
1037 }
1038 int
1039 topo_prop_setnonvolatile(tnode_t *node, const char *pgname, const char *pname,
1040     int *err)
1041 {
1042 	topo_propval_t *pv = NULL;
1043 
1044 	topo_node_lock(node);
1045 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
1046 		topo_node_unlock(node);
1047 		*err = ETOPO_PROP_NOENT;
1048 		return (-1);
1049 	}
1050 
1051 	pv->tp_flag |= TOPO_PROP_NONVOLATILE;
1052 
1053 	topo_node_unlock(node);
1054 
1055 	return (0);
1056 }
1057 
1058 static int
1059 inherit_seterror(tnode_t *node, int *errp, int err)
1060 {
1061 	topo_node_unlock(node);
1062 	topo_node_unlock(node->tn_parent);
1063 
1064 	*errp = err;
1065 
1066 	return (-1);
1067 }
1068 
1069 int
1070 topo_prop_inherit(tnode_t *node, const char *pgname, const char *name, int *err)
1071 {
1072 	topo_hdl_t *thp = node->tn_hdl;
1073 	tnode_t *pnode = node->tn_parent;
1074 	topo_pgroup_t *pg;
1075 	topo_propval_t *pv;
1076 	topo_proplist_t *pvl;
1077 
1078 	topo_node_lock(pnode);
1079 	topo_node_lock(node);
1080 
1081 	/*
1082 	 * Check if the requested property group and prop val are already set
1083 	 * on the node.
1084 	 */
1085 	if (propval_get(pgroup_get(node, pgname), name) != NULL)
1086 		return (inherit_seterror(node, err, ETOPO_PROP_DEFD));
1087 
1088 	/*
1089 	 * Check if the requested property group and prop val exists on the
1090 	 * parent node
1091 	 */
1092 	if ((pv = propval_get(pgroup_get(pnode, pgname), name)) == NULL)
1093 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1094 
1095 	/*
1096 	 * Can this propval be inherited?
1097 	 */
1098 	if (pv->tp_flag & TOPO_PROP_MUTABLE)
1099 		return (inherit_seterror(node, err, ETOPO_PROP_NOINHERIT));
1100 
1101 	/*
1102 	 * Property group should already exist: bump the ref count for this
1103 	 * propval and add it to the node's property group
1104 	 */
1105 	if ((pg = pgroup_get(node, pgname)) == NULL)
1106 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1107 
1108 	if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
1109 	    == NULL)
1110 		return (inherit_seterror(node, err, ETOPO_NOMEM));
1111 
1112 	topo_prop_hold(pv);
1113 	pvl->tp_pval = pv;
1114 	topo_list_append(&pg->tpg_pvals, pvl);
1115 
1116 	topo_node_unlock(node);
1117 	topo_node_unlock(pnode);
1118 
1119 	return (0);
1120 }
1121 
1122 topo_pgroup_info_t *
1123 topo_pgroup_info(tnode_t *node, const char *pgname, int *err)
1124 {
1125 	topo_hdl_t *thp = node->tn_hdl;
1126 	topo_pgroup_t *pg;
1127 	topo_ipgroup_info_t *pip;
1128 	topo_pgroup_info_t *info;
1129 
1130 	topo_node_lock(node);
1131 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1132 	    pg = topo_list_next(pg)) {
1133 		if (strcmp(pgname, pg->tpg_info->tpi_name) == 0) {
1134 			if ((info = topo_hdl_alloc(thp,
1135 			    sizeof (topo_pgroup_info_t))) == NULL)
1136 				return (NULL);
1137 
1138 			pip = pg->tpg_info;
1139 			if ((info->tpi_name =
1140 			    topo_hdl_strdup(thp, pip->tpi_name)) == NULL) {
1141 				*err = ETOPO_PROP_NOMEM;
1142 				topo_hdl_free(thp, info,
1143 				    sizeof (topo_pgroup_info_t));
1144 				topo_node_unlock(node);
1145 				return (NULL);
1146 			}
1147 			info->tpi_namestab = pip->tpi_namestab;
1148 			info->tpi_datastab = pip->tpi_datastab;
1149 			info->tpi_version = pip->tpi_version;
1150 			topo_node_unlock(node);
1151 			return (info);
1152 		}
1153 	}
1154 
1155 	*err = ETOPO_PROP_NOENT;
1156 	topo_node_unlock(node);
1157 	return (NULL);
1158 }
1159 
1160 static int
1161 pgroup_seterr(tnode_t *node, topo_pgroup_t *pg, topo_ipgroup_info_t *pip,
1162     int *err)
1163 {
1164 	topo_hdl_t *thp = node->tn_hdl;
1165 
1166 	if (pip != NULL) {
1167 		if (pip->tpi_name != NULL)
1168 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1169 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1170 	}
1171 
1172 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1173 	*err = ETOPO_NOMEM;
1174 
1175 	topo_node_unlock(node);
1176 
1177 	return (-1);
1178 }
1179 
1180 int
1181 topo_pgroup_create(tnode_t *node, const topo_pgroup_info_t *pinfo, int *err)
1182 {
1183 	topo_pgroup_t *pg;
1184 	topo_ipgroup_info_t *pip;
1185 	topo_hdl_t *thp = node->tn_hdl;
1186 
1187 	*err = 0;
1188 
1189 	topo_node_lock(node);
1190 	/*
1191 	 * Check for an existing pgroup
1192 	 */
1193 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1194 	    pg = topo_list_next(pg)) {
1195 		if (strcmp(pg->tpg_info->tpi_name, pinfo->tpi_name) == 0) {
1196 			*err = ETOPO_PROP_DEFD;
1197 			topo_node_unlock(node);
1198 			return (-1);
1199 		}
1200 	}
1201 
1202 	if ((pg = topo_hdl_zalloc(thp, sizeof (topo_pgroup_t))) == NULL) {
1203 		*err = ETOPO_NOMEM;
1204 		topo_node_unlock(node);
1205 		return (-1);
1206 	}
1207 
1208 	if ((pip = topo_hdl_zalloc(thp, sizeof (topo_ipgroup_info_t)))
1209 	    == NULL)
1210 		return (pgroup_seterr(node, pg, pip, err));
1211 
1212 	if ((pip->tpi_name = topo_hdl_strdup(thp, pinfo->tpi_name))
1213 	    == NULL)
1214 		return (pgroup_seterr(node, pg, pip, err));
1215 
1216 	pip->tpi_namestab = pinfo->tpi_namestab;
1217 	pip->tpi_datastab = pinfo->tpi_datastab;
1218 	pip->tpi_version = pinfo->tpi_version;
1219 
1220 	pg->tpg_info = pip;
1221 
1222 	topo_list_append(&node->tn_pgroups, pg);
1223 	topo_node_unlock(node);
1224 
1225 	return (0);
1226 }
1227 
1228 void
1229 topo_pgroup_destroy(tnode_t *node, const char *pname)
1230 {
1231 	topo_hdl_t *thp = node->tn_hdl;
1232 	topo_pgroup_t *pg;
1233 	topo_proplist_t *pvl;
1234 	topo_ipgroup_info_t *pip;
1235 
1236 	topo_node_lock(node);
1237 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1238 	    pg = topo_list_next(pg)) {
1239 		if (strcmp(pg->tpg_info->tpi_name, pname) == 0) {
1240 			break;
1241 		}
1242 	}
1243 
1244 	if (pg == NULL) {
1245 		topo_node_unlock(node);
1246 		return;
1247 	}
1248 
1249 	while ((pvl = topo_list_next(&pg->tpg_list)) != NULL) {
1250 		topo_list_delete(&pg->tpg_pvals, pvl);
1251 		topo_prop_rele(pvl->tp_pval);
1252 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1253 	}
1254 
1255 	topo_list_delete(&node->tn_pgroups, pg);
1256 	topo_node_unlock(node);
1257 
1258 	pip = pg->tpg_info;
1259 	if (pip != NULL) {
1260 		if (pip->tpi_name != NULL)
1261 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1262 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1263 	}
1264 
1265 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1266 }
1267 
1268 void
1269 topo_pgroup_destroy_all(tnode_t *node)
1270 {
1271 	topo_hdl_t *thp = node->tn_hdl;
1272 	topo_pgroup_t *pg;
1273 	topo_proplist_t *pvl;
1274 	topo_ipgroup_info_t *pip;
1275 
1276 	topo_node_lock(node);
1277 	while ((pg = topo_list_next(&node->tn_pgroups)) != NULL) {
1278 		while ((pvl = topo_list_next(&pg->tpg_pvals)) != NULL) {
1279 			topo_list_delete(&pg->tpg_pvals, pvl);
1280 			topo_prop_rele(pvl->tp_pval);
1281 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1282 		}
1283 
1284 		topo_list_delete(&node->tn_pgroups, pg);
1285 
1286 		pip = pg->tpg_info;
1287 		if (pip != NULL) {
1288 			if (pip->tpi_name != NULL)
1289 				topo_hdl_strfree(thp, (char *)pip->tpi_name);
1290 			topo_hdl_free(thp, pip, sizeof (topo_pgroup_info_t));
1291 		}
1292 
1293 		topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1294 	}
1295 	topo_node_unlock(node);
1296 }
1297 
1298 static void
1299 propmethod_destroy(topo_hdl_t *thp, topo_propval_t *pv)
1300 {
1301 	topo_propmethod_t *pm;
1302 
1303 	pm = pv->tp_method;
1304 	if (pm != NULL) {
1305 		if (pm->tpm_name != NULL)
1306 			topo_hdl_strfree(thp, pm->tpm_name);
1307 		nvlist_free(pm->tpm_args);
1308 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
1309 		pv->tp_method = NULL;
1310 	}
1311 }
1312 
1313 static void
1314 topo_propval_destroy(topo_propval_t *pv)
1315 {
1316 	topo_hdl_t *thp;
1317 
1318 	if (pv == NULL)
1319 		return;
1320 
1321 	thp = pv->tp_hdl;
1322 
1323 	if (pv->tp_name != NULL)
1324 		topo_hdl_strfree(thp, pv->tp_name);
1325 
1326 	nvlist_free(pv->tp_val);
1327 
1328 	propmethod_destroy(thp, pv);
1329 
1330 	topo_hdl_free(thp, pv, sizeof (topo_propval_t));
1331 }
1332 
1333 void
1334 topo_prop_hold(topo_propval_t *pv)
1335 {
1336 	pv->tp_refs++;
1337 }
1338 
1339 void
1340 topo_prop_rele(topo_propval_t *pv)
1341 {
1342 	pv->tp_refs--;
1343 
1344 	assert(pv->tp_refs >= 0);
1345 
1346 	if (pv->tp_refs == 0)
1347 		topo_propval_destroy(pv);
1348 }
1349 
1350 /*
1351  * topo_prop_getprop() and topo_prop_getprops() are private project functions
1352  * for fmtopo
1353  */
1354 int
1355 topo_prop_getprop(tnode_t *node, const char *pgname, const char *pname,
1356     nvlist_t *args, nvlist_t **prop, int *err)
1357 {
1358 	topo_hdl_t *thp = node->tn_hdl;
1359 	topo_propval_t *pv;
1360 
1361 	topo_node_lock(node);
1362 	if ((pv = prop_get(node, pgname, pname, args, err)) == NULL) {
1363 		(void) get_properror(node, err, *err);
1364 		return (-1);
1365 	}
1366 
1367 	if (topo_hdl_nvdup(thp, pv->tp_val, prop) != 0) {
1368 		(void) get_properror(node, err, ETOPO_NOMEM);
1369 		return (-1);
1370 	}
1371 	topo_node_unlock(node);
1372 
1373 	return (0);
1374 }
1375 
1376 static int
1377 prop_val_add(tnode_t *node, nvlist_t **nvl, topo_propval_t *pv, int *err)
1378 {
1379 	if (pv->tp_method != NULL)
1380 		if (prop_method_get(node, pv, pv->tp_method, NULL, err) < 0)
1381 			return (-1);
1382 
1383 	if (pv->tp_val == NULL) {
1384 		*err = ETOPO_PROP_NOENT;
1385 		return (-1);
1386 	}
1387 
1388 	if (topo_hdl_nvdup(pv->tp_hdl, pv->tp_val, nvl) != 0) {
1389 		*err = ETOPO_PROP_NOMEM;
1390 		return (-1);
1391 	}
1392 
1393 	return (0);
1394 }
1395 
1396 static int
1397 get_pgrp_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1398 {
1399 	topo_node_unlock(node);
1400 
1401 	nvlist_free(nvl);
1402 
1403 	*errp = err;
1404 
1405 	return (-1);
1406 }
1407 
1408 int
1409 topo_prop_getpgrp(tnode_t *node, const char *pgname, nvlist_t **pgrp,
1410     int *err)
1411 {
1412 	int ret;
1413 	topo_hdl_t *thp = node->tn_hdl;
1414 	nvlist_t *nvl, *pvnvl;
1415 	topo_pgroup_t *pg;
1416 	topo_propval_t *pv;
1417 	topo_proplist_t *pvl;
1418 
1419 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1420 		*err = ETOPO_NOMEM;
1421 		return (-1);
1422 	}
1423 
1424 	topo_node_lock(node);
1425 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1426 	    pg = topo_list_next(pg)) {
1427 
1428 		if (strcmp(pgname, pg->tpg_info->tpi_name) != 0)
1429 			continue;
1430 
1431 		if (nvlist_add_string(nvl, TOPO_PROP_GROUP_NAME,
1432 		    pg->tpg_info->tpi_name) != 0 ||
1433 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_NSTAB,
1434 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1435 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_DSTAB,
1436 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1437 		    nvlist_add_int32(nvl, TOPO_PROP_GROUP_VERSION,
1438 		    pg->tpg_info->tpi_version) != 0)
1439 			return (get_pgrp_seterror(node, nvl, err,
1440 			    ETOPO_PROP_NVL));
1441 
1442 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1443 		    pvl = topo_list_next(pvl)) {
1444 
1445 			pv = pvl->tp_pval;
1446 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1447 				return (get_pgrp_seterror(node, nvl, err,
1448 				    *err));
1449 			}
1450 			if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_VAL,
1451 			    pvnvl)) != 0) {
1452 				nvlist_free(pvnvl);
1453 				return (get_pgrp_seterror(node, nvl, err, ret));
1454 			}
1455 
1456 			nvlist_free(pvnvl);
1457 		}
1458 		topo_node_unlock(node);
1459 		*pgrp = nvl;
1460 		return (0);
1461 	}
1462 
1463 	topo_node_unlock(node);
1464 	*err = ETOPO_PROP_NOENT;
1465 	return (-1);
1466 }
1467 
1468 static nvlist_t *
1469 get_all_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1470 {
1471 	topo_node_unlock(node);
1472 
1473 	nvlist_free(nvl);
1474 
1475 	*errp = err;
1476 
1477 	return (NULL);
1478 }
1479 
1480 nvlist_t *
1481 topo_prop_getprops(tnode_t *node, int *err)
1482 {
1483 	int ret;
1484 	topo_hdl_t *thp = node->tn_hdl;
1485 	nvlist_t *nvl, *pgnvl, *pvnvl;
1486 	topo_pgroup_t *pg;
1487 	topo_propval_t *pv;
1488 	topo_proplist_t *pvl;
1489 
1490 	topo_node_lock(node);
1491 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1492 		return (get_all_seterror(node, NULL, err, ETOPO_NOMEM));
1493 	}
1494 
1495 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1496 	    pg = topo_list_next(pg)) {
1497 		if (topo_hdl_nvalloc(thp, &pgnvl, 0) != 0)
1498 			return (get_all_seterror(node, nvl, err, ETOPO_NOMEM));
1499 
1500 		if (nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NAME,
1501 		    pg->tpg_info->tpi_name) != 0 ||
1502 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NSTAB,
1503 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1504 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_DSTAB,
1505 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1506 		    nvlist_add_int32(pgnvl, TOPO_PROP_GROUP_VERSION,
1507 		    pg->tpg_info->tpi_version) != 0)
1508 			return (get_all_seterror(node, nvl, err,
1509 			    ETOPO_PROP_NVL));
1510 
1511 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1512 		    pvl = topo_list_next(pvl)) {
1513 
1514 			pv = pvl->tp_pval;
1515 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1516 				nvlist_free(pgnvl);
1517 				return (get_all_seterror(node, nvl, err, *err));
1518 			}
1519 			if ((ret = nvlist_add_nvlist(pgnvl, TOPO_PROP_VAL,
1520 			    pvnvl)) != 0) {
1521 				nvlist_free(pgnvl);
1522 				nvlist_free(pvnvl);
1523 				return (get_all_seterror(node, nvl, err, ret));
1524 			}
1525 
1526 			nvlist_free(pvnvl);
1527 		}
1528 		if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_GROUP, pgnvl))
1529 		    != 0) {
1530 			nvlist_free(pgnvl);
1531 			return (get_all_seterror(node, nvl, err, ret));
1532 		}
1533 
1534 		nvlist_free(pgnvl);
1535 	}
1536 
1537 	topo_node_unlock(node);
1538 
1539 	return (nvl);
1540 }
1541