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