xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_node.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * Topology Nodes
27  *
28  * Topology nodes, tnode_t, are data structures containing per-FMRI
29  * information and are linked together to form the topology tree.
30  * Nodes are created during the enumeration process of topo_snap_hold()
31  * and destroyed during topo_snap_rele().  For the most part, tnode_t data
32  * is read-only and no lock protection is required.  Nodes are
33  * held in place during tree walk functions.  Tree walk functions
34  * may access node data safely without locks.  The exception to this rule
35  * is data associated with node properties (topo_prop.c).  Properties
36  * may change at anytime and are protected by a per-property locking
37  * strategy.
38  *
39  * Enumerator plugin modules may also safely access topology nodes within their
40  * scope of operation: the parent node passed into the enumeration op or those
41  * nodes created by the enumerator.  Enumeration occurs only during
42  * topo_snap_hold() where a per-topo_hdl_t lock prevents multi-threaded access
43  * to the topology trees.
44  *
45  * Enumerator method operation functions may safely access and change topology
46  * node property data, and contruct or destroy child nodes for the node
47  * on which the operation applies.  The method may also be called to destroy
48  * the node for which the method operation is called.  This permits
49  * dynamic topology tree snapshots and partial enumerations for branches that
50  * may not be needed right away.
51  *
52  * Node Interfaces
53  *
54  * Nodes are created when an enumerator calls topo_node_bind().  Prior to
55  * calling topo_node_bind(), the enumerator should have reserved a range of
56  * node instances with topo_node_range_create().  topo_node_range_create()
57  * does not allocate any node resources but creates the infrastruture
58  * required for a fully populated topology level.  This allows enumerators
59  * reading from a <scheme>-topology.xml file to parse the file for a range
60  * of resources before confirming the existence of a resource via a helper
61  * plugin.  Only when the resource has been confirmed to exist should
62  * the node be bound.
63  *
64  * Node range and node linkage and unlinkage is performed during enumeration and
65  * method operations when it is safe to change node hash lists. Nodes and node
66  * ranges are deallocated when all references to the node have been released:
67  * last walk completes and topo_snap_rele() is called.
68  *
69  * Node Hash/Ranges
70  *
71  * Each parent node may have one or more ranges of child nodes.  Each range
72  * is uniquely named and serves as a hash list of like sibling nodes with
73  * different instance numbers.  A parent may have more than one node hash
74  * (child range). If that is the case, the hash lists are strung together to
75  * form sibling relationships between ranges.  Hash/Ranges are sparsely
76  * populated with only nodes that have represented resources in the system.
77  *
78  *	_________________
79  *	|		|
80  *      |   tnode_t	|    -----------------------------
81  *      |      tn_phash ---> |  topo_nodehash_t          |
82  *      |     (children)|    |     th_nodearr (instances)|
83  *      -----------------    |     -------------------   |
84  *                           |  ---| 0 | 1  | ...| N |   |
85  *                           |  |  -------------------   |  -------------------
86  *                           |  |  th_list (siblings) ----->| topo_nodehash_t |
87  *                           |  |                        |  -------------------
88  *                           ---|-------------------------
89  *                              |
90  *                              v
91  *                           -----------
92  *                           | tnode_t |
93  *                           -----------
94  *
95  * Facility Nodes
96  *
97  * Facility nodes are always leaf nodes in the topology and represent a FMRI
98  * sensor or indicator facility for the path to which it is connected.
99  * Facility nodes are bound to the topology with topo_node_facbind() and
100  * unbound with topo_node_unbind().
101  */
102 
103 #include <assert.h>
104 #include <pthread.h>
105 #include <strings.h>
106 #include <sys/fm/protocol.h>
107 #include <topo_alloc.h>
108 #include <topo_error.h>
109 #include <topo_list.h>
110 #include <topo_method.h>
111 #include <topo_subr.h>
112 #include <topo_tree.h>
113 
114 static topo_pgroup_info_t protocol_pgroup = {
115 	TOPO_PGROUP_PROTOCOL,
116 	TOPO_STABILITY_PRIVATE,
117 	TOPO_STABILITY_PRIVATE,
118 	1
119 };
120 
121 static const topo_pgroup_info_t auth_pgroup = {
122 	FM_FMRI_AUTHORITY,
123 	TOPO_STABILITY_PRIVATE,
124 	TOPO_STABILITY_PRIVATE,
125 	1
126 };
127 
128 static void
129 topo_node_destroy(tnode_t *node)
130 {
131 	int i;
132 	tnode_t *pnode = node->tn_parent;
133 	topo_nodehash_t *nhp;
134 	topo_mod_t *hmod, *mod = node->tn_enum;
135 
136 	if (node == NULL)
137 		return;
138 
139 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, "destroying node %s=%d\n",
140 	    topo_node_name(node), topo_node_instance(node));
141 
142 	assert(node->tn_refs == 0);
143 
144 	/*
145 	 * If not a root node, remove this node from the parent's node hash
146 	 */
147 
148 	if (!(node->tn_state & TOPO_NODE_ROOT)) {
149 		topo_node_lock(pnode);
150 
151 		nhp = node->tn_phash;
152 		for (i = 0; i < nhp->th_arrlen; i++) {
153 			if (node == nhp->th_nodearr[i]) {
154 				nhp->th_nodearr[i] = NULL;
155 
156 				/*
157 				 * Release hold on parent
158 				 */
159 				--pnode->tn_refs;
160 				if (pnode->tn_refs == 0)
161 					topo_node_destroy(pnode);
162 			}
163 		}
164 		topo_node_unlock(pnode);
165 	}
166 
167 	topo_node_unlock(node);
168 
169 	/*
170 	 * Allow enumerator to clean-up private data and then release
171 	 * ref count
172 	 */
173 	if (mod->tm_info->tmi_ops->tmo_release != NULL)
174 		mod->tm_info->tmi_ops->tmo_release(mod, node);
175 
176 	topo_method_unregister_all(mod, node);
177 
178 	/*
179 	 * Destroy all node hash lists
180 	 */
181 	while ((nhp = topo_list_next(&node->tn_children)) != NULL) {
182 		for (i = 0; i < nhp->th_arrlen; i++) {
183 			assert(nhp->th_nodearr[i] == NULL);
184 		}
185 		hmod = nhp->th_enum;
186 		topo_mod_strfree(hmod, nhp->th_name);
187 		topo_mod_free(hmod, nhp->th_nodearr,
188 		    nhp->th_arrlen * sizeof (tnode_t *));
189 		topo_list_delete(&node->tn_children, nhp);
190 		topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t));
191 		topo_mod_rele(hmod);
192 	}
193 
194 	/*
195 	 * Destroy all property data structures, free the node and release
196 	 * the module that created it
197 	 */
198 	topo_pgroup_destroy_all(node);
199 	topo_mod_free(mod, node, sizeof (tnode_t));
200 	topo_mod_rele(mod);
201 }
202 
203 void
204 topo_node_lock(tnode_t *node)
205 {
206 	(void) pthread_mutex_lock(&node->tn_lock);
207 }
208 
209 void
210 topo_node_unlock(tnode_t *node)
211 {
212 	(void) pthread_mutex_unlock(&node->tn_lock);
213 }
214 
215 void
216 topo_node_hold(tnode_t *node)
217 {
218 	topo_node_lock(node);
219 	++node->tn_refs;
220 	topo_node_unlock(node);
221 }
222 
223 void
224 topo_node_rele(tnode_t *node)
225 {
226 	topo_node_lock(node);
227 	--node->tn_refs;
228 
229 	/*
230 	 * Ok to remove this node from the topo tree and destroy it
231 	 */
232 	if (node->tn_refs == 0)
233 		topo_node_destroy(node);
234 	else
235 		topo_node_unlock(node);
236 }
237 
238 char *
239 topo_node_name(tnode_t *node)
240 {
241 	return (node->tn_name);
242 }
243 
244 topo_instance_t
245 topo_node_instance(tnode_t *node)
246 {
247 	return (node->tn_instance);
248 }
249 
250 tnode_t *
251 topo_node_parent(tnode_t *node)
252 {
253 	return (node->tn_parent);
254 }
255 
256 int
257 topo_node_flags(tnode_t *node)
258 {
259 	return (node->tn_fflags);
260 }
261 
262 void
263 topo_node_setspecific(tnode_t *node, void *data)
264 {
265 	node->tn_priv = data;
266 }
267 
268 void *
269 topo_node_getspecific(tnode_t *node)
270 {
271 	return (node->tn_priv);
272 }
273 
274 static int
275 node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
276     int err)
277 {
278 	topo_node_unlock(pnode);
279 
280 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to insert child:"
281 	    "%s\n", topo_strerror(err));
282 
283 	if (nhp != NULL) {
284 		if (nhp->th_name != NULL)
285 			topo_mod_strfree(mod, nhp->th_name);
286 		if (nhp->th_nodearr != NULL) {
287 			topo_mod_free(mod, nhp->th_nodearr,
288 			    nhp->th_arrlen * sizeof (tnode_t *));
289 		}
290 		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
291 	}
292 
293 	return (topo_mod_seterrno(mod, err));
294 }
295 
296 int
297 topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
298     topo_instance_t min, topo_instance_t max)
299 {
300 	topo_nodehash_t *nhp;
301 
302 	topo_node_lock(pnode);
303 
304 	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
305 	    (pnode->tn_state & TOPO_NODE_ROOT));
306 
307 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
308 	    nhp = topo_list_next(nhp)) {
309 		if (strcmp(nhp->th_name, name) == 0)
310 			return (node_create_seterror(mod, pnode, NULL,
311 			    EMOD_NODE_DUP));
312 	}
313 
314 	if (min < 0 || max < min)
315 		return (node_create_seterror(mod, pnode, NULL,
316 		    EMOD_NODE_RANGE));
317 
318 	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
319 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
320 
321 	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
322 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
323 
324 	nhp->th_arrlen = max - min + 1;
325 
326 	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
327 	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
328 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
329 
330 	nhp->th_range.tr_min = min;
331 	nhp->th_range.tr_max = max;
332 	nhp->th_enum = mod;
333 	topo_mod_hold(mod);
334 
335 	/*
336 	 * Add these nodes to parent child list
337 	 */
338 	topo_list_append(&pnode->tn_children, nhp);
339 	topo_node_unlock(pnode);
340 
341 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
342 	    "created node range %s[%d-%d]\n", name, min, max);
343 
344 	return (0);
345 }
346 
347 void
348 topo_node_range_destroy(tnode_t *pnode, const char *name)
349 {
350 	int i;
351 	topo_nodehash_t *nhp;
352 	topo_mod_t *mod;
353 
354 	topo_node_lock(pnode);
355 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
356 	    nhp = topo_list_next(nhp)) {
357 		if (strcmp(nhp->th_name, name) == 0) {
358 			break;
359 		}
360 	}
361 
362 	if (nhp == NULL) {
363 		topo_node_unlock(pnode);
364 		return;
365 	}
366 
367 	for (i = 0; i < nhp->th_arrlen; i++)
368 		assert(nhp->th_nodearr[i] == NULL);
369 
370 	topo_list_delete(&pnode->tn_children, nhp);
371 	topo_node_unlock(pnode);
372 
373 	mod = nhp->th_enum;
374 	if (nhp->th_name != NULL)
375 		topo_mod_strfree(mod, nhp->th_name);
376 	if (nhp->th_nodearr != NULL) {
377 		topo_mod_free(mod, nhp->th_nodearr,
378 		    nhp->th_arrlen * sizeof (tnode_t *));
379 	}
380 	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
381 	topo_mod_rele(mod);
382 
383 }
384 
385 tnode_t *
386 topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
387 {
388 	int h;
389 	tnode_t *node;
390 	topo_nodehash_t *nhp;
391 
392 	topo_dprintf(pnode->tn_hdl, TOPO_DBG_MODSVC,
393 	    "topo_node_lookup: looking for '%s' instance %d\n", name, inst);
394 
395 	topo_node_lock(pnode);
396 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
397 	    nhp = topo_list_next(nhp)) {
398 		if (strcmp(nhp->th_name, name) == 0) {
399 
400 			if (inst > nhp->th_range.tr_max ||
401 			    inst < nhp->th_range.tr_min) {
402 				topo_node_unlock(pnode);
403 				return (NULL);
404 			}
405 
406 			h = topo_node_hash(nhp, inst);
407 			node = nhp->th_nodearr[h];
408 			topo_node_unlock(pnode);
409 			return (node);
410 		}
411 	}
412 	topo_node_unlock(pnode);
413 
414 	return (NULL);
415 }
416 
417 int
418 topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
419 {
420 	return ((inst - nhp->th_range.tr_min) % nhp->th_arrlen);
421 }
422 
423 static tnode_t *
424 node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node,
425     boolean_t pnode_locked, int err)
426 {
427 	if (pnode_locked)
428 		topo_node_unlock(pnode);
429 
430 	(void) topo_mod_seterrno(mod, err);
431 
432 	if (node == NULL)
433 		return (NULL);
434 
435 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
436 	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
437 	    node->tn_instance, topo_strerror(err));
438 
439 	topo_node_lock(node); /* expected to be locked */
440 	topo_node_destroy(node);
441 
442 	return (NULL);
443 }
444 
445 tnode_t *
446 topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
447     topo_instance_t inst, nvlist_t *fmri)
448 {
449 	int h, err;
450 	tnode_t *node;
451 	topo_nodehash_t *nhp;
452 
453 	topo_node_lock(pnode);
454 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
455 	    nhp = topo_list_next(nhp)) {
456 		if (strcmp(nhp->th_name, name) == 0) {
457 
458 			if (inst > nhp->th_range.tr_max ||
459 			    inst < nhp->th_range.tr_min)
460 				return (node_bind_seterror(mod, pnode, NULL,
461 				    B_TRUE, EMOD_NODE_RANGE));
462 
463 			h = topo_node_hash(nhp, inst);
464 			if (nhp->th_nodearr[h] != NULL)
465 				return (node_bind_seterror(mod, pnode, NULL,
466 				    B_TRUE, EMOD_NODE_BOUND));
467 			else
468 				break;
469 
470 		}
471 	}
472 
473 	if (nhp == NULL)
474 		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
475 		    EMOD_NODE_NOENT));
476 
477 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
478 		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
479 		    EMOD_NOMEM));
480 
481 	(void) pthread_mutex_init(&node->tn_lock, NULL);
482 
483 	node->tn_enum = mod;
484 	node->tn_hdl = mod->tm_hdl;
485 	node->tn_parent = pnode;
486 	node->tn_name = nhp->th_name;
487 	node->tn_instance = inst;
488 	node->tn_phash = nhp;
489 	node->tn_refs = 0;
490 
491 	/* Ref count module that bound this node */
492 	topo_mod_hold(mod);
493 
494 	if (fmri == NULL)
495 		return (node_bind_seterror(mod, pnode, node, B_TRUE,
496 		    EMOD_NVL_INVAL));
497 
498 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
499 		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
500 
501 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
502 	    TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
503 		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
504 
505 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
506 	    "node bound %s=%d/%s=%d\n", topo_node_name(pnode),
507 	    topo_node_instance(pnode), node->tn_name, node->tn_instance);
508 
509 	node->tn_state |= TOPO_NODE_BOUND;
510 
511 	topo_node_hold(node);
512 	nhp->th_nodearr[h] = node;
513 	++pnode->tn_refs;
514 
515 	topo_node_unlock(pnode);
516 
517 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
518 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
519 		    FM_FMRI_AUTH_PRODUCT, &err);
520 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
521 		    FM_FMRI_AUTH_PRODUCT_SN, &err);
522 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
523 		    FM_FMRI_AUTH_CHASSIS, &err);
524 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
525 		    FM_FMRI_AUTH_SERVER, &err);
526 	}
527 
528 	return (node);
529 }
530 
531 tnode_t *
532 topo_node_facbind(topo_mod_t *mod, tnode_t *pnode, const char *name,
533     const char *type)
534 {
535 	int h, err;
536 	tnode_t *node;
537 	topo_nodehash_t *nhp;
538 	topo_instance_t inst = 0;
539 	nvlist_t *pfmri, *fnvl;
540 
541 	/*
542 	 * Create a single entry range for this facility
543 	 */
544 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0)
545 		return (NULL);  /* mod errno set */
546 
547 	topo_node_hold(pnode);
548 	topo_node_lock(pnode);
549 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
550 	    nhp = topo_list_next(nhp)) {
551 		if (strcmp(nhp->th_name, name) == 0) {
552 
553 			if (inst > nhp->th_range.tr_max ||
554 			    inst < nhp->th_range.tr_min) {
555 				topo_node_rele(pnode);
556 				return (node_bind_seterror(mod, pnode, NULL,
557 				    B_TRUE, EMOD_NVL_INVAL));
558 			}
559 			h = topo_node_hash(nhp, inst);
560 			if (nhp->th_nodearr[h] != NULL) {
561 				topo_node_rele(pnode);
562 				return (node_bind_seterror(mod, pnode, NULL,
563 				    B_TRUE, EMOD_NODE_BOUND));
564 			} else
565 				break;
566 
567 		}
568 	}
569 	topo_node_unlock(pnode);
570 
571 	if (nhp == NULL) {
572 		topo_node_rele(pnode);
573 		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
574 		    EMOD_NODE_NOENT));
575 	}
576 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL) {
577 		topo_node_rele(pnode);
578 		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
579 		    EMOD_NOMEM));
580 	}
581 
582 	(void) pthread_mutex_init(&node->tn_lock, NULL);
583 
584 	node->tn_enum = mod;
585 	node->tn_hdl = mod->tm_hdl;
586 	node->tn_parent = pnode;
587 	node->tn_name = nhp->th_name;
588 	node->tn_instance = inst;
589 	node->tn_phash = nhp;
590 	node->tn_refs = 0;
591 	node->tn_fflags = TOPO_NODE_FACILITY;
592 
593 	/* Ref count module that bound this node */
594 	topo_mod_hold(mod);
595 
596 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0) {
597 		topo_node_rele(pnode);
598 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
599 	}
600 	if (topo_mod_nvalloc(mod, &fnvl, NV_UNIQUE_NAME) < 0) {
601 		topo_node_rele(pnode);
602 		return (node_bind_seterror(mod, pnode, node, B_FALSE,
603 		    EMOD_NOMEM));
604 	}
605 	if (nvlist_add_string(fnvl, FM_FMRI_FACILITY_NAME, name) != 0 ||
606 	    nvlist_add_string(fnvl, FM_FMRI_FACILITY_TYPE, type) != 0) {
607 		nvlist_free(fnvl);
608 		topo_node_rele(pnode);
609 		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
610 		    EMOD_FMRI_NVL));
611 	}
612 
613 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
614 		nvlist_free(fnvl);
615 		topo_node_rele(pnode);
616 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
617 	}
618 
619 	if (nvlist_add_nvlist(pfmri, FM_FMRI_FACILITY, fnvl) != 0) {
620 		nvlist_free(fnvl);
621 		nvlist_free(pfmri);
622 		topo_node_rele(pnode);
623 		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
624 		    EMOD_FMRI_NVL));
625 	}
626 
627 	nvlist_free(fnvl);
628 
629 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
630 	    TOPO_PROP_IMMUTABLE, pfmri, &err) < 0) {
631 		nvlist_free(pfmri);
632 		topo_node_rele(pnode);
633 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
634 	}
635 
636 	nvlist_free(pfmri);
637 
638 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
639 	    "facility node bound %s=%s\n", type, node->tn_name);
640 
641 	node->tn_state |= TOPO_NODE_BOUND;
642 
643 	topo_node_hold(node);
644 	nhp->th_nodearr[h] = node;
645 
646 	topo_node_lock(pnode);
647 	++pnode->tn_refs;
648 	topo_node_unlock(pnode);
649 	topo_node_rele(pnode);
650 
651 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
652 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
653 		    FM_FMRI_AUTH_PRODUCT, &err);
654 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
655 		    FM_FMRI_AUTH_PRODUCT_SN, &err);
656 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
657 		    FM_FMRI_AUTH_CHASSIS, &err);
658 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
659 		    FM_FMRI_AUTH_SERVER, &err);
660 	}
661 
662 	return (node);
663 }
664 
665 int
666 topo_node_facility(topo_hdl_t *thp, tnode_t *node, const char *fac_type,
667     uint32_t fac_subtype, topo_faclist_t *faclist, int *errp)
668 {
669 	tnode_t *tmp;
670 	nvlist_t *rsrc, *fac;
671 	char *tmp_factype;
672 	uint32_t tmp_facsubtype;
673 	boolean_t list_empty = 1;
674 	topo_faclist_t *fac_ele;
675 
676 	bzero(faclist, sizeof (topo_faclist_t));
677 	for (tmp = topo_child_first(node); tmp != NULL;
678 	    tmp = topo_child_next(node, tmp)) {
679 
680 		topo_node_hold(tmp);
681 		/*
682 		 * If it's not a facility node, move on
683 		 */
684 		if (topo_node_flags(tmp) != TOPO_NODE_FACILITY) {
685 			topo_node_rele(tmp);
686 			continue;
687 		}
688 
689 		/*
690 		 * Lookup whether the fac type is sensor or indicator and if
691 		 * it's not the type we're looking for, move on
692 		 */
693 		if (topo_node_resource(tmp, &rsrc, errp) != 0) {
694 			topo_dprintf(thp, TOPO_DBG_ERR,
695 			    "Failed to get resource for node %s=%d (%s)\n",
696 			    topo_node_name(node), topo_node_instance(node),
697 			    topo_strerror(*errp));
698 			topo_node_rele(tmp);
699 			return (-1);
700 		}
701 		if ((nvlist_lookup_nvlist(rsrc, "facility", &fac) != 0) ||
702 		    (nvlist_lookup_string(fac, FM_FMRI_FACILITY_TYPE,
703 		    &tmp_factype) != 0)) {
704 
705 			nvlist_free(rsrc);
706 			topo_node_rele(tmp);
707 			return (-1);
708 		}
709 
710 		if (strcmp(fac_type, tmp_factype) != 0) {
711 			topo_node_rele(tmp);
712 			nvlist_free(rsrc);
713 			continue;
714 		}
715 		nvlist_free(rsrc);
716 
717 		/*
718 		 * Finally, look up the subtype, which is a property in the
719 		 * facility propgroup.  If it's a match return a pointer to the
720 		 * node.  Otherwise, move on.
721 		 */
722 		if (topo_prop_get_uint32(tmp, TOPO_PGROUP_FACILITY,
723 		    TOPO_FACILITY_TYPE, &tmp_facsubtype, errp) != 0) {
724 			topo_node_rele(tmp);
725 			return (-1);
726 		}
727 		if (fac_subtype == tmp_facsubtype ||
728 		    fac_subtype == TOPO_FAC_TYPE_ANY) {
729 			if ((fac_ele = topo_mod_zalloc(tmp->tn_enum,
730 			    sizeof (topo_faclist_t))) == NULL) {
731 				*errp = ETOPO_NOMEM;
732 				topo_node_rele(tmp);
733 				return (-1);
734 			}
735 			fac_ele->tf_node = tmp;
736 			topo_list_append(&faclist->tf_list, fac_ele);
737 			list_empty = 0;
738 		}
739 		topo_node_rele(tmp);
740 	}
741 
742 	if (list_empty) {
743 		*errp = ETOPO_FAC_NOENT;
744 		return (-1);
745 	}
746 	return (0);
747 }
748 
749 void
750 topo_node_unbind(tnode_t *node)
751 {
752 	if (node == NULL)
753 		return;
754 
755 	topo_node_lock(node);
756 	if (!(node->tn_state & TOPO_NODE_BOUND)) {
757 		topo_node_unlock(node);
758 		return;
759 	}
760 
761 	node->tn_state &= ~TOPO_NODE_BOUND;
762 	topo_node_unlock(node);
763 
764 	topo_dprintf(node->tn_hdl, TOPO_DBG_MODSVC,
765 	    "node unbound %s=%d/%s=%d refs = %d\n",
766 	    topo_node_name(node->tn_parent),
767 	    topo_node_instance(node->tn_parent), node->tn_name,
768 	    node->tn_instance, node->tn_refs);
769 
770 	topo_node_rele(node);
771 }
772 
773 /*ARGSUSED*/
774 int
775 topo_node_present(tnode_t *node)
776 {
777 	return (0);
778 }
779 
780 /*ARGSUSED*/
781 int
782 topo_node_contains(tnode_t *er, tnode_t *ee)
783 {
784 	return (0);
785 }
786 
787 /*ARGSUSED*/
788 int
789 topo_node_unusable(tnode_t *node)
790 {
791 	return (0);
792 }
793 
794 topo_walk_t *
795 topo_node_walk_init(topo_hdl_t *thp, topo_mod_t *mod, tnode_t *node,
796     int (*cb_f)(), void *pdata, int *errp)
797 {
798 	tnode_t *child;
799 	topo_walk_t *wp;
800 
801 	topo_node_hold(node);
802 
803 	if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) == NULL) {
804 		*errp = ETOPO_HDL_NOMEM;
805 		topo_node_rele(node);
806 		return (NULL);
807 	}
808 
809 	/*
810 	 * If this is the root of the scheme tree, start with the first
811 	 * child
812 	 */
813 	topo_node_lock(node);
814 	if (node->tn_state & TOPO_NODE_ROOT) {
815 		if ((child = topo_child_first(node)) == NULL) {
816 			/* Nothing to walk */
817 			*errp = ETOPO_WALK_EMPTY;
818 			topo_node_unlock(node);
819 			topo_node_rele(node);
820 			topo_hdl_free(thp, wp, sizeof (topo_walk_t));
821 			return (NULL);
822 		}
823 		topo_node_unlock(node);
824 		topo_node_hold(child);
825 		wp->tw_node = child;
826 	} else {
827 		topo_node_unlock(node);
828 		topo_node_hold(node); /* rele at walk end */
829 		wp->tw_node = node;
830 	}
831 
832 	wp->tw_root = node;
833 	wp->tw_cb = cb_f;
834 	wp->tw_pdata = pdata;
835 	wp->tw_thp = thp;
836 	wp->tw_mod = mod;
837 
838 	return (wp);
839 }
840 
841 /*
842  * Walk the direct children of the given node.
843  */
844 int
845 topo_node_child_walk(topo_hdl_t *thp, tnode_t *pnode, topo_walk_cb_t cb_f,
846     void *arg, int *errp)
847 {
848 	int ret = TOPO_WALK_TERMINATE;
849 	tnode_t *cnode;
850 
851 	topo_node_hold(pnode);
852 
853 	/*
854 	 * First Child:
855 	 */
856 	topo_node_lock(pnode);
857 	cnode = topo_child_first(pnode);
858 	topo_node_unlock(pnode);
859 
860 	if (cnode == NULL) {
861 		*errp = ETOPO_WALK_EMPTY;
862 		ret = TOPO_WALK_ERR;
863 		goto out;
864 	}
865 
866 	while (cnode != NULL) {
867 		int iret;
868 
869 		/*
870 		 * Call the walker callback:
871 		 */
872 		topo_node_hold(cnode);
873 		iret = cb_f(thp, cnode, arg);
874 		topo_node_rele(cnode);
875 		if (iret != TOPO_WALK_NEXT) {
876 			ret = iret;
877 			break;
878 		}
879 
880 		/*
881 		 * Next child:
882 		 */
883 		topo_node_lock(pnode);
884 		cnode = topo_child_next(pnode, cnode);
885 		topo_node_unlock(pnode);
886 	}
887 
888 out:
889 	topo_node_rele(pnode);
890 	return (ret);
891 }
892