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