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