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