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