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