usr/src/lib/fm/topo/libtopo/common/topo_node.c
changeset 1414 b4126407ac5b
child 3062 46d280f5351d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_node.c	Sat Feb 11 15:36:52 2006 -0800
@@ -0,0 +1,502 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Topology Nodes
+ *
+ * Topology nodes, tnode_t, are data structures containing per-FMRI
+ * information and are linked together to form the topology tree.
+ * Nodes are created during the enumeration process of topo_snap_hold()
+ * and destroyed during topo_snap_rele().  For the most part, tnode_t data
+ * is read-only and no lock protection is required.  Nodes are
+ * held in place during tree walk functions.  Tree walk functions
+ * may access node data safely without locks.  The exception to this rule
+ * is data associated with node properties (topo_prop.c).  Properties
+ * may change at anytime and are protected by a per-property locking
+ * strategy.
+ *
+ * Enumerator plugin modules may also safely access node data.  Enumeration
+ * occurs only during topo_snap_hold() where a per-topo_hdl_t lock prevents
+ * multi-threaded access to the topology trees.
+ *
+ * Like tree walking functions, method plugin modules have access to read-only
+ * node data but may make changes to property information.
+ *
+ * Node Interfaces
+ *
+ * Nodes are created when an enumerator calls topo_node_bind().  Prior to the
+ * call to topo_node_bind(), the caller should have reserved a range of
+ * node instances with topo_node_range_create().  topo_node_range_create()
+ * does not allocate any node resources but creates the infrastruture
+ * required for a fully populated topology level.  This allows enumerators
+ * reading from a <scheme>-topology.xml file to parse the file for a range
+ * of resources before confirming the existence of a resource via a helper
+ * plugin.  Only when the resource has been confirmed to exist should
+ * the node be bound.
+ *
+ * Node range and node linkage is only performed during enumeration when it
+ * is safe to change node hash lists and next pointers. Nodes and node ranges
+ * are deallocated when all references to the node have been released:
+ * last walk completes and topo_snap_rele() is called.
+ *
+ * Node Hash/Ranges
+ *
+ * Each parent node may have one or more ranges of child nodes.  Each range
+ * serves as a hash list of like sibling nodes all with the same name but
+ * different instance numbers.  A parent may have more than one node hash
+ * (child range). If that is the case, the hash lists are strung together to
+ * form sibling relationships between ranges.  Hash/Ranges are sparsely
+ * populated with only nodes that have represented resources in the system.
+ */
+
+#include <assert.h>
+#include <pthread.h>
+#include <strings.h>
+#include <topo_alloc.h>
+#include <topo_tree.h>
+#include <topo_subr.h>
+#include <topo_error.h>
+
+static void
+topo_node_destroy(tnode_t *node)
+{
+	int i;
+	tnode_t *pnode = node->tn_parent;
+	topo_nodehash_t *nhp;
+	topo_mod_t *hmod, *mod = node->tn_enum;
+
+	if (node == NULL)
+		return;
+
+	assert(node->tn_refs == 0);
+
+	topo_dprintf(TOPO_DBG_TREE, "destroying node %s=%d\n", node->tn_name,
+	    node->tn_instance);
+	/*
+	 * If not a root node, remove this node from the parent's node hash
+	 */
+
+	if (!(node->tn_state & TOPO_NODE_ROOT)) {
+		topo_node_lock(pnode);
+
+		nhp = node->tn_phash;
+		for (i = 0; i < nhp->th_arrlen; i++) {
+			if (node == nhp->th_nodearr[i]) {
+				nhp->th_nodearr[i] = NULL;
+
+				/*
+				 * Release hold on parent
+				 */
+				--pnode->tn_refs;
+				if (pnode->tn_refs == 0)
+					topo_node_destroy(pnode);
+			}
+		}
+		topo_node_unlock(pnode);
+	}
+
+	topo_node_unlock(node);
+
+	/*
+	 * Allow enumerator to clean-up private data and then release
+	 * ref count
+	 */
+	if (mod->tm_info->tmi_release != NULL)
+		mod->tm_info->tmi_release(mod, node);
+
+	topo_method_unregister_all(mod, node);
+
+	/*
+	 * Destroy all node hash lists
+	 */
+	while ((nhp = topo_list_next(&node->tn_children)) != NULL) {
+		for (i = 0; i < nhp->th_arrlen; i++) {
+			assert(nhp->th_nodearr[i] == NULL);
+		}
+		hmod = nhp->th_enum;
+		topo_mod_strfree(hmod, nhp->th_name);
+		topo_mod_free(hmod, nhp->th_nodearr,
+		    nhp->th_arrlen * sizeof (tnode_t *));
+		topo_list_delete(&node->tn_children, nhp);
+		topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t));
+		topo_mod_rele(hmod);
+	}
+
+	/*
+	 * Destroy all property data structures, free the node and release
+	 * the module that created it
+	 */
+	topo_pgroup_destroy_all(node);
+	topo_mod_free(mod, node, sizeof (tnode_t));
+	topo_mod_rele(mod);
+}
+
+void
+topo_node_lock(tnode_t *node)
+{
+	(void) pthread_mutex_lock(&node->tn_lock);
+}
+
+void
+topo_node_unlock(tnode_t *node)
+{
+	(void) pthread_mutex_unlock(&node->tn_lock);
+}
+
+void
+topo_node_hold(tnode_t *node)
+{
+	topo_node_lock(node);
+	++node->tn_refs;
+	topo_node_unlock(node);
+}
+
+void
+topo_node_rele(tnode_t *node)
+{
+	topo_node_lock(node);
+	--node->tn_refs;
+
+	/*
+	 * Ok to remove this node from the topo tree and destroy it
+	 */
+	if (node->tn_refs == 0)
+		topo_node_destroy(node);
+	else
+		topo_node_unlock(node);
+}
+
+char *
+topo_node_name(tnode_t *node)
+{
+	return (node->tn_name);
+}
+
+topo_instance_t
+topo_node_instance(tnode_t *node)
+{
+	return (node->tn_instance);
+}
+
+void *
+topo_node_private(tnode_t *node)
+{
+	return (node->tn_priv);
+}
+
+static int
+node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
+    int err)
+{
+	topo_node_unlock(pnode);
+
+	topo_dprintf(TOPO_DBG_ERR, "unable to insert child:"
+	    "%s\n", topo_strerror(err));
+
+	if (nhp != NULL) {
+		if (nhp->th_name != NULL)
+			topo_mod_strfree(mod, nhp->th_name);
+		if (nhp->th_nodearr != NULL) {
+			topo_mod_free(mod, nhp->th_nodearr,
+			    nhp->th_arrlen * sizeof (tnode_t *));
+		}
+		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
+	}
+
+	return (topo_mod_seterrno(mod, err));
+}
+
+int
+topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
+    topo_instance_t min, topo_instance_t max)
+{
+	topo_nodehash_t *nhp;
+
+	topo_node_lock(pnode);
+
+	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
+	    (pnode->tn_state & TOPO_NODE_ROOT));
+
+	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
+	    nhp = topo_list_next(nhp)) {
+		if (strcmp(nhp->th_name, name) == 0)
+			return (node_create_seterror(mod, pnode, NULL,
+			    ETOPO_NODE_DUP));
+	}
+
+	if (min < 0 || max < min)
+		return (node_create_seterror(mod, pnode, NULL,
+		    ETOPO_NODE_INVAL));
+
+	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
+		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
+
+	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
+		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
+
+	nhp->th_arrlen = max - min + 1;
+
+	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
+	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
+		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
+
+	nhp->th_range.tr_min = min;
+	nhp->th_range.tr_max = max;
+	nhp->th_enum = mod;
+	topo_mod_hold(mod);
+
+	/*
+	 * Add these nodes to parent child list
+	 */
+	topo_list_append(&pnode->tn_children, nhp);
+	topo_node_unlock(pnode);
+
+	topo_dprintf(TOPO_DBG_MOD, "created node range %s[%d-%d]\n", name,
+	    min, max);
+
+	return (0);
+}
+
+void
+topo_node_range_destroy(tnode_t *pnode, const char *name)
+{
+	int i;
+	topo_nodehash_t *nhp;
+	topo_mod_t *mod;
+
+	topo_node_lock(pnode);
+	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
+	    nhp = topo_list_next(nhp)) {
+		if (strcmp(nhp->th_name, name) == 0) {
+			break;
+		}
+	}
+
+	if (nhp == NULL) {
+		topo_node_unlock(pnode);
+		return;
+	}
+
+	topo_list_delete(&pnode->tn_children, nhp);
+	topo_node_unlock(pnode);
+
+	/*
+	 * Should be an empty node range
+	 */
+	for (i = 0; i < nhp->th_arrlen; i++) {
+		topo_node_unbind(nhp->th_nodearr[i]);
+	}
+
+	mod = nhp->th_enum;
+	if (nhp->th_name != NULL)
+		topo_mod_strfree(mod, nhp->th_name);
+	if (nhp->th_nodearr != NULL) {
+		topo_mod_free(mod, nhp->th_nodearr,
+		    nhp->th_arrlen * sizeof (tnode_t *));
+	}
+	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
+	topo_mod_rele(mod);
+
+}
+
+tnode_t *
+topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
+{
+	int h;
+	tnode_t *node;
+	topo_nodehash_t *nhp;
+
+	topo_node_lock(pnode);
+	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
+	    nhp = topo_list_next(nhp)) {
+		if (strcmp(nhp->th_name, name) == 0) {
+
+			if (inst > nhp->th_range.tr_max ||
+			    inst < nhp->th_range.tr_min) {
+				topo_node_unlock(pnode);
+				return (NULL);
+			}
+
+			h = topo_node_hash(nhp, inst);
+			node = nhp->th_nodearr[h];
+			topo_node_unlock(pnode);
+			return (node);
+		}
+	}
+	topo_node_unlock(pnode);
+
+	return (NULL);
+}
+
+int
+topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
+{
+	return (nhp->th_range.tr_max == 0 ?
+	    nhp->th_range.tr_max : inst % (nhp->th_range.tr_max + 1));
+}
+
+static tnode_t *
+node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node, int err)
+{
+	topo_node_unlock(pnode);
+
+	(void) topo_mod_seterrno(mod, err);
+
+	if (node == NULL)
+		return (NULL);
+
+	topo_dprintf(TOPO_DBG_ERR, "unable to bind %s=%d: "
+	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
+	    node->tn_instance, topo_strerror(err));
+
+	topo_node_lock(node); /* expected to be locked */
+	topo_node_destroy(node);
+
+	return (NULL);
+}
+
+tnode_t *
+topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
+    topo_instance_t inst, nvlist_t *fmri, void *priv)
+{
+	int h, err;
+	tnode_t *node;
+	topo_nodehash_t *nhp;
+
+	topo_node_lock(pnode);
+	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
+	    nhp = topo_list_next(nhp)) {
+		if (strcmp(nhp->th_name, name) == 0) {
+
+			if (inst > nhp->th_range.tr_max ||
+			    inst < nhp->th_range.tr_min)
+				return (node_bind_seterror(mod, pnode, NULL,
+				    ETOPO_NODE_INVAL));
+
+			h = topo_node_hash(nhp, inst);
+			if (nhp->th_nodearr[h] != NULL)
+				return (node_bind_seterror(mod, pnode, NULL,
+				    ETOPO_NODE_BOUND));
+			else
+				break;
+
+		}
+	}
+
+	if (nhp == NULL)
+		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NODE_NOENT));
+
+	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
+		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NOMEM));
+
+	(void) pthread_mutex_init(&node->tn_lock, NULL);
+
+	node->tn_enum = mod;
+	node->tn_hdl = mod->tm_hdl;
+	node->tn_parent = pnode;
+	node->tn_name = nhp->th_name;
+	node->tn_instance = inst;
+	node->tn_phash = nhp;
+	node->tn_refs = 0;
+
+	/* Ref count module that bound this node */
+	topo_mod_hold(mod);
+
+	if (fmri == NULL)
+		return (node_bind_seterror(mod, pnode, node, ETOPO_NODE_INVAL));
+
+	if (topo_pgroup_create(node, TOPO_PGROUP_PROTOCOL,
+	    TOPO_STABILITY_PRIVATE, &err) < 0)
+		return (node_bind_seterror(mod, pnode, node, err));
+
+	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
+	    TOPO_PROP_SET_ONCE, fmri, &err) < 0)
+		return (node_bind_seterror(mod, pnode, node, err));
+
+	topo_dprintf(TOPO_DBG_MOD, "node bound %s=%d\n", node->tn_name,
+	    node->tn_instance);
+
+	node->tn_state |= TOPO_NODE_BOUND;
+	node->tn_priv = priv;
+
+	topo_node_hold(node);
+	nhp->th_nodearr[h] = node;
+	++pnode->tn_refs;
+	topo_node_unlock(pnode);
+
+	if (topo_pgroup_create(node, TOPO_PGROUP_SYSTEM,
+	    TOPO_STABILITY_PRIVATE, &err) == 0) {
+		(void) topo_prop_inherit(node, TOPO_PGROUP_SYSTEM,
+		    TOPO_PROP_PLATFORM, &err);
+		(void) topo_prop_inherit(node, TOPO_PGROUP_SYSTEM,
+		    TOPO_PROP_ISA, &err);
+		(void) topo_prop_inherit(node, TOPO_PGROUP_SYSTEM,
+		    TOPO_PROP_MACHINE, &err);
+	}
+
+	return (node);
+}
+
+void
+topo_node_unbind(tnode_t *node)
+{
+	if (node == NULL)
+		return;
+
+	topo_node_lock(node);
+	if (!(node->tn_state & TOPO_NODE_BOUND)) {
+		topo_node_unlock(node);
+		return;
+	}
+
+	node->tn_state &= ~TOPO_NODE_BOUND;
+	topo_node_unlock(node);
+
+	topo_node_rele(node);
+}
+
+/*ARGSUSED*/
+int
+topo_node_present(tnode_t *node)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+topo_node_contains(tnode_t *er, tnode_t *ee)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+topo_node_unusable(tnode_t *node)
+{
+	return (0);
+}