xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_node.c (revision a07094369b21309434206d9b3601d162693466fc)
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 
85 static void
86 topo_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 
159 void
160 topo_node_lock(tnode_t *node)
161 {
162 	(void) pthread_mutex_lock(&node->tn_lock);
163 }
164 
165 void
166 topo_node_unlock(tnode_t *node)
167 {
168 	(void) pthread_mutex_unlock(&node->tn_lock);
169 }
170 
171 void
172 topo_node_hold(tnode_t *node)
173 {
174 	topo_node_lock(node);
175 	++node->tn_refs;
176 	topo_node_unlock(node);
177 }
178 
179 void
180 topo_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 
194 char *
195 topo_node_name(tnode_t *node)
196 {
197 	return (node->tn_name);
198 }
199 
200 topo_instance_t
201 topo_node_instance(tnode_t *node)
202 {
203 	return (node->tn_instance);
204 }
205 
206 void *
207 topo_node_private(tnode_t *node)
208 {
209 	return (node->tn_priv);
210 }
211 
212 static int
213 node_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 
234 int
235 topo_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 
285 void
286 topo_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 
327 tnode_t *
328 topo_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 
356 int
357 topo_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 
363 static tnode_t *
364 node_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 
383 tnode_t *
384 topo_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 
465 void
466 topo_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*/
484 int
485 topo_node_present(tnode_t *node)
486 {
487 	return (0);
488 }
489 
490 /*ARGSUSED*/
491 int
492 topo_node_contains(tnode_t *er, tnode_t *ee)
493 {
494 	return (0);
495 }
496 
497 /*ARGSUSED*/
498 int
499 topo_node_unusable(tnode_t *node)
500 {
501 	return (0);
502 }
503