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 }