topo_node.c revision 7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fe
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Topology Nodes
31 *
32 * Topology nodes, tnode_t, are data structures containing per-FMRI
33 * information and are linked together to form the topology tree.
34 * Nodes are created during the enumeration process of topo_snap_hold()
35 * and destroyed during topo_snap_rele().  For the most part, tnode_t data
36 * is read-only and no lock protection is required.  Nodes are
37 * held in place during tree walk functions.  Tree walk functions
38 * may access node data safely without locks.  The exception to this rule
39 * is data associated with node properties (topo_prop.c).  Properties
40 * may change at anytime and are protected by a per-property locking
41 * strategy.
42 *
43 * Enumerator plugin modules may also safely access node data.  Enumeration
44 * occurs only during topo_snap_hold() where a per-topo_hdl_t lock prevents
45 * multi-threaded access to the topology trees.
46 *
47 * Like tree walking functions, method plugin modules have access to read-only
48 * node data but may make changes to property information.
49 *
50 * Node Interfaces
51 *
52 * Nodes are created when an enumerator calls topo_node_bind().  Prior to the
53 * call to topo_node_bind(), the caller should have reserved a range of
54 * node instances with topo_node_range_create().  topo_node_range_create()
55 * does not allocate any node resources but creates the infrastruture
56 * required for a fully populated topology level.  This allows enumerators
57 * reading from a <scheme>-topology.xml file to parse the file for a range
58 * of resources before confirming the existence of a resource via a helper
59 * plugin.  Only when the resource has been confirmed to exist should
60 * the node be bound.
61 *
62 * Node range and node linkage is only performed during enumeration when it
63 * is safe to change node hash lists and next pointers. Nodes and node ranges
64 * are deallocated when all references to the node have been released:
65 * last walk completes and topo_snap_rele() is called.
66 *
67 * Node Hash/Ranges
68 *
69 * Each parent node may have one or more ranges of child nodes.  Each range
70 * serves as a hash list of like sibling nodes all with the same name but
71 * different instance numbers.  A parent may have more than one node hash
72 * (child range). If that is the case, the hash lists are strung together to
73 * form sibling relationships between ranges.  Hash/Ranges are sparsely
74 * populated with only nodes that have represented resources in the system.
75 */
76
77#include <assert.h>
78#include <pthread.h>
79#include <strings.h>
80#include <topo_alloc.h>
81#include <topo_tree.h>
82#include <topo_subr.h>
83#include <topo_error.h>
84
85static void
86topo_node_destroy(tnode_t *node)
87{
88	int i;
89	tnode_t *pnode = node->tn_parent;
90	topo_nodehash_t *nhp;
91	topo_mod_t *hmod, *mod = node->tn_enum;
92
93	if (node == NULL)
94		return;
95
96	assert(node->tn_refs == 0);
97
98	topo_dprintf(TOPO_DBG_TREE, "destroying node %s=%d\n", node->tn_name,
99	    node->tn_instance);
100	/*
101	 * If not a root node, remove this node from the parent's node hash
102	 */
103
104	if (!(node->tn_state & TOPO_NODE_ROOT)) {
105		topo_node_lock(pnode);
106
107		nhp = node->tn_phash;
108		for (i = 0; i < nhp->th_arrlen; i++) {
109			if (node == nhp->th_nodearr[i]) {
110				nhp->th_nodearr[i] = NULL;
111
112				/*
113				 * Release hold on parent
114				 */
115				--pnode->tn_refs;
116				if (pnode->tn_refs == 0)
117					topo_node_destroy(pnode);
118			}
119		}
120		topo_node_unlock(pnode);
121	}
122
123	topo_node_unlock(node);
124
125	/*
126	 * Allow enumerator to clean-up private data and then release
127	 * ref count
128	 */
129	if (mod->tm_info->tmi_release != NULL)
130		mod->tm_info->tmi_release(mod, node);
131
132	topo_method_unregister_all(mod, node);
133
134	/*
135	 * Destroy all node hash lists
136	 */
137	while ((nhp = topo_list_next(&node->tn_children)) != NULL) {
138		for (i = 0; i < nhp->th_arrlen; i++) {
139			assert(nhp->th_nodearr[i] == NULL);
140		}
141		hmod = nhp->th_enum;
142		topo_mod_strfree(hmod, nhp->th_name);
143		topo_mod_free(hmod, nhp->th_nodearr,
144		    nhp->th_arrlen * sizeof (tnode_t *));
145		topo_list_delete(&node->tn_children, nhp);
146		topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t));
147		topo_mod_rele(hmod);
148	}
149
150	/*
151	 * Destroy all property data structures, free the node and release
152	 * the module that created it
153	 */
154	topo_pgroup_destroy_all(node);
155	topo_mod_free(mod, node, sizeof (tnode_t));
156	topo_mod_rele(mod);
157}
158
159void
160topo_node_lock(tnode_t *node)
161{
162	(void) pthread_mutex_lock(&node->tn_lock);
163}
164
165void
166topo_node_unlock(tnode_t *node)
167{
168	(void) pthread_mutex_unlock(&node->tn_lock);
169}
170
171void
172topo_node_hold(tnode_t *node)
173{
174	topo_node_lock(node);
175	++node->tn_refs;
176	topo_node_unlock(node);
177}
178
179void
180topo_node_rele(tnode_t *node)
181{
182	topo_node_lock(node);
183	--node->tn_refs;
184
185	/*
186	 * Ok to remove this node from the topo tree and destroy it
187	 */
188	if (node->tn_refs == 0)
189		topo_node_destroy(node);
190	else
191		topo_node_unlock(node);
192}
193
194char *
195topo_node_name(tnode_t *node)
196{
197	return (node->tn_name);
198}
199
200topo_instance_t
201topo_node_instance(tnode_t *node)
202{
203	return (node->tn_instance);
204}
205
206void *
207topo_node_private(tnode_t *node)
208{
209	return (node->tn_priv);
210}
211
212static int
213node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
214    int err)
215{
216	topo_node_unlock(pnode);
217
218	topo_dprintf(TOPO_DBG_ERR, "unable to insert child:"
219	    "%s\n", topo_strerror(err));
220
221	if (nhp != NULL) {
222		if (nhp->th_name != NULL)
223			topo_mod_strfree(mod, nhp->th_name);
224		if (nhp->th_nodearr != NULL) {
225			topo_mod_free(mod, nhp->th_nodearr,
226			    nhp->th_arrlen * sizeof (tnode_t *));
227		}
228		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
229	}
230
231	return (topo_mod_seterrno(mod, err));
232}
233
234int
235topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
236    topo_instance_t min, topo_instance_t max)
237{
238	topo_nodehash_t *nhp;
239
240	topo_node_lock(pnode);
241
242	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
243	    (pnode->tn_state & TOPO_NODE_ROOT));
244
245	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
246	    nhp = topo_list_next(nhp)) {
247		if (strcmp(nhp->th_name, name) == 0)
248			return (node_create_seterror(mod, pnode, NULL,
249			    ETOPO_NODE_DUP));
250	}
251
252	if (min < 0 || max < min)
253		return (node_create_seterror(mod, pnode, NULL,
254		    ETOPO_NODE_INVAL));
255
256	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
257		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
258
259	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
260		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
261
262	nhp->th_arrlen = max - min + 1;
263
264	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
265	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
266		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
267
268	nhp->th_range.tr_min = min;
269	nhp->th_range.tr_max = max;
270	nhp->th_enum = mod;
271	topo_mod_hold(mod);
272
273	/*
274	 * Add these nodes to parent child list
275	 */
276	topo_list_append(&pnode->tn_children, nhp);
277	topo_node_unlock(pnode);
278
279	topo_dprintf(TOPO_DBG_MOD, "created node range %s[%d-%d]\n", name,
280	    min, max);
281
282	return (0);
283}
284
285void
286topo_node_range_destroy(tnode_t *pnode, const char *name)
287{
288	int i;
289	topo_nodehash_t *nhp;
290	topo_mod_t *mod;
291
292	topo_node_lock(pnode);
293	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
294	    nhp = topo_list_next(nhp)) {
295		if (strcmp(nhp->th_name, name) == 0) {
296			break;
297		}
298	}
299
300	if (nhp == NULL) {
301		topo_node_unlock(pnode);
302		return;
303	}
304
305	topo_list_delete(&pnode->tn_children, nhp);
306	topo_node_unlock(pnode);
307
308	/*
309	 * Should be an empty node range
310	 */
311	for (i = 0; i < nhp->th_arrlen; i++) {
312		topo_node_unbind(nhp->th_nodearr[i]);
313	}
314
315	mod = nhp->th_enum;
316	if (nhp->th_name != NULL)
317		topo_mod_strfree(mod, nhp->th_name);
318	if (nhp->th_nodearr != NULL) {
319		topo_mod_free(mod, nhp->th_nodearr,
320		    nhp->th_arrlen * sizeof (tnode_t *));
321	}
322	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
323	topo_mod_rele(mod);
324
325}
326
327tnode_t *
328topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
329{
330	int h;
331	tnode_t *node;
332	topo_nodehash_t *nhp;
333
334	topo_node_lock(pnode);
335	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
336	    nhp = topo_list_next(nhp)) {
337		if (strcmp(nhp->th_name, name) == 0) {
338
339			if (inst > nhp->th_range.tr_max ||
340			    inst < nhp->th_range.tr_min) {
341				topo_node_unlock(pnode);
342				return (NULL);
343			}
344
345			h = topo_node_hash(nhp, inst);
346			node = nhp->th_nodearr[h];
347			topo_node_unlock(pnode);
348			return (node);
349		}
350	}
351	topo_node_unlock(pnode);
352
353	return (NULL);
354}
355
356int
357topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
358{
359	return (nhp->th_range.tr_max == 0 ?
360	    nhp->th_range.tr_max : inst % (nhp->th_range.tr_max + 1));
361}
362
363static tnode_t *
364node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node, int err)
365{
366	topo_node_unlock(pnode);
367
368	(void) topo_mod_seterrno(mod, err);
369
370	if (node == NULL)
371		return (NULL);
372
373	topo_dprintf(TOPO_DBG_ERR, "unable to bind %s=%d: "
374	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
375	    node->tn_instance, topo_strerror(err));
376
377	topo_node_lock(node); /* expected to be locked */
378	topo_node_destroy(node);
379
380	return (NULL);
381}
382
383tnode_t *
384topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
385    topo_instance_t inst, nvlist_t *fmri, void *priv)
386{
387	int h, err;
388	tnode_t *node;
389	topo_nodehash_t *nhp;
390
391	topo_node_lock(pnode);
392	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
393	    nhp = topo_list_next(nhp)) {
394		if (strcmp(nhp->th_name, name) == 0) {
395
396			if (inst > nhp->th_range.tr_max ||
397			    inst < nhp->th_range.tr_min)
398				return (node_bind_seterror(mod, pnode, NULL,
399				    ETOPO_NODE_INVAL));
400
401			h = topo_node_hash(nhp, inst);
402			if (nhp->th_nodearr[h] != NULL)
403				return (node_bind_seterror(mod, pnode, NULL,
404				    ETOPO_NODE_BOUND));
405			else
406				break;
407
408		}
409	}
410
411	if (nhp == NULL)
412		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NODE_NOENT));
413
414	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
415		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NOMEM));
416
417	(void) pthread_mutex_init(&node->tn_lock, NULL);
418
419	node->tn_enum = mod;
420	node->tn_hdl = mod->tm_hdl;
421	node->tn_parent = pnode;
422	node->tn_name = nhp->th_name;
423	node->tn_instance = inst;
424	node->tn_phash = nhp;
425	node->tn_refs = 0;
426
427	/* Ref count module that bound this node */
428	topo_mod_hold(mod);
429
430	if (fmri == NULL)
431		return (node_bind_seterror(mod, pnode, node, ETOPO_NODE_INVAL));
432
433	if (topo_pgroup_create(node, TOPO_PGROUP_PROTOCOL,
434	    TOPO_STABILITY_PRIVATE, &err) < 0)
435		return (node_bind_seterror(mod, pnode, node, err));
436
437	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
438	    TOPO_PROP_SET_ONCE, fmri, &err) < 0)
439		return (node_bind_seterror(mod, pnode, node, err));
440
441	topo_dprintf(TOPO_DBG_MOD, "node bound %s=%d\n", node->tn_name,
442	    node->tn_instance);
443
444	node->tn_state |= TOPO_NODE_BOUND;
445	node->tn_priv = priv;
446
447	topo_node_hold(node);
448	nhp->th_nodearr[h] = node;
449	++pnode->tn_refs;
450	topo_node_unlock(pnode);
451
452	if (topo_pgroup_create(node, TOPO_PGROUP_SYSTEM,
453	    TOPO_STABILITY_PRIVATE, &err) == 0) {
454		(void) topo_prop_inherit(node, TOPO_PGROUP_SYSTEM,
455		    TOPO_PROP_PLATFORM, &err);
456		(void) topo_prop_inherit(node, TOPO_PGROUP_SYSTEM,
457		    TOPO_PROP_ISA, &err);
458		(void) topo_prop_inherit(node, TOPO_PGROUP_SYSTEM,
459		    TOPO_PROP_MACHINE, &err);
460	}
461
462	return (node);
463}
464
465void
466topo_node_unbind(tnode_t *node)
467{
468	if (node == NULL)
469		return;
470
471	topo_node_lock(node);
472	if (!(node->tn_state & TOPO_NODE_BOUND)) {
473		topo_node_unlock(node);
474		return;
475	}
476
477	node->tn_state &= ~TOPO_NODE_BOUND;
478	topo_node_unlock(node);
479
480	topo_node_rele(node);
481}
482
483/*ARGSUSED*/
484int
485topo_node_present(tnode_t *node)
486{
487	return (0);
488}
489
490/*ARGSUSED*/
491int
492topo_node_contains(tnode_t *er, tnode_t *ee)
493{
494	return (0);
495}
496
497/*ARGSUSED*/
498int
499topo_node_unusable(tnode_t *node)
500{
501	return (0);
502}
503