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 	assert(node->tn_refs == 0);
135 
136 	/*
137 	 * If not a root node, remove this node from the parent's node hash
138 	 */
139 
140 	if (!(node->tn_state & TOPO_NODE_ROOT)) {
141 		topo_node_lock(pnode);
142 
143 		nhp = node->tn_phash;
144 		for (i = 0; i < nhp->th_arrlen; i++) {
145 			if (node == nhp->th_nodearr[i]) {
146 				nhp->th_nodearr[i] = NULL;
147 
148 				/*
149 				 * Release hold on parent
150 				 */
151 				--pnode->tn_refs;
152 				if (pnode->tn_refs == 0)
153 					topo_node_destroy(pnode);
154 			}
155 		}
156 		topo_node_unlock(pnode);
157 	}
158 
159 	topo_node_unlock(node);
160 
161 	/*
162 	 * Allow enumerator to clean-up private data and then release
163 	 * ref count
164 	 */
165 	if (mod->tm_info->tmi_ops->tmo_release != NULL)
166 		mod->tm_info->tmi_ops->tmo_release(mod, node);
167 
168 	topo_method_unregister_all(mod, node);
169 
170 	/*
171 	 * Destroy all node hash lists
172 	 */
173 	while ((nhp = topo_list_next(&node->tn_children)) != NULL) {
174 		for (i = 0; i < nhp->th_arrlen; i++) {
175 			assert(nhp->th_nodearr[i] == NULL);
176 		}
177 		hmod = nhp->th_enum;
178 		topo_mod_strfree(hmod, nhp->th_name);
179 		topo_mod_free(hmod, nhp->th_nodearr,
180 		    nhp->th_arrlen * sizeof (tnode_t *));
181 		topo_list_delete(&node->tn_children, nhp);
182 		topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t));
183 		topo_mod_rele(hmod);
184 	}
185 
186 	/*
187 	 * Destroy all property data structures, free the node and release
188 	 * the module that created it
189 	 */
190 	topo_pgroup_destroy_all(node);
191 	topo_mod_free(mod, node, sizeof (tnode_t));
192 	topo_mod_rele(mod);
193 }
194 
195 void
196 topo_node_lock(tnode_t *node)
197 {
198 	(void) pthread_mutex_lock(&node->tn_lock);
199 }
200 
201 void
202 topo_node_unlock(tnode_t *node)
203 {
204 	(void) pthread_mutex_unlock(&node->tn_lock);
205 }
206 
207 void
208 topo_node_hold(tnode_t *node)
209 {
210 	topo_node_lock(node);
211 	++node->tn_refs;
212 	topo_node_unlock(node);
213 }
214 
215 void
216 topo_node_rele(tnode_t *node)
217 {
218 	topo_node_lock(node);
219 	--node->tn_refs;
220 
221 	/*
222 	 * Ok to remove this node from the topo tree and destroy it
223 	 */
224 	if (node->tn_refs == 0)
225 		topo_node_destroy(node);
226 	else
227 		topo_node_unlock(node);
228 }
229 
230 char *
231 topo_node_name(tnode_t *node)
232 {
233 	return (node->tn_name);
234 }
235 
236 topo_instance_t
237 topo_node_instance(tnode_t *node)
238 {
239 	return (node->tn_instance);
240 }
241 
242 void
243 topo_node_setspecific(tnode_t *node, void *data)
244 {
245 	node->tn_priv = data;
246 }
247 
248 void *
249 topo_node_getspecific(tnode_t *node)
250 {
251 	return (node->tn_priv);
252 }
253 
254 static int
255 node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
256     int err)
257 {
258 	topo_node_unlock(pnode);
259 
260 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to insert child:"
261 	    "%s\n", topo_strerror(err));
262 
263 	if (nhp != NULL) {
264 		if (nhp->th_name != NULL)
265 			topo_mod_strfree(mod, nhp->th_name);
266 		if (nhp->th_nodearr != NULL) {
267 			topo_mod_free(mod, nhp->th_nodearr,
268 			    nhp->th_arrlen * sizeof (tnode_t *));
269 		}
270 		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
271 	}
272 
273 	return (topo_mod_seterrno(mod, err));
274 }
275 
276 int
277 topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
278     topo_instance_t min, topo_instance_t max)
279 {
280 	topo_nodehash_t *nhp;
281 
282 	topo_node_lock(pnode);
283 
284 	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
285 	    (pnode->tn_state & TOPO_NODE_ROOT));
286 
287 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
288 	    nhp = topo_list_next(nhp)) {
289 		if (strcmp(nhp->th_name, name) == 0)
290 			return (node_create_seterror(mod, pnode, NULL,
291 			    ETOPO_NODE_DUP));
292 	}
293 
294 	if (min < 0 || max < min)
295 		return (node_create_seterror(mod, pnode, NULL,
296 		    ETOPO_NODE_INVAL));
297 
298 	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
299 		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
300 
301 	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
302 		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
303 
304 	nhp->th_arrlen = max - min + 1;
305 
306 	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
307 	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
308 		return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM));
309 
310 	nhp->th_range.tr_min = min;
311 	nhp->th_range.tr_max = max;
312 	nhp->th_enum = mod;
313 	topo_mod_hold(mod);
314 
315 	/*
316 	 * Add these nodes to parent child list
317 	 */
318 	topo_list_append(&pnode->tn_children, nhp);
319 	topo_node_unlock(pnode);
320 
321 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
322 	    "created node range %s[%d-%d]\n", name, min, max);
323 
324 	return (0);
325 }
326 
327 void
328 topo_node_range_destroy(tnode_t *pnode, const char *name)
329 {
330 	int i;
331 	topo_nodehash_t *nhp;
332 	topo_mod_t *mod;
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 			break;
339 		}
340 	}
341 
342 	if (nhp == NULL) {
343 		topo_node_unlock(pnode);
344 		return;
345 	}
346 
347 	topo_list_delete(&pnode->tn_children, nhp);
348 	topo_node_unlock(pnode);
349 
350 	/*
351 	 * Should be an empty node range
352 	 */
353 	for (i = 0; i < nhp->th_arrlen; i++) {
354 		topo_node_unbind(nhp->th_nodearr[i]);
355 	}
356 
357 	mod = nhp->th_enum;
358 	if (nhp->th_name != NULL)
359 		topo_mod_strfree(mod, nhp->th_name);
360 	if (nhp->th_nodearr != NULL) {
361 		topo_mod_free(mod, nhp->th_nodearr,
362 		    nhp->th_arrlen * sizeof (tnode_t *));
363 	}
364 	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
365 	topo_mod_rele(mod);
366 
367 }
368 
369 tnode_t *
370 topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
371 {
372 	int h;
373 	tnode_t *node;
374 	topo_nodehash_t *nhp;
375 
376 	topo_node_lock(pnode);
377 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
378 	    nhp = topo_list_next(nhp)) {
379 		if (strcmp(nhp->th_name, name) == 0) {
380 
381 			if (inst > nhp->th_range.tr_max ||
382 			    inst < nhp->th_range.tr_min) {
383 				topo_node_unlock(pnode);
384 				return (NULL);
385 			}
386 
387 			h = topo_node_hash(nhp, inst);
388 			node = nhp->th_nodearr[h];
389 			topo_node_unlock(pnode);
390 			return (node);
391 		}
392 	}
393 	topo_node_unlock(pnode);
394 
395 	return (NULL);
396 }
397 
398 int
399 topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
400 {
401 	return (nhp->th_range.tr_max == 0 ?
402 	    nhp->th_range.tr_max : inst % (nhp->th_range.tr_max + 1));
403 }
404 
405 static tnode_t *
406 node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node, int err)
407 {
408 	topo_node_unlock(pnode);
409 
410 	(void) topo_mod_seterrno(mod, err);
411 
412 	if (node == NULL)
413 		return (NULL);
414 
415 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
416 	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
417 	    node->tn_instance, topo_strerror(err));
418 
419 	topo_node_lock(node); /* expected to be locked */
420 	topo_node_destroy(node);
421 
422 	return (NULL);
423 }
424 
425 tnode_t *
426 topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
427     topo_instance_t inst, nvlist_t *fmri)
428 {
429 	int h, err;
430 	tnode_t *node;
431 	topo_nodehash_t *nhp;
432 
433 	topo_node_lock(pnode);
434 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
435 	    nhp = topo_list_next(nhp)) {
436 		if (strcmp(nhp->th_name, name) == 0) {
437 
438 			if (inst > nhp->th_range.tr_max ||
439 			    inst < nhp->th_range.tr_min)
440 				return (node_bind_seterror(mod, pnode, NULL,
441 				    ETOPO_NODE_INVAL));
442 
443 			h = topo_node_hash(nhp, inst);
444 			if (nhp->th_nodearr[h] != NULL)
445 				return (node_bind_seterror(mod, pnode, NULL,
446 				    ETOPO_NODE_BOUND));
447 			else
448 				break;
449 
450 		}
451 	}
452 
453 	if (nhp == NULL)
454 		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NODE_NOENT));
455 
456 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
457 		return (node_bind_seterror(mod, pnode, NULL, ETOPO_NOMEM));
458 
459 	(void) pthread_mutex_init(&node->tn_lock, NULL);
460 
461 	node->tn_enum = mod;
462 	node->tn_hdl = mod->tm_hdl;
463 	node->tn_parent = pnode;
464 	node->tn_name = nhp->th_name;
465 	node->tn_instance = inst;
466 	node->tn_phash = nhp;
467 	node->tn_refs = 0;
468 
469 	/* Ref count module that bound this node */
470 	topo_mod_hold(mod);
471 
472 	if (fmri == NULL)
473 		return (node_bind_seterror(mod, pnode, node, ETOPO_NODE_INVAL));
474 
475 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
476 		return (node_bind_seterror(mod, pnode, node, err));
477 
478 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
479 	    TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
480 		return (node_bind_seterror(mod, pnode, node, err));
481 
482 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
483 	    "node bound %s=%d\n", 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_node_rele(node);
520 }
521 
522 /*ARGSUSED*/
523 int
524 topo_node_present(tnode_t *node)
525 {
526 	return (0);
527 }
528 
529 /*ARGSUSED*/
530 int
531 topo_node_contains(tnode_t *er, tnode_t *ee)
532 {
533 	return (0);
534 }
535 
536 /*ARGSUSED*/
537 int
538 topo_node_unusable(tnode_t *node)
539 {
540 	return (0);
541 }
542 
543 topo_walk_t *
544 topo_node_walk_init(topo_hdl_t *thp, topo_mod_t *mod, tnode_t *node,
545     int (*cb_f)(), void *pdata, int *errp)
546 {
547 	tnode_t *child;
548 	topo_walk_t *wp;
549 
550 	topo_node_hold(node);
551 
552 	if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) == NULL) {
553 		*errp = ETOPO_NOMEM;
554 		topo_node_rele(node);
555 		return (NULL);
556 	}
557 
558 	/*
559 	 * If this is the root of the scheme tree, start with the first
560 	 * child
561 	 */
562 	topo_node_lock(node);
563 	if (node->tn_state & TOPO_NODE_ROOT) {
564 		if ((child = topo_child_first(node)) == NULL) {
565 			/* Nothing to walk */
566 			*errp = ETOPO_WALK_EMPTY;
567 			topo_node_unlock(node);
568 			topo_node_rele(node);
569 			return (NULL);
570 		}
571 		topo_node_unlock(node);
572 		topo_node_hold(child);
573 		wp->tw_node = child;
574 	} else {
575 		topo_node_unlock(node);
576 		topo_node_hold(child);
577 		wp->tw_node = node;
578 	}
579 
580 	wp->tw_root = node;
581 	wp->tw_cb = cb_f;
582 	wp->tw_pdata = pdata;
583 	wp->tw_thp = thp;
584 	wp->tw_mod = mod;
585 
586 	return (wp);
587 }
588