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