topo_node.c revision 1410cb930a3e26032c59c6835837a28c47366b3c
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
114static topo_pgroup_info_t protocol_pgroup = {
115	TOPO_PGROUP_PROTOCOL,
116	TOPO_STABILITY_PRIVATE,
117	TOPO_STABILITY_PRIVATE,
118	1
119};
120
121static const topo_pgroup_info_t auth_pgroup = {
122	FM_FMRI_AUTHORITY,
123	TOPO_STABILITY_PRIVATE,
124	TOPO_STABILITY_PRIVATE,
125	1
126};
127
128static void
129topo_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
203void
204topo_node_lock(tnode_t *node)
205{
206	(void) pthread_mutex_lock(&node->tn_lock);
207}
208
209void
210topo_node_unlock(tnode_t *node)
211{
212	(void) pthread_mutex_unlock(&node->tn_lock);
213}
214
215void
216topo_node_hold(tnode_t *node)
217{
218	topo_node_lock(node);
219	++node->tn_refs;
220	topo_node_unlock(node);
221}
222
223void
224topo_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
238char *
239topo_node_name(tnode_t *node)
240{
241	return (node->tn_name);
242}
243
244topo_instance_t
245topo_node_instance(tnode_t *node)
246{
247	return (node->tn_instance);
248}
249
250tnode_t *
251topo_node_parent(tnode_t *node)
252{
253	return (node->tn_parent);
254}
255
256int
257topo_node_flags(tnode_t *node)
258{
259	return (node->tn_fflags);
260}
261
262void
263topo_node_setspecific(tnode_t *node, void *data)
264{
265	node->tn_priv = data;
266}
267
268void *
269topo_node_getspecific(tnode_t *node)
270{
271	return (node->tn_priv);
272}
273
274static int
275node_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
296int
297topo_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
347void
348topo_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
385tnode_t *
386topo_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
417int
418topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
419{
420	return ((inst - nhp->th_range.tr_min) % nhp->th_arrlen);
421}
422
423static tnode_t *
424node_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
445tnode_t *
446topo_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
531tnode_t *
532topo_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
665int
666topo_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
749void
750topo_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*/
774int
775topo_node_present(tnode_t *node)
776{
777	return (0);
778}
779
780/*ARGSUSED*/
781int
782topo_node_contains(tnode_t *er, tnode_t *ee)
783{
784	return (0);
785}
786
787/*ARGSUSED*/
788int
789topo_node_unusable(tnode_t *node)
790{
791	return (0);
792}
793
794topo_walk_t *
795topo_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 */
844int
845topo_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
888out:
889	topo_node_rele(pnode);
890	return (ret);
891}
892