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 * Copyright 2020 Joyent, Inc.
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
115static topo_pgroup_info_t protocol_pgroup = {
116	TOPO_PGROUP_PROTOCOL,
117	TOPO_STABILITY_PRIVATE,
118	TOPO_STABILITY_PRIVATE,
119	1
120};
121
122static const topo_pgroup_info_t auth_pgroup = {
123	FM_FMRI_AUTHORITY,
124	TOPO_STABILITY_PRIVATE,
125	TOPO_STABILITY_PRIVATE,
126	1
127};
128
129static void
130topo_node_destroy(tnode_t *node)
131{
132	int i;
133	tnode_t *pnode;
134	topo_nodehash_t *nhp;
135	topo_mod_t *hmod, *mod;
136
137	if (node == NULL)
138		return;
139
140	pnode = node->tn_parent;
141	mod = node->tn_enum;
142
143	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, "destroying node %s=%d\n",
144	    topo_node_name(node), topo_node_instance(node));
145
146	assert(node->tn_refs == 0);
147
148	/*
149	 * If not a root node, remove this node from the parent's node hash
150	 */
151
152	if (!(node->tn_state & TOPO_NODE_ROOT)) {
153		topo_node_lock(pnode);
154
155		nhp = node->tn_phash;
156		for (i = 0; i < nhp->th_arrlen; i++) {
157			if (node == nhp->th_nodearr[i]) {
158				nhp->th_nodearr[i] = NULL;
159
160				/*
161				 * Release hold on parent
162				 */
163				--pnode->tn_refs;
164				if (pnode->tn_refs == 0)
165					topo_node_destroy(pnode);
166			}
167		}
168		topo_node_unlock(pnode);
169	}
170
171	topo_node_unlock(node);
172
173	/*
174	 * Allow enumerator to clean-up private data and then release
175	 * ref count
176	 */
177	if (mod->tm_info->tmi_ops->tmo_release != NULL)
178		mod->tm_info->tmi_ops->tmo_release(mod, node);
179
180	topo_method_unregister_all(mod, node);
181
182	/*
183	 * Destroy all node hash lists
184	 */
185	while ((nhp = topo_list_next(&node->tn_children)) != NULL) {
186		for (i = 0; i < nhp->th_arrlen; i++) {
187			assert(nhp->th_nodearr[i] == NULL);
188		}
189		hmod = nhp->th_enum;
190		topo_mod_strfree(hmod, nhp->th_name);
191		topo_mod_free(hmod, nhp->th_nodearr,
192		    nhp->th_arrlen * sizeof (tnode_t *));
193		topo_list_delete(&node->tn_children, nhp);
194		topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t));
195		topo_mod_rele(hmod);
196	}
197
198	/*
199	 * Nodes in a directed graph structure have no children, so the node
200	 * name is still intact. We must free it now.
201	 */
202	if (node->tn_vtx != NULL) {
203		topo_mod_strfree(mod, node->tn_name);
204	}
205
206	/*
207	 * Destroy all property data structures, free the node and release
208	 * the module that created it
209	 */
210	topo_pgroup_destroy_all(node);
211	topo_mod_free(mod, node, sizeof (tnode_t));
212	topo_mod_rele(mod);
213}
214
215void
216topo_node_lock(tnode_t *node)
217{
218	(void) pthread_mutex_lock(&node->tn_lock);
219}
220
221void
222topo_node_unlock(tnode_t *node)
223{
224	(void) pthread_mutex_unlock(&node->tn_lock);
225}
226
227void
228topo_node_hold(tnode_t *node)
229{
230	topo_node_lock(node);
231	++node->tn_refs;
232	topo_node_unlock(node);
233}
234
235void
236topo_node_rele(tnode_t *node)
237{
238	topo_node_lock(node);
239	--node->tn_refs;
240
241	/*
242	 * Ok to remove this node from the topo tree and destroy it
243	 */
244	if (node->tn_refs == 0)
245		topo_node_destroy(node);
246	else
247		topo_node_unlock(node);
248}
249
250char *
251topo_node_name(tnode_t *node)
252{
253	return (node->tn_name);
254}
255
256topo_instance_t
257topo_node_instance(tnode_t *node)
258{
259	return (node->tn_instance);
260}
261
262tnode_t *
263topo_node_parent(tnode_t *node)
264{
265	return (node->tn_parent);
266}
267
268topo_vertex_t *
269topo_node_vertex(tnode_t *node)
270{
271	return (node->tn_vtx);
272}
273
274int
275topo_node_flags(tnode_t *node)
276{
277	return (node->tn_fflags);
278}
279
280void
281topo_node_setspecific(tnode_t *node, void *data)
282{
283	node->tn_priv = data;
284}
285
286void *
287topo_node_getspecific(tnode_t *node)
288{
289	return (node->tn_priv);
290}
291
292static int
293node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
294    int err)
295{
296	topo_node_unlock(pnode);
297
298	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to insert child:"
299	    "%s\n", topo_strerror(err));
300
301	if (nhp != NULL) {
302		if (nhp->th_name != NULL)
303			topo_mod_strfree(mod, nhp->th_name);
304		if (nhp->th_nodearr != NULL) {
305			topo_mod_free(mod, nhp->th_nodearr,
306			    nhp->th_arrlen * sizeof (tnode_t *));
307		}
308		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
309	}
310
311	return (topo_mod_seterrno(mod, err));
312}
313
314int
315topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
316    topo_instance_t min, topo_instance_t max)
317{
318	topo_nodehash_t *nhp;
319
320	topo_node_lock(pnode);
321
322	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
323	    (pnode->tn_state & TOPO_NODE_ROOT));
324
325	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
326	    nhp = topo_list_next(nhp)) {
327		if (strcmp(nhp->th_name, name) == 0)
328			return (node_create_seterror(mod, pnode, NULL,
329			    EMOD_NODE_DUP));
330	}
331
332	if (max < min)
333		return (node_create_seterror(mod, pnode, NULL,
334		    EMOD_NODE_RANGE));
335
336	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
337		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
338
339	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
340		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
341
342	nhp->th_arrlen = max - min + 1;
343
344	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
345	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
346		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
347
348	nhp->th_range.tr_min = min;
349	nhp->th_range.tr_max = max;
350	nhp->th_enum = mod;
351	topo_mod_hold(mod);
352
353	/*
354	 * Add these nodes to parent child list
355	 */
356	topo_list_append(&pnode->tn_children, nhp);
357	topo_node_unlock(pnode);
358
359	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
360	    "created node range %s[%d-%d]\n", name, min, max);
361
362	return (0);
363}
364
365void
366topo_node_range_destroy(tnode_t *pnode, const char *name)
367{
368	int i;
369	topo_nodehash_t *nhp;
370	topo_mod_t *mod;
371
372	topo_node_lock(pnode);
373	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
374	    nhp = topo_list_next(nhp)) {
375		if (strcmp(nhp->th_name, name) == 0) {
376			break;
377		}
378	}
379
380	if (nhp == NULL) {
381		topo_node_unlock(pnode);
382		return;
383	}
384
385	for (i = 0; i < nhp->th_arrlen; i++)
386		assert(nhp->th_nodearr[i] == NULL);
387
388	topo_list_delete(&pnode->tn_children, nhp);
389	topo_node_unlock(pnode);
390
391	mod = nhp->th_enum;
392	if (nhp->th_name != NULL)
393		topo_mod_strfree(mod, nhp->th_name);
394	if (nhp->th_nodearr != NULL) {
395		topo_mod_free(mod, nhp->th_nodearr,
396		    nhp->th_arrlen * sizeof (tnode_t *));
397	}
398	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
399	topo_mod_rele(mod);
400
401}
402
403tnode_t *
404topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
405{
406	int h;
407	tnode_t *node;
408	topo_nodehash_t *nhp;
409
410	topo_dprintf(pnode->tn_hdl, TOPO_DBG_MODSVC,
411	    "topo_node_lookup: looking for '%s' instance %d\n", name, inst);
412
413	topo_node_lock(pnode);
414	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
415	    nhp = topo_list_next(nhp)) {
416		if (strcmp(nhp->th_name, name) == 0) {
417
418			if (inst > nhp->th_range.tr_max ||
419			    inst < nhp->th_range.tr_min) {
420				topo_node_unlock(pnode);
421				return (NULL);
422			}
423
424			h = topo_node_hash(nhp, inst);
425			node = nhp->th_nodearr[h];
426			topo_node_unlock(pnode);
427			return (node);
428		}
429	}
430	topo_node_unlock(pnode);
431
432	return (NULL);
433}
434
435int
436topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
437{
438	return ((inst - nhp->th_range.tr_min) % nhp->th_arrlen);
439}
440
441static tnode_t *
442node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node,
443    boolean_t pnode_locked, int err)
444{
445	if (pnode_locked)
446		topo_node_unlock(pnode);
447
448	(void) topo_mod_seterrno(mod, err);
449
450	if (node == NULL)
451		return (NULL);
452
453	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
454	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
455	    node->tn_instance, topo_strerror(err));
456
457	topo_node_lock(node); /* expected to be locked */
458	topo_node_destroy(node);
459
460	return (NULL);
461}
462
463tnode_t *
464topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
465    topo_instance_t inst, nvlist_t *fmri)
466{
467	int h, err;
468	tnode_t *node;
469	topo_nodehash_t *nhp;
470
471	topo_node_lock(pnode);
472	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
473	    nhp = topo_list_next(nhp)) {
474		if (strcmp(nhp->th_name, name) == 0) {
475
476			if (inst > nhp->th_range.tr_max ||
477			    inst < nhp->th_range.tr_min)
478				return (node_bind_seterror(mod, pnode, NULL,
479				    B_TRUE, EMOD_NODE_RANGE));
480
481			h = topo_node_hash(nhp, inst);
482			if (nhp->th_nodearr[h] != NULL)
483				return (node_bind_seterror(mod, pnode, NULL,
484				    B_TRUE, EMOD_NODE_BOUND));
485			else
486				break;
487
488		}
489	}
490
491	if (nhp == NULL)
492		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
493		    EMOD_NODE_NOENT));
494
495	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
496		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
497		    EMOD_NOMEM));
498
499	(void) pthread_mutex_init(&node->tn_lock, NULL);
500
501	node->tn_enum = mod;
502	node->tn_hdl = mod->tm_hdl;
503	node->tn_parent = pnode;
504	node->tn_name = nhp->th_name;
505	node->tn_instance = inst;
506	node->tn_phash = nhp;
507	node->tn_refs = 0;
508
509	/* Ref count module that bound this node */
510	topo_mod_hold(mod);
511
512	if (fmri == NULL)
513		return (node_bind_seterror(mod, pnode, node, B_TRUE,
514		    EMOD_NVL_INVAL));
515
516	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
517		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
518
519	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
520	    TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
521		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
522
523	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
524	    "node bound %s=%d/%s=%d\n", topo_node_name(pnode),
525	    topo_node_instance(pnode), node->tn_name, node->tn_instance);
526
527	node->tn_state |= TOPO_NODE_BOUND;
528
529	topo_node_hold(node);
530	nhp->th_nodearr[h] = node;
531	++pnode->tn_refs;
532
533	topo_node_unlock(pnode);
534
535	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
536		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
537		    FM_FMRI_AUTH_PRODUCT, &err);
538		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
539		    FM_FMRI_AUTH_PRODUCT_SN, &err);
540		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
541		    FM_FMRI_AUTH_CHASSIS, &err);
542		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
543		    FM_FMRI_AUTH_SERVER, &err);
544	}
545
546	return (node);
547}
548
549tnode_t *
550topo_node_facbind(topo_mod_t *mod, tnode_t *pnode, const char *name,
551    const char *type)
552{
553	int h, err;
554	tnode_t *node;
555	topo_nodehash_t *nhp;
556	topo_instance_t inst = 0;
557	nvlist_t *pfmri, *fnvl;
558
559	/*
560	 * Create a single entry range for this facility
561	 */
562	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0)
563		return (NULL);  /* mod errno set */
564
565	topo_node_hold(pnode);
566	topo_node_lock(pnode);
567	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
568	    nhp = topo_list_next(nhp)) {
569		if (strcmp(nhp->th_name, name) == 0) {
570
571			if (inst > nhp->th_range.tr_max ||
572			    inst < nhp->th_range.tr_min) {
573				topo_node_rele(pnode);
574				return (node_bind_seterror(mod, pnode, NULL,
575				    B_TRUE, EMOD_NVL_INVAL));
576			}
577			h = topo_node_hash(nhp, inst);
578			if (nhp->th_nodearr[h] != NULL) {
579				topo_node_rele(pnode);
580				return (node_bind_seterror(mod, pnode, NULL,
581				    B_TRUE, EMOD_NODE_BOUND));
582			} else
583				break;
584
585		}
586	}
587	topo_node_unlock(pnode);
588
589	if (nhp == NULL) {
590		topo_node_rele(pnode);
591		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
592		    EMOD_NODE_NOENT));
593	}
594	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL) {
595		topo_node_rele(pnode);
596		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
597		    EMOD_NOMEM));
598	}
599
600	(void) pthread_mutex_init(&node->tn_lock, NULL);
601
602	node->tn_enum = mod;
603	node->tn_hdl = mod->tm_hdl;
604	node->tn_parent = pnode;
605	node->tn_name = nhp->th_name;
606	node->tn_instance = inst;
607	node->tn_phash = nhp;
608	node->tn_refs = 0;
609	node->tn_fflags = TOPO_NODE_FACILITY;
610
611	/* Ref count module that bound this node */
612	topo_mod_hold(mod);
613
614	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0) {
615		topo_node_rele(pnode);
616		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
617	}
618	if (topo_mod_nvalloc(mod, &fnvl, NV_UNIQUE_NAME) < 0) {
619		topo_node_rele(pnode);
620		return (node_bind_seterror(mod, pnode, node, B_FALSE,
621		    EMOD_NOMEM));
622	}
623	if (nvlist_add_string(fnvl, FM_FMRI_FACILITY_NAME, name) != 0 ||
624	    nvlist_add_string(fnvl, FM_FMRI_FACILITY_TYPE, type) != 0) {
625		nvlist_free(fnvl);
626		topo_node_rele(pnode);
627		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
628		    EMOD_FMRI_NVL));
629	}
630
631	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
632		nvlist_free(fnvl);
633		topo_node_rele(pnode);
634		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
635	}
636
637	if (nvlist_add_nvlist(pfmri, FM_FMRI_FACILITY, fnvl) != 0) {
638		nvlist_free(fnvl);
639		nvlist_free(pfmri);
640		topo_node_rele(pnode);
641		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
642		    EMOD_FMRI_NVL));
643	}
644
645	nvlist_free(fnvl);
646
647	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
648	    TOPO_PROP_IMMUTABLE, pfmri, &err) < 0) {
649		nvlist_free(pfmri);
650		topo_node_rele(pnode);
651		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
652	}
653
654	nvlist_free(pfmri);
655
656	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
657	    "facility node bound %s=%s\n", type, node->tn_name);
658
659	node->tn_state |= TOPO_NODE_BOUND;
660
661	topo_node_hold(node);
662	nhp->th_nodearr[h] = node;
663
664	topo_node_lock(pnode);
665	++pnode->tn_refs;
666	topo_node_unlock(pnode);
667	topo_node_rele(pnode);
668
669	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
670		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
671		    FM_FMRI_AUTH_PRODUCT, &err);
672		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
673		    FM_FMRI_AUTH_PRODUCT_SN, &err);
674		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
675		    FM_FMRI_AUTH_CHASSIS, &err);
676		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
677		    FM_FMRI_AUTH_SERVER, &err);
678	}
679
680	return (node);
681}
682
683int
684topo_node_facility(topo_hdl_t *thp, tnode_t *node, const char *fac_type,
685    uint32_t fac_subtype, topo_faclist_t *faclist, int *errp)
686{
687	tnode_t *tmp;
688	nvlist_t *rsrc, *fac;
689	char *tmp_factype;
690	uint32_t tmp_facsubtype;
691	boolean_t list_empty = 1;
692	topo_faclist_t *fac_ele;
693
694	bzero(faclist, sizeof (topo_faclist_t));
695	for (tmp = topo_child_first(node); tmp != NULL;
696	    tmp = topo_child_next(node, tmp)) {
697
698		topo_node_hold(tmp);
699		/*
700		 * If it's not a facility node, move on
701		 */
702		if (topo_node_flags(tmp) != TOPO_NODE_FACILITY) {
703			topo_node_rele(tmp);
704			continue;
705		}
706
707		/*
708		 * Lookup whether the fac type is sensor or indicator and if
709		 * it's not the type we're looking for, move on
710		 */
711		if (topo_node_resource(tmp, &rsrc, errp) != 0) {
712			topo_dprintf(thp, TOPO_DBG_ERR,
713			    "Failed to get resource for node %s=%d (%s)\n",
714			    topo_node_name(node), topo_node_instance(node),
715			    topo_strerror(*errp));
716			topo_node_rele(tmp);
717			return (-1);
718		}
719		if ((nvlist_lookup_nvlist(rsrc, "facility", &fac) != 0) ||
720		    (nvlist_lookup_string(fac, FM_FMRI_FACILITY_TYPE,
721		    &tmp_factype) != 0)) {
722
723			nvlist_free(rsrc);
724			topo_node_rele(tmp);
725			return (-1);
726		}
727
728		if (strcmp(fac_type, tmp_factype) != 0) {
729			topo_node_rele(tmp);
730			nvlist_free(rsrc);
731			continue;
732		}
733		nvlist_free(rsrc);
734
735		/*
736		 * Finally, look up the subtype, which is a property in the
737		 * facility propgroup.  If it's a match return a pointer to the
738		 * node.  Otherwise, move on.
739		 */
740		if (topo_prop_get_uint32(tmp, TOPO_PGROUP_FACILITY,
741		    TOPO_FACILITY_TYPE, &tmp_facsubtype, errp) != 0) {
742			topo_node_rele(tmp);
743			return (-1);
744		}
745		if (fac_subtype == tmp_facsubtype ||
746		    fac_subtype == TOPO_FAC_TYPE_ANY) {
747			if ((fac_ele = topo_mod_zalloc(tmp->tn_enum,
748			    sizeof (topo_faclist_t))) == NULL) {
749				*errp = ETOPO_NOMEM;
750				topo_node_rele(tmp);
751				return (-1);
752			}
753			fac_ele->tf_node = tmp;
754			topo_list_append(&faclist->tf_list, fac_ele);
755			list_empty = 0;
756		}
757		topo_node_rele(tmp);
758	}
759
760	if (list_empty) {
761		*errp = ETOPO_FAC_NOENT;
762		return (-1);
763	}
764	return (0);
765}
766
767void
768topo_node_unbind(tnode_t *node)
769{
770	if (node == NULL)
771		return;
772
773	topo_node_lock(node);
774	if (!(node->tn_state & TOPO_NODE_BOUND)) {
775		topo_node_unlock(node);
776		return;
777	}
778
779	node->tn_state &= ~TOPO_NODE_BOUND;
780	topo_node_unlock(node);
781
782	topo_dprintf(node->tn_hdl, TOPO_DBG_MODSVC,
783	    "node unbound %s=%d refs = %d\n", node->tn_name,
784	    node->tn_instance, node->tn_refs);
785
786	topo_node_rele(node);
787}
788
789/*ARGSUSED*/
790int
791topo_node_present(tnode_t *node)
792{
793	return (0);
794}
795
796/*ARGSUSED*/
797int
798topo_node_contains(tnode_t *er, tnode_t *ee)
799{
800	return (0);
801}
802
803/*ARGSUSED*/
804int
805topo_node_unusable(tnode_t *node)
806{
807	return (0);
808}
809
810topo_walk_t *
811topo_node_walk_init(topo_hdl_t *thp, topo_mod_t *mod, tnode_t *node,
812    int (*cb_f)(), void *pdata, int *errp)
813{
814	tnode_t *child;
815	topo_walk_t *wp;
816
817	topo_node_hold(node);
818
819	if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) == NULL) {
820		*errp = ETOPO_HDL_NOMEM;
821		topo_node_rele(node);
822		return (NULL);
823	}
824
825	/*
826	 * If this is the root of the scheme tree, start with the first
827	 * child
828	 */
829	topo_node_lock(node);
830	if (node->tn_state & TOPO_NODE_ROOT) {
831		if ((child = topo_child_first(node)) == NULL) {
832			/* Nothing to walk */
833			*errp = ETOPO_WALK_EMPTY;
834			topo_node_unlock(node);
835			topo_node_rele(node);
836			topo_hdl_free(thp, wp, sizeof (topo_walk_t));
837			return (NULL);
838		}
839		topo_node_unlock(node);
840		topo_node_hold(child);
841		wp->tw_node = child;
842	} else {
843		topo_node_unlock(node);
844		topo_node_hold(node); /* rele at walk end */
845		wp->tw_node = node;
846	}
847
848	wp->tw_root = node;
849	wp->tw_cb = cb_f;
850	wp->tw_pdata = pdata;
851	wp->tw_thp = thp;
852	wp->tw_mod = mod;
853
854	return (wp);
855}
856
857/*
858 * Walk the direct children of the given node.
859 */
860int
861topo_node_child_walk(topo_hdl_t *thp, tnode_t *pnode, topo_walk_cb_t cb_f,
862    void *arg, int *errp)
863{
864	int ret = TOPO_WALK_TERMINATE;
865	tnode_t *cnode;
866
867	topo_node_hold(pnode);
868
869	/*
870	 * First Child:
871	 */
872	topo_node_lock(pnode);
873	cnode = topo_child_first(pnode);
874	topo_node_unlock(pnode);
875
876	if (cnode == NULL) {
877		*errp = ETOPO_WALK_EMPTY;
878		ret = TOPO_WALK_ERR;
879		goto out;
880	}
881
882	while (cnode != NULL) {
883		int iret;
884
885		/*
886		 * Call the walker callback:
887		 */
888		topo_node_hold(cnode);
889		iret = cb_f(thp, cnode, arg);
890		topo_node_rele(cnode);
891		if (iret != TOPO_WALK_NEXT) {
892			ret = iret;
893			break;
894		}
895
896		/*
897		 * Next child:
898		 */
899		topo_node_lock(pnode);
900		cnode = topo_child_next(pnode, cnode);
901		topo_node_unlock(pnode);
902	}
903
904out:
905	topo_node_rele(pnode);
906	return (ret);
907}
908
909int
910topo_node_occupied(tnode_t *node, boolean_t *is_occupied)
911{
912	nvlist_t *out;
913	int err;
914
915	if (topo_method_invoke(node, TOPO_METH_OCCUPIED,
916	    TOPO_METH_OCCUPIED_VERSION, NULL, &out, &err) != 0) {
917		return (err);
918	}
919	(void) nvlist_lookup_boolean_value(out, TOPO_METH_OCCUPIED_RET,
920	    is_occupied);
921
922	nvlist_free(out);
923	return (0);
924}
925