17aec1d6eScindi /*
27aec1d6eScindi  * CDDL HEADER START
37aec1d6eScindi  *
47aec1d6eScindi  * The contents of this file are subject to the terms of the
50eb822a1Scindi  * Common Development and Distribution License (the "License").
60eb822a1Scindi  * You may not use this file except in compliance with the License.
77aec1d6eScindi  *
87aec1d6eScindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97aec1d6eScindi  * or http://www.opensolaris.org/os/licensing.
107aec1d6eScindi  * See the License for the specific language governing permissions
117aec1d6eScindi  * and limitations under the License.
127aec1d6eScindi  *
137aec1d6eScindi  * When distributing Covered Code, include this CDDL HEADER in each
147aec1d6eScindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157aec1d6eScindi  * If applicable, add the following below this CDDL HEADER, with the
167aec1d6eScindi  * fields enclosed by brackets "[]" replaced with your own identifying
177aec1d6eScindi  * information: Portions Copyright [yyyy] [name of copyright owner]
187aec1d6eScindi  *
197aec1d6eScindi  * CDDL HEADER END
207aec1d6eScindi  */
217aec1d6eScindi /*
22*f6e214c7SGavin Maltby  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
237aec1d6eScindi  */
247aec1d6eScindi 
257aec1d6eScindi /*
267aec1d6eScindi  * Topology Nodes
277aec1d6eScindi  *
287aec1d6eScindi  * Topology nodes, tnode_t, are data structures containing per-FMRI
297aec1d6eScindi  * information and are linked together to form the topology tree.
307aec1d6eScindi  * Nodes are created during the enumeration process of topo_snap_hold()
317aec1d6eScindi  * and destroyed during topo_snap_rele().  For the most part, tnode_t data
327aec1d6eScindi  * is read-only and no lock protection is required.  Nodes are
337aec1d6eScindi  * held in place during tree walk functions.  Tree walk functions
347aec1d6eScindi  * may access node data safely without locks.  The exception to this rule
357aec1d6eScindi  * is data associated with node properties (topo_prop.c).  Properties
367aec1d6eScindi  * may change at anytime and are protected by a per-property locking
377aec1d6eScindi  * strategy.
387aec1d6eScindi  *
39c40d7343Scindi  * Enumerator plugin modules may also safely access topology nodes within their
40c40d7343Scindi  * scope of operation: the parent node passed into the enumeration op or those
41c40d7343Scindi  * nodes created by the enumerator.  Enumeration occurs only during
42c40d7343Scindi  * topo_snap_hold() where a per-topo_hdl_t lock prevents multi-threaded access
43c40d7343Scindi  * to the topology trees.
447aec1d6eScindi  *
45c40d7343Scindi  * Enumerator method operation functions may safely access and change topology
46c40d7343Scindi  * node property data, and contruct or destroy child nodes for the node
47c40d7343Scindi  * on which the operation applies.  The method may also be called to destroy
48c40d7343Scindi  * the node for which the method operation is called.  This permits
49c40d7343Scindi  * dynamic topology tree snapshots and partial enumerations for branches that
50c40d7343Scindi  * may not be needed right away.
517aec1d6eScindi  *
527aec1d6eScindi  * Node Interfaces
537aec1d6eScindi  *
54c40d7343Scindi  * Nodes are created when an enumerator calls topo_node_bind().  Prior to
55c40d7343Scindi  * calling topo_node_bind(), the enumerator should have reserved a range of
567aec1d6eScindi  * node instances with topo_node_range_create().  topo_node_range_create()
577aec1d6eScindi  * does not allocate any node resources but creates the infrastruture
587aec1d6eScindi  * required for a fully populated topology level.  This allows enumerators
597aec1d6eScindi  * reading from a <scheme>-topology.xml file to parse the file for a range
607aec1d6eScindi  * of resources before confirming the existence of a resource via a helper
617aec1d6eScindi  * plugin.  Only when the resource has been confirmed to exist should
627aec1d6eScindi  * the node be bound.
637aec1d6eScindi  *
64c40d7343Scindi  * Node range and node linkage and unlinkage is performed during enumeration and
65c40d7343Scindi  * method operations when it is safe to change node hash lists. Nodes and node
66c40d7343Scindi  * ranges are deallocated when all references to the node have been released:
677aec1d6eScindi  * last walk completes and topo_snap_rele() is called.
687aec1d6eScindi  *
697aec1d6eScindi  * Node Hash/Ranges
707aec1d6eScindi  *
717aec1d6eScindi  * Each parent node may have one or more ranges of child nodes.  Each range
72c40d7343Scindi  * is uniquely named and serves as a hash list of like sibling nodes with
737aec1d6eScindi  * different instance numbers.  A parent may have more than one node hash
747aec1d6eScindi  * (child range). If that is the case, the hash lists are strung together to
757aec1d6eScindi  * form sibling relationships between ranges.  Hash/Ranges are sparsely
767aec1d6eScindi  * populated with only nodes that have represented resources in the system.
77c40d7343Scindi  *
78c40d7343Scindi  *	_________________
79c40d7343Scindi  *	|		|
80c40d7343Scindi  *      |   tnode_t	|    -----------------------------
81c40d7343Scindi  *      |      tn_phash ---> |  topo_nodehash_t          |
82c40d7343Scindi  *      |     (children)|    |     th_nodearr (instances)|
83c40d7343Scindi  *      -----------------    |     -------------------   |
84c40d7343Scindi  *                           |  ---| 0 | 1  | ...| N |   |
85c40d7343Scindi  *                           |  |  -------------------   |  -------------------
86c40d7343Scindi  *                           |  |  th_list (siblings) ----->| topo_nodehash_t |
87c40d7343Scindi  *                           |  |                        |  -------------------
88c40d7343Scindi  *                           ---|-------------------------
89c40d7343Scindi  *                              |
90c40d7343Scindi  *                              v
91c40d7343Scindi  *                           -----------
92c40d7343Scindi  *                           | tnode_t |
93c40d7343Scindi  *                           -----------
94825ba0f2Srobj  *
95825ba0f2Srobj  * Facility Nodes
96825ba0f2Srobj  *
97825ba0f2Srobj  * Facility nodes are always leaf nodes in the topology and represent a FMRI
98825ba0f2Srobj  * sensor or indicator facility for the path to which it is connected.
99825ba0f2Srobj  * Facility nodes are bound to the topology with topo_node_facbind() and
100825ba0f2Srobj  * unbound with topo_node_unbind().
1017aec1d6eScindi  */
1027aec1d6eScindi 
1037aec1d6eScindi #include <assert.h>
1047aec1d6eScindi #include <pthread.h>
1057aec1d6eScindi #include <strings.h>
1060eb822a1Scindi #include <sys/fm/protocol.h>
1077aec1d6eScindi #include <topo_alloc.h>
1087aec1d6eScindi #include <topo_error.h>
109825ba0f2Srobj #include <topo_list.h>
1100eb822a1Scindi #include <topo_method.h>
1110eb822a1Scindi #include <topo_subr.h>
1120eb822a1Scindi #include <topo_tree.h>
1130eb822a1Scindi 
1140eb822a1Scindi static topo_pgroup_info_t protocol_pgroup = {
1150eb822a1Scindi 	TOPO_PGROUP_PROTOCOL,
1160eb822a1Scindi 	TOPO_STABILITY_PRIVATE,
1170eb822a1Scindi 	TOPO_STABILITY_PRIVATE,
1180eb822a1Scindi 	1
1190eb822a1Scindi };
1200eb822a1Scindi 
1210eb822a1Scindi static const topo_pgroup_info_t auth_pgroup = {
1220eb822a1Scindi 	FM_FMRI_AUTHORITY,
1230eb822a1Scindi 	TOPO_STABILITY_PRIVATE,
1240eb822a1Scindi 	TOPO_STABILITY_PRIVATE,
1250eb822a1Scindi 	1
1260eb822a1Scindi };
1277aec1d6eScindi 
1287aec1d6eScindi static void
1297aec1d6eScindi topo_node_destroy(tnode_t *node)
1307aec1d6eScindi {
1317aec1d6eScindi 	int i;
1327aec1d6eScindi 	tnode_t *pnode = node->tn_parent;
1337aec1d6eScindi 	topo_nodehash_t *nhp;
1347aec1d6eScindi 	topo_mod_t *hmod, *mod = node->tn_enum;
1357aec1d6eScindi 
1367aec1d6eScindi 	if (node == NULL)
1377aec1d6eScindi 		return;
1387aec1d6eScindi 
13912cc75c8Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, "destroying node %s=%d\n",
14012cc75c8Scindi 	    topo_node_name(node), topo_node_instance(node));
14112cc75c8Scindi 
1427aec1d6eScindi 	assert(node->tn_refs == 0);
1437aec1d6eScindi 
1447aec1d6eScindi 	/*
1457aec1d6eScindi 	 * If not a root node, remove this node from the parent's node hash
1467aec1d6eScindi 	 */
1477aec1d6eScindi 
1487aec1d6eScindi 	if (!(node->tn_state & TOPO_NODE_ROOT)) {
1497aec1d6eScindi 		topo_node_lock(pnode);
1507aec1d6eScindi 
1517aec1d6eScindi 		nhp = node->tn_phash;
1527aec1d6eScindi 		for (i = 0; i < nhp->th_arrlen; i++) {
1537aec1d6eScindi 			if (node == nhp->th_nodearr[i]) {
1547aec1d6eScindi 				nhp->th_nodearr[i] = NULL;
1557aec1d6eScindi 
1567aec1d6eScindi 				/*
1577aec1d6eScindi 				 * Release hold on parent
1587aec1d6eScindi 				 */
1597aec1d6eScindi 				--pnode->tn_refs;
1607aec1d6eScindi 				if (pnode->tn_refs == 0)
1617aec1d6eScindi 					topo_node_destroy(pnode);
1627aec1d6eScindi 			}
1637aec1d6eScindi 		}
1647aec1d6eScindi 		topo_node_unlock(pnode);
1657aec1d6eScindi 	}
1667aec1d6eScindi 
1677aec1d6eScindi 	topo_node_unlock(node);
1687aec1d6eScindi 
1697aec1d6eScindi 	/*
1707aec1d6eScindi 	 * Allow enumerator to clean-up private data and then release
1717aec1d6eScindi 	 * ref count
1727aec1d6eScindi 	 */
1730eb822a1Scindi 	if (mod->tm_info->tmi_ops->tmo_release != NULL)
1740eb822a1Scindi 		mod->tm_info->tmi_ops->tmo_release(mod, node);
1757aec1d6eScindi 
1767aec1d6eScindi 	topo_method_unregister_all(mod, node);
1777aec1d6eScindi 
1787aec1d6eScindi 	/*
1797aec1d6eScindi 	 * Destroy all node hash lists
1807aec1d6eScindi 	 */
1817aec1d6eScindi 	while ((nhp = topo_list_next(&node->tn_children)) != NULL) {
1827aec1d6eScindi 		for (i = 0; i < nhp->th_arrlen; i++) {
1837aec1d6eScindi 			assert(nhp->th_nodearr[i] == NULL);
1847aec1d6eScindi 		}
1857aec1d6eScindi 		hmod = nhp->th_enum;
1867aec1d6eScindi 		topo_mod_strfree(hmod, nhp->th_name);
1877aec1d6eScindi 		topo_mod_free(hmod, nhp->th_nodearr,
1887aec1d6eScindi 		    nhp->th_arrlen * sizeof (tnode_t *));
1897aec1d6eScindi 		topo_list_delete(&node->tn_children, nhp);
1907aec1d6eScindi 		topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t));
1917aec1d6eScindi 		topo_mod_rele(hmod);
1927aec1d6eScindi 	}
1937aec1d6eScindi 
1947aec1d6eScindi 	/*
1957aec1d6eScindi 	 * Destroy all property data structures, free the node and release
1967aec1d6eScindi 	 * the module that created it
1977aec1d6eScindi 	 */
1987aec1d6eScindi 	topo_pgroup_destroy_all(node);
1997aec1d6eScindi 	topo_mod_free(mod, node, sizeof (tnode_t));
2007aec1d6eScindi 	topo_mod_rele(mod);
2017aec1d6eScindi }
2027aec1d6eScindi 
2037aec1d6eScindi void
2047aec1d6eScindi topo_node_lock(tnode_t *node)
2057aec1d6eScindi {
2067aec1d6eScindi 	(void) pthread_mutex_lock(&node->tn_lock);
2077aec1d6eScindi }
2087aec1d6eScindi 
2097aec1d6eScindi void
2107aec1d6eScindi topo_node_unlock(tnode_t *node)
2117aec1d6eScindi {
2127aec1d6eScindi 	(void) pthread_mutex_unlock(&node->tn_lock);
2137aec1d6eScindi }
2147aec1d6eScindi 
2157aec1d6eScindi void
2167aec1d6eScindi topo_node_hold(tnode_t *node)
2177aec1d6eScindi {
2187aec1d6eScindi 	topo_node_lock(node);
2197aec1d6eScindi 	++node->tn_refs;
2207aec1d6eScindi 	topo_node_unlock(node);
2217aec1d6eScindi }
2227aec1d6eScindi 
2237aec1d6eScindi void
2247aec1d6eScindi topo_node_rele(tnode_t *node)
2257aec1d6eScindi {
2267aec1d6eScindi 	topo_node_lock(node);
2277aec1d6eScindi 	--node->tn_refs;
2287aec1d6eScindi 
2297aec1d6eScindi 	/*
2307aec1d6eScindi 	 * Ok to remove this node from the topo tree and destroy it
2317aec1d6eScindi 	 */
2327aec1d6eScindi 	if (node->tn_refs == 0)
2337aec1d6eScindi 		topo_node_destroy(node);
2347aec1d6eScindi 	else
2357aec1d6eScindi 		topo_node_unlock(node);
2367aec1d6eScindi }
2377aec1d6eScindi 
2387aec1d6eScindi char *
2397aec1d6eScindi topo_node_name(tnode_t *node)
2407aec1d6eScindi {
2417aec1d6eScindi 	return (node->tn_name);
2427aec1d6eScindi }
2437aec1d6eScindi 
2447aec1d6eScindi topo_instance_t
2457aec1d6eScindi topo_node_instance(tnode_t *node)
2467aec1d6eScindi {
2477aec1d6eScindi 	return (node->tn_instance);
2487aec1d6eScindi }
2497aec1d6eScindi 
2504557a2a1Srobj tnode_t *
2514557a2a1Srobj topo_node_parent(tnode_t *node)
2524557a2a1Srobj {
2534557a2a1Srobj 	return (node->tn_parent);
2544557a2a1Srobj }
2554557a2a1Srobj 
256825ba0f2Srobj int
257825ba0f2Srobj topo_node_flags(tnode_t *node)
258825ba0f2Srobj {
259825ba0f2Srobj 	return (node->tn_fflags);
260825ba0f2Srobj }
261825ba0f2Srobj 
2620eb822a1Scindi void
2630eb822a1Scindi topo_node_setspecific(tnode_t *node, void *data)
2640eb822a1Scindi {
2650eb822a1Scindi 	node->tn_priv = data;
2660eb822a1Scindi }
2670eb822a1Scindi 
2687aec1d6eScindi void *
2690eb822a1Scindi topo_node_getspecific(tnode_t *node)
2707aec1d6eScindi {
2717aec1d6eScindi 	return (node->tn_priv);
2727aec1d6eScindi }
2737aec1d6eScindi 
2747aec1d6eScindi static int
2757aec1d6eScindi node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
2767aec1d6eScindi     int err)
2777aec1d6eScindi {
2787aec1d6eScindi 	topo_node_unlock(pnode);
2797aec1d6eScindi 
2800eb822a1Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to insert child:"
2817aec1d6eScindi 	    "%s\n", topo_strerror(err));
2827aec1d6eScindi 
2837aec1d6eScindi 	if (nhp != NULL) {
2847aec1d6eScindi 		if (nhp->th_name != NULL)
2857aec1d6eScindi 			topo_mod_strfree(mod, nhp->th_name);
2867aec1d6eScindi 		if (nhp->th_nodearr != NULL) {
2877aec1d6eScindi 			topo_mod_free(mod, nhp->th_nodearr,
2887aec1d6eScindi 			    nhp->th_arrlen * sizeof (tnode_t *));
2897aec1d6eScindi 		}
2907aec1d6eScindi 		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
2917aec1d6eScindi 	}
2927aec1d6eScindi 
2937aec1d6eScindi 	return (topo_mod_seterrno(mod, err));
2947aec1d6eScindi }
2957aec1d6eScindi 
2967aec1d6eScindi int
2977aec1d6eScindi topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
2987aec1d6eScindi     topo_instance_t min, topo_instance_t max)
2997aec1d6eScindi {
3007aec1d6eScindi 	topo_nodehash_t *nhp;
3017aec1d6eScindi 
3027aec1d6eScindi 	topo_node_lock(pnode);
3037aec1d6eScindi 
3047aec1d6eScindi 	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
3057aec1d6eScindi 	    (pnode->tn_state & TOPO_NODE_ROOT));
3067aec1d6eScindi 
3077aec1d6eScindi 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
3087aec1d6eScindi 	    nhp = topo_list_next(nhp)) {
3097aec1d6eScindi 		if (strcmp(nhp->th_name, name) == 0)
3107aec1d6eScindi 			return (node_create_seterror(mod, pnode, NULL,
311825ba0f2Srobj 			    EMOD_NODE_DUP));
3127aec1d6eScindi 	}
3137aec1d6eScindi 
3147aec1d6eScindi 	if (min < 0 || max < min)
3157aec1d6eScindi 		return (node_create_seterror(mod, pnode, NULL,
316825ba0f2Srobj 		    EMOD_NODE_RANGE));
3177aec1d6eScindi 
3187aec1d6eScindi 	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
319825ba0f2Srobj 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
3207aec1d6eScindi 
3217aec1d6eScindi 	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
322825ba0f2Srobj 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
3237aec1d6eScindi 
3247aec1d6eScindi 	nhp->th_arrlen = max - min + 1;
3257aec1d6eScindi 
3267aec1d6eScindi 	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
3277aec1d6eScindi 	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
328825ba0f2Srobj 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
3297aec1d6eScindi 
3307aec1d6eScindi 	nhp->th_range.tr_min = min;
3317aec1d6eScindi 	nhp->th_range.tr_max = max;
3327aec1d6eScindi 	nhp->th_enum = mod;
3337aec1d6eScindi 	topo_mod_hold(mod);
3347aec1d6eScindi 
3357aec1d6eScindi 	/*
3367aec1d6eScindi 	 * Add these nodes to parent child list
3377aec1d6eScindi 	 */
3387aec1d6eScindi 	topo_list_append(&pnode->tn_children, nhp);
3397aec1d6eScindi 	topo_node_unlock(pnode);
3407aec1d6eScindi 
3410eb822a1Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
3420eb822a1Scindi 	    "created node range %s[%d-%d]\n", name, min, max);
3437aec1d6eScindi 
3447aec1d6eScindi 	return (0);
3457aec1d6eScindi }
3467aec1d6eScindi 
3477aec1d6eScindi void
3487aec1d6eScindi topo_node_range_destroy(tnode_t *pnode, const char *name)
3497aec1d6eScindi {
3507aec1d6eScindi 	int i;
3517aec1d6eScindi 	topo_nodehash_t *nhp;
3527aec1d6eScindi 	topo_mod_t *mod;
3537aec1d6eScindi 
3547aec1d6eScindi 	topo_node_lock(pnode);
3557aec1d6eScindi 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
3567aec1d6eScindi 	    nhp = topo_list_next(nhp)) {
3577aec1d6eScindi 		if (strcmp(nhp->th_name, name) == 0) {
3587aec1d6eScindi 			break;
3597aec1d6eScindi 		}
3607aec1d6eScindi 	}
3617aec1d6eScindi 
3627aec1d6eScindi 	if (nhp == NULL) {
3637aec1d6eScindi 		topo_node_unlock(pnode);
3647aec1d6eScindi 		return;
3657aec1d6eScindi 	}
3667aec1d6eScindi 
36712cc75c8Scindi 	for (i = 0; i < nhp->th_arrlen; i++)
36812cc75c8Scindi 		assert(nhp->th_nodearr[i] == NULL);
36912cc75c8Scindi 
3707aec1d6eScindi 	topo_list_delete(&pnode->tn_children, nhp);
3717aec1d6eScindi 	topo_node_unlock(pnode);
3727aec1d6eScindi 
3737aec1d6eScindi 	mod = nhp->th_enum;
3747aec1d6eScindi 	if (nhp->th_name != NULL)
3757aec1d6eScindi 		topo_mod_strfree(mod, nhp->th_name);
3767aec1d6eScindi 	if (nhp->th_nodearr != NULL) {
3777aec1d6eScindi 		topo_mod_free(mod, nhp->th_nodearr,
3787aec1d6eScindi 		    nhp->th_arrlen * sizeof (tnode_t *));
3797aec1d6eScindi 	}
3807aec1d6eScindi 	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
3817aec1d6eScindi 	topo_mod_rele(mod);
3827aec1d6eScindi 
3837aec1d6eScindi }
3847aec1d6eScindi 
3857aec1d6eScindi tnode_t *
3867aec1d6eScindi topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
3877aec1d6eScindi {
3887aec1d6eScindi 	int h;
3897aec1d6eScindi 	tnode_t *node;
3907aec1d6eScindi 	topo_nodehash_t *nhp;
3917aec1d6eScindi 
392*f6e214c7SGavin Maltby 	topo_dprintf(pnode->tn_hdl, TOPO_DBG_MODSVC,
393*f6e214c7SGavin Maltby 	    "topo_node_lookup: looking for '%s' instance %d\n", name, inst);
394*f6e214c7SGavin Maltby 
3957aec1d6eScindi 	topo_node_lock(pnode);
3967aec1d6eScindi 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
3977aec1d6eScindi 	    nhp = topo_list_next(nhp)) {
3987aec1d6eScindi 		if (strcmp(nhp->th_name, name) == 0) {
3997aec1d6eScindi 
4007aec1d6eScindi 			if (inst > nhp->th_range.tr_max ||
4017aec1d6eScindi 			    inst < nhp->th_range.tr_min) {
4027aec1d6eScindi 				topo_node_unlock(pnode);
4037aec1d6eScindi 				return (NULL);
4047aec1d6eScindi 			}
4057aec1d6eScindi 
4067aec1d6eScindi 			h = topo_node_hash(nhp, inst);
4077aec1d6eScindi 			node = nhp->th_nodearr[h];
4087aec1d6eScindi 			topo_node_unlock(pnode);
4097aec1d6eScindi 			return (node);
4107aec1d6eScindi 		}
4117aec1d6eScindi 	}
4127aec1d6eScindi 	topo_node_unlock(pnode);
4137aec1d6eScindi 
4147aec1d6eScindi 	return (NULL);
4157aec1d6eScindi }
4167aec1d6eScindi 
4177aec1d6eScindi int
4187aec1d6eScindi topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
4197aec1d6eScindi {
42020c794b3Sgavinm 	return ((inst - nhp->th_range.tr_min) % nhp->th_arrlen);
4217aec1d6eScindi }
4227aec1d6eScindi 
4237aec1d6eScindi static tnode_t *
424a38fc4eaSRobert Johnston node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node,
425a38fc4eaSRobert Johnston     boolean_t pnode_locked, int err)
4267aec1d6eScindi {
427a38fc4eaSRobert Johnston 	if (pnode_locked)
428a38fc4eaSRobert Johnston 		topo_node_unlock(pnode);
4297aec1d6eScindi 
4307aec1d6eScindi 	(void) topo_mod_seterrno(mod, err);
4317aec1d6eScindi 
4327aec1d6eScindi 	if (node == NULL)
4337aec1d6eScindi 		return (NULL);
4347aec1d6eScindi 
4350eb822a1Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
4367aec1d6eScindi 	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
4377aec1d6eScindi 	    node->tn_instance, topo_strerror(err));
4387aec1d6eScindi 
4397aec1d6eScindi 	topo_node_lock(node); /* expected to be locked */
4407aec1d6eScindi 	topo_node_destroy(node);
4417aec1d6eScindi 
4427aec1d6eScindi 	return (NULL);
4437aec1d6eScindi }
4447aec1d6eScindi 
4457aec1d6eScindi tnode_t *
4467aec1d6eScindi topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
4470eb822a1Scindi     topo_instance_t inst, nvlist_t *fmri)
4487aec1d6eScindi {
4497aec1d6eScindi 	int h, err;
4507aec1d6eScindi 	tnode_t *node;
4517aec1d6eScindi 	topo_nodehash_t *nhp;
4527aec1d6eScindi 
4537aec1d6eScindi 	topo_node_lock(pnode);
4547aec1d6eScindi 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
4557aec1d6eScindi 	    nhp = topo_list_next(nhp)) {
4567aec1d6eScindi 		if (strcmp(nhp->th_name, name) == 0) {
4577aec1d6eScindi 
4587aec1d6eScindi 			if (inst > nhp->th_range.tr_max ||
4597aec1d6eScindi 			    inst < nhp->th_range.tr_min)
4607aec1d6eScindi 				return (node_bind_seterror(mod, pnode, NULL,
461a38fc4eaSRobert Johnston 				    B_TRUE, EMOD_NODE_RANGE));
4627aec1d6eScindi 
4637aec1d6eScindi 			h = topo_node_hash(nhp, inst);
4647aec1d6eScindi 			if (nhp->th_nodearr[h] != NULL)
4657aec1d6eScindi 				return (node_bind_seterror(mod, pnode, NULL,
466a38fc4eaSRobert Johnston 				    B_TRUE, EMOD_NODE_BOUND));
4677aec1d6eScindi 			else
4687aec1d6eScindi 				break;
4697aec1d6eScindi 
4707aec1d6eScindi 		}
4717aec1d6eScindi 	}
4727aec1d6eScindi 
4737aec1d6eScindi 	if (nhp == NULL)
474a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
475a38fc4eaSRobert Johnston 		    EMOD_NODE_NOENT));
4767aec1d6eScindi 
4777aec1d6eScindi 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
478a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
479a38fc4eaSRobert Johnston 		    EMOD_NOMEM));
4807aec1d6eScindi 
4817aec1d6eScindi 	(void) pthread_mutex_init(&node->tn_lock, NULL);
4827aec1d6eScindi 
4837aec1d6eScindi 	node->tn_enum = mod;
4847aec1d6eScindi 	node->tn_hdl = mod->tm_hdl;
4857aec1d6eScindi 	node->tn_parent = pnode;
4867aec1d6eScindi 	node->tn_name = nhp->th_name;
4877aec1d6eScindi 	node->tn_instance = inst;
4887aec1d6eScindi 	node->tn_phash = nhp;
4897aec1d6eScindi 	node->tn_refs = 0;
4907aec1d6eScindi 
4917aec1d6eScindi 	/* Ref count module that bound this node */
4927aec1d6eScindi 	topo_mod_hold(mod);
4937aec1d6eScindi 
4947aec1d6eScindi 	if (fmri == NULL)
495a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, node, B_TRUE,
496a38fc4eaSRobert Johnston 		    EMOD_NVL_INVAL));
4977aec1d6eScindi 
4980eb822a1Scindi 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
499a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
5007aec1d6eScindi 
5017aec1d6eScindi 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
5020eb822a1Scindi 	    TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
503a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
5047aec1d6eScindi 
5050eb822a1Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
50612cc75c8Scindi 	    "node bound %s=%d/%s=%d\n", topo_node_name(pnode),
50712cc75c8Scindi 	    topo_node_instance(pnode), node->tn_name, node->tn_instance);
5087aec1d6eScindi 
5097aec1d6eScindi 	node->tn_state |= TOPO_NODE_BOUND;
5107aec1d6eScindi 
5117aec1d6eScindi 	topo_node_hold(node);
5127aec1d6eScindi 	nhp->th_nodearr[h] = node;
5137aec1d6eScindi 	++pnode->tn_refs;
514825ba0f2Srobj 
5157aec1d6eScindi 	topo_node_unlock(pnode);
5167aec1d6eScindi 
5170eb822a1Scindi 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
5180eb822a1Scindi 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
5190eb822a1Scindi 		    FM_FMRI_AUTH_PRODUCT, &err);
5209c94f155SCheng Sean Ye 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
5219c94f155SCheng Sean Ye 		    FM_FMRI_AUTH_PRODUCT_SN, &err);
5220eb822a1Scindi 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
5230eb822a1Scindi 		    FM_FMRI_AUTH_CHASSIS, &err);
5240eb822a1Scindi 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
5250eb822a1Scindi 		    FM_FMRI_AUTH_SERVER, &err);
5267aec1d6eScindi 	}
5277aec1d6eScindi 
5287aec1d6eScindi 	return (node);
5297aec1d6eScindi }
5307aec1d6eScindi 
531825ba0f2Srobj tnode_t *
532825ba0f2Srobj topo_node_facbind(topo_mod_t *mod, tnode_t *pnode, const char *name,
533825ba0f2Srobj     const char *type)
534825ba0f2Srobj {
535825ba0f2Srobj 	int h, err;
536825ba0f2Srobj 	tnode_t *node;
537825ba0f2Srobj 	topo_nodehash_t *nhp;
538825ba0f2Srobj 	topo_instance_t inst = 0;
539825ba0f2Srobj 	nvlist_t *pfmri, *fnvl;
540825ba0f2Srobj 
541825ba0f2Srobj 	/*
542825ba0f2Srobj 	 * Create a single entry range for this facility
543825ba0f2Srobj 	 */
544825ba0f2Srobj 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0)
545825ba0f2Srobj 		return (NULL);  /* mod errno set */
546825ba0f2Srobj 
547a38fc4eaSRobert Johnston 	topo_node_hold(pnode);
548825ba0f2Srobj 	topo_node_lock(pnode);
549825ba0f2Srobj 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
550825ba0f2Srobj 	    nhp = topo_list_next(nhp)) {
551825ba0f2Srobj 		if (strcmp(nhp->th_name, name) == 0) {
552825ba0f2Srobj 
553825ba0f2Srobj 			if (inst > nhp->th_range.tr_max ||
554a38fc4eaSRobert Johnston 			    inst < nhp->th_range.tr_min) {
555a38fc4eaSRobert Johnston 				topo_node_rele(pnode);
556825ba0f2Srobj 				return (node_bind_seterror(mod, pnode, NULL,
557a38fc4eaSRobert Johnston 				    B_TRUE, EMOD_NVL_INVAL));
558a38fc4eaSRobert Johnston 			}
559825ba0f2Srobj 			h = topo_node_hash(nhp, inst);
560a38fc4eaSRobert Johnston 			if (nhp->th_nodearr[h] != NULL) {
561a38fc4eaSRobert Johnston 				topo_node_rele(pnode);
562825ba0f2Srobj 				return (node_bind_seterror(mod, pnode, NULL,
563a38fc4eaSRobert Johnston 				    B_TRUE, EMOD_NODE_BOUND));
564a38fc4eaSRobert Johnston 			} else
565825ba0f2Srobj 				break;
566825ba0f2Srobj 
567825ba0f2Srobj 		}
568825ba0f2Srobj 	}
569a38fc4eaSRobert Johnston 	topo_node_unlock(pnode);
570825ba0f2Srobj 
571a38fc4eaSRobert Johnston 	if (nhp == NULL) {
572a38fc4eaSRobert Johnston 		topo_node_rele(pnode);
573a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
574a38fc4eaSRobert Johnston 		    EMOD_NODE_NOENT));
575a38fc4eaSRobert Johnston 	}
576a38fc4eaSRobert Johnston 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL) {
577a38fc4eaSRobert Johnston 		topo_node_rele(pnode);
578a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
579a38fc4eaSRobert Johnston 		    EMOD_NOMEM));
580a38fc4eaSRobert Johnston 	}
581825ba0f2Srobj 
582825ba0f2Srobj 	(void) pthread_mutex_init(&node->tn_lock, NULL);
583825ba0f2Srobj 
584825ba0f2Srobj 	node->tn_enum = mod;
585825ba0f2Srobj 	node->tn_hdl = mod->tm_hdl;
586825ba0f2Srobj 	node->tn_parent = pnode;
587825ba0f2Srobj 	node->tn_name = nhp->th_name;
588825ba0f2Srobj 	node->tn_instance = inst;
589825ba0f2Srobj 	node->tn_phash = nhp;
590825ba0f2Srobj 	node->tn_refs = 0;
591825ba0f2Srobj 	node->tn_fflags = TOPO_NODE_FACILITY;
592825ba0f2Srobj 
593825ba0f2Srobj 	/* Ref count module that bound this node */
594825ba0f2Srobj 	topo_mod_hold(mod);
595825ba0f2Srobj 
596a38fc4eaSRobert Johnston 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0) {
597a38fc4eaSRobert Johnston 		topo_node_rele(pnode);
598a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
599a38fc4eaSRobert Johnston 	}
600a38fc4eaSRobert Johnston 	if (topo_mod_nvalloc(mod, &fnvl, NV_UNIQUE_NAME) < 0) {
601a38fc4eaSRobert Johnston 		topo_node_rele(pnode);
602a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, node, B_FALSE,
603a38fc4eaSRobert Johnston 		    EMOD_NOMEM));
604a38fc4eaSRobert Johnston 	}
605825ba0f2Srobj 	if (nvlist_add_string(fnvl, FM_FMRI_FACILITY_NAME, name) != 0 ||
606825ba0f2Srobj 	    nvlist_add_string(fnvl, FM_FMRI_FACILITY_TYPE, type) != 0) {
607825ba0f2Srobj 		nvlist_free(fnvl);
608a38fc4eaSRobert Johnston 		topo_node_rele(pnode);
609a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
610a38fc4eaSRobert Johnston 		    EMOD_FMRI_NVL));
611825ba0f2Srobj 	}
612825ba0f2Srobj 
613825ba0f2Srobj 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
614825ba0f2Srobj 		nvlist_free(fnvl);
615a38fc4eaSRobert Johnston 		topo_node_rele(pnode);
616a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
617825ba0f2Srobj 	}
618825ba0f2Srobj 
619825ba0f2Srobj 	if (nvlist_add_nvlist(pfmri, FM_FMRI_FACILITY, fnvl) != 0) {
620825ba0f2Srobj 		nvlist_free(fnvl);
621825ba0f2Srobj 		nvlist_free(pfmri);
622a38fc4eaSRobert Johnston 		topo_node_rele(pnode);
623a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
624a38fc4eaSRobert Johnston 		    EMOD_FMRI_NVL));
625825ba0f2Srobj 	}
626825ba0f2Srobj 
627825ba0f2Srobj 	nvlist_free(fnvl);
628825ba0f2Srobj 
629825ba0f2Srobj 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
630825ba0f2Srobj 	    TOPO_PROP_IMMUTABLE, pfmri, &err) < 0) {
631825ba0f2Srobj 		nvlist_free(pfmri);
632a38fc4eaSRobert Johnston 		topo_node_rele(pnode);
633a38fc4eaSRobert Johnston 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
634825ba0f2Srobj 	}
635825ba0f2Srobj 
636825ba0f2Srobj 	nvlist_free(pfmri);
637825ba0f2Srobj 
638825ba0f2Srobj 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
639825ba0f2Srobj 	    "facility node bound %s=%s\n", type, node->tn_name);
640825ba0f2Srobj 
641825ba0f2Srobj 	node->tn_state |= TOPO_NODE_BOUND;
642825ba0f2Srobj 
643825ba0f2Srobj 	topo_node_hold(node);
644825ba0f2Srobj 	nhp->th_nodearr[h] = node;
645825ba0f2Srobj 
646a38fc4eaSRobert Johnston 	topo_node_lock(pnode);
647a38fc4eaSRobert Johnston 	++pnode->tn_refs;
648825ba0f2Srobj 	topo_node_unlock(pnode);
649a38fc4eaSRobert Johnston 	topo_node_rele(pnode);
650825ba0f2Srobj 
651825ba0f2Srobj 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
652825ba0f2Srobj 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
653825ba0f2Srobj 		    FM_FMRI_AUTH_PRODUCT, &err);
6549c94f155SCheng Sean Ye 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
6559c94f155SCheng Sean Ye 		    FM_FMRI_AUTH_PRODUCT_SN, &err);
656825ba0f2Srobj 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
657825ba0f2Srobj 		    FM_FMRI_AUTH_CHASSIS, &err);
658825ba0f2Srobj 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
659825ba0f2Srobj 		    FM_FMRI_AUTH_SERVER, &err);
660825ba0f2Srobj 	}
661825ba0f2Srobj 
662825ba0f2Srobj 	return (node);
663825ba0f2Srobj }
664825ba0f2Srobj 
665825ba0f2Srobj int
666825ba0f2Srobj topo_node_facility(topo_hdl_t *thp, tnode_t *node, const char *fac_type,
667825ba0f2Srobj     uint32_t fac_subtype, topo_faclist_t *faclist, int *errp)
668825ba0f2Srobj {
669825ba0f2Srobj 	tnode_t *tmp;
670a38fc4eaSRobert Johnston 	nvlist_t *rsrc, *fac;
671825ba0f2Srobj 	char *tmp_factype;
672825ba0f2Srobj 	uint32_t tmp_facsubtype;
673825ba0f2Srobj 	boolean_t list_empty = 1;
674825ba0f2Srobj 	topo_faclist_t *fac_ele;
675825ba0f2Srobj 
676825ba0f2Srobj 	bzero(faclist, sizeof (topo_faclist_t));
677825ba0f2Srobj 	for (tmp = topo_child_first(node); tmp != NULL;
678825ba0f2Srobj 	    tmp = topo_child_next(node, tmp)) {
679825ba0f2Srobj 
680825ba0f2Srobj 		topo_node_hold(tmp);
681825ba0f2Srobj 		/*
682825ba0f2Srobj 		 * If it's not a facility node, move on
683825ba0f2Srobj 		 */
684825ba0f2Srobj 		if (topo_node_flags(tmp) != TOPO_NODE_FACILITY) {
685825ba0f2Srobj 			topo_node_rele(tmp);
686825ba0f2Srobj 			continue;
687825ba0f2Srobj 		}
688825ba0f2Srobj 
689825ba0f2Srobj 		/*
690825ba0f2Srobj 		 * Lookup whether the fac type is sensor or indicator and if
691825ba0f2Srobj 		 * it's not the type we're looking for, move on
692825ba0f2Srobj 		 */
693825ba0f2Srobj 		if (topo_node_resource(tmp, &rsrc, errp) != 0) {
694825ba0f2Srobj 			topo_dprintf(thp, TOPO_DBG_ERR,
695825ba0f2Srobj 			    "Failed to get resource for node %s=%d (%s)\n",
696825ba0f2Srobj 			    topo_node_name(node), topo_node_instance(node),
697825ba0f2Srobj 			    topo_strerror(*errp));
698825ba0f2Srobj 			topo_node_rele(tmp);
699825ba0f2Srobj 			return (-1);
700825ba0f2Srobj 		}
701825ba0f2Srobj 		if ((nvlist_lookup_nvlist(rsrc, "facility", &fac) != 0) ||
702825ba0f2Srobj 		    (nvlist_lookup_string(fac, FM_FMRI_FACILITY_TYPE,
703825ba0f2Srobj 		    &tmp_factype) != 0)) {
704825ba0f2Srobj 
705f08344cbSEric Schrock 			nvlist_free(rsrc);
706825ba0f2Srobj 			topo_node_rele(tmp);
707825ba0f2Srobj 			return (-1);
708825ba0f2Srobj 		}
709f08344cbSEric Schrock 
710825ba0f2Srobj 		if (strcmp(fac_type, tmp_factype) != 0) {
711825ba0f2Srobj 			topo_node_rele(tmp);
712e5dcf7beSRobert Johnston 			nvlist_free(rsrc);
713825ba0f2Srobj 			continue;
714825ba0f2Srobj 		}
715e5dcf7beSRobert Johnston 		nvlist_free(rsrc);
716825ba0f2Srobj 
717825ba0f2Srobj 		/*
718825ba0f2Srobj 		 * Finally, look up the subtype, which is a property in the
719825ba0f2Srobj 		 * facility propgroup.  If it's a match return a pointer to the
720825ba0f2Srobj 		 * node.  Otherwise, move on.
721825ba0f2Srobj 		 */
722825ba0f2Srobj 		if (topo_prop_get_uint32(tmp, TOPO_PGROUP_FACILITY,
723825ba0f2Srobj 		    TOPO_FACILITY_TYPE, &tmp_facsubtype, errp) != 0) {
724825ba0f2Srobj 			topo_node_rele(tmp);
725825ba0f2Srobj 			return (-1);
726825ba0f2Srobj 		}
727e5dcf7beSRobert Johnston 		if (fac_subtype == tmp_facsubtype ||
728e5dcf7beSRobert Johnston 		    fac_subtype == TOPO_FAC_TYPE_ANY) {
729825ba0f2Srobj 			if ((fac_ele = topo_mod_zalloc(tmp->tn_enum,
730825ba0f2Srobj 			    sizeof (topo_faclist_t))) == NULL) {
731825ba0f2Srobj 				*errp = ETOPO_NOMEM;
732f08344cbSEric Schrock 				topo_node_rele(tmp);
733825ba0f2Srobj 				return (-1);
734825ba0f2Srobj 			}
735825ba0f2Srobj 			fac_ele->tf_node = tmp;
736825ba0f2Srobj 			topo_list_append(&faclist->tf_list, fac_ele);
737825ba0f2Srobj 			list_empty = 0;
738825ba0f2Srobj 		}
739825ba0f2Srobj 		topo_node_rele(tmp);
740825ba0f2Srobj 	}
741825ba0f2Srobj 
742825ba0f2Srobj 	if (list_empty) {
743825ba0f2Srobj 		*errp = ETOPO_FAC_NOENT;
744825ba0f2Srobj 		return (-1);
745825ba0f2Srobj 	}
746825ba0f2Srobj 	return (0);
747825ba0f2Srobj }
748825ba0f2Srobj 
7497aec1d6eScindi void
7507aec1d6eScindi topo_node_unbind(tnode_t *node)
7517aec1d6eScindi {
7527aec1d6eScindi 	if (node == NULL)
7537aec1d6eScindi 		return;
7547aec1d6eScindi 
7557aec1d6eScindi 	topo_node_lock(node);
7567aec1d6eScindi 	if (!(node->tn_state & TOPO_NODE_BOUND)) {
7577aec1d6eScindi 		topo_node_unlock(node);
7587aec1d6eScindi 		return;
7597aec1d6eScindi 	}
7607aec1d6eScindi 
7617aec1d6eScindi 	node->tn_state &= ~TOPO_NODE_BOUND;
7627aec1d6eScindi 	topo_node_unlock(node);
7637aec1d6eScindi 
76412cc75c8Scindi 	topo_dprintf(node->tn_hdl, TOPO_DBG_MODSVC,
76512cc75c8Scindi 	    "node unbound %s=%d/%s=%d refs = %d\n",
76612cc75c8Scindi 	    topo_node_name(node->tn_parent),
76712cc75c8Scindi 	    topo_node_instance(node->tn_parent), node->tn_name,
76812cc75c8Scindi 	    node->tn_instance, node->tn_refs);
76912cc75c8Scindi 
7707aec1d6eScindi 	topo_node_rele(node);
7717aec1d6eScindi }
7727aec1d6eScindi 
7737aec1d6eScindi /*ARGSUSED*/
7747aec1d6eScindi int
7757aec1d6eScindi topo_node_present(tnode_t *node)
7767aec1d6eScindi {
7777aec1d6eScindi 	return (0);
7787aec1d6eScindi }
7797aec1d6eScindi 
7807aec1d6eScindi /*ARGSUSED*/
7817aec1d6eScindi int
7827aec1d6eScindi topo_node_contains(tnode_t *er, tnode_t *ee)
7837aec1d6eScindi {
7847aec1d6eScindi 	return (0);
7857aec1d6eScindi }
7867aec1d6eScindi 
7877aec1d6eScindi /*ARGSUSED*/
7887aec1d6eScindi int
7897aec1d6eScindi topo_node_unusable(tnode_t *node)
7907aec1d6eScindi {
7917aec1d6eScindi 	return (0);
7927aec1d6eScindi }
793c40d7343Scindi 
794c40d7343Scindi topo_walk_t *
795c40d7343Scindi topo_node_walk_init(topo_hdl_t *thp, topo_mod_t *mod, tnode_t *node,
796c40d7343Scindi     int (*cb_f)(), void *pdata, int *errp)
797c40d7343Scindi {
798c40d7343Scindi 	tnode_t *child;
799c40d7343Scindi 	topo_walk_t *wp;
800c40d7343Scindi 
801c40d7343Scindi 	topo_node_hold(node);
802c40d7343Scindi 
803c40d7343Scindi 	if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) == NULL) {
804825ba0f2Srobj 		*errp = ETOPO_HDL_NOMEM;
805c40d7343Scindi 		topo_node_rele(node);
806c40d7343Scindi 		return (NULL);
807c40d7343Scindi 	}
808c40d7343Scindi 
809c40d7343Scindi 	/*
810c40d7343Scindi 	 * If this is the root of the scheme tree, start with the first
811c40d7343Scindi 	 * child
812c40d7343Scindi 	 */
813c40d7343Scindi 	topo_node_lock(node);
814c40d7343Scindi 	if (node->tn_state & TOPO_NODE_ROOT) {
815c40d7343Scindi 		if ((child = topo_child_first(node)) == NULL) {
816c40d7343Scindi 			/* Nothing to walk */
817c40d7343Scindi 			*errp = ETOPO_WALK_EMPTY;
818c40d7343Scindi 			topo_node_unlock(node);
819c40d7343Scindi 			topo_node_rele(node);
820c6765aabSeschrock 			topo_hdl_free(thp, wp, sizeof (topo_walk_t));
821c40d7343Scindi 			return (NULL);
822c40d7343Scindi 		}
823c40d7343Scindi 		topo_node_unlock(node);
824c40d7343Scindi 		topo_node_hold(child);
825c40d7343Scindi 		wp->tw_node = child;
826c40d7343Scindi 	} else {
827c40d7343Scindi 		topo_node_unlock(node);
82812cc75c8Scindi 		topo_node_hold(node); /* rele at walk end */
829c40d7343Scindi 		wp->tw_node = node;
830c40d7343Scindi 	}
831c40d7343Scindi 
832c40d7343Scindi 	wp->tw_root = node;
833c40d7343Scindi 	wp->tw_cb = cb_f;
834c40d7343Scindi 	wp->tw_pdata = pdata;
835c40d7343Scindi 	wp->tw_thp = thp;
836c40d7343Scindi 	wp->tw_mod = mod;
837c40d7343Scindi 
838c40d7343Scindi 	return (wp);
839c40d7343Scindi }
840