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