xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_node.c (revision 7dacfc4494f6d14358974ef2830b5cd8c66a84de)
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 tnode_t *
246 topo_node_parent(tnode_t *node)
247 {
248 	return (node->tn_parent);
249 }
250 
251 void
252 topo_node_setspecific(tnode_t *node, void *data)
253 {
254 	node->tn_priv = data;
255 }
256 
257 void *
258 topo_node_getspecific(tnode_t *node)
259 {
260 	return (node->tn_priv);
261 }
262 
263 static int
264 node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
265     int err)
266 {
267 	topo_node_unlock(pnode);
268 
269 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to insert child:"
270 	    "%s\n", topo_strerror(err));
271 
272 	if (nhp != NULL) {
273 		if (nhp->th_name != NULL)
274 			topo_mod_strfree(mod, nhp->th_name);
275 		if (nhp->th_nodearr != NULL) {
276 			topo_mod_free(mod, nhp->th_nodearr,
277 			    nhp->th_arrlen * sizeof (tnode_t *));
278 		}
279 		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
280 	}
281 
282 	return (topo_mod_seterrno(mod, err));
283 }
284 
285 int
286 topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
287     topo_instance_t min, topo_instance_t max)
288 {
289 	topo_nodehash_t *nhp;
290 
291 	topo_node_lock(pnode);
292 
293 	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
294 	    (pnode->tn_state & TOPO_NODE_ROOT));
295 
296 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
297 	    nhp = topo_list_next(nhp)) {
298 		if (strcmp(nhp->th_name, name) == 0)
299 			return (node_create_seterror(mod, pnode, NULL,
300 			    ETOPO_NODE_DUP));
301 	}
302 
303 	if (min < 0 || max < min)
304 		return (node_create_seterror(mod, pnode, NULL,
305 		    ETOPO_NODE_INVAL));
306 
307 	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
308 		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
309 
310 	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
311 		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
312 
313 	nhp->th_arrlen = max - min + 1;
314 
315 	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
316 	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
317 		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
318 
319 	nhp->th_range.tr_min = min;
320 	nhp->th_range.tr_max = max;
321 	nhp->th_enum = mod;
322 	topo_mod_hold(mod);
323 
324 	/*
325 	 * Add these nodes to parent child list
326 	 */
327 	topo_list_append(&pnode->tn_children, nhp);
328 	topo_node_unlock(pnode);
329 
330 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
331 	    "created node range %s[%d-%d]\n", name, min, max);
332 
333 	return (0);
334 }
335 
336 void
337 topo_node_range_destroy(tnode_t *pnode, const char *name)
338 {
339 	int i;
340 	topo_nodehash_t *nhp;
341 	topo_mod_t *mod;
342 
343 	topo_node_lock(pnode);
344 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
345 	    nhp = topo_list_next(nhp)) {
346 		if (strcmp(nhp->th_name, name) == 0) {
347 			break;
348 		}
349 	}
350 
351 	if (nhp == NULL) {
352 		topo_node_unlock(pnode);
353 		return;
354 	}
355 
356 	for (i = 0; i < nhp->th_arrlen; i++)
357 		assert(nhp->th_nodearr[i] == NULL);
358 
359 	topo_list_delete(&pnode->tn_children, nhp);
360 	topo_node_unlock(pnode);
361 
362 	mod = nhp->th_enum;
363 	if (nhp->th_name != NULL)
364 		topo_mod_strfree(mod, nhp->th_name);
365 	if (nhp->th_nodearr != NULL) {
366 		topo_mod_free(mod, nhp->th_nodearr,
367 		    nhp->th_arrlen * sizeof (tnode_t *));
368 	}
369 	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
370 	topo_mod_rele(mod);
371 
372 }
373 
374 tnode_t *
375 topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
376 {
377 	int h;
378 	tnode_t *node;
379 	topo_nodehash_t *nhp;
380 
381 	topo_node_lock(pnode);
382 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
383 	    nhp = topo_list_next(nhp)) {
384 		if (strcmp(nhp->th_name, name) == 0) {
385 
386 			if (inst > nhp->th_range.tr_max ||
387 			    inst < nhp->th_range.tr_min) {
388 				topo_node_unlock(pnode);
389 				return (NULL);
390 			}
391 
392 			h = topo_node_hash(nhp, inst);
393 			node = nhp->th_nodearr[h];
394 			topo_node_unlock(pnode);
395 			return (node);
396 		}
397 	}
398 	topo_node_unlock(pnode);
399 
400 	return (NULL);
401 }
402 
403 int
404 topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
405 {
406 	return (nhp->th_range.tr_max == 0 ?
407 	    nhp->th_range.tr_max : inst % (nhp->th_range.tr_max + 1));
408 }
409 
410 static tnode_t *
411 node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node, int err)
412 {
413 	topo_node_unlock(pnode);
414 
415 	(void) topo_mod_seterrno(mod, err);
416 
417 	if (node == NULL)
418 		return (NULL);
419 
420 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
421 	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
422 	    node->tn_instance, topo_strerror(err));
423 
424 	topo_node_lock(node); /* expected to be locked */
425 	topo_node_destroy(node);
426 
427 	return (NULL);
428 }
429 
430 tnode_t *
431 topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
432     topo_instance_t inst, nvlist_t *fmri)
433 {
434 	int h, err;
435 	tnode_t *node;
436 	topo_nodehash_t *nhp;
437 
438 	topo_node_lock(pnode);
439 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
440 	    nhp = topo_list_next(nhp)) {
441 		if (strcmp(nhp->th_name, name) == 0) {
442 
443 			if (inst > nhp->th_range.tr_max ||
444 			    inst < nhp->th_range.tr_min)
445 				return (node_bind_seterror(mod, pnode, NULL,
446 				    ETOPO_NODE_INVAL));
447 
448 			h = topo_node_hash(nhp, inst);
449 			if (nhp->th_nodearr[h] != NULL)
450 				return (node_bind_seterror(mod, pnode, NULL,
451 				    ETOPO_NODE_BOUND));
452 			else
453 				break;
454 
455 		}
456 	}
457 
458 	if (nhp == NULL)
459 		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NODE_NOENT));
460 
461 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
462 		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NOMEM));
463 
464 	(void) pthread_mutex_init(&node->tn_lock, NULL);
465 
466 	node->tn_enum = mod;
467 	node->tn_hdl = mod->tm_hdl;
468 	node->tn_parent = pnode;
469 	node->tn_name = nhp->th_name;
470 	node->tn_instance = inst;
471 	node->tn_phash = nhp;
472 	node->tn_refs = 0;
473 
474 	/* Ref count module that bound this node */
475 	topo_mod_hold(mod);
476 
477 	if (fmri == NULL)
478 		return (node_bind_seterror(mod, pnode, node, ETOPO_NODE_INVAL));
479 
480 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
481 		return (node_bind_seterror(mod, pnode, node, err));
482 
483 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
484 	    TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
485 		return (node_bind_seterror(mod, pnode, node, err));
486 
487 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
488 	    "node bound %s=%d/%s=%d\n", topo_node_name(pnode),
489 	    topo_node_instance(pnode), node->tn_name, node->tn_instance);
490 
491 	node->tn_state |= TOPO_NODE_BOUND;
492 
493 	topo_node_hold(node);
494 	nhp->th_nodearr[h] = node;
495 	++pnode->tn_refs;
496 	topo_node_unlock(pnode);
497 
498 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
499 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
500 		    FM_FMRI_AUTH_PRODUCT, &err);
501 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
502 		    FM_FMRI_AUTH_CHASSIS, &err);
503 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
504 		    FM_FMRI_AUTH_SERVER, &err);
505 	}
506 
507 	return (node);
508 }
509 
510 void
511 topo_node_unbind(tnode_t *node)
512 {
513 	if (node == NULL)
514 		return;
515 
516 	topo_node_lock(node);
517 	if (!(node->tn_state & TOPO_NODE_BOUND)) {
518 		topo_node_unlock(node);
519 		return;
520 	}
521 
522 	node->tn_state &= ~TOPO_NODE_BOUND;
523 	topo_node_unlock(node);
524 
525 	topo_dprintf(node->tn_hdl, TOPO_DBG_MODSVC,
526 	    "node unbound %s=%d/%s=%d refs = %d\n",
527 	    topo_node_name(node->tn_parent),
528 	    topo_node_instance(node->tn_parent), node->tn_name,
529 	    node->tn_instance, node->tn_refs);
530 
531 	topo_node_rele(node);
532 }
533 
534 /*ARGSUSED*/
535 int
536 topo_node_present(tnode_t *node)
537 {
538 	return (0);
539 }
540 
541 /*ARGSUSED*/
542 int
543 topo_node_contains(tnode_t *er, tnode_t *ee)
544 {
545 	return (0);
546 }
547 
548 /*ARGSUSED*/
549 int
550 topo_node_unusable(tnode_t *node)
551 {
552 	return (0);
553 }
554 
555 topo_walk_t *
556 topo_node_walk_init(topo_hdl_t *thp, topo_mod_t *mod, tnode_t *node,
557     int (*cb_f)(), void *pdata, int *errp)
558 {
559 	tnode_t *child;
560 	topo_walk_t *wp;
561 
562 	topo_node_hold(node);
563 
564 	if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) == NULL) {
565 		*errp = ETOPO_NOMEM;
566 		topo_node_rele(node);
567 		return (NULL);
568 	}
569 
570 	/*
571 	 * If this is the root of the scheme tree, start with the first
572 	 * child
573 	 */
574 	topo_node_lock(node);
575 	if (node->tn_state & TOPO_NODE_ROOT) {
576 		if ((child = topo_child_first(node)) == NULL) {
577 			/* Nothing to walk */
578 			*errp = ETOPO_WALK_EMPTY;
579 			topo_node_unlock(node);
580 			topo_node_rele(node);
581 			return (NULL);
582 		}
583 		topo_node_unlock(node);
584 		topo_node_hold(child);
585 		wp->tw_node = child;
586 	} else {
587 		topo_node_unlock(node);
588 		topo_node_hold(node); /* rele at walk end */
589 		wp->tw_node = node;
590 	}
591 
592 	wp->tw_root = node;
593 	wp->tw_cb = cb_f;
594 	wp->tw_pdata = pdata;
595 	wp->tw_thp = thp;
596 	wp->tw_mod = mod;
597 
598 	return (wp);
599 }
600