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  */
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
topo_node_destroy(tnode_t * node)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 	 * Nodes in a directed graph structure have no children, so the node
200 	 * name is still intact. We must free it now.
201 	 */
202 	if (node->tn_vtx != NULL) {
203 		topo_mod_strfree(mod, node->tn_name);
204 	}
205 
206 	/*
207 	 * Destroy all property data structures, free the node and release
208 	 * the module that created it
209 	 */
210 	topo_pgroup_destroy_all(node);
211 	topo_mod_free(mod, node, sizeof (tnode_t));
212 	topo_mod_rele(mod);
213 }
214 
215 void
topo_node_lock(tnode_t * node)216 topo_node_lock(tnode_t *node)
217 {
218 	(void) pthread_mutex_lock(&node->tn_lock);
219 }
220 
221 void
topo_node_unlock(tnode_t * node)222 topo_node_unlock(tnode_t *node)
223 {
224 	(void) pthread_mutex_unlock(&node->tn_lock);
225 }
226 
227 void
topo_node_hold(tnode_t * node)228 topo_node_hold(tnode_t *node)
229 {
230 	topo_node_lock(node);
231 	++node->tn_refs;
232 	topo_node_unlock(node);
233 }
234 
235 void
topo_node_rele(tnode_t * node)236 topo_node_rele(tnode_t *node)
237 {
238 	topo_node_lock(node);
239 	--node->tn_refs;
240 
241 	/*
242 	 * Ok to remove this node from the topo tree and destroy it
243 	 */
244 	if (node->tn_refs == 0)
245 		topo_node_destroy(node);
246 	else
247 		topo_node_unlock(node);
248 }
249 
250 char *
topo_node_name(tnode_t * node)251 topo_node_name(tnode_t *node)
252 {
253 	return (node->tn_name);
254 }
255 
256 topo_instance_t
topo_node_instance(tnode_t * node)257 topo_node_instance(tnode_t *node)
258 {
259 	return (node->tn_instance);
260 }
261 
262 tnode_t *
topo_node_parent(tnode_t * node)263 topo_node_parent(tnode_t *node)
264 {
265 	return (node->tn_parent);
266 }
267 
268 topo_vertex_t *
topo_node_vertex(tnode_t * node)269 topo_node_vertex(tnode_t *node)
270 {
271 	return (node->tn_vtx);
272 }
273 
274 int
topo_node_flags(tnode_t * node)275 topo_node_flags(tnode_t *node)
276 {
277 	return (node->tn_fflags);
278 }
279 
280 void
topo_node_setspecific(tnode_t * node,void * data)281 topo_node_setspecific(tnode_t *node, void *data)
282 {
283 	node->tn_priv = data;
284 }
285 
286 void *
topo_node_getspecific(tnode_t * node)287 topo_node_getspecific(tnode_t *node)
288 {
289 	return (node->tn_priv);
290 }
291 
292 static int
node_create_seterror(topo_mod_t * mod,tnode_t * pnode,topo_nodehash_t * nhp,int err)293 node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp,
294     int err)
295 {
296 	topo_node_unlock(pnode);
297 
298 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to insert child:"
299 	    "%s\n", topo_strerror(err));
300 
301 	if (nhp != NULL) {
302 		if (nhp->th_name != NULL)
303 			topo_mod_strfree(mod, nhp->th_name);
304 		if (nhp->th_nodearr != NULL) {
305 			topo_mod_free(mod, nhp->th_nodearr,
306 			    nhp->th_arrlen * sizeof (tnode_t *));
307 		}
308 		topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
309 	}
310 
311 	return (topo_mod_seterrno(mod, err));
312 }
313 
314 int
topo_node_range_create(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max)315 topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
316     topo_instance_t min, topo_instance_t max)
317 {
318 	topo_nodehash_t *nhp;
319 
320 	topo_node_lock(pnode);
321 
322 	assert((pnode->tn_state & TOPO_NODE_BOUND) ||
323 	    (pnode->tn_state & TOPO_NODE_ROOT));
324 
325 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
326 	    nhp = topo_list_next(nhp)) {
327 		if (strcmp(nhp->th_name, name) == 0)
328 			return (node_create_seterror(mod, pnode, NULL,
329 			    EMOD_NODE_DUP));
330 	}
331 
332 	if (max < min)
333 		return (node_create_seterror(mod, pnode, NULL,
334 		    EMOD_NODE_RANGE));
335 
336 	if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL)
337 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
338 
339 	if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL)
340 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
341 
342 	nhp->th_arrlen = max - min + 1;
343 
344 	if ((nhp->th_nodearr = topo_mod_zalloc(mod,
345 	    nhp->th_arrlen * sizeof (tnode_t *))) == NULL)
346 		return (node_create_seterror(mod, pnode, nhp, EMOD_NOMEM));
347 
348 	nhp->th_range.tr_min = min;
349 	nhp->th_range.tr_max = max;
350 	nhp->th_enum = mod;
351 	topo_mod_hold(mod);
352 
353 	/*
354 	 * Add these nodes to parent child list
355 	 */
356 	topo_list_append(&pnode->tn_children, nhp);
357 	topo_node_unlock(pnode);
358 
359 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
360 	    "created node range %s[%d-%d]\n", name, min, max);
361 
362 	return (0);
363 }
364 
365 void
topo_node_range_destroy(tnode_t * pnode,const char * name)366 topo_node_range_destroy(tnode_t *pnode, const char *name)
367 {
368 	int i;
369 	topo_nodehash_t *nhp;
370 	topo_mod_t *mod;
371 
372 	topo_node_lock(pnode);
373 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
374 	    nhp = topo_list_next(nhp)) {
375 		if (strcmp(nhp->th_name, name) == 0) {
376 			break;
377 		}
378 	}
379 
380 	if (nhp == NULL) {
381 		topo_node_unlock(pnode);
382 		return;
383 	}
384 
385 	for (i = 0; i < nhp->th_arrlen; i++)
386 		assert(nhp->th_nodearr[i] == NULL);
387 
388 	topo_list_delete(&pnode->tn_children, nhp);
389 	topo_node_unlock(pnode);
390 
391 	mod = nhp->th_enum;
392 	if (nhp->th_name != NULL)
393 		topo_mod_strfree(mod, nhp->th_name);
394 	if (nhp->th_nodearr != NULL) {
395 		topo_mod_free(mod, nhp->th_nodearr,
396 		    nhp->th_arrlen * sizeof (tnode_t *));
397 	}
398 	topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
399 	topo_mod_rele(mod);
400 
401 }
402 
403 tnode_t *
topo_node_lookup(tnode_t * pnode,const char * name,topo_instance_t inst)404 topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
405 {
406 	int h;
407 	tnode_t *node;
408 	topo_nodehash_t *nhp;
409 
410 	topo_dprintf(pnode->tn_hdl, TOPO_DBG_MODSVC,
411 	    "topo_node_lookup: looking for '%s' instance %d\n", name, inst);
412 
413 	topo_node_lock(pnode);
414 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
415 	    nhp = topo_list_next(nhp)) {
416 		if (strcmp(nhp->th_name, name) == 0) {
417 
418 			if (inst > nhp->th_range.tr_max ||
419 			    inst < nhp->th_range.tr_min) {
420 				topo_node_unlock(pnode);
421 				return (NULL);
422 			}
423 
424 			h = topo_node_hash(nhp, inst);
425 			node = nhp->th_nodearr[h];
426 			topo_node_unlock(pnode);
427 			return (node);
428 		}
429 	}
430 	topo_node_unlock(pnode);
431 
432 	return (NULL);
433 }
434 
435 int
topo_node_hash(topo_nodehash_t * nhp,topo_instance_t inst)436 topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst)
437 {
438 	return ((inst - nhp->th_range.tr_min) % nhp->th_arrlen);
439 }
440 
441 static tnode_t *
node_bind_seterror(topo_mod_t * mod,tnode_t * pnode,tnode_t * node,boolean_t pnode_locked,int err)442 node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node,
443     boolean_t pnode_locked, int err)
444 {
445 	if (pnode_locked)
446 		topo_node_unlock(pnode);
447 
448 	(void) topo_mod_seterrno(mod, err);
449 
450 	if (node == NULL)
451 		return (NULL);
452 
453 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, "unable to bind %s=%d: "
454 	    "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"),
455 	    node->tn_instance, topo_strerror(err));
456 
457 	topo_node_lock(node); /* expected to be locked */
458 	topo_node_destroy(node);
459 
460 	return (NULL);
461 }
462 
463 tnode_t *
topo_node_bind(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t inst,nvlist_t * fmri)464 topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name,
465     topo_instance_t inst, nvlist_t *fmri)
466 {
467 	int h, err;
468 	tnode_t *node;
469 	topo_nodehash_t *nhp;
470 
471 	topo_node_lock(pnode);
472 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
473 	    nhp = topo_list_next(nhp)) {
474 		if (strcmp(nhp->th_name, name) == 0) {
475 
476 			if (inst > nhp->th_range.tr_max ||
477 			    inst < nhp->th_range.tr_min)
478 				return (node_bind_seterror(mod, pnode, NULL,
479 				    B_TRUE, EMOD_NODE_RANGE));
480 
481 			h = topo_node_hash(nhp, inst);
482 			if (nhp->th_nodearr[h] != NULL)
483 				return (node_bind_seterror(mod, pnode, NULL,
484 				    B_TRUE, EMOD_NODE_BOUND));
485 			else
486 				break;
487 
488 		}
489 	}
490 
491 	if (nhp == NULL)
492 		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
493 		    EMOD_NODE_NOENT));
494 
495 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
496 		return (node_bind_seterror(mod, pnode, NULL, B_TRUE,
497 		    EMOD_NOMEM));
498 
499 	(void) pthread_mutex_init(&node->tn_lock, NULL);
500 
501 	node->tn_enum = mod;
502 	node->tn_hdl = mod->tm_hdl;
503 	node->tn_parent = pnode;
504 	node->tn_name = nhp->th_name;
505 	node->tn_instance = inst;
506 	node->tn_phash = nhp;
507 	node->tn_refs = 0;
508 
509 	/* Ref count module that bound this node */
510 	topo_mod_hold(mod);
511 
512 	if (fmri == NULL)
513 		return (node_bind_seterror(mod, pnode, node, B_TRUE,
514 		    EMOD_NVL_INVAL));
515 
516 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0)
517 		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
518 
519 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
520 	    TOPO_PROP_IMMUTABLE, fmri, &err) < 0)
521 		return (node_bind_seterror(mod, pnode, node, B_TRUE, err));
522 
523 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
524 	    "node bound %s=%d/%s=%d\n", topo_node_name(pnode),
525 	    topo_node_instance(pnode), node->tn_name, node->tn_instance);
526 
527 	node->tn_state |= TOPO_NODE_BOUND;
528 
529 	topo_node_hold(node);
530 	nhp->th_nodearr[h] = node;
531 	++pnode->tn_refs;
532 
533 	topo_node_unlock(pnode);
534 
535 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
536 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
537 		    FM_FMRI_AUTH_PRODUCT, &err);
538 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
539 		    FM_FMRI_AUTH_PRODUCT_SN, &err);
540 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
541 		    FM_FMRI_AUTH_CHASSIS, &err);
542 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
543 		    FM_FMRI_AUTH_SERVER, &err);
544 	}
545 
546 	return (node);
547 }
548 
549 tnode_t *
topo_node_facbind(topo_mod_t * mod,tnode_t * pnode,const char * name,const char * type)550 topo_node_facbind(topo_mod_t *mod, tnode_t *pnode, const char *name,
551     const char *type)
552 {
553 	int h, err;
554 	tnode_t *node;
555 	topo_nodehash_t *nhp;
556 	topo_instance_t inst = 0;
557 	nvlist_t *pfmri, *fnvl;
558 
559 	/*
560 	 * Create a single entry range for this facility
561 	 */
562 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0)
563 		return (NULL);  /* mod errno set */
564 
565 	topo_node_hold(pnode);
566 	topo_node_lock(pnode);
567 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
568 	    nhp = topo_list_next(nhp)) {
569 		if (strcmp(nhp->th_name, name) == 0) {
570 
571 			if (inst > nhp->th_range.tr_max ||
572 			    inst < nhp->th_range.tr_min) {
573 				topo_node_rele(pnode);
574 				return (node_bind_seterror(mod, pnode, NULL,
575 				    B_TRUE, EMOD_NVL_INVAL));
576 			}
577 			h = topo_node_hash(nhp, inst);
578 			if (nhp->th_nodearr[h] != NULL) {
579 				topo_node_rele(pnode);
580 				return (node_bind_seterror(mod, pnode, NULL,
581 				    B_TRUE, EMOD_NODE_BOUND));
582 			} else
583 				break;
584 
585 		}
586 	}
587 	topo_node_unlock(pnode);
588 
589 	if (nhp == NULL) {
590 		topo_node_rele(pnode);
591 		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
592 		    EMOD_NODE_NOENT));
593 	}
594 	if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL) {
595 		topo_node_rele(pnode);
596 		return (node_bind_seterror(mod, pnode, NULL, B_FALSE,
597 		    EMOD_NOMEM));
598 	}
599 
600 	(void) pthread_mutex_init(&node->tn_lock, NULL);
601 
602 	node->tn_enum = mod;
603 	node->tn_hdl = mod->tm_hdl;
604 	node->tn_parent = pnode;
605 	node->tn_name = nhp->th_name;
606 	node->tn_instance = inst;
607 	node->tn_phash = nhp;
608 	node->tn_refs = 0;
609 	node->tn_fflags = TOPO_NODE_FACILITY;
610 
611 	/* Ref count module that bound this node */
612 	topo_mod_hold(mod);
613 
614 	if (topo_pgroup_create(node, &protocol_pgroup, &err) < 0) {
615 		topo_node_rele(pnode);
616 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
617 	}
618 	if (topo_mod_nvalloc(mod, &fnvl, NV_UNIQUE_NAME) < 0) {
619 		topo_node_rele(pnode);
620 		return (node_bind_seterror(mod, pnode, node, B_FALSE,
621 		    EMOD_NOMEM));
622 	}
623 	if (nvlist_add_string(fnvl, FM_FMRI_FACILITY_NAME, name) != 0 ||
624 	    nvlist_add_string(fnvl, FM_FMRI_FACILITY_TYPE, type) != 0) {
625 		nvlist_free(fnvl);
626 		topo_node_rele(pnode);
627 		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
628 		    EMOD_FMRI_NVL));
629 	}
630 
631 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
632 		nvlist_free(fnvl);
633 		topo_node_rele(pnode);
634 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
635 	}
636 
637 	if (nvlist_add_nvlist(pfmri, FM_FMRI_FACILITY, fnvl) != 0) {
638 		nvlist_free(fnvl);
639 		nvlist_free(pfmri);
640 		topo_node_rele(pnode);
641 		return (node_bind_seterror(mod, pnode, node,  B_FALSE,
642 		    EMOD_FMRI_NVL));
643 	}
644 
645 	nvlist_free(fnvl);
646 
647 	if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE,
648 	    TOPO_PROP_IMMUTABLE, pfmri, &err) < 0) {
649 		nvlist_free(pfmri);
650 		topo_node_rele(pnode);
651 		return (node_bind_seterror(mod, pnode, node, B_FALSE, err));
652 	}
653 
654 	nvlist_free(pfmri);
655 
656 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
657 	    "facility node bound %s=%s\n", type, node->tn_name);
658 
659 	node->tn_state |= TOPO_NODE_BOUND;
660 
661 	topo_node_hold(node);
662 	nhp->th_nodearr[h] = node;
663 
664 	topo_node_lock(pnode);
665 	++pnode->tn_refs;
666 	topo_node_unlock(pnode);
667 	topo_node_rele(pnode);
668 
669 	if (topo_pgroup_create(node, &auth_pgroup, &err) == 0) {
670 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
671 		    FM_FMRI_AUTH_PRODUCT, &err);
672 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
673 		    FM_FMRI_AUTH_PRODUCT_SN, &err);
674 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
675 		    FM_FMRI_AUTH_CHASSIS, &err);
676 		(void) topo_prop_inherit(node, FM_FMRI_AUTHORITY,
677 		    FM_FMRI_AUTH_SERVER, &err);
678 	}
679 
680 	return (node);
681 }
682 
683 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)684 topo_node_facility(topo_hdl_t *thp, tnode_t *node, const char *fac_type,
685     uint32_t fac_subtype, topo_faclist_t *faclist, int *errp)
686 {
687 	tnode_t *tmp;
688 	nvlist_t *rsrc, *fac;
689 	char *tmp_factype;
690 	uint32_t tmp_facsubtype;
691 	boolean_t list_empty = 1;
692 	topo_faclist_t *fac_ele;
693 
694 	bzero(faclist, sizeof (topo_faclist_t));
695 	for (tmp = topo_child_first(node); tmp != NULL;
696 	    tmp = topo_child_next(node, tmp)) {
697 
698 		topo_node_hold(tmp);
699 		/*
700 		 * If it's not a facility node, move on
701 		 */
702 		if (topo_node_flags(tmp) != TOPO_NODE_FACILITY) {
703 			topo_node_rele(tmp);
704 			continue;
705 		}
706 
707 		/*
708 		 * Lookup whether the fac type is sensor or indicator and if
709 		 * it's not the type we're looking for, move on
710 		 */
711 		if (topo_node_resource(tmp, &rsrc, errp) != 0) {
712 			topo_dprintf(thp, TOPO_DBG_ERR,
713 			    "Failed to get resource for node %s=%d (%s)\n",
714 			    topo_node_name(node), topo_node_instance(node),
715 			    topo_strerror(*errp));
716 			topo_node_rele(tmp);
717 			return (-1);
718 		}
719 		if ((nvlist_lookup_nvlist(rsrc, "facility", &fac) != 0) ||
720 		    (nvlist_lookup_string(fac, FM_FMRI_FACILITY_TYPE,
721 		    &tmp_factype) != 0)) {
722 
723 			nvlist_free(rsrc);
724 			topo_node_rele(tmp);
725 			return (-1);
726 		}
727 
728 		if (strcmp(fac_type, tmp_factype) != 0) {
729 			topo_node_rele(tmp);
730 			nvlist_free(rsrc);
731 			continue;
732 		}
733 		nvlist_free(rsrc);
734 
735 		/*
736 		 * Finally, look up the subtype, which is a property in the
737 		 * facility propgroup.  If it's a match return a pointer to the
738 		 * node.  Otherwise, move on.
739 		 */
740 		if (topo_prop_get_uint32(tmp, TOPO_PGROUP_FACILITY,
741 		    TOPO_FACILITY_TYPE, &tmp_facsubtype, errp) != 0) {
742 			topo_node_rele(tmp);
743 			return (-1);
744 		}
745 		if (fac_subtype == tmp_facsubtype ||
746 		    fac_subtype == TOPO_FAC_TYPE_ANY) {
747 			if ((fac_ele = topo_mod_zalloc(tmp->tn_enum,
748 			    sizeof (topo_faclist_t))) == NULL) {
749 				*errp = ETOPO_NOMEM;
750 				topo_node_rele(tmp);
751 				return (-1);
752 			}
753 			fac_ele->tf_node = tmp;
754 			topo_list_append(&faclist->tf_list, fac_ele);
755 			list_empty = 0;
756 		}
757 		topo_node_rele(tmp);
758 	}
759 
760 	if (list_empty) {
761 		*errp = ETOPO_FAC_NOENT;
762 		return (-1);
763 	}
764 	return (0);
765 }
766 
767 void
topo_node_unbind(tnode_t * node)768 topo_node_unbind(tnode_t *node)
769 {
770 	if (node == NULL)
771 		return;
772 
773 	topo_node_lock(node);
774 	if (!(node->tn_state & TOPO_NODE_BOUND)) {
775 		topo_node_unlock(node);
776 		return;
777 	}
778 
779 	node->tn_state &= ~TOPO_NODE_BOUND;
780 	topo_node_unlock(node);
781 
782 	topo_dprintf(node->tn_hdl, TOPO_DBG_MODSVC,
783 	    "node unbound %s=%d refs = %d\n", node->tn_name,
784 	    node->tn_instance, node->tn_refs);
785 
786 	topo_node_rele(node);
787 }
788 
789 /*ARGSUSED*/
790 int
topo_node_present(tnode_t * node)791 topo_node_present(tnode_t *node)
792 {
793 	return (0);
794 }
795 
796 /*ARGSUSED*/
797 int
topo_node_contains(tnode_t * er,tnode_t * ee)798 topo_node_contains(tnode_t *er, tnode_t *ee)
799 {
800 	return (0);
801 }
802 
803 /*ARGSUSED*/
804 int
topo_node_unusable(tnode_t * node)805 topo_node_unusable(tnode_t *node)
806 {
807 	return (0);
808 }
809 
810 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)811 topo_node_walk_init(topo_hdl_t *thp, topo_mod_t *mod, tnode_t *node,
812     int (*cb_f)(), void *pdata, int *errp)
813 {
814 	tnode_t *child;
815 	topo_walk_t *wp;
816 
817 	topo_node_hold(node);
818 
819 	if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) == NULL) {
820 		*errp = ETOPO_HDL_NOMEM;
821 		topo_node_rele(node);
822 		return (NULL);
823 	}
824 
825 	/*
826 	 * If this is the root of the scheme tree, start with the first
827 	 * child
828 	 */
829 	topo_node_lock(node);
830 	if (node->tn_state & TOPO_NODE_ROOT) {
831 		if ((child = topo_child_first(node)) == NULL) {
832 			/* Nothing to walk */
833 			*errp = ETOPO_WALK_EMPTY;
834 			topo_node_unlock(node);
835 			topo_node_rele(node);
836 			topo_hdl_free(thp, wp, sizeof (topo_walk_t));
837 			return (NULL);
838 		}
839 		topo_node_unlock(node);
840 		topo_node_hold(child);
841 		wp->tw_node = child;
842 	} else {
843 		topo_node_unlock(node);
844 		topo_node_hold(node); /* rele at walk end */
845 		wp->tw_node = node;
846 	}
847 
848 	wp->tw_root = node;
849 	wp->tw_cb = cb_f;
850 	wp->tw_pdata = pdata;
851 	wp->tw_thp = thp;
852 	wp->tw_mod = mod;
853 
854 	return (wp);
855 }
856 
857 /*
858  * Walk the direct children of the given node.
859  */
860 int
topo_node_child_walk(topo_hdl_t * thp,tnode_t * pnode,topo_walk_cb_t cb_f,void * arg,int * errp)861 topo_node_child_walk(topo_hdl_t *thp, tnode_t *pnode, topo_walk_cb_t cb_f,
862     void *arg, int *errp)
863 {
864 	int ret = TOPO_WALK_TERMINATE;
865 	tnode_t *cnode;
866 
867 	topo_node_hold(pnode);
868 
869 	/*
870 	 * First Child:
871 	 */
872 	topo_node_lock(pnode);
873 	cnode = topo_child_first(pnode);
874 	topo_node_unlock(pnode);
875 
876 	if (cnode == NULL) {
877 		*errp = ETOPO_WALK_EMPTY;
878 		ret = TOPO_WALK_ERR;
879 		goto out;
880 	}
881 
882 	while (cnode != NULL) {
883 		int iret;
884 
885 		/*
886 		 * Call the walker callback:
887 		 */
888 		topo_node_hold(cnode);
889 		iret = cb_f(thp, cnode, arg);
890 		topo_node_rele(cnode);
891 		if (iret != TOPO_WALK_NEXT) {
892 			ret = iret;
893 			break;
894 		}
895 
896 		/*
897 		 * Next child:
898 		 */
899 		topo_node_lock(pnode);
900 		cnode = topo_child_next(pnode, cnode);
901 		topo_node_unlock(pnode);
902 	}
903 
904 out:
905 	topo_node_rele(pnode);
906 	return (ret);
907 }
908 
909 int
topo_node_occupied(tnode_t * node,boolean_t * is_occupied)910 topo_node_occupied(tnode_t *node, boolean_t *is_occupied)
911 {
912 	nvlist_t *out;
913 	int err;
914 
915 	if (topo_method_invoke(node, TOPO_METH_OCCUPIED,
916 	    TOPO_METH_OCCUPIED_VERSION, NULL, &out, &err) != 0) {
917 		return (err);
918 	}
919 	(void) nvlist_lookup_boolean_value(out, TOPO_METH_OCCUPIED_RET,
920 	    is_occupied);
921 
922 	nvlist_free(out);
923 	return (0);
924 }
925