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