xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_node.c (revision 74e7dc986c89efca1f2e4451c7a572e05e4a6e4f)
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 2008 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, int err)
423 {
424 	topo_node_unlock(pnode);
425 
426 	(void) topo_mod_seterrno(mod, err);
427 
428 	if (node == NULL)
429 		return (NULL);
430 
431 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
432 	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
433 	    node->tn_instance, topo_strerror(err));
434 
435 	topo_node_lock(node); /* expected to be locked */
436 	topo_node_destroy(node);
437 
438 	return (NULL);
439 }
440 
441 tnode_t *
442 topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
443     topo_instance_t inst, nvlist_t *fmri)
444 {
445 	int h, err;
446 	tnode_t *node;
447 	topo_nodehash_t *nhp;
448 
449 	topo_node_lock(pnode);
450 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
451 	    nhp = topo_list_next(nhp)) {
452 		if (strcmp(nhp->th_name, name) == 0) {
453 
454 			if (inst > nhp->th_range.tr_max ||
455 			    inst < nhp->th_range.tr_min)
456 				return (node_bind_seterror(mod, pnode, NULL,
457 				    EMOD_NODE_RANGE));
458 
459 			h = topo_node_hash(nhp, inst);
460 			if (nhp->th_nodearr[h] != NULL)
461 				return (node_bind_seterror(mod, pnode, NULL,
462 				    EMOD_NODE_BOUND));
463 			else
464 				break;
465 
466 		}
467 	}
468 
469 	if (nhp == NULL)
470 		return (node_bind_seterror(mod, pnode, NULL, EMOD_NODE_NOENT));
471 
472 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
473 		return (node_bind_seterror(mod, pnode, NULL, EMOD_NOMEM));
474 
475 	(void) pthread_mutex_init(&node->tn_lock, NULL);
476 
477 	node->tn_enum = mod;
478 	node->tn_hdl = mod->tm_hdl;
479 	node->tn_parent = pnode;
480 	node->tn_name = nhp->th_name;
481 	node->tn_instance = inst;
482 	node->tn_phash = nhp;
483 	node->tn_refs = 0;
484 
485 	/* Ref count module that bound this node */
486 	topo_mod_hold(mod);
487 
488 	if (fmri == NULL)
489 		return (node_bind_seterror(mod, pnode, node, EMOD_NVL_INVAL));
490 
491 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
492 		return (node_bind_seterror(mod, pnode, node, err));
493 
494 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
495 	    TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
496 		return (node_bind_seterror(mod, pnode, node, err));
497 
498 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
499 	    "node bound %s=%d/%s=%d\n", topo_node_name(pnode),
500 	    topo_node_instance(pnode), node->tn_name, node->tn_instance);
501 
502 	node->tn_state |= TOPO_NODE_BOUND;
503 
504 	topo_node_hold(node);
505 	nhp->th_nodearr[h] = node;
506 	++pnode->tn_refs;
507 
508 	topo_node_unlock(pnode);
509 
510 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
511 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
512 		    FM_FMRI_AUTH_PRODUCT, &err);
513 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
514 		    FM_FMRI_AUTH_CHASSIS, &err);
515 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
516 		    FM_FMRI_AUTH_SERVER, &err);
517 	}
518 
519 	return (node);
520 }
521 
522 tnode_t *
523 topo_node_facbind(topo_mod_t *mod, tnode_t *pnode, const char *name,
524     const char *type)
525 {
526 	int h, err;
527 	tnode_t *node;
528 	topo_nodehash_t *nhp;
529 	topo_instance_t inst = 0;
530 	nvlist_t *pfmri, *fnvl;
531 
532 	/*
533 	 * Create a single entry range for this facility
534 	 */
535 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0)
536 		return (NULL);  /* mod errno set */
537 
538 	topo_node_lock(pnode);
539 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
540 	    nhp = topo_list_next(nhp)) {
541 		if (strcmp(nhp->th_name, name) == 0) {
542 
543 			if (inst > nhp->th_range.tr_max ||
544 			    inst < nhp->th_range.tr_min)
545 				return (node_bind_seterror(mod, pnode, NULL,
546 				    EMOD_NVL_INVAL));
547 
548 			h = topo_node_hash(nhp, inst);
549 			if (nhp->th_nodearr[h] != NULL)
550 				return (node_bind_seterror(mod, pnode, NULL,
551 				    EMOD_NODE_BOUND));
552 			else
553 				break;
554 
555 		}
556 	}
557 
558 	if (nhp == NULL)
559 		return (node_bind_seterror(mod, pnode, NULL, EMOD_NODE_NOENT));
560 
561 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
562 		return (node_bind_seterror(mod, pnode, NULL, EMOD_NOMEM));
563 
564 	(void) pthread_mutex_init(&node->tn_lock, NULL);
565 
566 	node->tn_enum = mod;
567 	node->tn_hdl = mod->tm_hdl;
568 	node->tn_parent = pnode;
569 	node->tn_name = nhp->th_name;
570 	node->tn_instance = inst;
571 	node->tn_phash = nhp;
572 	node->tn_refs = 0;
573 	node->tn_fflags = TOPO_NODE_FACILITY;
574 
575 	/* Ref count module that bound this node */
576 	topo_mod_hold(mod);
577 
578 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
579 		return (node_bind_seterror(mod, pnode, node, err));
580 
581 	if (topo_mod_nvalloc(mod, &fnvl, NV_UNIQUE_NAME) < 0)
582 		return (node_bind_seterror(mod, pnode, node, EMOD_NOMEM));
583 	if (nvlist_add_string(fnvl, FM_FMRI_FACILITY_NAME, name) != 0 ||
584 	    nvlist_add_string(fnvl, FM_FMRI_FACILITY_TYPE, type) != 0) {
585 		nvlist_free(fnvl);
586 		return (node_bind_seterror(mod, pnode, node,  EMOD_FMRI_NVL));
587 	}
588 
589 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
590 		nvlist_free(fnvl);
591 		return (node_bind_seterror(mod, pnode, node, err));
592 	}
593 
594 	if (nvlist_add_nvlist(pfmri, FM_FMRI_FACILITY, fnvl) != 0) {
595 		nvlist_free(fnvl);
596 		nvlist_free(pfmri);
597 		return (node_bind_seterror(mod, pnode, node,  EMOD_FMRI_NVL));
598 	}
599 
600 	nvlist_free(fnvl);
601 
602 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
603 	    TOPO_PROP_IMMUTABLE, pfmri, &err) < 0) {
604 		nvlist_free(pfmri);
605 		return (node_bind_seterror(mod, pnode, node, err));
606 	}
607 
608 	nvlist_free(pfmri);
609 
610 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
611 	    "facility node bound %s=%s\n", type, node->tn_name);
612 
613 	node->tn_state |= TOPO_NODE_BOUND;
614 
615 	topo_node_hold(node);
616 	nhp->th_nodearr[h] = node;
617 	++pnode->tn_refs;
618 
619 	topo_node_unlock(pnode);
620 
621 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
622 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
623 		    FM_FMRI_AUTH_PRODUCT, &err);
624 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
625 		    FM_FMRI_AUTH_CHASSIS, &err);
626 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
627 		    FM_FMRI_AUTH_SERVER, &err);
628 	}
629 
630 	return (node);
631 }
632 
633 int
634 topo_node_facility(topo_hdl_t *thp, tnode_t *node, const char *fac_type,
635     uint32_t fac_subtype, topo_faclist_t *faclist, int *errp)
636 {
637 	tnode_t *tmp;
638 	nvlist_t *rsrc, *fac, *out;
639 	char *tmp_factype;
640 	uint32_t tmp_facsubtype;
641 	boolean_t list_empty = 1;
642 	topo_faclist_t *fac_ele;
643 
644 	/*
645 	 * Some facility nodes will be statically enumerated in the xml maps.
646 	 * Other may be enumerated on-demand.  We can check for/handle the
647 	 * latter case by looking for a node method for doing facility
648 	 * enumeration and invoking it, if found.
649 	 *
650 	 * If the TOPO_FACILITIES_BOUND flag is set on this node, then we've
651 	 * already enumerated the facilties for this node (by a previous call
652 	 * to topo_node_facility) so there's no need to invoke the enumeration
653 	 * method.
654 	 */
655 	topo_node_lock(node);
656 	if (!(node->tn_state & TOPO_FACILITIES_BOUND) &&
657 	    topo_method_supported(node, TOPO_METH_FAC_ENUM, 0)) {
658 
659 		if (topo_method_invoke(node, TOPO_METH_FAC_ENUM, 0, NULL, &out,
660 		    errp) != 0) {
661 			topo_dprintf(thp, TOPO_DBG_ERR,
662 			    "topo_method_invoke failed (%s) on node %s=%d\n",
663 			    topo_strerror(*errp), topo_node_name(node),
664 			    topo_node_instance(node));
665 			topo_node_unlock(node);
666 			return (-1);
667 		} else
668 			node->tn_state |= TOPO_FACILITIES_BOUND;
669 	}
670 
671 	bzero(faclist, sizeof (topo_faclist_t));
672 	for (tmp = topo_child_first(node); tmp != NULL;
673 	    tmp = topo_child_next(node, tmp)) {
674 
675 		topo_node_hold(tmp);
676 		/*
677 		 * If it's not a facility node, move on
678 		 */
679 		if (topo_node_flags(tmp) != TOPO_NODE_FACILITY) {
680 			topo_node_rele(tmp);
681 			continue;
682 		}
683 
684 		/*
685 		 * Lookup whether the fac type is sensor or indicator and if
686 		 * it's not the type we're looking for, move on
687 		 */
688 		if (topo_node_resource(tmp, &rsrc, errp) != 0) {
689 			topo_dprintf(thp, TOPO_DBG_ERR,
690 			    "Failed to get resource for node %s=%d (%s)\n",
691 			    topo_node_name(node), topo_node_instance(node),
692 			    topo_strerror(*errp));
693 			topo_node_rele(tmp);
694 			topo_node_unlock(node);
695 			return (-1);
696 		}
697 		if ((nvlist_lookup_nvlist(rsrc, "facility", &fac) != 0) ||
698 		    (nvlist_lookup_string(fac, FM_FMRI_FACILITY_TYPE,
699 		    &tmp_factype) != 0)) {
700 
701 			nvlist_free(rsrc);
702 			topo_node_rele(tmp);
703 			topo_node_unlock(node);
704 			return (-1);
705 		}
706 
707 		nvlist_free(rsrc);
708 
709 		if (strcmp(fac_type, tmp_factype) != 0) {
710 			topo_node_rele(tmp);
711 			continue;
712 		}
713 
714 		/*
715 		 * Finally, look up the subtype, which is a property in the
716 		 * facility propgroup.  If it's a match return a pointer to the
717 		 * node.  Otherwise, move on.
718 		 */
719 		if (topo_prop_get_uint32(tmp, TOPO_PGROUP_FACILITY,
720 		    TOPO_FACILITY_TYPE, &tmp_facsubtype, errp) != 0) {
721 
722 			topo_node_rele(tmp);
723 			topo_node_unlock(node);
724 			return (-1);
725 		}
726 		if (fac_subtype == tmp_facsubtype) {
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 				topo_node_unlock(node);
732 				return (-1);
733 			}
734 			fac_ele->tf_node = tmp;
735 			topo_list_append(&faclist->tf_list, fac_ele);
736 			list_empty = 0;
737 		}
738 		topo_node_rele(tmp);
739 	}
740 	topo_node_unlock(node);
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