xref: /onnv-gate/usr/src/lib/fm/topo/libtopo/common/topo_node.c (revision 12967:ab9ae749152f)
11414Scindi /*
21414Scindi  * CDDL HEADER START
31414Scindi  *
41414Scindi  * The contents of this file are subject to the terms of the
53062Scindi  * Common Development and Distribution License (the "License").
63062Scindi  * You may not use this file except in compliance with the License.
71414Scindi  *
81414Scindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91414Scindi  * or http://www.opensolaris.org/os/licensing.
101414Scindi  * See the License for the specific language governing permissions
111414Scindi  * and limitations under the License.
121414Scindi  *
131414Scindi  * When distributing Covered Code, include this CDDL HEADER in each
141414Scindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151414Scindi  * If applicable, add the following below this CDDL HEADER, with the
161414Scindi  * fields enclosed by brackets "[]" replaced with your own identifying
171414Scindi  * information: Portions Copyright [yyyy] [name of copyright owner]
181414Scindi  *
191414Scindi  * CDDL HEADER END
201414Scindi  */
211414Scindi /*
22*12967Sgavin.maltby@oracle.com  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
231414Scindi  */
241414Scindi 
251414Scindi /*
261414Scindi  * Topology Nodes
271414Scindi  *
281414Scindi  * Topology nodes, tnode_t, are data structures containing per-FMRI
291414Scindi  * information and are linked together to form the topology tree.
301414Scindi  * Nodes are created during the enumeration process of topo_snap_hold()
311414Scindi  * and destroyed during topo_snap_rele().  For the most part, tnode_t data
321414Scindi  * is read-only and no lock protection is required.  Nodes are
331414Scindi  * held in place during tree walk functions.  Tree walk functions
341414Scindi  * may access node data safely without locks.  The exception to this rule
351414Scindi  * is data associated with node properties (topo_prop.c).  Properties
361414Scindi  * may change at anytime and are protected by a per-property locking
371414Scindi  * strategy.
381414Scindi  *
394087Scindi  * Enumerator plugin modules may also safely access topology nodes within their
404087Scindi  * scope of operation: the parent node passed into the enumeration op or those
414087Scindi  * nodes created by the enumerator.  Enumeration occurs only during
424087Scindi  * topo_snap_hold() where a per-topo_hdl_t lock prevents multi-threaded access
434087Scindi  * to the topology trees.
441414Scindi  *
454087Scindi  * Enumerator method operation functions may safely access and change topology
464087Scindi  * node property data, and contruct or destroy child nodes for the node
474087Scindi  * on which the operation applies.  The method may also be called to destroy
484087Scindi  * the node for which the method operation is called.  This permits
494087Scindi  * dynamic topology tree snapshots and partial enumerations for branches that
504087Scindi  * may not be needed right away.
511414Scindi  *
521414Scindi  * Node Interfaces
531414Scindi  *
544087Scindi  * Nodes are created when an enumerator calls topo_node_bind().  Prior to
554087Scindi  * calling topo_node_bind(), the enumerator should have reserved a range of
561414Scindi  * node instances with topo_node_range_create().  topo_node_range_create()
571414Scindi  * does not allocate any node resources but creates the infrastruture
581414Scindi  * required for a fully populated topology level.  This allows enumerators
591414Scindi  * reading from a <scheme>-topology.xml file to parse the file for a range
601414Scindi  * of resources before confirming the existence of a resource via a helper
611414Scindi  * plugin.  Only when the resource has been confirmed to exist should
621414Scindi  * the node be bound.
631414Scindi  *
644087Scindi  * Node range and node linkage and unlinkage is performed during enumeration and
654087Scindi  * method operations when it is safe to change node hash lists. Nodes and node
664087Scindi  * ranges are deallocated when all references to the node have been released:
671414Scindi  * last walk completes and topo_snap_rele() is called.
681414Scindi  *
691414Scindi  * Node Hash/Ranges
701414Scindi  *
711414Scindi  * Each parent node may have one or more ranges of child nodes.  Each range
724087Scindi  * is uniquely named and serves as a hash list of like sibling nodes with
731414Scindi  * different instance numbers.  A parent may have more than one node hash
741414Scindi  * (child range). If that is the case, the hash lists are strung together to
751414Scindi  * form sibling relationships between ranges.  Hash/Ranges are sparsely
761414Scindi  * populated with only nodes that have represented resources in the system.
774087Scindi  *
784087Scindi  *	_________________
794087Scindi  *	|		|
804087Scindi  *      |   tnode_t	|    -----------------------------
814087Scindi  *      |      tn_phash ---> |  topo_nodehash_t          |
824087Scindi  *      |     (children)|    |     th_nodearr (instances)|
834087Scindi  *      -----------------    |     -------------------   |
844087Scindi  *                           |  ---| 0 | 1  | ...| N |   |
854087Scindi  *                           |  |  -------------------   |  -------------------
864087Scindi  *                           |  |  th_list (siblings) ----->| topo_nodehash_t |
874087Scindi  *                           |  |                        |  -------------------
884087Scindi  *                           ---|-------------------------
894087Scindi  *                              |
904087Scindi  *                              v
914087Scindi  *                           -----------
924087Scindi  *                           | tnode_t |
934087Scindi  *                           -----------
947243Srobj  *
957243Srobj  * Facility Nodes
967243Srobj  *
977243Srobj  * Facility nodes are always leaf nodes in the topology and represent a FMRI
987243Srobj  * sensor or indicator facility for the path to which it is connected.
997243Srobj  * Facility nodes are bound to the topology with topo_node_facbind() and
1007243Srobj  * unbound with topo_node_unbind().
1011414Scindi  */
1021414Scindi 
1031414Scindi #include <assert.h>
1041414Scindi #include <pthread.h>
1051414Scindi #include <strings.h>
1063062Scindi #include <sys/fm/protocol.h>
1071414Scindi #include <topo_alloc.h>
1083062Scindi #include <topo_error.h>
1097243Srobj #include <topo_list.h>
1103062Scindi #include <topo_method.h>
1113062Scindi #include <topo_subr.h>
1121414Scindi #include <topo_tree.h>
1133062Scindi 
1143062Scindi static topo_pgroup_info_t protocol_pgroup = {
1153062Scindi 	TOPO_PGROUP_PROTOCOL,
1163062Scindi 	TOPO_STABILITY_PRIVATE,
1173062Scindi 	TOPO_STABILITY_PRIVATE,
1183062Scindi 	1
1193062Scindi };
1203062Scindi 
1213062Scindi static const topo_pgroup_info_t auth_pgroup = {
1223062Scindi 	FM_FMRI_AUTHORITY,
1233062Scindi 	TOPO_STABILITY_PRIVATE,
1243062Scindi 	TOPO_STABILITY_PRIVATE,
1253062Scindi 	1
1263062Scindi };
1271414Scindi 
1281414Scindi static void
topo_node_destroy(tnode_t * node)1291414Scindi topo_node_destroy(tnode_t *node)
1301414Scindi {
1311414Scindi 	int i;
1321414Scindi 	tnode_t *pnode = node->tn_parent;
1331414Scindi 	topo_nodehash_t *nhp;
1341414Scindi 	topo_mod_t *hmod, *mod = node->tn_enum;
1351414Scindi 
1361414Scindi 	if (node == NULL)
1371414Scindi 		return;
1381414Scindi 
1394328Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, "destroying node %s=%d\n",
1404328Scindi 	    topo_node_name(node), topo_node_instance(node));
1414328Scindi 
1421414Scindi 	assert(node->tn_refs == 0);
1431414Scindi 
1441414Scindi 	/*
1451414Scindi 	 * If not a root node, remove this node from the parent's node hash
1461414Scindi 	 */
1471414Scindi 
1481414Scindi 	if (!(node->tn_state & TOPO_NODE_ROOT)) {
1491414Scindi 		topo_node_lock(pnode);
1501414Scindi 
1511414Scindi 		nhp = node->tn_phash;
1521414Scindi 		for (i = 0; i < nhp->th_arrlen; i++) {
1531414Scindi 			if (node == nhp->th_nodearr[i]) {
1541414Scindi 				nhp->th_nodearr[i] = NULL;
1551414Scindi 
1561414Scindi 				/*
1571414Scindi 				 * Release hold on parent
1581414Scindi 				 */
1591414Scindi 				--pnode->tn_refs;
1601414Scindi 				if (pnode->tn_refs == 0)
1611414Scindi 					topo_node_destroy(pnode);
1621414Scindi 			}
1631414Scindi 		}
1641414Scindi 		topo_node_unlock(pnode);
1651414Scindi 	}
1661414Scindi 
1671414Scindi 	topo_node_unlock(node);
1681414Scindi 
1691414Scindi 	/*
1701414Scindi 	 * Allow enumerator to clean-up private data and then release
1711414Scindi 	 * ref count
1721414Scindi 	 */
1733062Scindi 	if (mod->tm_info->tmi_ops->tmo_release != NULL)
1743062Scindi 		mod->tm_info->tmi_ops->tmo_release(mod, node);
1751414Scindi 
1761414Scindi 	topo_method_unregister_all(mod, node);
1771414Scindi 
1781414Scindi 	/*
1791414Scindi 	 * Destroy all node hash lists
1801414Scindi 	 */
1811414Scindi 	while ((nhp = topo_list_next(&node->tn_children)) != NULL) {
1821414Scindi 		for (i = 0; i < nhp->th_arrlen; i++) {
1831414Scindi 			assert(nhp->th_nodearr[i] == NULL);
1841414Scindi 		}
1851414Scindi 		hmod = nhp->th_enum;
1861414Scindi 		topo_mod_strfree(hmod, nhp->th_name);
1871414Scindi 		topo_mod_free(hmod, nhp->th_nodearr,
1881414Scindi 		    nhp->th_arrlen * sizeof (tnode_t *));
1891414Scindi 		topo_list_delete(&node->tn_children, nhp);
1901414Scindi 		topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t));
1911414Scindi 		topo_mod_rele(hmod);
1921414Scindi 	}
1931414Scindi 
1941414Scindi 	/*
1951414Scindi 	 * Destroy all property data structures, free the node and release
1961414Scindi 	 * the module that created it
1971414Scindi 	 */
1981414Scindi 	topo_pgroup_destroy_all(node);
1991414Scindi 	topo_mod_free(mod, node, sizeof (tnode_t));
2001414Scindi 	topo_mod_rele(mod);
2011414Scindi }
2021414Scindi 
2031414Scindi void
topo_node_lock(tnode_t * node)2041414Scindi topo_node_lock(tnode_t *node)
2051414Scindi {
2061414Scindi 	(void) pthread_mutex_lock(&node->tn_lock);
2071414Scindi }
2081414Scindi 
2091414Scindi void
topo_node_unlock(tnode_t * node)2101414Scindi topo_node_unlock(tnode_t *node)
2111414Scindi {
2121414Scindi 	(void) pthread_mutex_unlock(&node->tn_lock);
2131414Scindi }
2141414Scindi 
2151414Scindi void
topo_node_hold(tnode_t * node)2161414Scindi topo_node_hold(tnode_t *node)
2171414Scindi {
2181414Scindi 	topo_node_lock(node);
2191414Scindi 	++node->tn_refs;
2201414Scindi 	topo_node_unlock(node);
2211414Scindi }
2221414Scindi 
2231414Scindi void
topo_node_rele(tnode_t * node)2241414Scindi topo_node_rele(tnode_t *node)
2251414Scindi {
2261414Scindi 	topo_node_lock(node);
2271414Scindi 	--node->tn_refs;
2281414Scindi 
2291414Scindi 	/*
2301414Scindi 	 * Ok to remove this node from the topo tree and destroy it
2311414Scindi 	 */
2321414Scindi 	if (node->tn_refs == 0)
2331414Scindi 		topo_node_destroy(node);
2341414Scindi 	else
2351414Scindi 		topo_node_unlock(node);
2361414Scindi }
2371414Scindi 
2381414Scindi char *
topo_node_name(tnode_t * node)2391414Scindi topo_node_name(tnode_t *node)
2401414Scindi {
2411414Scindi 	return (node->tn_name);
2421414Scindi }
2431414Scindi 
2441414Scindi topo_instance_t
topo_node_instance(tnode_t * node)2451414Scindi topo_node_instance(tnode_t *node)
2461414Scindi {
2471414Scindi 	return (node->tn_instance);
2481414Scindi }
2491414Scindi 
2505068Srobj tnode_t *
topo_node_parent(tnode_t * node)2515068Srobj topo_node_parent(tnode_t *node)
2525068Srobj {
2535068Srobj 	return (node->tn_parent);
2545068Srobj }
2555068Srobj 
2567243Srobj int
topo_node_flags(tnode_t * node)2577243Srobj topo_node_flags(tnode_t *node)
2587243Srobj {
2597243Srobj 	return (node->tn_fflags);
2607243Srobj }
2617243Srobj 
2623062Scindi void
topo_node_setspecific(tnode_t * node,void * data)2633062Scindi topo_node_setspecific(tnode_t *node, void *data)
2643062Scindi {
2653062Scindi 	node->tn_priv = data;
2663062Scindi }
2673062Scindi 
2681414Scindi void *
topo_node_getspecific(tnode_t * node)2693062Scindi topo_node_getspecific(tnode_t *node)
2701414Scindi {
2711414Scindi 	return (node->tn_priv);
2721414Scindi }
2731414Scindi 
2741414Scindi static int
node_create_seterror(topo_mod_t * mod,tnode_t * pnode,topo_nodehash_t * nhp,int err)2751414Scindi node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
2761414Scindi     int err)
2771414Scindi {
2781414Scindi 	topo_node_unlock(pnode);
2791414Scindi 
2803062Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to insert child:"
2811414Scindi 	    "%s\n", topo_strerror(err));
2821414Scindi 
2831414Scindi 	if (nhp != NULL) {
2841414Scindi 		if (nhp->th_name != NULL)
2851414Scindi 			topo_mod_strfree(mod, nhp->th_name);
2861414Scindi 		if (nhp->th_nodearr != NULL) {
2871414Scindi 			topo_mod_free(mod, nhp->th_nodearr,
2881414Scindi 			    nhp->th_arrlen * sizeof (tnode_t *));
2891414Scindi 		}
2901414Scindi 		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
2911414Scindi 	}
2921414Scindi 
2931414Scindi 	return (topo_mod_seterrno(mod, err));
2941414Scindi }
2951414Scindi 
2961414Scindi int
topo_node_range_create(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max)2971414Scindi topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
2981414Scindi     topo_instance_t min, topo_instance_t max)
2991414Scindi {
3001414Scindi 	topo_nodehash_t *nhp;
3011414Scindi 
3021414Scindi 	topo_node_lock(pnode);
3031414Scindi 
3041414Scindi 	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
3051414Scindi 	    (pnode->tn_state & TOPO_NODE_ROOT));
3061414Scindi 
3071414Scindi 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
3081414Scindi 	    nhp = topo_list_next(nhp)) {
3091414Scindi 		if (strcmp(nhp->th_name, name) == 0)
3101414Scindi 			return (node_create_seterror(mod, pnode, NULL,
3117243Srobj 			    EMOD_NODE_DUP));
3121414Scindi 	}
3131414Scindi 
3141414Scindi 	if (min < 0 || max < min)
3151414Scindi 		return (node_create_seterror(mod, pnode, NULL,
3167243Srobj 		    EMOD_NODE_RANGE));
3171414Scindi 
3181414Scindi 	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
3197243Srobj 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
3201414Scindi 
3211414Scindi 	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
3227243Srobj 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
3231414Scindi 
3241414Scindi 	nhp->th_arrlen = max - min + 1;
3251414Scindi 
3261414Scindi 	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
3271414Scindi 	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
3287243Srobj 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
3291414Scindi 
3301414Scindi 	nhp->th_range.tr_min = min;
3311414Scindi 	nhp->th_range.tr_max = max;
3321414Scindi 	nhp->th_enum = mod;
3331414Scindi 	topo_mod_hold(mod);
3341414Scindi 
3351414Scindi 	/*
3361414Scindi 	 * Add these nodes to parent child list
3371414Scindi 	 */
3381414Scindi 	topo_list_append(&pnode->tn_children, nhp);
3391414Scindi 	topo_node_unlock(pnode);
3401414Scindi 
3413062Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
3423062Scindi 	    "created node range %s[%d-%d]\n", name, min, max);
3431414Scindi 
3441414Scindi 	return (0);
3451414Scindi }
3461414Scindi 
3471414Scindi void
topo_node_range_destroy(tnode_t * pnode,const char * name)3481414Scindi topo_node_range_destroy(tnode_t *pnode, const char *name)
3491414Scindi {
3501414Scindi 	int i;
3511414Scindi 	topo_nodehash_t *nhp;
3521414Scindi 	topo_mod_t *mod;
3531414Scindi 
3541414Scindi 	topo_node_lock(pnode);
3551414Scindi 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
3561414Scindi 	    nhp = topo_list_next(nhp)) {
3571414Scindi 		if (strcmp(nhp->th_name, name) == 0) {
3581414Scindi 			break;
3591414Scindi 		}
3601414Scindi 	}
3611414Scindi 
3621414Scindi 	if (nhp == NULL) {
3631414Scindi 		topo_node_unlock(pnode);
3641414Scindi 		return;
3651414Scindi 	}
3661414Scindi 
3674328Scindi 	for (i = 0; i < nhp->th_arrlen; i++)
3684328Scindi 		assert(nhp->th_nodearr[i] == NULL);
3694328Scindi 
3701414Scindi 	topo_list_delete(&pnode->tn_children, nhp);
3711414Scindi 	topo_node_unlock(pnode);
3721414Scindi 
3731414Scindi 	mod = nhp->th_enum;
3741414Scindi 	if (nhp->th_name != NULL)
3751414Scindi 		topo_mod_strfree(mod, nhp->th_name);
3761414Scindi 	if (nhp->th_nodearr != NULL) {
3771414Scindi 		topo_mod_free(mod, nhp->th_nodearr,
3781414Scindi 		    nhp->th_arrlen * sizeof (tnode_t *));
3791414Scindi 	}
3801414Scindi 	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
3811414Scindi 	topo_mod_rele(mod);
3821414Scindi 
3831414Scindi }
3841414Scindi 
3851414Scindi tnode_t *
topo_node_lookup(tnode_t * pnode,const char * name,topo_instance_t inst)3861414Scindi topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
3871414Scindi {
3881414Scindi 	int h;
3891414Scindi 	tnode_t *node;
3901414Scindi 	topo_nodehash_t *nhp;
3911414Scindi 
392*12967Sgavin.maltby@oracle.com 	topo_dprintf(pnode->tn_hdl, TOPO_DBG_MODSVC,
393*12967Sgavin.maltby@oracle.com 	    "topo_node_lookup: looking for '%s' instance %d\n", name, inst);
394*12967Sgavin.maltby@oracle.com 
3951414Scindi 	topo_node_lock(pnode);
3961414Scindi 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
3971414Scindi 	    nhp = topo_list_next(nhp)) {
3981414Scindi 		if (strcmp(nhp->th_name, name) == 0) {
3991414Scindi 
4001414Scindi 			if (inst > nhp->th_range.tr_max ||
4011414Scindi 			    inst < nhp->th_range.tr_min) {
4021414Scindi 				topo_node_unlock(pnode);
4031414Scindi 				return (NULL);
4041414Scindi 			}
4051414Scindi 
4061414Scindi 			h = topo_node_hash(nhp, inst);
4071414Scindi 			node = nhp->th_nodearr[h];
4081414Scindi 			topo_node_unlock(pnode);
4091414Scindi 			return (node);
4101414Scindi 		}
4111414Scindi 	}
4121414Scindi 	topo_node_unlock(pnode);
4131414Scindi 
4141414Scindi 	return (NULL);
4151414Scindi }
4161414Scindi 
4171414Scindi int
topo_node_hash(topo_nodehash_t * nhp,topo_instance_t inst)4181414Scindi topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
4191414Scindi {
4205254Sgavinm 	return ((inst - nhp->th_range.tr_min) % nhp->th_arrlen);
4211414Scindi }
4221414Scindi 
4231414Scindi static tnode_t *
node_bind_seterror(topo_mod_t * mod,tnode_t * pnode,tnode_t * node,boolean_t pnode_locked,int err)4247585SRobert.Johnston@Sun.COM node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node,
4257585SRobert.Johnston@Sun.COM     boolean_t pnode_locked, int err)
4261414Scindi {
4277585SRobert.Johnston@Sun.COM 	if (pnode_locked)
4287585SRobert.Johnston@Sun.COM 		topo_node_unlock(pnode);
4291414Scindi 
4301414Scindi 	(void) topo_mod_seterrno(mod, err);
4311414Scindi 
4321414Scindi 	if (node == NULL)
4331414Scindi 		return (NULL);
4341414Scindi 
4353062Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
4361414Scindi 	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
4371414Scindi 	    node->tn_instance, topo_strerror(err));
4381414Scindi 
4391414Scindi 	topo_node_lock(node); /* expected to be locked */
4401414Scindi 	topo_node_destroy(node);
4411414Scindi 
4421414Scindi 	return (NULL);
4431414Scindi }
4441414Scindi 
4451414Scindi tnode_t *
topo_node_bind(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t inst,nvlist_t * fmri)4461414Scindi topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
4473062Scindi     topo_instance_t inst, nvlist_t *fmri)
4481414Scindi {
4491414Scindi 	int h, err;
4501414Scindi 	tnode_t *node;
4511414Scindi 	topo_nodehash_t *nhp;
4521414Scindi 
4531414Scindi 	topo_node_lock(pnode);
4541414Scindi 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
4551414Scindi 	    nhp = topo_list_next(nhp)) {
4561414Scindi 		if (strcmp(nhp->th_name, name) == 0) {
4571414Scindi 
4581414Scindi 			if (inst > nhp->th_range.tr_max ||
4591414Scindi 			    inst < nhp->th_range.tr_min)
4601414Scindi 				return (node_bind_seterror(mod, pnode, NULL,
4617585SRobert.Johnston@Sun.COM 				    B_TRUE, EMOD_NODE_RANGE));
4621414Scindi 
4631414Scindi 			h = topo_node_hash(nhp, inst);
4641414Scindi 			if (nhp->th_nodearr[h] != NULL)
4651414Scindi 				return (node_bind_seterror(mod, pnode, NULL,
4667585SRobert.Johnston@Sun.COM 				    B_TRUE, EMOD_NODE_BOUND));
4671414Scindi 			else
4681414Scindi 				break;
4691414Scindi 
4701414Scindi 		}
4711414Scindi 	}
4721414Scindi 
4731414Scindi 	if (nhp == NULL)
4747585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
4757585SRobert.Johnston@Sun.COM 		    EMOD_NODE_NOENT));
4761414Scindi 
4771414Scindi 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
4787585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
4797585SRobert.Johnston@Sun.COM 		    EMOD_NOMEM));
4801414Scindi 
4811414Scindi 	(void) pthread_mutex_init(&node->tn_lock, NULL);
4821414Scindi 
4831414Scindi 	node->tn_enum = mod;
4841414Scindi 	node->tn_hdl = mod->tm_hdl;
4851414Scindi 	node->tn_parent = pnode;
4861414Scindi 	node->tn_name = nhp->th_name;
4871414Scindi 	node->tn_instance = inst;
4881414Scindi 	node->tn_phash = nhp;
4891414Scindi 	node->tn_refs = 0;
4901414Scindi 
4911414Scindi 	/* Ref count module that bound this node */
4921414Scindi 	topo_mod_hold(mod);
4931414Scindi 
4941414Scindi 	if (fmri == NULL)
4957585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, node, B_TRUE,
4967585SRobert.Johnston@Sun.COM 		    EMOD_NVL_INVAL));
4971414Scindi 
4983062Scindi 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
4997585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
5001414Scindi 
5011414Scindi 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
5023062Scindi 	    TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
5037585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
5041414Scindi 
5053062Scindi 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
5064328Scindi 	    "node bound %s=%d/%s=%d\n", topo_node_name(pnode),
5074328Scindi 	    topo_node_instance(pnode), node->tn_name, node->tn_instance);
5081414Scindi 
5091414Scindi 	node->tn_state |= TOPO_NODE_BOUND;
5101414Scindi 
5111414Scindi 	topo_node_hold(node);
5121414Scindi 	nhp->th_nodearr[h] = node;
5131414Scindi 	++pnode->tn_refs;
5147243Srobj 
5151414Scindi 	topo_node_unlock(pnode);
5161414Scindi 
5173062Scindi 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
5183062Scindi 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
5193062Scindi 		    FM_FMRI_AUTH_PRODUCT, &err);
5203062Scindi 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
52110462SSean.Ye@Sun.COM 		    FM_FMRI_AUTH_PRODUCT_SN, &err);
52210462SSean.Ye@Sun.COM 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
5233062Scindi 		    FM_FMRI_AUTH_CHASSIS, &err);
5243062Scindi 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
5253062Scindi 		    FM_FMRI_AUTH_SERVER, &err);
5261414Scindi 	}
5271414Scindi 
5281414Scindi 	return (node);
5291414Scindi }
5301414Scindi 
5317243Srobj tnode_t *
topo_node_facbind(topo_mod_t * mod,tnode_t * pnode,const char * name,const char * type)5327243Srobj topo_node_facbind(topo_mod_t *mod, tnode_t *pnode, const char *name,
5337243Srobj     const char *type)
5347243Srobj {
5357243Srobj 	int h, err;
5367243Srobj 	tnode_t *node;
5377243Srobj 	topo_nodehash_t *nhp;
5387243Srobj 	topo_instance_t inst = 0;
5397243Srobj 	nvlist_t *pfmri, *fnvl;
5407243Srobj 
5417243Srobj 	/*
5427243Srobj 	 * Create a single entry range for this facility
5437243Srobj 	 */
5447243Srobj 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0)
5457243Srobj 		return (NULL);  /* mod errno set */
5467243Srobj 
5477585SRobert.Johnston@Sun.COM 	topo_node_hold(pnode);
5487243Srobj 	topo_node_lock(pnode);
5497243Srobj 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
5507243Srobj 	    nhp = topo_list_next(nhp)) {
5517243Srobj 		if (strcmp(nhp->th_name, name) == 0) {
5527243Srobj 
5537243Srobj 			if (inst > nhp->th_range.tr_max ||
5547585SRobert.Johnston@Sun.COM 			    inst < nhp->th_range.tr_min) {
5557585SRobert.Johnston@Sun.COM 				topo_node_rele(pnode);
5567243Srobj 				return (node_bind_seterror(mod, pnode, NULL,
5577585SRobert.Johnston@Sun.COM 				    B_TRUE, EMOD_NVL_INVAL));
5587585SRobert.Johnston@Sun.COM 			}
5597243Srobj 			h = topo_node_hash(nhp, inst);
5607585SRobert.Johnston@Sun.COM 			if (nhp->th_nodearr[h] != NULL) {
5617585SRobert.Johnston@Sun.COM 				topo_node_rele(pnode);
5627243Srobj 				return (node_bind_seterror(mod, pnode, NULL,
5637585SRobert.Johnston@Sun.COM 				    B_TRUE, EMOD_NODE_BOUND));
5647585SRobert.Johnston@Sun.COM 			} else
5657243Srobj 				break;
5667243Srobj 
5677243Srobj 		}
5687243Srobj 	}
5697585SRobert.Johnston@Sun.COM 	topo_node_unlock(pnode);
5707243Srobj 
5717585SRobert.Johnston@Sun.COM 	if (nhp == NULL) {
5727585SRobert.Johnston@Sun.COM 		topo_node_rele(pnode);
5737585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
5747585SRobert.Johnston@Sun.COM 		    EMOD_NODE_NOENT));
5757585SRobert.Johnston@Sun.COM 	}
5767585SRobert.Johnston@Sun.COM 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL) {
5777585SRobert.Johnston@Sun.COM 		topo_node_rele(pnode);
5787585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
5797585SRobert.Johnston@Sun.COM 		    EMOD_NOMEM));
5807585SRobert.Johnston@Sun.COM 	}
5817243Srobj 
5827243Srobj 	(void) pthread_mutex_init(&node->tn_lock, NULL);
5837243Srobj 
5847243Srobj 	node->tn_enum = mod;
5857243Srobj 	node->tn_hdl = mod->tm_hdl;
5867243Srobj 	node->tn_parent = pnode;
5877243Srobj 	node->tn_name = nhp->th_name;
5887243Srobj 	node->tn_instance = inst;
5897243Srobj 	node->tn_phash = nhp;
5907243Srobj 	node->tn_refs = 0;
5917243Srobj 	node->tn_fflags = TOPO_NODE_FACILITY;
5927243Srobj 
5937243Srobj 	/* Ref count module that bound this node */
5947243Srobj 	topo_mod_hold(mod);
5957243Srobj 
5967585SRobert.Johnston@Sun.COM 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0) {
5977585SRobert.Johnston@Sun.COM 		topo_node_rele(pnode);
5987585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
5997585SRobert.Johnston@Sun.COM 	}
6007585SRobert.Johnston@Sun.COM 	if (topo_mod_nvalloc(mod, &fnvl, NV_UNIQUE_NAME) < 0) {
6017585SRobert.Johnston@Sun.COM 		topo_node_rele(pnode);
6027585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, node, B_FALSE,
6037585SRobert.Johnston@Sun.COM 		    EMOD_NOMEM));
6047585SRobert.Johnston@Sun.COM 	}
6057243Srobj 	if (nvlist_add_string(fnvl, FM_FMRI_FACILITY_NAME, name) != 0 ||
6067243Srobj 	    nvlist_add_string(fnvl, FM_FMRI_FACILITY_TYPE, type) != 0) {
6077243Srobj 		nvlist_free(fnvl);
6087585SRobert.Johnston@Sun.COM 		topo_node_rele(pnode);
6097585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
6107585SRobert.Johnston@Sun.COM 		    EMOD_FMRI_NVL));
6117243Srobj 	}
6127243Srobj 
6137243Srobj 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
6147243Srobj 		nvlist_free(fnvl);
6157585SRobert.Johnston@Sun.COM 		topo_node_rele(pnode);
6167585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
6177243Srobj 	}
6187243Srobj 
6197243Srobj 	if (nvlist_add_nvlist(pfmri, FM_FMRI_FACILITY, fnvl) != 0) {
6207243Srobj 		nvlist_free(fnvl);
6217243Srobj 		nvlist_free(pfmri);
6227585SRobert.Johnston@Sun.COM 		topo_node_rele(pnode);
6237585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
6247585SRobert.Johnston@Sun.COM 		    EMOD_FMRI_NVL));
6257243Srobj 	}
6267243Srobj 
6277243Srobj 	nvlist_free(fnvl);
6287243Srobj 
6297243Srobj 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
6307243Srobj 	    TOPO_PROP_IMMUTABLE, pfmri, &err) < 0) {
6317243Srobj 		nvlist_free(pfmri);
6327585SRobert.Johnston@Sun.COM 		topo_node_rele(pnode);
6337585SRobert.Johnston@Sun.COM 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
6347243Srobj 	}
6357243Srobj 
6367243Srobj 	nvlist_free(pfmri);
6377243Srobj 
6387243Srobj 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
6397243Srobj 	    "facility node bound %s=%s\n", type, node->tn_name);
6407243Srobj 
6417243Srobj 	node->tn_state |= TOPO_NODE_BOUND;
6427243Srobj 
6437243Srobj 	topo_node_hold(node);
6447243Srobj 	nhp->th_nodearr[h] = node;
6457585SRobert.Johnston@Sun.COM 
6467585SRobert.Johnston@Sun.COM 	topo_node_lock(pnode);
6477243Srobj 	++pnode->tn_refs;
6487243Srobj 	topo_node_unlock(pnode);
6497585SRobert.Johnston@Sun.COM 	topo_node_rele(pnode);
6507243Srobj 
6517243Srobj 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
6527243Srobj 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
6537243Srobj 		    FM_FMRI_AUTH_PRODUCT, &err);
6547243Srobj 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
65510462SSean.Ye@Sun.COM 		    FM_FMRI_AUTH_PRODUCT_SN, &err);
65610462SSean.Ye@Sun.COM 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
6577243Srobj 		    FM_FMRI_AUTH_CHASSIS, &err);
6587243Srobj 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
6597243Srobj 		    FM_FMRI_AUTH_SERVER, &err);
6607243Srobj 	}
6617243Srobj 
6627243Srobj 	return (node);
6637243Srobj }
6647243Srobj 
6657243Srobj int
topo_node_facility(topo_hdl_t * thp,tnode_t * node,const char * fac_type,uint32_t fac_subtype,topo_faclist_t * faclist,int * errp)6667243Srobj topo_node_facility(topo_hdl_t *thp, tnode_t *node, const char *fac_type,
6677243Srobj     uint32_t fac_subtype, topo_faclist_t *faclist, int *errp)
6687243Srobj {
6697243Srobj 	tnode_t *tmp;
6707585SRobert.Johnston@Sun.COM 	nvlist_t *rsrc, *fac;
6717243Srobj 	char *tmp_factype;
6727243Srobj 	uint32_t tmp_facsubtype;
6737243Srobj 	boolean_t list_empty = 1;
6747243Srobj 	topo_faclist_t *fac_ele;
6757243Srobj 
6767243Srobj 	bzero(faclist, sizeof (topo_faclist_t));
6777243Srobj 	for (tmp = topo_child_first(node); tmp != NULL;
6787243Srobj 	    tmp = topo_child_next(node, tmp)) {
6797243Srobj 
6807243Srobj 		topo_node_hold(tmp);
6817243Srobj 		/*
6827243Srobj 		 * If it's not a facility node, move on
6837243Srobj 		 */
6847243Srobj 		if (topo_node_flags(tmp) != TOPO_NODE_FACILITY) {
6857243Srobj 			topo_node_rele(tmp);
6867243Srobj 			continue;
6877243Srobj 		}
6887243Srobj 
6897243Srobj 		/*
6907243Srobj 		 * Lookup whether the fac type is sensor or indicator and if
6917243Srobj 		 * it's not the type we're looking for, move on
6927243Srobj 		 */
6937243Srobj 		if (topo_node_resource(tmp, &rsrc, errp) != 0) {
6947243Srobj 			topo_dprintf(thp, TOPO_DBG_ERR,
6957243Srobj 			    "Failed to get resource for node %s=%d (%s)\n",
6967243Srobj 			    topo_node_name(node), topo_node_instance(node),
6977243Srobj 			    topo_strerror(*errp));
6987243Srobj 			topo_node_rele(tmp);
6997243Srobj 			return (-1);
7007243Srobj 		}
7017243Srobj 		if ((nvlist_lookup_nvlist(rsrc, "facility", &fac) != 0) ||
7027243Srobj 		    (nvlist_lookup_string(fac, FM_FMRI_FACILITY_TYPE,
7037243Srobj 		    &tmp_factype) != 0)) {
7047243Srobj 
7057345SEric.Schrock@Sun.COM 			nvlist_free(rsrc);
7067243Srobj 			topo_node_rele(tmp);
7077243Srobj 			return (-1);
7087243Srobj 		}
7097345SEric.Schrock@Sun.COM 
7107243Srobj 		if (strcmp(fac_type, tmp_factype) != 0) {
7117243Srobj 			topo_node_rele(tmp);
7128526SRobert.Johnston@Sun.COM 			nvlist_free(rsrc);
7137243Srobj 			continue;
7147243Srobj 		}
7158526SRobert.Johnston@Sun.COM 		nvlist_free(rsrc);
7167243Srobj 
7177243Srobj 		/*
7187243Srobj 		 * Finally, look up the subtype, which is a property in the
7197243Srobj 		 * facility propgroup.  If it's a match return a pointer to the
7207243Srobj 		 * node.  Otherwise, move on.
7217243Srobj 		 */
7227243Srobj 		if (topo_prop_get_uint32(tmp, TOPO_PGROUP_FACILITY,
7237243Srobj 		    TOPO_FACILITY_TYPE, &tmp_facsubtype, errp) != 0) {
7247243Srobj 			topo_node_rele(tmp);
7257243Srobj 			return (-1);
7267243Srobj 		}
7278526SRobert.Johnston@Sun.COM 		if (fac_subtype == tmp_facsubtype ||
7288526SRobert.Johnston@Sun.COM 		    fac_subtype == TOPO_FAC_TYPE_ANY) {
7297243Srobj 			if ((fac_ele = topo_mod_zalloc(tmp->tn_enum,
7307243Srobj 			    sizeof (topo_faclist_t))) == NULL) {
7317243Srobj 				*errp = ETOPO_NOMEM;
7327345SEric.Schrock@Sun.COM 				topo_node_rele(tmp);
7337243Srobj 				return (-1);
7347243Srobj 			}
7357243Srobj 			fac_ele->tf_node = tmp;
7367243Srobj 			topo_list_append(&faclist->tf_list, fac_ele);
7377243Srobj 			list_empty = 0;
7387243Srobj 		}
7397243Srobj 		topo_node_rele(tmp);
7407243Srobj 	}
7417243Srobj 
7427243Srobj 	if (list_empty) {
7437243Srobj 		*errp = ETOPO_FAC_NOENT;
7447243Srobj 		return (-1);
7457243Srobj 	}
7467243Srobj 	return (0);
7477243Srobj }
7487243Srobj 
7491414Scindi void
topo_node_unbind(tnode_t * node)7501414Scindi topo_node_unbind(tnode_t *node)
7511414Scindi {
7521414Scindi 	if (node == NULL)
7531414Scindi 		return;
7541414Scindi 
7551414Scindi 	topo_node_lock(node);
7561414Scindi 	if (!(node->tn_state & TOPO_NODE_BOUND)) {
7571414Scindi 		topo_node_unlock(node);
7581414Scindi 		return;
7591414Scindi 	}
7601414Scindi 
7611414Scindi 	node->tn_state &= ~TOPO_NODE_BOUND;
7621414Scindi 	topo_node_unlock(node);
7631414Scindi 
7644328Scindi 	topo_dprintf(node->tn_hdl, TOPO_DBG_MODSVC,
7654328Scindi 	    "node unbound %s=%d/%s=%d refs = %d\n",
7664328Scindi 	    topo_node_name(node->tn_parent),
7674328Scindi 	    topo_node_instance(node->tn_parent), node->tn_name,
7684328Scindi 	    node->tn_instance, node->tn_refs);
7694328Scindi 
7701414Scindi 	topo_node_rele(node);
7711414Scindi }
7721414Scindi 
7731414Scindi /*ARGSUSED*/
7741414Scindi int
topo_node_present(tnode_t * node)7751414Scindi topo_node_present(tnode_t *node)
7761414Scindi {
7771414Scindi 	return (0);
7781414Scindi }
7791414Scindi 
7801414Scindi /*ARGSUSED*/
7811414Scindi int
topo_node_contains(tnode_t * er,tnode_t * ee)7821414Scindi topo_node_contains(tnode_t *er, tnode_t *ee)
7831414Scindi {
7841414Scindi 	return (0);
7851414Scindi }
7861414Scindi 
7871414Scindi /*ARGSUSED*/
7881414Scindi int
topo_node_unusable(tnode_t * node)7891414Scindi topo_node_unusable(tnode_t *node)
7901414Scindi {
7911414Scindi 	return (0);
7921414Scindi }
7934087Scindi 
7944087Scindi topo_walk_t *
topo_node_walk_init(topo_hdl_t * thp,topo_mod_t * mod,tnode_t * node,int (* cb_f)(),void * pdata,int * errp)7954087Scindi topo_node_walk_init(topo_hdl_t *thp, topo_mod_t *mod, tnode_t *node,
7964087Scindi     int (*cb_f)(), void *pdata, int *errp)
7974087Scindi {
7984087Scindi 	tnode_t *child;
7994087Scindi 	topo_walk_t *wp;
8004087Scindi 
8014087Scindi 	topo_node_hold(node);
8024087Scindi 
8034087Scindi 	if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) == NULL) {
8047243Srobj 		*errp = ETOPO_HDL_NOMEM;
8054087Scindi 		topo_node_rele(node);
8064087Scindi 		return (NULL);
8074087Scindi 	}
8084087Scindi 
8094087Scindi 	/*
8104087Scindi 	 * If this is the root of the scheme tree, start with the first
8114087Scindi 	 * child
8124087Scindi 	 */
8134087Scindi 	topo_node_lock(node);
8144087Scindi 	if (node->tn_state & TOPO_NODE_ROOT) {
8154087Scindi 		if ((child = topo_child_first(node)) == NULL) {
8164087Scindi 			/* Nothing to walk */
8174087Scindi 			*errp = ETOPO_WALK_EMPTY;
8184087Scindi 			topo_node_unlock(node);
8194087Scindi 			topo_node_rele(node);
8205501Seschrock 			topo_hdl_free(thp, wp, sizeof (topo_walk_t));
8214087Scindi 			return (NULL);
8224087Scindi 		}
8234087Scindi 		topo_node_unlock(node);
8244087Scindi 		topo_node_hold(child);
8254087Scindi 		wp->tw_node = child;
8264087Scindi 	} else {
8274087Scindi 		topo_node_unlock(node);
8284328Scindi 		topo_node_hold(node); /* rele at walk end */
8294087Scindi 		wp->tw_node = node;
8304087Scindi 	}
8314087Scindi 
8324087Scindi 	wp->tw_root = node;
8334087Scindi 	wp->tw_cb = cb_f;
8344087Scindi 	wp->tw_pdata = pdata;
8354087Scindi 	wp->tw_thp = thp;
8364087Scindi 	wp->tw_mod = mod;
8374087Scindi 
8384087Scindi 	return (wp);
8394087Scindi }
840