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