Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <strings.h>
     27 #include <assert.h>
     28 #include <fm/libtopo.h>
     29 #include <topo_prop.h>
     30 #include <topo_string.h>
     31 #include <topo_alloc.h>
     32 #include <topo_error.h>
     33 #include <topo_method.h>
     34 
     35 /*
     36  * Topology nodes are permitted to contain property information.
     37  * Property information is organized according to property grouping.
     38  * Each property group defines a name, a stability level for that name,
     39  * a stability level for all underlying property data (name, type, values),
     40  * a version for the property group definition and and a list of uniquely
     41  * defined properties.  Property group versions are incremented when one of
     42  * the following changes occurs:
     43  *	- a property name changes
     44  *	- a property type changes
     45  *	- a property definition is removed from the group
     46  * Compatible changes such as new property definitions in the group do
     47  * not require version changes.
     48  *
     49  * Each property defines a unique (within the group) name, a type and
     50  * a value.  Properties may be statically defined as int32, uint32, int64,
     51  * uint64, fmri, string or arrays of each type.  Properties may also be
     52  * dynamically exported via module registered methods.  For example, a module
     53  * may register a method to export an ASRU property that is dynamically
     54  * contructed when a call to topo_node_fmri() is invoked for a particular
     55  * topology node.
     56  *
     57  * Static properties are persistently attached to topology nodes during
     58  * enumeration by an enumeration module or as part of XML statements in a
     59  * toplogy map file using the topo_prop_set* family of routines.  Similarly,
     60  * property methods are registered during enumeration or as part of
     61  * statements in topololgy map files.  Set-up of property methods is performed
     62  * by calling topo_prop_method_register().
     63  *
     64  * All properties, whether statically persisted in a snapshot or dynamically
     65  * obtained, may be read via the topo_prop_get* family of interfaces.
     66  * Callers wishing to receive all property groups and properties for a given
     67  * node may use topo_prop_getall().  This routine returns a nested nvlist
     68  * of all groupings and property (name, type, value) sets.  Groupings
     69  * are defined by TOPO_PROP_GROUP (name, data stability, name stability and
     70  * version) and a nested nvlist of properties (TOPO_PROP_VAL).  Each property
     71  * value is defined by its name, type and value.
     72  */
     73 static void topo_propval_destroy(topo_propval_t *);
     74 
     75 static topo_pgroup_t *
     76 pgroup_get(tnode_t *node, const char *pgname)
     77 {
     78 	topo_pgroup_t *pg;
     79 	/*
     80 	 * Check for an existing pgroup
     81 	 */
     82 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
     83 	    pg = topo_list_next(pg)) {
     84 		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
     85 			return (pg);
     86 		}
     87 	}
     88 
     89 	return (NULL);
     90 }
     91 
     92 static topo_propval_t *
     93 propval_get(topo_pgroup_t *pg, const char *pname)
     94 {
     95 	topo_proplist_t *pvl;
     96 
     97 	if (pg == NULL)
     98 		return (NULL);
     99 
    100 	for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
    101 	    pvl = topo_list_next(pvl)) {
    102 		if (strcmp(pvl->tp_pval->tp_name, pname) == 0)
    103 			return (pvl->tp_pval);
    104 	}
    105 
    106 	return (NULL);
    107 }
    108 
    109 static int
    110 method_geterror(nvlist_t *nvl, int err, int *errp)
    111 {
    112 	if (nvl != NULL)
    113 		nvlist_free(nvl);
    114 
    115 	*errp = err;
    116 
    117 	return (-1);
    118 }
    119 
    120 static int
    121 prop_method_get(tnode_t *node, topo_propval_t *pv, topo_propmethod_t *pm,
    122     nvlist_t *pargs, int *err)
    123 {
    124 	int ret;
    125 	nvlist_t *args, *nvl;
    126 	char *name;
    127 	topo_type_t type;
    128 
    129 	if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0 ||
    130 	    nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args) != 0)
    131 		return (method_geterror(NULL, ETOPO_PROP_NVL, err));
    132 
    133 	if (pargs != NULL)
    134 		if (nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs) != 0)
    135 			return (method_geterror(args, ETOPO_PROP_NVL, err));
    136 
    137 	/*
    138 	 * Now, get the latest value
    139 	 *
    140 	 * Grab a reference to the property and then unlock the node.  This will
    141 	 * allow property methods to safely re-enter the prop_get codepath,
    142 	 * making it possible for property methods to access other property
    143 	 * values on the same node w\o causing a deadlock.
    144 	 */
    145 	topo_prop_hold(pv);
    146 	topo_node_unlock(node);
    147 	if (topo_method_call(node, pm->tpm_name, pm->tpm_version,
    148 	    args, &nvl, err) < 0) {
    149 		topo_node_lock(node);
    150 		topo_prop_rele(pv);
    151 		return (method_geterror(args, *err, err));
    152 	}
    153 	topo_node_lock(node);
    154 	topo_prop_rele(pv);
    155 
    156 	nvlist_free(args);
    157 
    158 	/* Verify the property contents */
    159 	ret = nvlist_lookup_string(nvl, TOPO_PROP_VAL_NAME, &name);
    160 	if (ret != 0 || strcmp(name, pv->tp_name) != 0)
    161 		return (method_geterror(nvl, ETOPO_PROP_NAME, err));
    162 
    163 	ret = nvlist_lookup_uint32(nvl, TOPO_PROP_VAL_TYPE, (uint32_t *)&type);
    164 	if (ret != 0 || type != pv->tp_type)
    165 		return (method_geterror(nvl, ETOPO_PROP_TYPE, err));
    166 
    167 	/* Release the last value and re-assign to the new value */
    168 	if (pv->tp_val != NULL)
    169 		nvlist_free(pv->tp_val);
    170 	pv->tp_val = nvl;
    171 
    172 	return (0);
    173 }
    174 
    175 static topo_propval_t *
    176 prop_get(tnode_t *node, const char *pgname, const char *pname, nvlist_t *pargs,
    177     int *err)
    178 {
    179 	topo_propval_t *pv = NULL;
    180 
    181 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
    182 		*err = ETOPO_PROP_NOENT;
    183 		return (NULL);
    184 	}
    185 
    186 	if (pv->tp_flag & TOPO_PROP_NONVOLATILE && pv->tp_val != NULL)
    187 		return (pv);
    188 
    189 	if (pv->tp_method != NULL) {
    190 		if (prop_method_get(node, pv, pv->tp_method, pargs, err) < 0)
    191 			return (NULL);
    192 	}
    193 
    194 	return (pv);
    195 }
    196 
    197 static int
    198 get_properror(tnode_t *node, int *errp, int err)
    199 {
    200 	topo_node_unlock(node);
    201 	*errp = err;
    202 	return (-1);
    203 }
    204 
    205 static int
    206 prop_getval(tnode_t *node, const char *pgname, const char *pname, void *val,
    207     topo_type_t type, uint_t *nelems, int *err)
    208 {
    209 	int i, j, ret = 0;
    210 	topo_hdl_t *thp = node->tn_hdl;
    211 	topo_propval_t *pv;
    212 
    213 	topo_node_lock(node);
    214 	if ((pv = prop_get(node, pgname, pname, NULL, err))
    215 	    == NULL)
    216 		return (get_properror(node, err, *err));
    217 
    218 	if (pv->tp_type != type)
    219 		return (get_properror(node, err, ETOPO_PROP_TYPE));
    220 
    221 	switch (type) {
    222 		case TOPO_TYPE_INT32:
    223 			ret = nvlist_lookup_int32(pv->tp_val, TOPO_PROP_VAL_VAL,
    224 			    (int32_t *)val);
    225 			break;
    226 		case TOPO_TYPE_UINT32:
    227 			ret = nvlist_lookup_uint32(pv->tp_val,
    228 			    TOPO_PROP_VAL_VAL, (uint32_t *)val);
    229 			break;
    230 		case TOPO_TYPE_INT64:
    231 			ret = nvlist_lookup_int64(pv->tp_val, TOPO_PROP_VAL_VAL,
    232 			    (int64_t *)val);
    233 			break;
    234 		case TOPO_TYPE_UINT64:
    235 			ret = nvlist_lookup_uint64(pv->tp_val,
    236 			    TOPO_PROP_VAL_VAL, (uint64_t *)val);
    237 			break;
    238 		case TOPO_TYPE_DOUBLE:
    239 			ret = nvlist_lookup_double(pv->tp_val,
    240 			    TOPO_PROP_VAL_VAL, (double *)val);
    241 			break;
    242 		case TOPO_TYPE_STRING: {
    243 			char *str;
    244 
    245 			ret = nvlist_lookup_string(pv->tp_val,
    246 			    TOPO_PROP_VAL_VAL, &str);
    247 			if (ret == 0) {
    248 				char *s2;
    249 				if ((s2 = topo_hdl_strdup(thp, str)) == NULL)
    250 					ret = -1;
    251 				else
    252 					*(char **)val = s2;
    253 			}
    254 			break;
    255 		}
    256 		case TOPO_TYPE_FMRI: {
    257 			nvlist_t *nvl;
    258 
    259 			ret = nvlist_lookup_nvlist(pv->tp_val,
    260 			    TOPO_PROP_VAL_VAL, &nvl);
    261 			if (ret == 0)
    262 				ret = topo_hdl_nvdup(thp, nvl,
    263 				    (nvlist_t **)val);
    264 			break;
    265 		}
    266 		case TOPO_TYPE_INT32_ARRAY: {
    267 			int32_t *a1, *a2;
    268 
    269 			if ((ret = nvlist_lookup_int32_array(pv->tp_val,
    270 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
    271 				break;
    272 			if ((a1 = topo_hdl_alloc(thp, sizeof (int32_t) *
    273 			    *nelems)) == NULL) {
    274 				ret = ETOPO_NOMEM;
    275 				break;
    276 			}
    277 			for (i = 0; i < *nelems; ++i)
    278 				a1[i] = a2[i];
    279 			*(int32_t **)val = a1;
    280 			break;
    281 		}
    282 		case TOPO_TYPE_UINT32_ARRAY: {
    283 			uint32_t *a1, *a2;
    284 
    285 			if ((ret = nvlist_lookup_uint32_array(pv->tp_val,
    286 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
    287 				break;
    288 			if ((a1 = topo_hdl_alloc(thp, sizeof (uint32_t) *
    289 			    *nelems)) == NULL) {
    290 				ret = ETOPO_NOMEM;
    291 				break;
    292 			}
    293 			for (i = 0; i < *nelems; ++i)
    294 				a1[i] = a2[i];
    295 			*(uint32_t **)val = a1;
    296 			break;
    297 		}
    298 		case TOPO_TYPE_INT64_ARRAY: {
    299 			int64_t *a1, *a2;
    300 
    301 			if ((ret = nvlist_lookup_int64_array(pv->tp_val,
    302 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
    303 				break;
    304 			if ((a1 = topo_hdl_alloc(thp, sizeof (int64_t) *
    305 			    *nelems)) == NULL) {
    306 				ret = ETOPO_NOMEM;
    307 				break;
    308 			}
    309 			for (i = 0; i < *nelems; ++i)
    310 				a1[i] = a2[i];
    311 			*(int64_t **)val = a1;
    312 			break;
    313 		}
    314 		case TOPO_TYPE_UINT64_ARRAY: {
    315 			uint64_t *a1, *a2;
    316 
    317 			if ((ret = nvlist_lookup_uint64_array(pv->tp_val,
    318 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
    319 				break;
    320 			if ((a1 = topo_hdl_alloc(thp, sizeof (uint64_t) *
    321 			    *nelems)) == NULL) {
    322 				ret = ETOPO_NOMEM;
    323 				break;
    324 			}
    325 			for (i = 0; i < *nelems; ++i)
    326 				a1[i] = a2[i];
    327 			*(uint64_t **)val = a1;
    328 			break;
    329 		}
    330 		case TOPO_TYPE_STRING_ARRAY: {
    331 			char **a1, **a2;
    332 
    333 			if ((ret = nvlist_lookup_string_array(pv->tp_val,
    334 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
    335 				break;
    336 			if ((a1 = topo_hdl_alloc(thp, sizeof (char *) *
    337 			    *nelems)) == NULL) {
    338 				ret = ETOPO_NOMEM;
    339 				break;
    340 			}
    341 			for (i = 0; i < *nelems; ++i) {
    342 				if ((a1[i] = topo_hdl_strdup(thp, a2[i]))
    343 				    == NULL) {
    344 					for (j = 0; j < i; ++j)
    345 						topo_hdl_free(thp, a1[j],
    346 						    sizeof (char *));
    347 					topo_hdl_free(thp, a1,
    348 					    sizeof (char *) * *nelems);
    349 					break;
    350 				}
    351 			}
    352 			*(char ***)val = a1;
    353 			break;
    354 		}
    355 		case TOPO_TYPE_FMRI_ARRAY: {
    356 			nvlist_t **a1, **a2;
    357 
    358 			if ((ret = nvlist_lookup_nvlist_array(pv->tp_val,
    359 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
    360 				break;
    361 			if ((a1 = topo_hdl_alloc(thp, sizeof (nvlist_t *) *
    362 			    *nelems)) == NULL) {
    363 				ret = ETOPO_NOMEM;
    364 				break;
    365 			}
    366 			for (i = 0; i < *nelems; ++i) {
    367 				if (topo_hdl_nvdup(thp, a2[i], &a1[i]) < 0) {
    368 					for (j = 0; j < i; ++j)
    369 						nvlist_free(a1[j]);
    370 					topo_hdl_free(thp, a1,
    371 					    sizeof (nvlist_t *) * *nelems);
    372 					break;
    373 				}
    374 			}
    375 			*(nvlist_t ***)val = a1;
    376 			break;
    377 		}
    378 		default:
    379 			ret = ETOPO_PROP_NOENT;
    380 	}
    381 
    382 	if (ret != 0)
    383 		if (ret == ENOENT)
    384 			return (get_properror(node, err, ETOPO_PROP_NOENT));
    385 		else if (ret < ETOPO_UNKNOWN)
    386 			return (get_properror(node, err, ETOPO_PROP_NVL));
    387 		else
    388 			return (get_properror(node, err, ret));
    389 
    390 	topo_node_unlock(node);
    391 	return (0);
    392 }
    393 
    394 int
    395 topo_prop_get_int32(tnode_t *node, const char *pgname, const char *pname,
    396     int32_t *val, int *err)
    397 {
    398 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT32,
    399 	    NULL, err));
    400 }
    401 
    402 int
    403 topo_prop_get_uint32(tnode_t *node, const char *pgname, const char *pname,
    404     uint32_t *val, int *err)
    405 {
    406 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT32,
    407 	    NULL, err));
    408 }
    409 
    410 int
    411 topo_prop_get_int64(tnode_t *node, const char *pgname, const char *pname,
    412     int64_t *val, int *err)
    413 {
    414 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT64,
    415 	    NULL, err));
    416 }
    417 
    418 int
    419 topo_prop_get_uint64(tnode_t *node, const char *pgname, const char *pname,
    420     uint64_t *val, int *err)
    421 {
    422 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT64,
    423 	    NULL, err));
    424 }
    425 
    426 int
    427 topo_prop_get_double(tnode_t *node, const char *pgname, const char *pname,
    428     double *val, int *err)
    429 {
    430 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_DOUBLE,
    431 	    NULL, err));
    432 }
    433 
    434 int
    435 topo_prop_get_string(tnode_t *node, const char *pgname, const char *pname,
    436     char **val, int *err)
    437 {
    438 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_STRING,
    439 	    NULL, err));
    440 }
    441 
    442 int
    443 topo_prop_get_fmri(tnode_t *node, const char *pgname, const char *pname,
    444     nvlist_t **val, int *err)
    445 {
    446 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_FMRI,
    447 	    NULL, err));
    448 }
    449 
    450 int
    451 topo_prop_get_int32_array(tnode_t *node, const char *pgname, const char *pname,
    452     int32_t **val, uint_t *nelem, int *err)
    453 {
    454 	return (prop_getval(node, pgname, pname, (void *)val,
    455 	    TOPO_TYPE_INT32_ARRAY, nelem, err));
    456 }
    457 
    458 int
    459 topo_prop_get_uint32_array(tnode_t *node, const char *pgname, const char *pname,
    460     uint32_t **val, uint_t *nelem, int *err)
    461 {
    462 	return (prop_getval(node, pgname, pname, (void *)val,
    463 	    TOPO_TYPE_UINT32_ARRAY, nelem, err));
    464 }
    465 
    466 int
    467 topo_prop_get_int64_array(tnode_t *node, const char *pgname, const char *pname,
    468     int64_t **val, uint_t *nelem, int *err)
    469 {
    470 	return (prop_getval(node, pgname, pname, (void *)val,
    471 	    TOPO_TYPE_INT64_ARRAY, nelem, err));
    472 }
    473 
    474 int
    475 topo_prop_get_uint64_array(tnode_t *node, const char *pgname, const char *pname,
    476     uint64_t **val, uint_t *nelem, int *err)
    477 {
    478 	return (prop_getval(node, pgname, pname, (void *)val,
    479 	    TOPO_TYPE_UINT64_ARRAY, nelem, err));
    480 }
    481 
    482 int
    483 topo_prop_get_string_array(tnode_t *node, const char *pgname, const char *pname,
    484     char ***val, uint_t *nelem, int *err)
    485 {
    486 	return (prop_getval(node, pgname, pname, (void *)val,
    487 	    TOPO_TYPE_STRING_ARRAY, nelem, err));
    488 }
    489 
    490 int
    491 topo_prop_get_fmri_array(tnode_t *node, const char *pgname, const char *pname,
    492     nvlist_t ***val, uint_t *nelem, int *err)
    493 {
    494 	return (prop_getval(node, pgname, pname, (void *)val,
    495 	    TOPO_TYPE_FMRI_ARRAY, nelem, err));
    496 }
    497 
    498 static topo_propval_t *
    499 set_seterror(tnode_t *node, topo_proplist_t *pvl, int *errp, int err)
    500 {
    501 	topo_hdl_t *thp = node->tn_hdl;
    502 	topo_propval_t *pv;
    503 
    504 	if (pvl != NULL) {
    505 		pv = pvl->tp_pval;
    506 		topo_propval_destroy(pv);
    507 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
    508 	}
    509 
    510 	topo_node_unlock(node);
    511 	*errp = err;
    512 
    513 	return (NULL);
    514 }
    515 
    516 static topo_propval_t *
    517 prop_create(tnode_t *node, const char *pgname, const char *pname,
    518     topo_type_t type, int flag, int *err)
    519 {
    520 	topo_hdl_t *thp = node->tn_hdl;
    521 	topo_pgroup_t *pg;
    522 	topo_propval_t *pv;
    523 	topo_proplist_t *pvl;
    524 
    525 	/*
    526 	 * Replace existing prop value with new one
    527 	 */
    528 	if ((pg = pgroup_get(node, pgname)) == NULL) {
    529 		topo_node_unlock(node);
    530 		*err = ETOPO_PROP_NOENT;
    531 		return (NULL);
    532 	}
    533 
    534 	if ((pv = propval_get(pg, pname)) != NULL) {
    535 		if (pv->tp_type != type)
    536 			return (set_seterror(node, NULL, err, ETOPO_PROP_TYPE));
    537 		else if (! (pv->tp_flag & TOPO_PROP_MUTABLE))
    538 			return (set_seterror(node, NULL, err, ETOPO_PROP_DEFD));
    539 
    540 		nvlist_free(pv->tp_val);
    541 		pv->tp_val = NULL;
    542 	} else {
    543 		if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
    544 		    == NULL)
    545 			return (set_seterror(node, NULL, err, ETOPO_NOMEM));
    546 
    547 		if ((pv = topo_hdl_zalloc(thp, sizeof (topo_propval_t)))
    548 		    == NULL)
    549 			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
    550 
    551 		pv->tp_hdl = thp;
    552 		pvl->tp_pval = pv;
    553 
    554 		if ((pv->tp_name = topo_hdl_strdup(thp, pname))
    555 		    == NULL)
    556 			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
    557 		pv->tp_flag = flag;
    558 		pv->tp_type = type;
    559 		topo_prop_hold(pv);
    560 		topo_list_append(&pg->tpg_pvals, pvl);
    561 	}
    562 
    563 	return (pv);
    564 }
    565 
    566 static int
    567 topo_prop_set(tnode_t *node, const char *pgname, const char *pname,
    568     topo_type_t type, int flag, void *val, int nelems, int *err)
    569 {
    570 	int ret;
    571 	topo_hdl_t *thp = node->tn_hdl;
    572 	nvlist_t *nvl;
    573 
    574 	if (topo_hdl_nvalloc(thp, &nvl, NV_UNIQUE_NAME) < 0) {
    575 		*err = ETOPO_PROP_NVL;
    576 		return (-1);
    577 	}
    578 
    579 	ret = nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, pname);
    580 	ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, type);
    581 	switch (type) {
    582 		case TOPO_TYPE_INT32:
    583 			ret |= nvlist_add_int32(nvl, TOPO_PROP_VAL_VAL,
    584 			    *(int32_t *)val);
    585 			break;
    586 		case TOPO_TYPE_UINT32:
    587 			ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL,
    588 			    *(uint32_t *)val);
    589 			break;
    590 		case TOPO_TYPE_INT64:
    591 			ret |= nvlist_add_int64(nvl, TOPO_PROP_VAL_VAL,
    592 			    *(int64_t *)val);
    593 			break;
    594 		case TOPO_TYPE_UINT64:
    595 			ret |= nvlist_add_uint64(nvl, TOPO_PROP_VAL_VAL,
    596 			    *(uint64_t *)val);
    597 			break;
    598 		case TOPO_TYPE_DOUBLE:
    599 			ret |= nvlist_add_double(nvl, TOPO_PROP_VAL_VAL,
    600 			    *(double *)val);
    601 			break;
    602 		case TOPO_TYPE_STRING:
    603 			ret |= nvlist_add_string(nvl, TOPO_PROP_VAL_VAL,
    604 			    (char *)val);
    605 			break;
    606 		case TOPO_TYPE_FMRI:
    607 			ret |= nvlist_add_nvlist(nvl, TOPO_PROP_VAL_VAL,
    608 			    (nvlist_t *)val);
    609 			break;
    610 		case TOPO_TYPE_INT32_ARRAY:
    611 			ret |= nvlist_add_int32_array(nvl,
    612 			    TOPO_PROP_VAL_VAL, (int32_t *)val, nelems);
    613 			break;
    614 		case TOPO_TYPE_UINT32_ARRAY:
    615 			ret |= nvlist_add_uint32_array(nvl,
    616 			    TOPO_PROP_VAL_VAL, (uint32_t *)val, nelems);
    617 			break;
    618 		case TOPO_TYPE_INT64_ARRAY:
    619 			ret |= nvlist_add_int64_array(nvl,
    620 			    TOPO_PROP_VAL_VAL, (int64_t *)val, nelems);
    621 			break;
    622 		case TOPO_TYPE_UINT64_ARRAY:
    623 			ret |= nvlist_add_uint64_array(nvl,
    624 			    TOPO_PROP_VAL_VAL, (uint64_t *)val, nelems);
    625 			break;
    626 		case TOPO_TYPE_STRING_ARRAY:
    627 			ret |= nvlist_add_string_array(nvl,
    628 			    TOPO_PROP_VAL_VAL, (char **)val, nelems);
    629 			break;
    630 		case TOPO_TYPE_FMRI_ARRAY:
    631 			ret |= nvlist_add_nvlist_array(nvl,
    632 			    TOPO_PROP_VAL_VAL, (nvlist_t **)val, nelems);
    633 			break;
    634 		default:
    635 			*err = ETOPO_PROP_TYPE;
    636 			return (-1);
    637 	}
    638 
    639 	if (ret != 0) {
    640 		nvlist_free(nvl);
    641 		if (ret == ENOMEM) {
    642 			*err = ETOPO_PROP_NOMEM;
    643 			return (-1);
    644 		} else {
    645 			*err = ETOPO_PROP_NVL;
    646 			return (-1);
    647 		}
    648 	}
    649 
    650 	if (topo_prop_setprop(node, pgname, nvl, flag, nvl, err) != 0) {
    651 		nvlist_free(nvl);
    652 		return (-1); /* err set */
    653 	}
    654 	nvlist_free(nvl);
    655 	return (ret);
    656 }
    657 
    658 int
    659 topo_prop_set_int32(tnode_t *node, const char *pgname, const char *pname,
    660     int flag, int32_t val, int *err)
    661 {
    662 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32, flag,
    663 	    &val, 1, err));
    664 }
    665 
    666 int
    667 topo_prop_set_uint32(tnode_t *node, const char *pgname, const char *pname,
    668     int flag, uint32_t val, int *err)
    669 {
    670 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32, flag,
    671 	    &val, 1, err));
    672 }
    673 
    674 int
    675 topo_prop_set_int64(tnode_t *node, const char *pgname, const char *pname,
    676     int flag, int64_t val, int *err)
    677 {
    678 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64, flag,
    679 	    &val, 1, err));
    680 }
    681 
    682 int
    683 topo_prop_set_uint64(tnode_t *node, const char *pgname, const char *pname,
    684     int flag, uint64_t val, int *err)
    685 {
    686 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64, flag,
    687 	    &val, 1, err));
    688 }
    689 
    690 int
    691 topo_prop_set_double(tnode_t *node, const char *pgname, const char *pname,
    692     int flag, double val, int *err)
    693 {
    694 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_DOUBLE, flag,
    695 	    &val, 1, err));
    696 }
    697 
    698 int
    699 topo_prop_set_string(tnode_t *node, const char *pgname, const char *pname,
    700     int flag, const char *val, int *err)
    701 {
    702 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING, flag,
    703 	    (void *)val, 1, err));
    704 }
    705 
    706 int
    707 topo_prop_set_fmri(tnode_t *node, const char *pgname, const char *pname,
    708     int flag, const nvlist_t *fmri, int *err)
    709 {
    710 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI, flag,
    711 	    (void *)fmri, 1, err));
    712 }
    713 
    714 int
    715 topo_prop_set_int32_array(tnode_t *node, const char *pgname, const char *pname,
    716     int flag, int32_t *val, uint_t nelems, int *err)
    717 {
    718 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32_ARRAY, flag,
    719 	    val, nelems, err));
    720 }
    721 
    722 int
    723 topo_prop_set_uint32_array(tnode_t *node, const char *pgname, const char *pname,
    724     int flag, uint32_t *val, uint_t nelems, int *err)
    725 {
    726 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32_ARRAY, flag,
    727 	    val, nelems, err));
    728 }
    729 
    730 int
    731 topo_prop_set_int64_array(tnode_t *node, const char *pgname, const char *pname,
    732     int flag, int64_t *val, uint_t nelems, int *err)
    733 {
    734 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64_ARRAY, flag,
    735 	    val, nelems, err));
    736 }
    737 
    738 int
    739 topo_prop_set_uint64_array(tnode_t *node, const char *pgname, const char *pname,
    740     int flag, uint64_t *val, uint_t nelems, int *err)
    741 {
    742 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64_ARRAY, flag,
    743 	    val, nelems, err));
    744 }
    745 
    746 int
    747 topo_prop_set_string_array(tnode_t *node, const char *pgname, const char *pname,
    748     int flag, const char **val, uint_t nelems, int *err)
    749 {
    750 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING_ARRAY, flag,
    751 	    (void *)val, nelems, err));
    752 }
    753 
    754 int
    755 topo_prop_set_fmri_array(tnode_t *node, const char *pgname, const char *pname,
    756     int flag, const nvlist_t **fmri, uint_t nelems, int *err)
    757 {
    758 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI_ARRAY, flag,
    759 	    (void *)fmri, nelems, err));
    760 }
    761 
    762 /*
    763  * topo_prop_setprop() is a private project function for fmtopo
    764  */
    765 int
    766 topo_prop_setprop(tnode_t *node, const char *pgname, nvlist_t *prop,
    767     int flag, nvlist_t *pargs, int *err)
    768 {
    769 	int ret;
    770 	topo_hdl_t *thp = node->tn_hdl;
    771 	topo_propval_t *pv;
    772 	nvlist_t *nvl, *args;
    773 	char *name;
    774 	topo_type_t type;
    775 
    776 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_NAME, &name) != 0) {
    777 		*err = ETOPO_PROP_NAME;
    778 		return (-1);
    779 	}
    780 	if (nvlist_lookup_uint32(prop, TOPO_PROP_VAL_TYPE, (uint32_t *)&type)
    781 	    != 0) {
    782 		*err = ETOPO_PROP_TYPE;
    783 		return (-1);
    784 	}
    785 
    786 	topo_node_lock(node);
    787 	if ((pv = prop_create(node, pgname, name, type, flag, err)) == NULL)
    788 		return (-1); /* unlocked and err set */
    789 
    790 	/*
    791 	 * Set by method or set to new prop value.  If we fail, leave
    792 	 * property in list with old value.
    793 	 */
    794 	if (pv->tp_method != NULL) {
    795 		topo_propmethod_t *pm = pv->tp_method;
    796 
    797 		if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0) {
    798 			topo_node_unlock(node);
    799 			*err = ETOPO_PROP_NOMEM;
    800 			return (-1);
    801 		}
    802 		ret = nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args);
    803 		if (pargs != NULL)
    804 			ret |= nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs);
    805 
    806 		if (ret != 0) {
    807 			topo_node_unlock(node);
    808 			nvlist_free(args);
    809 			*err = ETOPO_PROP_NVL;
    810 			return (-1);
    811 		}
    812 
    813 		/*
    814 		 *
    815 		 * Grab a reference to the property and then unlock the node.
    816 		 * This will allow property methods to safely re-enter the
    817 		 * prop_get codepath, making it possible for property methods
    818 		 * to access other property values on the same node w\o causing
    819 		 * a deadlock.
    820 		 *
    821 		 * We don't technically need this now, since this interface is
    822 		 * currently only used by fmtopo (which is single-threaded), but
    823 		 * we may make this interface available to other parts of
    824 		 * libtopo in the future, so best to make it MT-safe now.
    825 		 */
    826 		topo_prop_hold(pv);
    827 		topo_node_unlock(node);
    828 		ret = topo_method_call(node, pm->tpm_name, pm->tpm_version,
    829 		    args, &nvl, err);
    830 		topo_node_lock(node);
    831 		topo_prop_rele(pv);
    832 
    833 		nvlist_free(args);
    834 	} else {
    835 		if ((ret = topo_hdl_nvdup(thp, prop, &nvl)) != 0)
    836 			*err = ETOPO_PROP_NOMEM;
    837 	}
    838 
    839 	if (ret != 0) {
    840 		topo_node_unlock(node);
    841 		return (-1);
    842 	}
    843 
    844 	pv->tp_val = nvl;
    845 	topo_node_unlock(node);
    846 	return (0);
    847 }
    848 
    849 static int
    850 register_methoderror(tnode_t *node, topo_propmethod_t *pm, int *errp, int l,
    851     int err)
    852 {
    853 	topo_hdl_t *thp = node->tn_hdl;
    854 
    855 	if (pm != NULL) {
    856 		if (pm->tpm_name != NULL)
    857 			topo_hdl_strfree(thp, pm->tpm_name);
    858 		if (pm->tpm_args != NULL)
    859 			nvlist_free(pm->tpm_args);
    860 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
    861 	}
    862 
    863 	*errp = err;
    864 
    865 	if (l != 0)
    866 		topo_node_unlock(node);
    867 
    868 	return (-1);
    869 }
    870 
    871 int
    872 prop_method_register(tnode_t *node, const char *pgname, const char *pname,
    873     topo_type_t ptype, const char *mname, topo_version_t version,
    874     const nvlist_t *args, int *err)
    875 {
    876 	topo_hdl_t *thp = node->tn_hdl;
    877 	topo_propmethod_t *pm = NULL;
    878 	topo_propval_t *pv = NULL;
    879 
    880 	if ((pm = topo_hdl_zalloc(thp, sizeof (topo_propmethod_t))) == NULL)
    881 		return (register_methoderror(node, pm, err, 1,
    882 		    ETOPO_PROP_NOMEM));
    883 
    884 	if ((pm->tpm_name = topo_hdl_strdup(thp, mname)) == NULL)
    885 		return (register_methoderror(node, pm, err, 1,
    886 		    ETOPO_PROP_NOMEM));
    887 
    888 	pm->tpm_version = version;
    889 
    890 	if (topo_hdl_nvdup(thp, (nvlist_t *)args, &pm->tpm_args) != 0)
    891 		return (register_methoderror(node, pm, err, 1,
    892 		    ETOPO_PROP_NOMEM));
    893 
    894 	/*
    895 	 * It's possible the property may already exist.  However we still want
    896 	 * to allow the method to be registered.  This is to handle the case
    897 	 * where we specify a prop method in an xml map to override the value
    898 	 * that was set by the enumerator.
    899 	 *
    900 	 * By default, propmethod-backed properties are not MUTABLE.  This is
    901 	 * done to simplify the programming model for modules that implement
    902 	 * property methods as most propmethods tend to only support get
    903 	 * operations.  Enumerator modules can override this by calling
    904 	 * topo_prop_setmutable().  Propmethods that are registered via XML can
    905 	 * be set as mutable via the optional "mutable" attribute, which will
    906 	 * result in the xml parser calling topo_prop_setflags() after
    907 	 * registering the propmethod.
    908 	 */
    909 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL)
    910 		if ((pv = prop_create(node, pgname, pname, ptype,
    911 		    TOPO_PROP_IMMUTABLE, err)) == NULL) {
    912 			/* node unlocked */
    913 			return (register_methoderror(node, pm, err, 0, *err));
    914 		}
    915 
    916 	if (pv->tp_method != NULL)
    917 		return (register_methoderror(node, pm, err, 1,
    918 		    ETOPO_METHOD_DEFD));
    919 
    920 	if (pv->tp_val != NULL) {
    921 		nvlist_free(pv->tp_val);
    922 		pv->tp_val = NULL;
    923 	}
    924 	pv->tp_method = pm;
    925 
    926 	topo_node_unlock(node);
    927 
    928 	return (0);
    929 }
    930 
    931 int
    932 topo_prop_method_register(tnode_t *node, const char *pgname, const char *pname,
    933     topo_type_t ptype, const char *mname, const nvlist_t *args, int *err)
    934 {
    935 	topo_imethod_t *mp;
    936 
    937 	topo_node_lock(node);
    938 
    939 	if ((mp = topo_method_lookup(node, mname)) == NULL)
    940 		return (register_methoderror(node, NULL, err, 1,
    941 		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
    942 
    943 	topo_node_lock(node);
    944 
    945 	return (prop_method_register(node, pgname, pname, ptype, mname,
    946 	    mp->tim_version, args, err)); /* err set and node unlocked */
    947 }
    948 
    949 int
    950 topo_prop_method_version_register(tnode_t *node, const char *pgname,
    951     const char *pname, topo_type_t ptype, const char *mname,
    952     topo_version_t version, const nvlist_t *args, int *err)
    953 {
    954 	topo_imethod_t *mp;
    955 
    956 	topo_node_lock(node);
    957 
    958 	if ((mp = topo_method_lookup(node, mname)) == NULL)
    959 		return (register_methoderror(node, NULL, err, 1,
    960 		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
    961 
    962 	topo_node_lock(node);
    963 
    964 	if (version < mp->tim_version)
    965 		return (register_methoderror(node, NULL, err, 1,
    966 		    ETOPO_METHOD_VEROLD));
    967 	if (version > mp->tim_version)
    968 		return (register_methoderror(node, NULL, err, 1,
    969 		    ETOPO_METHOD_VERNEW));
    970 
    971 	return (prop_method_register(node, pgname, pname, ptype, mname,
    972 	    version, args, err)); /* err set and node unlocked */
    973 }
    974 
    975 void
    976 topo_prop_method_unregister(tnode_t *node, const char *pgname,
    977     const char *pname)
    978 {
    979 	topo_propval_t *pv;
    980 	topo_pgroup_t *pg;
    981 	topo_proplist_t *pvl;
    982 	topo_hdl_t *thp = node->tn_hdl;
    983 
    984 	topo_node_lock(node);
    985 
    986 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
    987 	    pg = topo_list_next(pg)) {
    988 		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
    989 			break;
    990 		}
    991 	}
    992 
    993 	if (pg == NULL) {
    994 		topo_node_unlock(node);
    995 		return;
    996 	}
    997 
    998 	for (pvl = topo_list_next(&pg->tpg_list); pvl != NULL;
    999 	    pvl = topo_list_next(pvl)) {
   1000 		pv = pvl->tp_pval;
   1001 		if (strcmp(pv->tp_name, pname) == 0) {
   1002 			topo_list_delete(&pg->tpg_pvals, pvl);
   1003 			assert(pv->tp_refs == 1);
   1004 			topo_prop_rele(pv);
   1005 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
   1006 			break;
   1007 		}
   1008 	}
   1009 
   1010 	topo_node_unlock(node);
   1011 }
   1012 
   1013 int
   1014 topo_prop_setmutable(tnode_t *node, const char *pgname, const char *pname,
   1015     int *err)
   1016 {
   1017 	topo_propval_t *pv = NULL;
   1018 
   1019 	topo_node_lock(node);
   1020 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
   1021 		topo_node_unlock(node);
   1022 		*err = ETOPO_PROP_NOENT;
   1023 		return (-1);
   1024 	}
   1025 
   1026 	/*
   1027 	 * If the property is being inherited then we don't want to allow a
   1028 	 * change from IMMUTABLE to MUTABLE.
   1029 	 */
   1030 	if (pv->tp_refs > 1) {
   1031 		topo_node_unlock(node);
   1032 		*err = ETOPO_PROP_DEFD;
   1033 		return (-1);
   1034 	}
   1035 	pv->tp_flag |= TOPO_PROP_MUTABLE;
   1036 
   1037 	topo_node_unlock(node);
   1038 
   1039 	return (0);
   1040 }
   1041 int
   1042 topo_prop_setnonvolatile(tnode_t *node, const char *pgname, const char *pname,
   1043     int *err)
   1044 {
   1045 	topo_propval_t *pv = NULL;
   1046 
   1047 	topo_node_lock(node);
   1048 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
   1049 		topo_node_unlock(node);
   1050 		*err = ETOPO_PROP_NOENT;
   1051 		return (-1);
   1052 	}
   1053 
   1054 	pv->tp_flag |= TOPO_PROP_NONVOLATILE;
   1055 
   1056 	topo_node_unlock(node);
   1057 
   1058 	return (0);
   1059 }
   1060 
   1061 static int
   1062 inherit_seterror(tnode_t *node, int *errp, int err)
   1063 {
   1064 	topo_node_unlock(node);
   1065 	topo_node_unlock(node->tn_parent);
   1066 
   1067 	*errp = err;
   1068 
   1069 	return (-1);
   1070 }
   1071 
   1072 int
   1073 topo_prop_inherit(tnode_t *node, const char *pgname, const char *name, int *err)
   1074 {
   1075 	topo_hdl_t *thp = node->tn_hdl;
   1076 	tnode_t *pnode = node->tn_parent;
   1077 	topo_pgroup_t *pg;
   1078 	topo_propval_t *pv;
   1079 	topo_proplist_t *pvl;
   1080 
   1081 	topo_node_lock(pnode);
   1082 	topo_node_lock(node);
   1083 
   1084 	/*
   1085 	 * Check if the requested property group and prop val are already set
   1086 	 * on the node.
   1087 	 */
   1088 	if (propval_get(pgroup_get(node, pgname), name) != NULL)
   1089 		return (inherit_seterror(node, err, ETOPO_PROP_DEFD));
   1090 
   1091 	/*
   1092 	 * Check if the requested property group and prop val exists on the
   1093 	 * parent node
   1094 	 */
   1095 	if ((pv = propval_get(pgroup_get(pnode, pgname), name)) == NULL)
   1096 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
   1097 
   1098 	/*
   1099 	 * Can this propval be inherited?
   1100 	 */
   1101 	if (pv->tp_flag & TOPO_PROP_MUTABLE)
   1102 		return (inherit_seterror(node, err, ETOPO_PROP_NOINHERIT));
   1103 
   1104 	/*
   1105 	 * Property group should already exist: bump the ref count for this
   1106 	 * propval and add it to the node's property group
   1107 	 */
   1108 	if ((pg = pgroup_get(node, pgname)) == NULL)
   1109 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
   1110 
   1111 	if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
   1112 	    == NULL)
   1113 		return (inherit_seterror(node, err, ETOPO_NOMEM));
   1114 
   1115 	topo_prop_hold(pv);
   1116 	pvl->tp_pval = pv;
   1117 	topo_list_append(&pg->tpg_pvals, pvl);
   1118 
   1119 	topo_node_unlock(node);
   1120 	topo_node_unlock(pnode);
   1121 
   1122 	return (0);
   1123 }
   1124 
   1125 topo_pgroup_info_t *
   1126 topo_pgroup_info(tnode_t *node, const char *pgname, int *err)
   1127 {
   1128 	topo_hdl_t *thp = node->tn_hdl;
   1129 	topo_pgroup_t *pg;
   1130 	topo_ipgroup_info_t *pip;
   1131 	topo_pgroup_info_t *info;
   1132 
   1133 	topo_node_lock(node);
   1134 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
   1135 	    pg = topo_list_next(pg)) {
   1136 		if (strcmp(pgname, pg->tpg_info->tpi_name) == 0) {
   1137 			if ((info = topo_hdl_alloc(thp,
   1138 			    sizeof (topo_pgroup_info_t))) == NULL)
   1139 				return (NULL);
   1140 
   1141 			pip = pg->tpg_info;
   1142 			if ((info->tpi_name =
   1143 			    topo_hdl_strdup(thp, pip->tpi_name)) == NULL) {
   1144 				*err = ETOPO_PROP_NOMEM;
   1145 				topo_hdl_free(thp, info,
   1146 				    sizeof (topo_pgroup_info_t));
   1147 				topo_node_unlock(node);
   1148 				return (NULL);
   1149 			}
   1150 			info->tpi_namestab = pip->tpi_namestab;
   1151 			info->tpi_datastab = pip->tpi_datastab;
   1152 			info->tpi_version = pip->tpi_version;
   1153 			topo_node_unlock(node);
   1154 			return (info);
   1155 		}
   1156 	}
   1157 
   1158 	*err = ETOPO_PROP_NOENT;
   1159 	topo_node_unlock(node);
   1160 	return (NULL);
   1161 }
   1162 
   1163 static int
   1164 pgroup_seterr(tnode_t *node, topo_pgroup_t *pg, topo_ipgroup_info_t *pip,
   1165     int *err)
   1166 {
   1167 	topo_hdl_t *thp = node->tn_hdl;
   1168 
   1169 	if (pip != NULL) {
   1170 		if (pip->tpi_name != NULL)
   1171 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
   1172 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
   1173 	}
   1174 
   1175 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
   1176 	*err = ETOPO_NOMEM;
   1177 
   1178 	topo_node_unlock(node);
   1179 
   1180 	return (-1);
   1181 }
   1182 
   1183 int
   1184 topo_pgroup_create(tnode_t *node, const topo_pgroup_info_t *pinfo, int *err)
   1185 {
   1186 	topo_pgroup_t *pg;
   1187 	topo_ipgroup_info_t *pip;
   1188 	topo_hdl_t *thp = node->tn_hdl;
   1189 
   1190 	*err = 0;
   1191 
   1192 	topo_node_lock(node);
   1193 	/*
   1194 	 * Check for an existing pgroup
   1195 	 */
   1196 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
   1197 	    pg = topo_list_next(pg)) {
   1198 		if (strcmp(pg->tpg_info->tpi_name, pinfo->tpi_name) == 0) {
   1199 			*err = ETOPO_PROP_DEFD;
   1200 			topo_node_unlock(node);
   1201 			return (-1);
   1202 		}
   1203 	}
   1204 
   1205 	if ((pg = topo_hdl_zalloc(thp, sizeof (topo_pgroup_t))) == NULL) {
   1206 		*err = ETOPO_NOMEM;
   1207 		topo_node_unlock(node);
   1208 		return (-1);
   1209 	}
   1210 
   1211 	if ((pip = topo_hdl_zalloc(thp, sizeof (topo_ipgroup_info_t)))
   1212 	    == NULL)
   1213 		return (pgroup_seterr(node, pg, pip, err));
   1214 
   1215 	if ((pip->tpi_name = topo_hdl_strdup(thp, pinfo->tpi_name))
   1216 	    == NULL)
   1217 		return (pgroup_seterr(node, pg, pip, err));
   1218 
   1219 	pip->tpi_namestab = pinfo->tpi_namestab;
   1220 	pip->tpi_datastab = pinfo->tpi_datastab;
   1221 	pip->tpi_version = pinfo->tpi_version;
   1222 
   1223 	pg->tpg_info = pip;
   1224 
   1225 	topo_list_append(&node->tn_pgroups, pg);
   1226 	topo_node_unlock(node);
   1227 
   1228 	return (0);
   1229 }
   1230 
   1231 void
   1232 topo_pgroup_destroy(tnode_t *node, const char *pname)
   1233 {
   1234 	topo_hdl_t *thp = node->tn_hdl;
   1235 	topo_pgroup_t *pg;
   1236 	topo_proplist_t *pvl;
   1237 	topo_ipgroup_info_t *pip;
   1238 
   1239 	topo_node_lock(node);
   1240 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
   1241 	    pg = topo_list_next(pg)) {
   1242 		if (strcmp(pg->tpg_info->tpi_name, pname) == 0) {
   1243 			break;
   1244 		}
   1245 	}
   1246 
   1247 	if (pg == NULL) {
   1248 		topo_node_unlock(node);
   1249 		return;
   1250 	}
   1251 
   1252 	while ((pvl = topo_list_next(&pg->tpg_list)) != NULL) {
   1253 		topo_list_delete(&pg->tpg_pvals, pvl);
   1254 		topo_prop_rele(pvl->tp_pval);
   1255 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
   1256 	}
   1257 
   1258 	topo_list_delete(&node->tn_pgroups, pg);
   1259 	topo_node_unlock(node);
   1260 
   1261 	pip = pg->tpg_info;
   1262 	if (pip != NULL) {
   1263 		if (pip->tpi_name != NULL)
   1264 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
   1265 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
   1266 	}
   1267 
   1268 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
   1269 }
   1270 
   1271 void
   1272 topo_pgroup_destroy_all(tnode_t *node)
   1273 {
   1274 	topo_hdl_t *thp = node->tn_hdl;
   1275 	topo_pgroup_t *pg;
   1276 	topo_proplist_t *pvl;
   1277 	topo_ipgroup_info_t *pip;
   1278 
   1279 	topo_node_lock(node);
   1280 	while ((pg = topo_list_next(&node->tn_pgroups)) != NULL) {
   1281 		while ((pvl = topo_list_next(&pg->tpg_pvals)) != NULL) {
   1282 			topo_list_delete(&pg->tpg_pvals, pvl);
   1283 			topo_prop_rele(pvl->tp_pval);
   1284 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
   1285 		}
   1286 
   1287 		topo_list_delete(&node->tn_pgroups, pg);
   1288 
   1289 		pip = pg->tpg_info;
   1290 		if (pip != NULL) {
   1291 			if (pip->tpi_name != NULL)
   1292 				topo_hdl_strfree(thp, (char *)pip->tpi_name);
   1293 			topo_hdl_free(thp, pip, sizeof (topo_pgroup_info_t));
   1294 		}
   1295 
   1296 		topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
   1297 	}
   1298 	topo_node_unlock(node);
   1299 }
   1300 
   1301 static void
   1302 propmethod_destroy(topo_hdl_t *thp, topo_propval_t *pv)
   1303 {
   1304 	topo_propmethod_t *pm;
   1305 
   1306 	pm = pv->tp_method;
   1307 	if (pm != NULL) {
   1308 		if (pm->tpm_name != NULL)
   1309 			topo_hdl_strfree(thp, pm->tpm_name);
   1310 		if (pm->tpm_args != NULL)
   1311 			nvlist_free(pm->tpm_args);
   1312 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
   1313 		pv->tp_method = NULL;
   1314 	}
   1315 }
   1316 
   1317 static void
   1318 topo_propval_destroy(topo_propval_t *pv)
   1319 {
   1320 	topo_hdl_t *thp;
   1321 
   1322 	if (pv == NULL)
   1323 		return;
   1324 
   1325 	thp = pv->tp_hdl;
   1326 
   1327 	if (pv->tp_name != NULL)
   1328 		topo_hdl_strfree(thp, pv->tp_name);
   1329 
   1330 	if (pv->tp_val != NULL)
   1331 		nvlist_free(pv->tp_val);
   1332 
   1333 	propmethod_destroy(thp, pv);
   1334 
   1335 	topo_hdl_free(thp, pv, sizeof (topo_propval_t));
   1336 }
   1337 
   1338 void
   1339 topo_prop_hold(topo_propval_t *pv)
   1340 {
   1341 	pv->tp_refs++;
   1342 }
   1343 
   1344 void
   1345 topo_prop_rele(topo_propval_t *pv)
   1346 {
   1347 	pv->tp_refs--;
   1348 
   1349 	assert(pv->tp_refs >= 0);
   1350 
   1351 	if (pv->tp_refs == 0)
   1352 		topo_propval_destroy(pv);
   1353 }
   1354 
   1355 /*
   1356  * topo_prop_getprop() and topo_prop_getprops() are private project functions
   1357  * for fmtopo
   1358  */
   1359 int
   1360 topo_prop_getprop(tnode_t *node, const char *pgname, const char *pname,
   1361     nvlist_t *args, nvlist_t **prop, int *err)
   1362 {
   1363 	topo_hdl_t *thp = node->tn_hdl;
   1364 	topo_propval_t *pv;
   1365 
   1366 	topo_node_lock(node);
   1367 	if ((pv = prop_get(node, pgname, pname, args, err)) == NULL) {
   1368 		(void) get_properror(node, err, *err);
   1369 		return (-1);
   1370 	}
   1371 
   1372 	if (topo_hdl_nvdup(thp, pv->tp_val, prop) != 0) {
   1373 		(void) get_properror(node, err, ETOPO_NOMEM);
   1374 		return (-1);
   1375 	}
   1376 	topo_node_unlock(node);
   1377 
   1378 	return (0);
   1379 }
   1380 
   1381 static int
   1382 prop_val_add(tnode_t *node, nvlist_t **nvl, topo_propval_t *pv, int *err)
   1383 {
   1384 	if (pv->tp_method != NULL)
   1385 		if (prop_method_get(node, pv, pv->tp_method, NULL, err) < 0)
   1386 			return (-1);
   1387 
   1388 	if (pv->tp_val == NULL) {
   1389 		*err = ETOPO_PROP_NOENT;
   1390 		return (-1);
   1391 	}
   1392 
   1393 	if (topo_hdl_nvdup(pv->tp_hdl, pv->tp_val, nvl) != 0) {
   1394 		*err = ETOPO_PROP_NOMEM;
   1395 		return (-1);
   1396 	}
   1397 
   1398 	return (0);
   1399 }
   1400 
   1401 static int
   1402 get_pgrp_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
   1403 {
   1404 	topo_node_unlock(node);
   1405 
   1406 	if (nvl != NULL)
   1407 		nvlist_free(nvl);
   1408 
   1409 	*errp = err;
   1410 
   1411 	return (-1);
   1412 }
   1413 
   1414 int
   1415 topo_prop_getpgrp(tnode_t *node, const char *pgname, nvlist_t **pgrp,
   1416     int *err)
   1417 {
   1418 	int ret;
   1419 	topo_hdl_t *thp = node->tn_hdl;
   1420 	nvlist_t *nvl, *pvnvl;
   1421 	topo_pgroup_t *pg;
   1422 	topo_propval_t *pv;
   1423 	topo_proplist_t *pvl;
   1424 
   1425 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
   1426 		*err = ETOPO_NOMEM;
   1427 		return (-1);
   1428 	}
   1429 
   1430 	topo_node_lock(node);
   1431 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
   1432 	    pg = topo_list_next(pg)) {
   1433 
   1434 		if (strcmp(pgname, pg->tpg_info->tpi_name) != 0)
   1435 			continue;
   1436 
   1437 		if (nvlist_add_string(nvl, TOPO_PROP_GROUP_NAME,
   1438 		    pg->tpg_info->tpi_name) != 0 ||
   1439 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_NSTAB,
   1440 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
   1441 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_DSTAB,
   1442 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
   1443 		    nvlist_add_int32(nvl, TOPO_PROP_GROUP_VERSION,
   1444 		    pg->tpg_info->tpi_version) != 0)
   1445 			return (get_pgrp_seterror(node, nvl, err,
   1446 			    ETOPO_PROP_NVL));
   1447 
   1448 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
   1449 		    pvl = topo_list_next(pvl)) {
   1450 
   1451 			pv = pvl->tp_pval;
   1452 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
   1453 				return (get_pgrp_seterror(node, nvl, err,
   1454 				    *err));
   1455 			}
   1456 			if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_VAL,
   1457 			    pvnvl)) != 0) {
   1458 				nvlist_free(pvnvl);
   1459 				return (get_pgrp_seterror(node, nvl, err, ret));
   1460 			}
   1461 
   1462 			nvlist_free(pvnvl);
   1463 		}
   1464 		topo_node_unlock(node);
   1465 		*pgrp = nvl;
   1466 		return (0);
   1467 	}
   1468 
   1469 	topo_node_unlock(node);
   1470 	*err = ETOPO_PROP_NOENT;
   1471 	return (-1);
   1472 }
   1473 
   1474 static nvlist_t *
   1475 get_all_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
   1476 {
   1477 	topo_node_unlock(node);
   1478 
   1479 	if (nvl != NULL)
   1480 		nvlist_free(nvl);
   1481 
   1482 	*errp = err;
   1483 
   1484 	return (NULL);
   1485 }
   1486 
   1487 nvlist_t *
   1488 topo_prop_getprops(tnode_t *node, int *err)
   1489 {
   1490 	int ret;
   1491 	topo_hdl_t *thp = node->tn_hdl;
   1492 	nvlist_t *nvl, *pgnvl, *pvnvl;
   1493 	topo_pgroup_t *pg;
   1494 	topo_propval_t *pv;
   1495 	topo_proplist_t *pvl;
   1496 
   1497 	topo_node_lock(node);
   1498 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
   1499 		return (get_all_seterror(node, NULL, err, ETOPO_NOMEM));
   1500 	}
   1501 
   1502 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
   1503 	    pg = topo_list_next(pg)) {
   1504 		if (topo_hdl_nvalloc(thp, &pgnvl, 0) != 0)
   1505 			return (get_all_seterror(node, nvl, err, ETOPO_NOMEM));
   1506 
   1507 		if (nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NAME,
   1508 		    pg->tpg_info->tpi_name) != 0 ||
   1509 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NSTAB,
   1510 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
   1511 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_DSTAB,
   1512 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
   1513 		    nvlist_add_int32(pgnvl, TOPO_PROP_GROUP_VERSION,
   1514 		    pg->tpg_info->tpi_version) != 0)
   1515 			return (get_all_seterror(node, nvl, err,
   1516 			    ETOPO_PROP_NVL));
   1517 
   1518 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
   1519 		    pvl = topo_list_next(pvl)) {
   1520 
   1521 			pv = pvl->tp_pval;
   1522 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
   1523 				nvlist_free(pgnvl);
   1524 				return (get_all_seterror(node, nvl, err, *err));
   1525 			}
   1526 			if ((ret = nvlist_add_nvlist(pgnvl, TOPO_PROP_VAL,
   1527 			    pvnvl)) != 0) {
   1528 				nvlist_free(pgnvl);
   1529 				nvlist_free(pvnvl);
   1530 				return (get_all_seterror(node, nvl, err, ret));
   1531 			}
   1532 
   1533 			nvlist_free(pvnvl);
   1534 		}
   1535 		if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_GROUP, pgnvl))
   1536 		    != 0) {
   1537 			nvlist_free(pgnvl);
   1538 			return (get_all_seterror(node, nvl, err, ret));
   1539 		}
   1540 
   1541 		nvlist_free(pgnvl);
   1542 	}
   1543 
   1544 	topo_node_unlock(node);
   1545 
   1546 	return (nvl);
   1547 }
   1548