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