xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_node.c (revision 12cc75c814f0c017004a9bbc96429911e008601b)
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 2007 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 topology nodes within their
43  * scope of operation: the parent node passed into the enumeration op or those
44  * nodes created by the enumerator.  Enumeration occurs only during
45  * topo_snap_hold() where a per-topo_hdl_t lock prevents multi-threaded access
46  * to the topology trees.
47  *
48  * Enumerator method operation functions may safely access and change topology
49  * node property data, and contruct or destroy child nodes for the node
50  * on which the operation applies.  The method may also be called to destroy
51  * the node for which the method operation is called.  This permits
52  * dynamic topology tree snapshots and partial enumerations for branches that
53  * may not be needed right away.
54  *
55  * Node Interfaces
56  *
57  * Nodes are created when an enumerator calls topo_node_bind().  Prior to
58  * calling topo_node_bind(), the enumerator should have reserved a range of
59  * node instances with topo_node_range_create().  topo_node_range_create()
60  * does not allocate any node resources but creates the infrastruture
61  * required for a fully populated topology level.  This allows enumerators
62  * reading from a <scheme>-topology.xml file to parse the file for a range
63  * of resources before confirming the existence of a resource via a helper
64  * plugin.  Only when the resource has been confirmed to exist should
65  * the node be bound.
66  *
67  * Node range and node linkage and unlinkage is performed during enumeration and
68  * method operations when it is safe to change node hash lists. Nodes and node
69  * ranges are deallocated when all references to the node have been released:
70  * last walk completes and topo_snap_rele() is called.
71  *
72  * Node Hash/Ranges
73  *
74  * Each parent node may have one or more ranges of child nodes.  Each range
75  * is uniquely named and serves as a hash list of like sibling nodes with
76  * different instance numbers.  A parent may have more than one node hash
77  * (child range). If that is the case, the hash lists are strung together to
78  * form sibling relationships between ranges.  Hash/Ranges are sparsely
79  * populated with only nodes that have represented resources in the system.
80  *
81  *	_________________
82  *	|		|
83  *      |   tnode_t	|    -----------------------------
84  *      |      tn_phash ---> |  topo_nodehash_t          |
85  *      |     (children)|    |     th_nodearr (instances)|
86  *      -----------------    |     -------------------   |
87  *                           |  ---| 0 | 1  | ...| N |   |
88  *                           |  |  -------------------   |  -------------------
89  *                           |  |  th_list (siblings) ----->| topo_nodehash_t |
90  *                           |  |                        |  -------------------
91  *                           ---|-------------------------
92  *                              |
93  *                              v
94  *                           -----------
95  *                           | tnode_t |
96  *                           -----------
97  */
98 
99 #include <assert.h>
100 #include <pthread.h>
101 #include <strings.h>
102 #include <sys/fm/protocol.h>
103 #include <topo_alloc.h>
104 #include <topo_error.h>
105 #include <topo_method.h>
106 #include <topo_subr.h>
107 #include <topo_tree.h>
108 
109 static topo_pgroup_info_t protocol_pgroup = {
110 	TOPO_PGROUP_PROTOCOL,
111 	TOPO_STABILITY_PRIVATE,
112 	TOPO_STABILITY_PRIVATE,
113 	1
114 };
115 
116 static const topo_pgroup_info_t auth_pgroup = {
117 	FM_FMRI_AUTHORITY,
118 	TOPO_STABILITY_PRIVATE,
119 	TOPO_STABILITY_PRIVATE,
120 	1
121 };
122 
123 static void
124 topo_node_destroy(tnode_t *node)
125 {
126 	int i;
127 	tnode_t *pnode = node->tn_parent;
128 	topo_nodehash_t *nhp;
129 	topo_mod_t *hmod, *mod = node->tn_enum;
130 
131 	if (node == NULL)
132 		return;
133 
134 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, "destroying node %s=%d\n",
135 	    topo_node_name(node), topo_node_instance(node));
136 
137 	assert(node->tn_refs == 0);
138 
139 	/*
140 	 * If not a root node, remove this node from the parent's node hash
141 	 */
142 
143 	if (!(node->tn_state & TOPO_NODE_ROOT)) {
144 		topo_node_lock(pnode);
145 
146 		nhp = node->tn_phash;
147 		for (i = 0; i < nhp->th_arrlen; i++) {
148 			if (node == nhp->th_nodearr[i]) {
149 				nhp->th_nodearr[i] = NULL;
150 
151 				/*
152 				 * Release hold on parent
153 				 */
154 				--pnode->tn_refs;
155 				if (pnode->tn_refs == 0)
156 					topo_node_destroy(pnode);
157 			}
158 		}
159 		topo_node_unlock(pnode);
160 	}
161 
162 	topo_node_unlock(node);
163 
164 	/*
165 	 * Allow enumerator to clean-up private data and then release
166 	 * ref count
167 	 */
168 	if (mod->tm_info->tmi_ops->tmo_release != NULL)
169 		mod->tm_info->tmi_ops->tmo_release(mod, node);
170 
171 	topo_method_unregister_all(mod, node);
172 
173 	/*
174 	 * Destroy all node hash lists
175 	 */
176 	while ((nhp = topo_list_next(&node->tn_children)) != NULL) {
177 		for (i = 0; i < nhp->th_arrlen; i++) {
178 			assert(nhp->th_nodearr[i] == NULL);
179 		}
180 		hmod = nhp->th_enum;
181 		topo_mod_strfree(hmod, nhp->th_name);
182 		topo_mod_free(hmod, nhp->th_nodearr,
183 		    nhp->th_arrlen * sizeof (tnode_t *));
184 		topo_list_delete(&node->tn_children, nhp);
185 		topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t));
186 		topo_mod_rele(hmod);
187 	}
188 
189 	/*
190 	 * Destroy all property data structures, free the node and release
191 	 * the module that created it
192 	 */
193 	topo_pgroup_destroy_all(node);
194 	topo_mod_free(mod, node, sizeof (tnode_t));
195 	topo_mod_rele(mod);
196 }
197 
198 void
199 topo_node_lock(tnode_t *node)
200 {
201 	(void) pthread_mutex_lock(&node->tn_lock);
202 }
203 
204 void
205 topo_node_unlock(tnode_t *node)
206 {
207 	(void) pthread_mutex_unlock(&node->tn_lock);
208 }
209 
210 void
211 topo_node_hold(tnode_t *node)
212 {
213 	topo_node_lock(node);
214 	++node->tn_refs;
215 	topo_node_unlock(node);
216 }
217 
218 void
219 topo_node_rele(tnode_t *node)
220 {
221 	topo_node_lock(node);
222 	--node->tn_refs;
223 
224 	/*
225 	 * Ok to remove this node from the topo tree and destroy it
226 	 */
227 	if (node->tn_refs == 0)
228 		topo_node_destroy(node);
229 	else
230 		topo_node_unlock(node);
231 }
232 
233 char *
234 topo_node_name(tnode_t *node)
235 {
236 	return (node->tn_name);
237 }
238 
239 topo_instance_t
240 topo_node_instance(tnode_t *node)
241 {
242 	return (node->tn_instance);
243 }
244 
245 void
246 topo_node_setspecific(tnode_t *node, void *data)
247 {
248 	node->tn_priv = data;
249 }
250 
251 void *
252 topo_node_getspecific(tnode_t *node)
253 {
254 	return (node->tn_priv);
255 }
256 
257 static int
258 node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
259     int err)
260 {
261 	topo_node_unlock(pnode);
262 
263 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to insert child:"
264 	    "%s\n", topo_strerror(err));
265 
266 	if (nhp != NULL) {
267 		if (nhp->th_name != NULL)
268 			topo_mod_strfree(mod, nhp->th_name);
269 		if (nhp->th_nodearr != NULL) {
270 			topo_mod_free(mod, nhp->th_nodearr,
271 			    nhp->th_arrlen * sizeof (tnode_t *));
272 		}
273 		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
274 	}
275 
276 	return (topo_mod_seterrno(mod, err));
277 }
278 
279 int
280 topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
281     topo_instance_t min, topo_instance_t max)
282 {
283 	topo_nodehash_t *nhp;
284 
285 	topo_node_lock(pnode);
286 
287 	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
288 	    (pnode->tn_state & TOPO_NODE_ROOT));
289 
290 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
291 	    nhp = topo_list_next(nhp)) {
292 		if (strcmp(nhp->th_name, name) == 0)
293 			return (node_create_seterror(mod, pnode, NULL,
294 			    ETOPO_NODE_DUP));
295 	}
296 
297 	if (min < 0 || max < min)
298 		return (node_create_seterror(mod, pnode, NULL,
299 		    ETOPO_NODE_INVAL));
300 
301 	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
302 		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
303 
304 	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
305 		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
306 
307 	nhp->th_arrlen = max - min + 1;
308 
309 	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
310 	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
311 		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
312 
313 	nhp->th_range.tr_min = min;
314 	nhp->th_range.tr_max = max;
315 	nhp->th_enum = mod;
316 	topo_mod_hold(mod);
317 
318 	/*
319 	 * Add these nodes to parent child list
320 	 */
321 	topo_list_append(&pnode->tn_children, nhp);
322 	topo_node_unlock(pnode);
323 
324 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
325 	    "created node range %s[%d-%d]\n", name, min, max);
326 
327 	return (0);
328 }
329 
330 void
331 topo_node_range_destroy(tnode_t *pnode, const char *name)
332 {
333 	int i;
334 	topo_nodehash_t *nhp;
335 	topo_mod_t *mod;
336 
337 	topo_node_lock(pnode);
338 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
339 	    nhp = topo_list_next(nhp)) {
340 		if (strcmp(nhp->th_name, name) == 0) {
341 			break;
342 		}
343 	}
344 
345 	if (nhp == NULL) {
346 		topo_node_unlock(pnode);
347 		return;
348 	}
349 
350 	for (i = 0; i < nhp->th_arrlen; i++)
351 		assert(nhp->th_nodearr[i] == NULL);
352 
353 	topo_list_delete(&pnode->tn_children, nhp);
354 	topo_node_unlock(pnode);
355 
356 	mod = nhp->th_enum;
357 	if (nhp->th_name != NULL)
358 		topo_mod_strfree(mod, nhp->th_name);
359 	if (nhp->th_nodearr != NULL) {
360 		topo_mod_free(mod, nhp->th_nodearr,
361 		    nhp->th_arrlen * sizeof (tnode_t *));
362 	}
363 	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
364 	topo_mod_rele(mod);
365 
366 }
367 
368 tnode_t *
369 topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
370 {
371 	int h;
372 	tnode_t *node;
373 	topo_nodehash_t *nhp;
374 
375 	topo_node_lock(pnode);
376 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
377 	    nhp = topo_list_next(nhp)) {
378 		if (strcmp(nhp->th_name, name) == 0) {
379 
380 			if (inst > nhp->th_range.tr_max ||
381 			    inst < nhp->th_range.tr_min) {
382 				topo_node_unlock(pnode);
383 				return (NULL);
384 			}
385 
386 			h = topo_node_hash(nhp, inst);
387 			node = nhp->th_nodearr[h];
388 			topo_node_unlock(pnode);
389 			return (node);
390 		}
391 	}
392 	topo_node_unlock(pnode);
393 
394 	return (NULL);
395 }
396 
397 int
398 topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
399 {
400 	return (nhp->th_range.tr_max == 0 ?
401 	    nhp->th_range.tr_max : inst % (nhp->th_range.tr_max + 1));
402 }
403 
404 static tnode_t *
405 node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node, int err)
406 {
407 	topo_node_unlock(pnode);
408 
409 	(void) topo_mod_seterrno(mod, err);
410 
411 	if (node == NULL)
412 		return (NULL);
413 
414 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
415 	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
416 	    node->tn_instance, topo_strerror(err));
417 
418 	topo_node_lock(node); /* expected to be locked */
419 	topo_node_destroy(node);
420 
421 	return (NULL);
422 }
423 
424 tnode_t *
425 topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
426     topo_instance_t inst, nvlist_t *fmri)
427 {
428 	int h, err;
429 	tnode_t *node;
430 	topo_nodehash_t *nhp;
431 
432 	topo_node_lock(pnode);
433 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
434 	    nhp = topo_list_next(nhp)) {
435 		if (strcmp(nhp->th_name, name) == 0) {
436 
437 			if (inst > nhp->th_range.tr_max ||
438 			    inst < nhp->th_range.tr_min)
439 				return (node_bind_seterror(mod, pnode, NULL,
440 				    ETOPO_NODE_INVAL));
441 
442 			h = topo_node_hash(nhp, inst);
443 			if (nhp->th_nodearr[h] != NULL)
444 				return (node_bind_seterror(mod, pnode, NULL,
445 				    ETOPO_NODE_BOUND));
446 			else
447 				break;
448 
449 		}
450 	}
451 
452 	if (nhp == NULL)
453 		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NODE_NOENT));
454 
455 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
456 		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NOMEM));
457 
458 	(void) pthread_mutex_init(&node->tn_lock, NULL);
459 
460 	node->tn_enum = mod;
461 	node->tn_hdl = mod->tm_hdl;
462 	node->tn_parent = pnode;
463 	node->tn_name = nhp->th_name;
464 	node->tn_instance = inst;
465 	node->tn_phash = nhp;
466 	node->tn_refs = 0;
467 
468 	/* Ref count module that bound this node */
469 	topo_mod_hold(mod);
470 
471 	if (fmri == NULL)
472 		return (node_bind_seterror(mod, pnode, node, ETOPO_NODE_INVAL));
473 
474 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
475 		return (node_bind_seterror(mod, pnode, node, err));
476 
477 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
478 	    TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
479 		return (node_bind_seterror(mod, pnode, node, err));
480 
481 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
482 	    "node bound %s=%d/%s=%d\n", topo_node_name(pnode),
483 	    topo_node_instance(pnode), node->tn_name, node->tn_instance);
484 
485 	node->tn_state |= TOPO_NODE_BOUND;
486 
487 	topo_node_hold(node);
488 	nhp->th_nodearr[h] = node;
489 	++pnode->tn_refs;
490 	topo_node_unlock(pnode);
491 
492 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
493 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
494 		    FM_FMRI_AUTH_PRODUCT, &err);
495 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
496 		    FM_FMRI_AUTH_CHASSIS, &err);
497 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
498 		    FM_FMRI_AUTH_SERVER, &err);
499 	}
500 
501 	return (node);
502 }
503 
504 void
505 topo_node_unbind(tnode_t *node)
506 {
507 	if (node == NULL)
508 		return;
509 
510 	topo_node_lock(node);
511 	if (!(node->tn_state & TOPO_NODE_BOUND)) {
512 		topo_node_unlock(node);
513 		return;
514 	}
515 
516 	node->tn_state &= ~TOPO_NODE_BOUND;
517 	topo_node_unlock(node);
518 
519 	topo_dprintf(node->tn_hdl, TOPO_DBG_MODSVC,
520 	    "node unbound %s=%d/%s=%d refs = %d\n",
521 	    topo_node_name(node->tn_parent),
522 	    topo_node_instance(node->tn_parent), node->tn_name,
523 	    node->tn_instance, node->tn_refs);
524 
525 	topo_node_rele(node);
526 }
527 
528 /*ARGSUSED*/
529 int
530 topo_node_present(tnode_t *node)
531 {
532 	return (0);
533 }
534 
535 /*ARGSUSED*/
536 int
537 topo_node_contains(tnode_t *er, tnode_t *ee)
538 {
539 	return (0);
540 }
541 
542 /*ARGSUSED*/
543 int
544 topo_node_unusable(tnode_t *node)
545 {
546 	return (0);
547 }
548 
549 topo_walk_t *
550 topo_node_walk_init(topo_hdl_t *thp, topo_mod_t *mod, tnode_t *node,
551     int (*cb_f)(), void *pdata, int *errp)
552 {
553 	tnode_t *child;
554 	topo_walk_t *wp;
555 
556 	topo_node_hold(node);
557 
558 	if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) == NULL) {
559 		*errp = ETOPO_NOMEM;
560 		topo_node_rele(node);
561 		return (NULL);
562 	}
563 
564 	/*
565 	 * If this is the root of the scheme tree, start with the first
566 	 * child
567 	 */
568 	topo_node_lock(node);
569 	if (node->tn_state & TOPO_NODE_ROOT) {
570 		if ((child = topo_child_first(node)) == NULL) {
571 			/* Nothing to walk */
572 			*errp = ETOPO_WALK_EMPTY;
573 			topo_node_unlock(node);
574 			topo_node_rele(node);
575 			return (NULL);
576 		}
577 		topo_node_unlock(node);
578 		topo_node_hold(child);
579 		wp->tw_node = child;
580 	} else {
581 		topo_node_unlock(node);
582 		topo_node_hold(node); /* rele at walk end */
583 		wp->tw_node = node;
584 	}
585 
586 	wp->tw_root = node;
587 	wp->tw_cb = cb_f;
588 	wp->tw_pdata = pdata;
589 	wp->tw_thp = thp;
590 	wp->tw_mod = mod;
591 
592 	return (wp);
593 }
594