xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_prop.c (revision 29852fb9434e36450cf23dc258893a4d2d7a6236)
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_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 	if ((pv = prop_create(node, pgname, pname, ptype, TOPO_PROP_MUTABLE,
876 	    err)) == NULL) {
877 		/* node unlocked */
878 		return (register_methoderror(node, pm, err, 0, *err));
879 	}
880 
881 	if (pv->tp_method != NULL)
882 		return (register_methoderror(node, pm, err, 1,
883 		    ETOPO_METHOD_DEFD));
884 
885 	pv->tp_val = NULL;
886 	pv->tp_method = pm;
887 
888 	topo_node_unlock(node);
889 
890 	return (0);
891 }
892 
893 int
894 topo_prop_method_register(tnode_t *node, const char *pgname, const char *pname,
895     topo_type_t ptype, const char *mname, const nvlist_t *args, int *err)
896 {
897 	topo_imethod_t *mp;
898 
899 	topo_node_lock(node);
900 
901 	if ((mp = topo_method_lookup(node, mname)) == NULL)
902 		return (register_methoderror(node, NULL, err, 1,
903 		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
904 
905 	topo_node_lock(node);
906 
907 	return (prop_method_register(node, pgname, pname, ptype, mname,
908 	    mp->tim_version, args, err)); /* err set and node unlocked */
909 }
910 
911 int
912 topo_prop_method_version_register(tnode_t *node, const char *pgname,
913     const char *pname, topo_type_t ptype, const char *mname,
914     topo_version_t version, const nvlist_t *args, int *err)
915 {
916 	topo_imethod_t *mp;
917 
918 	topo_node_lock(node);
919 
920 	if ((mp = topo_method_lookup(node, mname)) == NULL)
921 		return (register_methoderror(node, NULL, err, 1,
922 		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
923 
924 	topo_node_lock(node);
925 
926 	if (version < mp->tim_version)
927 		return (register_methoderror(node, NULL, err, 1,
928 		    ETOPO_METHOD_VEROLD));
929 	if (version > mp->tim_version)
930 		return (register_methoderror(node, NULL, err, 1,
931 		    ETOPO_METHOD_VERNEW));
932 
933 	return (prop_method_register(node, pgname, pname, ptype, mname,
934 	    version, args, err)); /* err set and node unlocked */
935 }
936 
937 void
938 topo_prop_method_unregister(tnode_t *node, const char *pgname,
939     const char *pname)
940 {
941 	topo_propval_t *pv;
942 	topo_pgroup_t *pg;
943 	topo_proplist_t *pvl;
944 	topo_hdl_t *thp = node->tn_hdl;
945 
946 	topo_node_lock(node);
947 
948 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
949 	    pg = topo_list_next(pg)) {
950 		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
951 			break;
952 		}
953 	}
954 
955 	if (pg == NULL) {
956 		topo_node_unlock(node);
957 		return;
958 	}
959 
960 	for (pvl = topo_list_next(&pg->tpg_list); pvl != NULL;
961 	    pvl = topo_list_next(pvl)) {
962 		pv = pvl->tp_pval;
963 		if (strcmp(pv->tp_name, pname) == 0) {
964 			topo_list_delete(&pg->tpg_pvals, pvl);
965 			assert(pv->tp_refs == 1);
966 			topo_prop_rele(pv);
967 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
968 			break;
969 		}
970 	}
971 
972 	topo_node_unlock(node);
973 }
974 
975 static int
976 inherit_seterror(tnode_t *node, int *errp, int err)
977 {
978 	topo_node_unlock(node);
979 	topo_node_unlock(node->tn_parent);
980 
981 	*errp = err;
982 
983 	return (-1);
984 }
985 
986 int
987 topo_prop_inherit(tnode_t *node, const char *pgname, const char *name, int *err)
988 {
989 	topo_hdl_t *thp = node->tn_hdl;
990 	tnode_t *pnode = node->tn_parent;
991 	topo_pgroup_t *pg;
992 	topo_propval_t *pv;
993 	topo_proplist_t *pvl;
994 
995 	topo_node_lock(pnode);
996 	topo_node_lock(node);
997 	/*
998 	 * Check for an existing property group and prop val
999 	 */
1000 	if ((pv = propval_get(pgroup_get(pnode, pgname), name)) == NULL)
1001 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1002 
1003 	/*
1004 	 * Can this propval be inherited?
1005 	 */
1006 	if (pv->tp_flag != TOPO_PROP_IMMUTABLE)
1007 		return (inherit_seterror(node, err, ETOPO_PROP_NOINHERIT));
1008 
1009 	/*
1010 	 * Property group should already exist: bump the ref count for this
1011 	 * propval and add it to the node's property group
1012 	 */
1013 	if ((pg = pgroup_get(node, pgname)) == NULL)
1014 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1015 
1016 	if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
1017 	    == NULL)
1018 		return (inherit_seterror(node, err, ETOPO_NOMEM));
1019 
1020 	topo_prop_hold(pv);
1021 	pvl->tp_pval = pv;
1022 	topo_list_append(&pg->tpg_pvals, pvl);
1023 
1024 	topo_node_unlock(node);
1025 	topo_node_unlock(pnode);
1026 
1027 	return (0);
1028 }
1029 
1030 topo_pgroup_info_t *
1031 topo_pgroup_info(tnode_t *node, const char *pgname, int *err)
1032 {
1033 	topo_hdl_t *thp = node->tn_hdl;
1034 	topo_pgroup_t *pg;
1035 	topo_ipgroup_info_t *pip;
1036 	topo_pgroup_info_t *info;
1037 
1038 	topo_node_lock(node);
1039 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1040 	    pg = topo_list_next(pg)) {
1041 		if (strcmp(pgname, pg->tpg_info->tpi_name) == 0) {
1042 			if ((info = topo_hdl_alloc(thp,
1043 			    sizeof (topo_pgroup_info_t))) == NULL)
1044 				return (NULL);
1045 
1046 			pip = pg->tpg_info;
1047 			if ((info->tpi_name =
1048 			    topo_hdl_strdup(thp, pip->tpi_name)) == NULL) {
1049 				*err = ETOPO_PROP_NOMEM;
1050 				topo_hdl_free(thp, info,
1051 				    sizeof (topo_pgroup_info_t));
1052 				topo_node_unlock(node);
1053 				return (NULL);
1054 			}
1055 			info->tpi_namestab = pip->tpi_namestab;
1056 			info->tpi_datastab = pip->tpi_datastab;
1057 			info->tpi_version = pip->tpi_version;
1058 			topo_node_unlock(node);
1059 			return (info);
1060 		}
1061 	}
1062 
1063 	*err = ETOPO_PROP_NOENT;
1064 	topo_node_unlock(node);
1065 	return (NULL);
1066 }
1067 
1068 static int
1069 pgroup_seterr(tnode_t *node, topo_pgroup_t *pg, topo_ipgroup_info_t *pip,
1070     int *err)
1071 {
1072 	topo_hdl_t *thp = node->tn_hdl;
1073 
1074 	if (pip != NULL) {
1075 		if (pip->tpi_name != NULL)
1076 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1077 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1078 	}
1079 
1080 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1081 	*err = ETOPO_NOMEM;
1082 
1083 	topo_node_unlock(node);
1084 
1085 	return (-1);
1086 }
1087 
1088 int
1089 topo_pgroup_create(tnode_t *node, const topo_pgroup_info_t *pinfo, int *err)
1090 {
1091 	topo_pgroup_t *pg;
1092 	topo_ipgroup_info_t *pip;
1093 	topo_hdl_t *thp = node->tn_hdl;
1094 
1095 	*err = 0;
1096 
1097 	topo_node_lock(node);
1098 	/*
1099 	 * Check for an existing pgroup
1100 	 */
1101 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1102 	    pg = topo_list_next(pg)) {
1103 		if (strcmp(pg->tpg_info->tpi_name, pinfo->tpi_name) == 0) {
1104 			*err = ETOPO_PROP_DEFD;
1105 			topo_node_unlock(node);
1106 			return (-1);
1107 		}
1108 	}
1109 
1110 	if ((pg = topo_hdl_zalloc(thp, sizeof (topo_pgroup_t))) == NULL) {
1111 		*err = ETOPO_NOMEM;
1112 		topo_node_unlock(node);
1113 		return (-1);
1114 	}
1115 
1116 	if ((pip = topo_hdl_zalloc(thp, sizeof (topo_ipgroup_info_t)))
1117 	    == NULL)
1118 		return (pgroup_seterr(node, pg, pip, err));
1119 
1120 	if ((pip->tpi_name = topo_hdl_strdup(thp, pinfo->tpi_name))
1121 	    == NULL)
1122 		return (pgroup_seterr(node, pg, pip, err));
1123 
1124 	pip->tpi_namestab = pinfo->tpi_namestab;
1125 	pip->tpi_datastab = pinfo->tpi_datastab;
1126 	pip->tpi_version = pinfo->tpi_version;
1127 
1128 	pg->tpg_info = pip;
1129 
1130 	topo_list_append(&node->tn_pgroups, pg);
1131 	topo_node_unlock(node);
1132 
1133 	return (0);
1134 }
1135 
1136 void
1137 topo_pgroup_destroy(tnode_t *node, const char *pname)
1138 {
1139 	topo_hdl_t *thp = node->tn_hdl;
1140 	topo_pgroup_t *pg;
1141 	topo_proplist_t *pvl;
1142 	topo_ipgroup_info_t *pip;
1143 
1144 	topo_node_lock(node);
1145 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1146 	    pg = topo_list_next(pg)) {
1147 		if (strcmp(pg->tpg_info->tpi_name, pname) == 0) {
1148 			break;
1149 		}
1150 	}
1151 
1152 	if (pg == NULL) {
1153 		topo_node_unlock(node);
1154 		return;
1155 	}
1156 
1157 	while ((pvl = topo_list_next(&pg->tpg_list)) != NULL) {
1158 		topo_list_delete(&pg->tpg_pvals, pvl);
1159 		topo_prop_rele(pvl->tp_pval);
1160 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1161 	}
1162 
1163 	topo_list_delete(&node->tn_pgroups, pg);
1164 	topo_node_unlock(node);
1165 
1166 	pip = pg->tpg_info;
1167 	if (pip != NULL) {
1168 		if (pip->tpi_name != NULL)
1169 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1170 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1171 	}
1172 
1173 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1174 }
1175 
1176 void
1177 topo_pgroup_destroy_all(tnode_t *node)
1178 {
1179 	topo_hdl_t *thp = node->tn_hdl;
1180 	topo_pgroup_t *pg;
1181 	topo_proplist_t *pvl;
1182 	topo_ipgroup_info_t *pip;
1183 
1184 	topo_node_lock(node);
1185 	while ((pg = topo_list_next(&node->tn_pgroups)) != NULL) {
1186 		while ((pvl = topo_list_next(&pg->tpg_pvals)) != NULL) {
1187 			topo_list_delete(&pg->tpg_pvals, pvl);
1188 			topo_prop_rele(pvl->tp_pval);
1189 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1190 		}
1191 
1192 		topo_list_delete(&node->tn_pgroups, pg);
1193 
1194 		pip = pg->tpg_info;
1195 		if (pip != NULL) {
1196 			if (pip->tpi_name != NULL)
1197 				topo_hdl_strfree(thp, (char *)pip->tpi_name);
1198 			topo_hdl_free(thp, pip, sizeof (topo_pgroup_info_t));
1199 		}
1200 
1201 		topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1202 	}
1203 	topo_node_unlock(node);
1204 }
1205 
1206 static void
1207 propmethod_destroy(topo_hdl_t *thp, topo_propval_t *pv)
1208 {
1209 	topo_propmethod_t *pm;
1210 
1211 	pm = pv->tp_method;
1212 	if (pm != NULL) {
1213 		if (pm->tpm_name != NULL)
1214 			topo_hdl_strfree(thp, pm->tpm_name);
1215 		if (pm->tpm_args != NULL)
1216 			nvlist_free(pm->tpm_args);
1217 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
1218 		pv->tp_method = NULL;
1219 	}
1220 }
1221 
1222 static void
1223 topo_propval_destroy(topo_propval_t *pv)
1224 {
1225 	topo_hdl_t *thp;
1226 
1227 	if (pv == NULL)
1228 		return;
1229 
1230 	thp = pv->tp_hdl;
1231 
1232 	if (pv->tp_name != NULL)
1233 		topo_hdl_strfree(thp, pv->tp_name);
1234 
1235 	if (pv->tp_val != NULL)
1236 		nvlist_free(pv->tp_val);
1237 
1238 	propmethod_destroy(thp, pv);
1239 
1240 	topo_hdl_free(thp, pv, sizeof (topo_propval_t));
1241 }
1242 
1243 void
1244 topo_prop_hold(topo_propval_t *pv)
1245 {
1246 	pv->tp_refs++;
1247 }
1248 
1249 void
1250 topo_prop_rele(topo_propval_t *pv)
1251 {
1252 	pv->tp_refs--;
1253 
1254 	assert(pv->tp_refs >= 0);
1255 
1256 	if (pv->tp_refs == 0)
1257 		topo_propval_destroy(pv);
1258 }
1259 
1260 /*
1261  * topo_prop_getprop() and topo_prop_getprops() are private project functions
1262  * for fmtopo
1263  */
1264 int
1265 topo_prop_getprop(tnode_t *node, const char *pgname, const char *pname,
1266     nvlist_t *args, nvlist_t **prop, int *err)
1267 {
1268 	topo_hdl_t *thp = node->tn_hdl;
1269 	topo_propval_t *pv;
1270 
1271 	topo_node_lock(node);
1272 	if ((pv = prop_get(node, pgname, pname, args, err)) == NULL) {
1273 		(void) get_properror(node, err, *err);
1274 		return (-1);
1275 	}
1276 
1277 	if (topo_hdl_nvdup(thp, pv->tp_val, prop) != 0) {
1278 		(void) get_properror(node, err, ETOPO_NOMEM);
1279 		return (-1);
1280 	}
1281 	topo_node_unlock(node);
1282 
1283 	return (0);
1284 }
1285 
1286 static int
1287 prop_val_add(tnode_t *node, nvlist_t **nvl, topo_propval_t *pv, int *err)
1288 {
1289 	if (pv->tp_method != NULL)
1290 		if (prop_method_get(node, pv, pv->tp_method, NULL, err) < 0)
1291 			return (-1);
1292 
1293 	if (pv->tp_val == NULL) {
1294 		*err = ETOPO_PROP_NOENT;
1295 		return (-1);
1296 	}
1297 
1298 	if (topo_hdl_nvdup(pv->tp_hdl, pv->tp_val, nvl) != 0) {
1299 		*err = ETOPO_PROP_NOMEM;
1300 		return (-1);
1301 	}
1302 
1303 	return (0);
1304 }
1305 
1306 static int
1307 get_pgrp_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1308 {
1309 	topo_node_unlock(node);
1310 
1311 	if (nvl != NULL)
1312 		nvlist_free(nvl);
1313 
1314 	*errp = err;
1315 
1316 	return (-1);
1317 }
1318 
1319 int
1320 topo_prop_getpgrp(tnode_t *node, const char *pgname, nvlist_t **pgrp,
1321     int *err)
1322 {
1323 	int ret;
1324 	topo_hdl_t *thp = node->tn_hdl;
1325 	nvlist_t *nvl, *pvnvl;
1326 	topo_pgroup_t *pg;
1327 	topo_propval_t *pv;
1328 	topo_proplist_t *pvl;
1329 
1330 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1331 		*err = ETOPO_NOMEM;
1332 		return (-1);
1333 	}
1334 
1335 	topo_node_lock(node);
1336 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1337 	    pg = topo_list_next(pg)) {
1338 
1339 		if (strcmp(pgname, pg->tpg_info->tpi_name) != 0)
1340 			continue;
1341 
1342 		if (nvlist_add_string(nvl, TOPO_PROP_GROUP_NAME,
1343 		    pg->tpg_info->tpi_name) != 0 ||
1344 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_NSTAB,
1345 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1346 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_DSTAB,
1347 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1348 		    nvlist_add_int32(nvl, TOPO_PROP_GROUP_VERSION,
1349 		    pg->tpg_info->tpi_version) != 0)
1350 			return (get_pgrp_seterror(node, nvl, err,
1351 			    ETOPO_PROP_NVL));
1352 
1353 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1354 		    pvl = topo_list_next(pvl)) {
1355 
1356 			pv = pvl->tp_pval;
1357 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1358 				return (get_pgrp_seterror(node, nvl, err,
1359 				    *err));
1360 			}
1361 			if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_VAL,
1362 			    pvnvl)) != 0) {
1363 				nvlist_free(pvnvl);
1364 				return (get_pgrp_seterror(node, nvl, err, ret));
1365 			}
1366 
1367 			nvlist_free(pvnvl);
1368 		}
1369 		topo_node_unlock(node);
1370 		*pgrp = nvl;
1371 		return (0);
1372 	}
1373 
1374 	topo_node_unlock(node);
1375 	*err = ETOPO_PROP_NOENT;
1376 	return (-1);
1377 }
1378 
1379 static nvlist_t *
1380 get_all_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1381 {
1382 	topo_node_unlock(node);
1383 
1384 	if (nvl != NULL)
1385 		nvlist_free(nvl);
1386 
1387 	*errp = err;
1388 
1389 	return (NULL);
1390 }
1391 
1392 nvlist_t *
1393 topo_prop_getprops(tnode_t *node, int *err)
1394 {
1395 	int ret;
1396 	topo_hdl_t *thp = node->tn_hdl;
1397 	nvlist_t *nvl, *pgnvl, *pvnvl;
1398 	topo_pgroup_t *pg;
1399 	topo_propval_t *pv;
1400 	topo_proplist_t *pvl;
1401 
1402 	topo_node_lock(node);
1403 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1404 		return (get_all_seterror(node, NULL, err, ETOPO_NOMEM));
1405 	}
1406 
1407 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1408 	    pg = topo_list_next(pg)) {
1409 		if (topo_hdl_nvalloc(thp, &pgnvl, 0) != 0)
1410 			return (get_all_seterror(node, nvl, err, ETOPO_NOMEM));
1411 
1412 		if (nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NAME,
1413 		    pg->tpg_info->tpi_name) != 0 ||
1414 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NSTAB,
1415 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1416 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_DSTAB,
1417 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1418 		    nvlist_add_int32(pgnvl, TOPO_PROP_GROUP_VERSION,
1419 		    pg->tpg_info->tpi_version) != 0)
1420 			return (get_all_seterror(node, nvl, err,
1421 			    ETOPO_PROP_NVL));
1422 
1423 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1424 		    pvl = topo_list_next(pvl)) {
1425 
1426 			pv = pvl->tp_pval;
1427 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1428 				nvlist_free(pgnvl);
1429 				return (get_all_seterror(node, nvl, err, *err));
1430 			}
1431 			if ((ret = nvlist_add_nvlist(pgnvl, TOPO_PROP_VAL,
1432 			    pvnvl)) != 0) {
1433 				nvlist_free(pgnvl);
1434 				nvlist_free(pvnvl);
1435 				return (get_all_seterror(node, nvl, err, ret));
1436 			}
1437 
1438 			nvlist_free(pvnvl);
1439 		}
1440 		if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_GROUP, pgnvl))
1441 		    != 0) {
1442 			nvlist_free(pgnvl);
1443 			return (get_all_seterror(node, nvl, err, ret));
1444 		}
1445 
1446 		nvlist_free(pgnvl);
1447 	}
1448 
1449 	topo_node_unlock(node);
1450 
1451 	return (nvl);
1452 }
1453