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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 /*
  26  * Topology Nodes
  27  *
  28  * Topology nodes, tnode_t, are data structures containing per-FMRI
  29  * information and are linked together to form the topology tree.
  30  * Nodes are created during the enumeration process of topo_snap_hold()
  31  * and destroyed during topo_snap_rele().  For the most part, tnode_t data
  32  * is read-only and no lock protection is required.  Nodes are
  33  * held in place during tree walk functions.  Tree walk functions
  34  * may access node data safely without locks.  The exception to this rule
  35  * is data associated with node properties (topo_prop.c).  Properties
  36  * may change at anytime and are protected by a per-property locking
  37  * strategy.
  38  *
  39  * Enumerator plugin modules may also safely access topology nodes within their
  40  * scope of operation: the parent node passed into the enumeration op or those
  41  * nodes created by the enumerator.  Enumeration occurs only during
  42  * topo_snap_hold() where a per-topo_hdl_t lock prevents multi-threaded access
  43  * to the topology trees.
  44  *
  45  * Enumerator method operation functions may safely access and change topology
  46  * node property data, and contruct or destroy child nodes for the node
  47  * on which the operation applies.  The method may also be called to destroy
  48  * the node for which the method operation is called.  This permits
  49  * dynamic topology tree snapshots and partial enumerations for branches that
  50  * may not be needed right away.
  51  *
  52  * Node Interfaces
  53  *
  54  * Nodes are created when an enumerator calls topo_node_bind().  Prior to
  55  * calling topo_node_bind(), the enumerator should have reserved a range of
  56  * node instances with topo_node_range_create().  topo_node_range_create()
  57  * does not allocate any node resources but creates the infrastruture
  58  * required for a fully populated topology level.  This allows enumerators
  59  * reading from a <scheme>-topology.xml file to parse the file for a range
  60  * of resources before confirming the existence of a resource via a helper
  61  * plugin.  Only when the resource has been confirmed to exist should
  62  * the node be bound.
  63  *
  64  * Node range and node linkage and unlinkage is performed during enumeration and
  65  * method operations when it is safe to change node hash lists. Nodes and node
  66  * ranges are deallocated when all references to the node have been released:
  67  * last walk completes and topo_snap_rele() is called.
  68  *
  69  * Node Hash/Ranges
  70  *
  71  * Each parent node may have one or more ranges of child nodes.  Each range
  72  * is uniquely named and serves as a hash list of like sibling nodes with
  73  * different instance numbers.  A parent may have more than one node hash
  74  * (child range). If that is the case, the hash lists are strung together to
  75  * form sibling relationships between ranges.  Hash/Ranges are sparsely
  76  * populated with only nodes that have represented resources in the system.
  77  *
  78  *      _________________
  79  *      |               |
  80  *      |   tnode_t     |    -----------------------------
  81  *      |      tn_phash ---> |  topo_nodehash_t          |
  82  *      |     (children)|    |     th_nodearr (instances)|
  83  *      -----------------    |     -------------------   |
  84  *                           |  ---| 0 | 1  | ...| N |   |
  85  *                           |  |  -------------------   |  -------------------
  86  *                           |  |  th_list (siblings) ----->| topo_nodehash_t |
  87  *                           |  |                        |  -------------------
  88  *                           ---|-------------------------
  89  *                              |
  90  *                              v
  91  *                           -----------
  92  *                           | tnode_t |
  93  *                           -----------
  94  *
  95  * Facility Nodes
  96  *
  97  * Facility nodes are always leaf nodes in the topology and represent a FMRI
  98  * sensor or indicator facility for the path to which it is connected.
  99  * Facility nodes are bound to the topology with topo_node_facbind() and
 100  * unbound with topo_node_unbind().
 101  */
 102 
 103 #include <assert.h>
 104 #include <pthread.h>
 105 #include <strings.h>
 106 #include <sys/fm/protocol.h>
 107 #include <topo_alloc.h>
 108 #include <topo_error.h>
 109 #include <topo_list.h>
 110 #include <topo_method.h>
 111 #include <topo_subr.h>
 112 #include <topo_tree.h>
 113 
 114 static topo_pgroup_info_t protocol_pgroup = {
 115         TOPO_PGROUP_PROTOCOL,
 116         TOPO_STABILITY_PRIVATE,
 117         TOPO_STABILITY_PRIVATE,
 118         1
 119 };
 120 
 121 static const topo_pgroup_info_t auth_pgroup = {
 122         FM_FMRI_AUTHORITY,
 123         TOPO_STABILITY_PRIVATE,
 124         TOPO_STABILITY_PRIVATE,
 125         1
 126 };
 127 
 128 static void
 129 topo_node_destroy(tnode_t *node)
 130 {
 131         int i;
 132         tnode_t *pnode = node->tn_parent;
 133         topo_nodehash_t *nhp;
 134         topo_mod_t *hmod, *mod = node->tn_enum;
 135 
 136         if (node == NULL)
 137                 return;
 138 
 139         topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, "destroying node %s=%d\n",
 140             topo_node_name(node), topo_node_instance(node));
 141 
 142         assert(node->tn_refs == 0);
 143 
 144         /*
 145          * If not a root node, remove this node from the parent's node hash
 146          */
 147 
 148         if (!(node->tn_state & TOPO_NODE_ROOT)) {
 149                 topo_node_lock(pnode);
 150 
 151                 nhp = node->tn_phash;
 152                 for (i = 0; i < nhp->th_arrlen; i++) {
 153                         if (node == nhp->th_nodearr[i]) {
 154                                 nhp->th_nodearr[i] = NULL;
 155 
 156                                 /*
 157                                  * Release hold on parent
 158                                  */
 159                                 --pnode->tn_refs;
 160                                 if (pnode->tn_refs == 0)
 161                                         topo_node_destroy(pnode);
 162                         }
 163                 }
 164                 topo_node_unlock(pnode);
 165         }
 166 
 167         topo_node_unlock(node);
 168 
 169         /*
 170          * Allow enumerator to clean-up private data and then release
 171          * ref count
 172          */
 173         if (mod->tm_info->tmi_ops->tmo_release != NULL)
 174                 mod->tm_info->tmi_ops->tmo_release(mod, node);
 175 
 176         topo_method_unregister_all(mod, node);
 177 
 178         /*
 179          * Destroy all node hash lists
 180          */
 181         while ((nhp = topo_list_next(&node->tn_children)) != NULL) {
 182                 for (i = 0; i < nhp->th_arrlen; i++) {
 183                         assert(nhp->th_nodearr[i] == NULL);
 184                 }
 185                 hmod = nhp->th_enum;
 186                 topo_mod_strfree(hmod, nhp->th_name);
 187                 topo_mod_free(hmod, nhp->th_nodearr,
 188                     nhp->th_arrlen * sizeof (tnode_t *));
 189                 topo_list_delete(&node->tn_children, nhp);
 190                 topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t));
 191                 topo_mod_rele(hmod);
 192         }
 193 
 194         /*
 195          * Destroy all property data structures, free the node and release
 196          * the module that created it
 197          */
 198         topo_pgroup_destroy_all(node);
 199         topo_mod_free(mod, node, sizeof (tnode_t));
 200         topo_mod_rele(mod);
 201 }
 202 
 203 void
 204 topo_node_lock(tnode_t *node)
 205 {
 206         (void) pthread_mutex_lock(&node->tn_lock);
 207 }
 208 
 209 void
 210 topo_node_unlock(tnode_t *node)
 211 {
 212         (void) pthread_mutex_unlock(&node->tn_lock);
 213 }
 214 
 215 void
 216 topo_node_hold(tnode_t *node)
 217 {
 218         topo_node_lock(node);
 219         ++node->tn_refs;
 220         topo_node_unlock(node);
 221 }
 222 
 223 void
 224 topo_node_rele(tnode_t *node)
 225 {
 226         topo_node_lock(node);
 227         --node->tn_refs;
 228 
 229         /*
 230          * Ok to remove this node from the topo tree and destroy it
 231          */
 232         if (node->tn_refs == 0)
 233                 topo_node_destroy(node);
 234         else
 235                 topo_node_unlock(node);
 236 }
 237 
 238 char *
 239 topo_node_name(tnode_t *node)
 240 {
 241         return (node->tn_name);
 242 }
 243 
 244 topo_instance_t
 245 topo_node_instance(tnode_t *node)
 246 {
 247         return (node->tn_instance);
 248 }
 249 
 250 tnode_t *
 251 topo_node_parent(tnode_t *node)
 252 {
 253         return (node->tn_parent);
 254 }
 255 
 256 int
 257 topo_node_flags(tnode_t *node)
 258 {
 259         return (node->tn_fflags);
 260 }
 261 
 262 void
 263 topo_node_setspecific(tnode_t *node, void *data)
 264 {
 265         node->tn_priv = data;
 266 }
 267 
 268 void *
 269 topo_node_getspecific(tnode_t *node)
 270 {
 271         return (node->tn_priv);
 272 }
 273 
 274 static int
 275 node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
 276     int err)
 277 {
 278         topo_node_unlock(pnode);
 279 
 280         topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to insert child:"
 281             "%s\n", topo_strerror(err));
 282 
 283         if (nhp != NULL) {
 284                 if (nhp->th_name != NULL)
 285                         topo_mod_strfree(mod, nhp->th_name);
 286                 if (nhp->th_nodearr != NULL) {
 287                         topo_mod_free(mod, nhp->th_nodearr,
 288                             nhp->th_arrlen * sizeof (tnode_t *));
 289                 }
 290                 topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
 291         }
 292 
 293         return (topo_mod_seterrno(mod, err));
 294 }
 295 
 296 int
 297 topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
 298     topo_instance_t min, topo_instance_t max)
 299 {
 300         topo_nodehash_t *nhp;
 301 
 302         topo_node_lock(pnode);
 303 
 304         assert((pnode->tn_state & TOPO_NODE_BOUND) ||
 305             (pnode->tn_state & TOPO_NODE_ROOT));
 306 
 307         for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
 308             nhp = topo_list_next(nhp)) {
 309                 if (strcmp(nhp->th_name, name) == 0)
 310                         return (node_create_seterror(mod, pnode, NULL,
 311                             EMOD_NODE_DUP));
 312         }
 313 
 314         if (min < 0 || max < min)
 315                 return (node_create_seterror(mod, pnode, NULL,
 316                     EMOD_NODE_RANGE));
 317 
 318         if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
 319                 return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
 320 
 321         if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
 322                 return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
 323 
 324         nhp->th_arrlen = max - min + 1;
 325 
 326         if ((nhp->th_nodearr = topo_mod_zalloc(mod,
 327             nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
 328                 return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
 329 
 330         nhp->th_range.tr_min = min;
 331         nhp->th_range.tr_max = max;
 332         nhp->th_enum = mod;
 333         topo_mod_hold(mod);
 334 
 335         /*
 336          * Add these nodes to parent child list
 337          */
 338         topo_list_append(&pnode->tn_children, nhp);
 339         topo_node_unlock(pnode);
 340 
 341         topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
 342             "created node range %s[%d-%d]\n", name, min, max);
 343 
 344         return (0);
 345 }
 346 
 347 void
 348 topo_node_range_destroy(tnode_t *pnode, const char *name)
 349 {
 350         int i;
 351         topo_nodehash_t *nhp;
 352         topo_mod_t *mod;
 353 
 354         topo_node_lock(pnode);
 355         for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
 356             nhp = topo_list_next(nhp)) {
 357                 if (strcmp(nhp->th_name, name) == 0) {
 358                         break;
 359                 }
 360         }
 361 
 362         if (nhp == NULL) {
 363                 topo_node_unlock(pnode);
 364                 return;
 365         }
 366 
 367         for (i = 0; i < nhp->th_arrlen; i++)
 368                 assert(nhp->th_nodearr[i] == NULL);
 369 
 370         topo_list_delete(&pnode->tn_children, nhp);
 371         topo_node_unlock(pnode);
 372 
 373         mod = nhp->th_enum;
 374         if (nhp->th_name != NULL)
 375                 topo_mod_strfree(mod, nhp->th_name);
 376         if (nhp->th_nodearr != NULL) {
 377                 topo_mod_free(mod, nhp->th_nodearr,
 378                     nhp->th_arrlen * sizeof (tnode_t *));
 379         }
 380         topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
 381         topo_mod_rele(mod);
 382 
 383 }
 384 
 385 tnode_t *
 386 topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
 387 {
 388         int h;
 389         tnode_t *node;
 390         topo_nodehash_t *nhp;
 391 
 392         topo_dprintf(pnode->tn_hdl, TOPO_DBG_MODSVC,
 393             "topo_node_lookup: looking for '%s' instance %d\n", name, inst);
 394 
 395         topo_node_lock(pnode);
 396         for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
 397             nhp = topo_list_next(nhp)) {
 398                 if (strcmp(nhp->th_name, name) == 0) {
 399 
 400                         if (inst > nhp->th_range.tr_max ||
 401                             inst < nhp->th_range.tr_min) {
 402                                 topo_node_unlock(pnode);
 403                                 return (NULL);
 404                         }
 405 
 406                         h = topo_node_hash(nhp, inst);
 407                         node = nhp->th_nodearr[h];
 408                         topo_node_unlock(pnode);
 409                         return (node);
 410                 }
 411         }
 412         topo_node_unlock(pnode);
 413 
 414         return (NULL);
 415 }
 416 
 417 int
 418 topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
 419 {
 420         return ((inst - nhp->th_range.tr_min) % nhp->th_arrlen);
 421 }
 422 
 423 static tnode_t *
 424 node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node,
 425     boolean_t pnode_locked, int err)
 426 {
 427         if (pnode_locked)
 428                 topo_node_unlock(pnode);
 429 
 430         (void) topo_mod_seterrno(mod, err);
 431 
 432         if (node == NULL)
 433                 return (NULL);
 434 
 435         topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
 436             "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
 437             node->tn_instance, topo_strerror(err));
 438 
 439         topo_node_lock(node); /* expected to be locked */
 440         topo_node_destroy(node);
 441 
 442         return (NULL);
 443 }
 444 
 445 tnode_t *
 446 topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
 447     topo_instance_t inst, nvlist_t *fmri)
 448 {
 449         int h, err;
 450         tnode_t *node;
 451         topo_nodehash_t *nhp;
 452 
 453         topo_node_lock(pnode);
 454         for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
 455             nhp = topo_list_next(nhp)) {
 456                 if (strcmp(nhp->th_name, name) == 0) {
 457 
 458                         if (inst > nhp->th_range.tr_max ||
 459                             inst < nhp->th_range.tr_min)
 460                                 return (node_bind_seterror(mod, pnode, NULL,
 461                                     B_TRUE, EMOD_NODE_RANGE));
 462 
 463                         h = topo_node_hash(nhp, inst);
 464                         if (nhp->th_nodearr[h] != NULL)
 465                                 return (node_bind_seterror(mod, pnode, NULL,
 466                                     B_TRUE, EMOD_NODE_BOUND));
 467                         else
 468                                 break;
 469 
 470                 }
 471         }
 472 
 473         if (nhp == NULL)
 474                 return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
 475                     EMOD_NODE_NOENT));
 476 
 477         if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
 478                 return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
 479                     EMOD_NOMEM));
 480 
 481         (void) pthread_mutex_init(&node->tn_lock, NULL);
 482 
 483         node->tn_enum = mod;
 484         node->tn_hdl = mod->tm_hdl;
 485         node->tn_parent = pnode;
 486         node->tn_name = nhp->th_name;
 487         node->tn_instance = inst;
 488         node->tn_phash = nhp;
 489         node->tn_refs = 0;
 490 
 491         /* Ref count module that bound this node */
 492         topo_mod_hold(mod);
 493 
 494         if (fmri == NULL)
 495                 return (node_bind_seterror(mod, pnode, node, B_TRUE,
 496                     EMOD_NVL_INVAL));
 497 
 498         if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
 499                 return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
 500 
 501         if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
 502             TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
 503                 return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
 504 
 505         topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
 506             "node bound %s=%d/%s=%d\n", topo_node_name(pnode),
 507             topo_node_instance(pnode), node->tn_name, node->tn_instance);
 508 
 509         node->tn_state |= TOPO_NODE_BOUND;
 510 
 511         topo_node_hold(node);
 512         nhp->th_nodearr[h] = node;
 513         ++pnode->tn_refs;
 514 
 515         topo_node_unlock(pnode);
 516 
 517         if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
 518                 (void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
 519                     FM_FMRI_AUTH_PRODUCT, &err);
 520                 (void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
 521                     FM_FMRI_AUTH_PRODUCT_SN, &err);
 522                 (void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
 523                     FM_FMRI_AUTH_CHASSIS, &err);
 524                 (void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
 525                     FM_FMRI_AUTH_SERVER, &err);
 526         }
 527 
 528         return (node);
 529 }
 530 
 531 tnode_t *
 532 topo_node_facbind(topo_mod_t *mod, tnode_t *pnode, const char *name,
 533     const char *type)
 534 {
 535         int h, err;
 536         tnode_t *node;
 537         topo_nodehash_t *nhp;
 538         topo_instance_t inst = 0;
 539         nvlist_t *pfmri, *fnvl;
 540 
 541         /*
 542          * Create a single entry range for this facility
 543          */
 544         if (topo_node_range_create(mod, pnode, name, 0, 0) < 0)
 545                 return (NULL);  /* mod errno set */
 546 
 547         topo_node_hold(pnode);
 548         topo_node_lock(pnode);
 549         for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
 550             nhp = topo_list_next(nhp)) {
 551                 if (strcmp(nhp->th_name, name) == 0) {
 552 
 553                         if (inst > nhp->th_range.tr_max ||
 554                             inst < nhp->th_range.tr_min) {
 555                                 topo_node_rele(pnode);
 556                                 return (node_bind_seterror(mod, pnode, NULL,
 557                                     B_TRUE, EMOD_NVL_INVAL));
 558                         }
 559                         h = topo_node_hash(nhp, inst);
 560                         if (nhp->th_nodearr[h] != NULL) {
 561                                 topo_node_rele(pnode);
 562                                 return (node_bind_seterror(mod, pnode, NULL,
 563                                     B_TRUE, EMOD_NODE_BOUND));
 564                         } else
 565                                 break;
 566 
 567                 }
 568         }
 569         topo_node_unlock(pnode);
 570 
 571         if (nhp == NULL) {
 572                 topo_node_rele(pnode);
 573                 return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
 574                     EMOD_NODE_NOENT));
 575         }
 576         if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL) {
 577                 topo_node_rele(pnode);
 578                 return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
 579                     EMOD_NOMEM));
 580         }
 581 
 582         (void) pthread_mutex_init(&node->tn_lock, NULL);
 583 
 584         node->tn_enum = mod;
 585         node->tn_hdl = mod->tm_hdl;
 586         node->tn_parent = pnode;
 587         node->tn_name = nhp->th_name;
 588         node->tn_instance = inst;
 589         node->tn_phash = nhp;
 590         node->tn_refs = 0;
 591         node->tn_fflags = TOPO_NODE_FACILITY;
 592 
 593         /* Ref count module that bound this node */
 594         topo_mod_hold(mod);
 595 
 596         if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0) {
 597                 topo_node_rele(pnode);
 598                 return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
 599         }
 600         if (topo_mod_nvalloc(mod, &fnvl, NV_UNIQUE_NAME) < 0) {
 601                 topo_node_rele(pnode);
 602                 return (node_bind_seterror(mod, pnode, node, B_FALSE,
 603                     EMOD_NOMEM));
 604         }
 605         if (nvlist_add_string(fnvl, FM_FMRI_FACILITY_NAME, name) != 0 ||
 606             nvlist_add_string(fnvl, FM_FMRI_FACILITY_TYPE, type) != 0) {
 607                 nvlist_free(fnvl);
 608                 topo_node_rele(pnode);
 609                 return (node_bind_seterror(mod, pnode, node,  B_FALSE,
 610                     EMOD_FMRI_NVL));
 611         }
 612 
 613         if (topo_node_resource(pnode, &pfmri, &err) < 0) {
 614                 nvlist_free(fnvl);
 615                 topo_node_rele(pnode);
 616                 return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
 617         }
 618 
 619         if (nvlist_add_nvlist(pfmri, FM_FMRI_FACILITY, fnvl) != 0) {
 620                 nvlist_free(fnvl);
 621                 nvlist_free(pfmri);
 622                 topo_node_rele(pnode);
 623                 return (node_bind_seterror(mod, pnode, node,  B_FALSE,
 624                     EMOD_FMRI_NVL));
 625         }
 626 
 627         nvlist_free(fnvl);
 628 
 629         if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
 630             TOPO_PROP_IMMUTABLE, pfmri, &err) < 0) {
 631                 nvlist_free(pfmri);
 632                 topo_node_rele(pnode);
 633                 return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
 634         }
 635 
 636         nvlist_free(pfmri);
 637 
 638         topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
 639             "facility node bound %s=%s\n", type, node->tn_name);
 640 
 641         node->tn_state |= TOPO_NODE_BOUND;
 642 
 643         topo_node_hold(node);
 644         nhp->th_nodearr[h] = node;
 645 
 646         topo_node_lock(pnode);
 647         ++pnode->tn_refs;
 648         topo_node_unlock(pnode);
 649         topo_node_rele(pnode);
 650 
 651         if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
 652                 (void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
 653                     FM_FMRI_AUTH_PRODUCT, &err);
 654                 (void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
 655                     FM_FMRI_AUTH_PRODUCT_SN, &err);
 656                 (void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
 657                     FM_FMRI_AUTH_CHASSIS, &err);
 658                 (void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
 659                     FM_FMRI_AUTH_SERVER, &err);
 660         }
 661 
 662         return (node);
 663 }
 664 
 665 int
 666 topo_node_facility(topo_hdl_t *thp, tnode_t *node, const char *fac_type,
 667     uint32_t fac_subtype, topo_faclist_t *faclist, int *errp)
 668 {
 669         tnode_t *tmp;
 670         nvlist_t *rsrc, *fac;
 671         char *tmp_factype;
 672         uint32_t tmp_facsubtype;
 673         boolean_t list_empty = 1;
 674         topo_faclist_t *fac_ele;
 675 
 676         bzero(faclist, sizeof (topo_faclist_t));
 677         for (tmp = topo_child_first(node); tmp != NULL;
 678             tmp = topo_child_next(node, tmp)) {
 679 
 680                 topo_node_hold(tmp);
 681                 /*
 682                  * If it's not a facility node, move on
 683                  */
 684                 if (topo_node_flags(tmp) != TOPO_NODE_FACILITY) {
 685                         topo_node_rele(tmp);
 686                         continue;
 687                 }
 688 
 689                 /*
 690                  * Lookup whether the fac type is sensor or indicator and if
 691                  * it's not the type we're looking for, move on
 692                  */
 693                 if (topo_node_resource(tmp, &rsrc, errp) != 0) {
 694                         topo_dprintf(thp, TOPO_DBG_ERR,
 695                             "Failed to get resource for node %s=%d (%s)\n",
 696                             topo_node_name(node), topo_node_instance(node),
 697                             topo_strerror(*errp));
 698                         topo_node_rele(tmp);
 699                         return (-1);
 700                 }
 701                 if ((nvlist_lookup_nvlist(rsrc, "facility", &fac) != 0) ||
 702                     (nvlist_lookup_string(fac, FM_FMRI_FACILITY_TYPE,
 703                     &tmp_factype) != 0)) {
 704 
 705                         nvlist_free(rsrc);
 706                         topo_node_rele(tmp);
 707                         return (-1);
 708                 }
 709 
 710                 if (strcmp(fac_type, tmp_factype) != 0) {
 711                         topo_node_rele(tmp);
 712                         nvlist_free(rsrc);
 713                         continue;
 714                 }
 715                 nvlist_free(rsrc);
 716 
 717                 /*
 718                  * Finally, look up the subtype, which is a property in the
 719                  * facility propgroup.  If it's a match return a pointer to the
 720                  * node.  Otherwise, move on.
 721                  */
 722                 if (topo_prop_get_uint32(tmp, TOPO_PGROUP_FACILITY,
 723                     TOPO_FACILITY_TYPE, &tmp_facsubtype, errp) != 0) {
 724                         topo_node_rele(tmp);
 725                         return (-1);
 726                 }
 727                 if (fac_subtype == tmp_facsubtype ||
 728                     fac_subtype == TOPO_FAC_TYPE_ANY) {
 729                         if ((fac_ele = topo_mod_zalloc(tmp->tn_enum,
 730                             sizeof (topo_faclist_t))) == NULL) {
 731                                 *errp = ETOPO_NOMEM;
 732                                 topo_node_rele(tmp);
 733                                 return (-1);
 734                         }
 735                         fac_ele->tf_node = tmp;
 736                         topo_list_append(&faclist->tf_list, fac_ele);
 737                         list_empty = 0;
 738                 }
 739                 topo_node_rele(tmp);
 740         }
 741 
 742         if (list_empty) {
 743                 *errp = ETOPO_FAC_NOENT;
 744                 return (-1);
 745         }
 746         return (0);
 747 }
 748 
 749 void
 750 topo_node_unbind(tnode_t *node)
 751 {
 752         if (node == NULL)
 753                 return;
 754 
 755         topo_node_lock(node);
 756         if (!(node->tn_state & TOPO_NODE_BOUND)) {
 757                 topo_node_unlock(node);
 758                 return;
 759         }
 760 
 761         node->tn_state &= ~TOPO_NODE_BOUND;
 762         topo_node_unlock(node);
 763 
 764         topo_dprintf(node->tn_hdl, TOPO_DBG_MODSVC,
 765             "node unbound %s=%d/%s=%d refs = %d\n",
 766             topo_node_name(node->tn_parent),
 767             topo_node_instance(node->tn_parent), node->tn_name,
 768             node->tn_instance, node->tn_refs);
 769 
 770         topo_node_rele(node);
 771 }
 772 
 773 /*ARGSUSED*/
 774 int
 775 topo_node_present(tnode_t *node)
 776 {
 777         return (0);
 778 }
 779 
 780 /*ARGSUSED*/
 781 int
 782 topo_node_contains(tnode_t *er, tnode_t *ee)
 783 {
 784         return (0);
 785 }
 786 
 787 /*ARGSUSED*/
 788 int
 789 topo_node_unusable(tnode_t *node)
 790 {
 791         return (0);
 792 }
 793 
 794 topo_walk_t *
 795 topo_node_walk_init(topo_hdl_t *thp, topo_mod_t *mod, tnode_t *node,
 796     int (*cb_f)(), void *pdata, int *errp)
 797 {
 798         tnode_t *child;
 799         topo_walk_t *wp;
 800 
 801         topo_node_hold(node);
 802 
 803         if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) == NULL) {
 804                 *errp = ETOPO_HDL_NOMEM;
 805                 topo_node_rele(node);
 806                 return (NULL);
 807         }
 808 
 809         /*
 810          * If this is the root of the scheme tree, start with the first
 811          * child
 812          */
 813         topo_node_lock(node);
 814         if (node->tn_state & TOPO_NODE_ROOT) {
 815                 if ((child = topo_child_first(node)) == NULL) {
 816                         /* Nothing to walk */
 817                         *errp = ETOPO_WALK_EMPTY;
 818                         topo_node_unlock(node);
 819                         topo_node_rele(node);
 820                         topo_hdl_free(thp, wp, sizeof (topo_walk_t));
 821                         return (NULL);
 822                 }
 823                 topo_node_unlock(node);
 824                 topo_node_hold(child);
 825                 wp->tw_node = child;
 826         } else {
 827                 topo_node_unlock(node);
 828                 topo_node_hold(node); /* rele at walk end */
 829                 wp->tw_node = node;
 830         }
 831 
 832         wp->tw_root = node;
 833         wp->tw_cb = cb_f;
 834         wp->tw_pdata = pdata;
 835         wp->tw_thp = thp;
 836         wp->tw_mod = mod;
 837 
 838         return (wp);
 839 }
 840 
 841 /*
 842  * Walk the direct children of the given node.
 843  */
 844 int
 845 topo_node_child_walk(topo_hdl_t *thp, tnode_t *pnode, topo_walk_cb_t cb_f,
 846     void *arg, int *errp)
 847 {
 848         int ret = TOPO_WALK_TERMINATE;
 849         tnode_t *cnode;
 850 
 851         topo_node_hold(pnode);
 852 
 853         /*
 854          * First Child:
 855          */
 856         topo_node_lock(pnode);
 857         cnode = topo_child_first(pnode);
 858         topo_node_unlock(pnode);
 859 
 860         if (cnode == NULL) {
 861                 *errp = ETOPO_WALK_EMPTY;
 862                 ret = TOPO_WALK_ERR;
 863                 goto out;
 864         }
 865 
 866         while (cnode != NULL) {
 867                 int iret;
 868 
 869                 /*
 870                  * Call the walker callback:
 871                  */
 872                 topo_node_hold(cnode);
 873                 iret = cb_f(thp, cnode, arg);
 874                 topo_node_rele(cnode);
 875                 if (iret != TOPO_WALK_NEXT) {
 876                         ret = iret;
 877                         break;
 878                 }
 879 
 880                 /*
 881                  * Next child:
 882                  */
 883                 topo_node_lock(pnode);
 884                 cnode = topo_child_next(pnode, cnode);
 885                 topo_node_unlock(pnode);
 886         }
 887 
 888 out:
 889         topo_node_rele(pnode);
 890         return (ret);
 891 }