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 * Snapshot Library Interfaces
29 *
30 * Consumers of topology data may use the interfaces in this file to open,
31 * snapshot and close a topology exported by FMRI scheme (hc, mem and cpu)
32 * builtin plugins and their helper modules. A topology handle is obtained
33 * by calling topo_open(). Upon a successful return, the caller may use this
34 * handle to open a new snapshot. Each snapshot is assigned a Universally
35 * Unique Identifier that in a future enchancement to the libtopo API will be
36 * used as the file locator in /var/fm/topo to persist new snapshots or lookup
37 * a previously captured snapshot. topo_snap_hold() will capture the current
38 * system topology. All consumers of the topo_hdl_t argument will be
39 * blocked from accessing the topology trees until the snapshot completes.
40 *
41 * A snapshot may be cleared by calling topo_snap_rele(). As with
42 * topo_snap_hold(), all topology accesses are blocked until the topology
43 * trees have been released and deallocated.
44 *
45 * Walker Library Interfaces
46 *
47 * Once a snapshot has been taken with topo_snap_hold(), topo_hdl_t holders
48 * may initiate topology tree walks on a scheme-tree basis. topo_walk_init()
49 * will initiate the data structures required to walk any one one of the
50 * FMRI scheme trees. The walker data structure, topo_walk_t, is an opaque
51 * handle passed to topo_walk_step to begin the walk. At each node in the
52 * topology tree, a callback function is called with access to the node at
53 * which our current walk falls. The callback function is passed in during
54 * calls to topo_walk_init() and used throughout the walk_step of the
55 * scheme tree. At any time, the callback may terminate the walk by returning
56 * TOPO_WALK_TERMINATE or TOPO_WALK_ERR. TOPO_WALK_NEXT will continue the walk.
57 *
58 * The type of walk through the tree may be sibling first or child first by
59 * respectively passing in TOPO_WALK_SIBLING or TOPO_WALK_CHILD to
60 * the topo_walk_step() function. Topology nodes
61 * associated with an outstanding walk are held in place and will not be
62 * deallocated until the walk through that node completes.
63 *
64 * Once the walk has terminated, the walking process should call
65 * topo_walk_fini() to clean-up resources created in topo_walk_init()
66 * and release nodes that may be still held.
67 */
68
69 #include <alloca.h>
70 #include <ctype.h>
71 #include <pthread.h>
72 #include <limits.h>
73 #include <assert.h>
74 #include <fcntl.h>
75 #include <smbios.h>
76 #include <sys/param.h>
77 #include <sys/types.h>
78 #include <sys/stat.h>
79 #include <sys/systeminfo.h>
80 #include <sys/utsname.h>
81 #include <uuid/uuid.h>
82 #include <zone.h>
83
84 #include <fm/libtopo.h>
85 #include <sys/fm/protocol.h>
86
87 #include <topo_alloc.h>
88 #include <topo_builtin.h>
89 #include <topo_string.h>
90 #include <topo_error.h>
91 #include <topo_subr.h>
92
93 static void topo_snap_destroy(topo_hdl_t *);
94
95 static topo_hdl_t *
set_open_errno(topo_hdl_t * thp,int * errp,int err)96 set_open_errno(topo_hdl_t *thp, int *errp, int err)
97 {
98 if (thp != NULL) {
99 topo_close(thp);
100 }
101 if (errp != NULL)
102 *errp = err;
103 return (NULL);
104 }
105
106 /*
107 * Set the product name for the topo handle. There are three things that we will
108 * use here in the following order:
109 *
110 * 1) See if SMBIOS gives us a product string that is usable.
111 * 2) Use the root devinfo node's name if we can get a snapshot. This may be the
112 * same as the uname -i aka th_platform on most systems, but on some it will
113 * be more specific. In particular this happens if we have a platform-wide
114 * unix but end up having machine/implementation knowledge without a
115 * machine/implementation-specific unix.
116 * 3) We fall back like we have historically to the platform name.
117 */
118 static void
topo_hdl_set_product(topo_hdl_t * thp)119 topo_hdl_set_product(topo_hdl_t *thp)
120 {
121 smbios_hdl_t *shp;
122 di_node_t root;
123 id_t id;
124
125 thp->th_product = NULL;
126 if ((shp = smbios_open(NULL, SMB_VERSION, 0, NULL)) != NULL) {
127 smbios_system_t s1;
128 smbios_info_t s2;
129
130 if ((id = smbios_info_system(shp, &s1)) != SMB_ERR &&
131 smbios_info_common(shp, id, &s2) != SMB_ERR) {
132
133 if (strcmp(s2.smbi_product, SMB_DEFAULT1) != 0 &&
134 strcmp(s2.smbi_product, SMB_DEFAULT2) != 0) {
135 thp->th_product = topo_cleanup_auth_str(thp,
136 (char *)s2.smbi_product);
137 }
138 }
139 smbios_close(shp);
140 }
141
142 if (thp->th_product != NULL)
143 return;
144
145 root = di_init("/", DINFOCACHE);
146 if (root != DI_NODE_NIL) {
147 thp->th_product = topo_hdl_strdup(thp, di_node_name(root));
148 di_fini(root);
149 }
150
151 if (thp->th_product != NULL)
152 return;
153
154 thp->th_product = topo_hdl_strdup(thp, thp->th_platform);
155 }
156
157 topo_hdl_t *
topo_open(int version,const char * rootdir,int * errp)158 topo_open(int version, const char *rootdir, int *errp)
159 {
160 topo_hdl_t *thp = NULL;
161 topo_alloc_t *tap;
162
163 char platform[MAXNAMELEN];
164 char isa[MAXNAMELEN];
165 struct utsname uts;
166 struct stat st;
167
168 char *dbflags, *dbout;
169
170 if (version != TOPO_VERSION)
171 return (set_open_errno(thp, errp, ETOPO_HDL_ABIVER));
172
173 if (rootdir != NULL && stat(rootdir, &st) < 0)
174 return (set_open_errno(thp, errp, ETOPO_HDL_INVAL));
175
176 if ((thp = topo_zalloc(sizeof (topo_hdl_t), 0)) == NULL)
177 return (set_open_errno(thp, errp, ETOPO_NOMEM));
178
179 (void) pthread_mutex_init(&thp->th_lock, NULL);
180
181 if ((tap = topo_zalloc(sizeof (topo_alloc_t), 0)) == NULL)
182 return (set_open_errno(thp, errp, ETOPO_NOMEM));
183
184 /*
185 * Install default allocators
186 */
187 tap->ta_flags = 0;
188 tap->ta_alloc = topo_alloc;
189 tap->ta_zalloc = topo_zalloc;
190 tap->ta_free = topo_free;
191 tap->ta_nvops.nv_ao_alloc = topo_nv_alloc;
192 tap->ta_nvops.nv_ao_free = topo_nv_free;
193 (void) nv_alloc_init(&tap->ta_nva, &tap->ta_nvops);
194 thp->th_alloc = tap;
195
196 if ((thp->th_modhash = topo_modhash_create(thp)) == NULL)
197 return (set_open_errno(thp, errp, ETOPO_NOMEM));
198
199 /*
200 * Set-up system information and search paths for modules
201 * and topology map files
202 */
203 if (rootdir == NULL) {
204 rootdir = topo_hdl_strdup(thp, "/");
205 thp->th_rootdir = (char *)rootdir;
206 } else {
207 int len;
208 char *rpath;
209
210 len = strlen(rootdir);
211 if (len >= PATH_MAX)
212 return (set_open_errno(thp, errp, EINVAL));
213
214 if (rootdir[len - 1] != '/') {
215 rpath = alloca(len + 2);
216 (void) snprintf(rpath, len + 2, "%s/", rootdir);
217 } else {
218 rpath = (char *)rootdir;
219 }
220 thp->th_rootdir = topo_hdl_strdup(thp, rpath);
221 }
222
223 platform[0] = '\0';
224 isa[0] = '\0';
225 (void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
226 (void) sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
227 (void) uname(&uts);
228 thp->th_platform = topo_hdl_strdup(thp, platform);
229 thp->th_isa = topo_hdl_strdup(thp, isa);
230 thp->th_machine = topo_hdl_strdup(thp, uts.machine);
231 topo_hdl_set_product(thp);
232
233 if (thp->th_rootdir == NULL || thp->th_platform == NULL ||
234 thp->th_machine == NULL || thp->th_product == NULL ||
235 thp->th_isa == NULL)
236 return (set_open_errno(thp, errp, ETOPO_NOMEM));
237
238 dbflags = getenv("TOPO_DEBUG");
239 dbout = getenv("TOPO_DEBUG_OUT");
240 if (dbflags != NULL)
241 topo_debug_set(thp, dbflags, dbout);
242
243 if (topo_builtin_create(thp, thp->th_rootdir) != 0) {
244 topo_dprintf(thp, TOPO_DBG_ERR,
245 "failed to load builtin modules: %s\n",
246 topo_hdl_errmsg(thp));
247 return (set_open_errno(thp, errp, topo_hdl_errno(thp)));
248 }
249
250 return (thp);
251 }
252
253 void
topo_close(topo_hdl_t * thp)254 topo_close(topo_hdl_t *thp)
255 {
256 ttree_t *tp;
257 topo_digraph_t *tdg;
258
259 topo_hdl_lock(thp);
260 if (thp->th_platform != NULL)
261 topo_hdl_strfree(thp, thp->th_platform);
262 if (thp->th_isa != NULL)
263 topo_hdl_strfree(thp, thp->th_isa);
264 if (thp->th_machine != NULL)
265 topo_hdl_strfree(thp, thp->th_machine);
266 if (thp->th_product != NULL)
267 topo_hdl_strfree(thp, thp->th_product);
268 if (thp->th_rootdir != NULL)
269 topo_hdl_strfree(thp, thp->th_rootdir);
270 if (thp->th_ipmi != NULL)
271 ipmi_close(thp->th_ipmi);
272 if (thp->th_smbios != NULL)
273 smbios_close(thp->th_smbios);
274 if (thp->th_pcidb != NULL)
275 pcidb_close(thp->th_pcidb);
276
277 /*
278 * Clean-up snapshot
279 */
280 topo_snap_destroy(thp);
281
282 /*
283 * Clean-up trees
284 */
285 while ((tp = topo_list_next(&thp->th_trees)) != NULL) {
286 topo_list_delete(&thp->th_trees, tp);
287 topo_tree_destroy(tp);
288 }
289
290 /*
291 * Clean-up digraphs
292 */
293 while ((tdg = topo_list_next(&thp->th_digraphs)) != NULL) {
294 topo_list_delete(&thp->th_digraphs, tdg);
295 topo_digraph_destroy(tdg);
296 }
297
298 /*
299 * Unload all plugins
300 */
301 topo_modhash_unload_all(thp);
302
303 if (thp->th_modhash != NULL)
304 topo_modhash_destroy(thp);
305 if (thp->th_alloc != NULL)
306 topo_free(thp->th_alloc, sizeof (topo_alloc_t));
307
308 topo_hdl_unlock(thp);
309
310 topo_free(thp, sizeof (topo_hdl_t));
311 }
312
313 static char *
topo_snap_create(topo_hdl_t * thp,int * errp,boolean_t need_force)314 topo_snap_create(topo_hdl_t *thp, int *errp, boolean_t need_force)
315 {
316 uuid_t uuid;
317 char *ustr = NULL;
318
319 topo_hdl_lock(thp);
320 if (thp->th_uuid != NULL) {
321 *errp = ETOPO_HDL_UUID;
322 topo_hdl_unlock(thp);
323 return (NULL);
324 }
325
326 if ((thp->th_uuid = topo_hdl_zalloc(thp, TOPO_UUID_SIZE)) == NULL) {
327 *errp = ETOPO_NOMEM;
328 topo_dprintf(thp, TOPO_DBG_ERR, "unable to allocate uuid: %s\n",
329 topo_strerror(*errp));
330 topo_hdl_unlock(thp);
331 return (NULL);
332 }
333
334 uuid_generate(uuid);
335 uuid_unparse(uuid, thp->th_uuid);
336 if ((ustr = topo_hdl_strdup(thp, thp->th_uuid)) == NULL) {
337 *errp = ETOPO_NOMEM;
338 topo_hdl_unlock(thp);
339 return (NULL);
340 }
341
342 if (need_force) {
343 topo_dprintf(thp, TOPO_DBG_FORCE,
344 "taking a DINFOFORCE snapshot\n");
345 thp->th_di = di_init("/", DINFOFORCE |
346 DINFOSUBTREE | DINFOMINOR | DINFOPROP | DINFOPATH);
347 } else {
348 thp->th_di = di_init("/", DINFOCACHE);
349 }
350 thp->th_pi = di_prom_init();
351
352 if (topo_tree_enum_all(thp) < 0) {
353 topo_dprintf(thp, TOPO_DBG_ERR, "enumeration failure: %s\n",
354 topo_hdl_errmsg(thp));
355 if (topo_hdl_errno(thp) == ETOPO_ENUM_FATAL) {
356 *errp = thp->th_errno;
357
358 if (thp->th_di != DI_NODE_NIL) {
359 di_fini(thp->th_di);
360 thp->th_di = DI_NODE_NIL;
361 }
362 if (thp->th_pi != DI_PROM_HANDLE_NIL) {
363 di_prom_fini(thp->th_pi);
364 thp->th_pi = DI_PROM_HANDLE_NIL;
365 }
366
367 topo_hdl_strfree(thp, ustr);
368 topo_hdl_unlock(thp);
369 return (NULL);
370 }
371 }
372
373 if (thp->th_ipmi != NULL &&
374 ipmi_sdr_changed(thp->th_ipmi) &&
375 ipmi_sdr_refresh(thp->th_ipmi) != 0) {
376 topo_dprintf(thp, TOPO_DBG_ERR,
377 "failed to refresh IPMI sdr repository: %s\n",
378 ipmi_errmsg(thp->th_ipmi));
379 }
380
381 topo_hdl_unlock(thp);
382
383 return (ustr);
384 }
385
386 /*ARGSUSED*/
387 static char *
topo_snap_log_create(topo_hdl_t * thp,const char * uuid,int * errp)388 topo_snap_log_create(topo_hdl_t *thp, const char *uuid, int *errp)
389 {
390 return ((char *)uuid);
391 }
392
393 /*ARGSUSED*/
394 static int
fac_walker(topo_hdl_t * thp,tnode_t * node,void * arg)395 fac_walker(topo_hdl_t *thp, tnode_t *node, void *arg)
396 {
397 int err;
398 nvlist_t *out;
399
400 if (topo_method_supported(node, TOPO_METH_FAC_ENUM, 0)) {
401 /*
402 * If the facility enumeration method fails, note the failure,
403 * but continue on with the walk.
404 */
405 if (topo_method_invoke(node, TOPO_METH_FAC_ENUM, 0, NULL, &out,
406 &err) != 0) {
407 topo_dprintf(thp, TOPO_DBG_ERR,
408 "facility enumeration method failed on node %s=%d "
409 "(%s)\n", topo_node_name(node),
410 topo_node_instance(node), topo_strerror(err));
411 }
412 }
413 return (TOPO_WALK_NEXT);
414 }
415
416 /*
417 * Return snapshot id
418 */
419 char *
topo_snap_hold(topo_hdl_t * thp,const char * uuid,int * errp)420 topo_snap_hold(topo_hdl_t *thp, const char *uuid, int *errp)
421 {
422 topo_walk_t *twp;
423
424 if (thp == NULL)
425 return (NULL);
426
427 if (uuid == NULL) {
428 char *ret;
429
430 if (thp->th_debug & TOPO_DBG_FORCE) {
431 ret = topo_snap_create(thp, errp, B_TRUE);
432 } else {
433 ret = topo_snap_create(thp, errp, B_FALSE);
434 }
435
436 /*
437 * Now walk the tree and invoke any facility enumeration methods
438 */
439 if (ret != NULL && getzoneid() == 0) {
440 if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
441 fac_walker, (void *)0, errp)) == NULL) {
442 return (ret);
443 }
444 (void) topo_walk_step(twp, TOPO_WALK_CHILD);
445 topo_walk_fini(twp);
446 }
447 return (ret);
448 }
449 return (topo_snap_log_create(thp, uuid, errp));
450 }
451
452 /*ARGSUSED*/
453 static int
topo_walk_destroy(topo_hdl_t * thp,tnode_t * node,void * notused)454 topo_walk_destroy(topo_hdl_t *thp, tnode_t *node, void *notused)
455 {
456 tnode_t *cnode;
457
458 cnode = topo_child_first(node);
459
460 if (cnode != NULL)
461 return (TOPO_WALK_NEXT);
462
463 topo_node_unbind(node);
464
465 return (TOPO_WALK_NEXT);
466 }
467
468 static void
topo_snap_destroy(topo_hdl_t * thp)469 topo_snap_destroy(topo_hdl_t *thp)
470 {
471 int i;
472 ttree_t *tp;
473 topo_digraph_t *tdg;
474 topo_walk_t *twp;
475 tnode_t *root;
476 topo_nodehash_t *nhp;
477 topo_mod_t *mod;
478
479 for (tp = topo_list_next(&thp->th_trees); tp != NULL;
480 tp = topo_list_next(tp)) {
481
482 root = tp->tt_root;
483 twp = tp->tt_walk;
484 /*
485 * Clean-up tree nodes from the bottom-up
486 */
487 if ((twp->tw_node = topo_child_first(root)) != NULL) {
488 twp->tw_cb = topo_walk_destroy;
489 topo_node_hold(root);
490 topo_node_hold(twp->tw_node); /* released at walk end */
491 (void) topo_walk_bottomup(twp, TOPO_WALK_CHILD);
492 topo_node_rele(root);
493 }
494
495 /*
496 * Tidy-up the root node
497 */
498 while ((nhp = topo_list_next(&root->tn_children)) != NULL) {
499 for (i = 0; i < nhp->th_arrlen; i++) {
500 assert(nhp->th_nodearr[i] == NULL);
501 }
502 mod = nhp->th_enum;
503 topo_mod_strfree(mod, nhp->th_name);
504 topo_mod_free(mod, nhp->th_nodearr,
505 nhp->th_arrlen * sizeof (tnode_t *));
506 topo_list_delete(&root->tn_children, nhp);
507 topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
508 topo_mod_rele(mod);
509 }
510
511 }
512
513 for (tdg = topo_list_next(&thp->th_digraphs); tdg != NULL;
514 tdg = topo_list_next(tdg)) {
515
516 topo_vertex_t *vtx;
517
518 if (tdg->tdg_nvertices == 0)
519 continue;
520 /*
521 * We maintain an adjacency list in the topo_digraph_t
522 * structure, so we can just walk the list to destroy all the
523 * vertices.
524 */
525 mod = tdg->tdg_mod;
526 vtx = topo_list_next(&tdg->tdg_vertices);
527 while (vtx != NULL) {
528 topo_vertex_t *tmp = vtx;
529
530 vtx = topo_list_next(vtx);
531 topo_vertex_destroy(mod, tmp);
532 }
533 tdg->tdg_nvertices = 0;
534 }
535
536 /*
537 * Clean-up our cached devinfo and prom tree handles.
538 */
539 if (thp->th_di != DI_NODE_NIL) {
540 di_fini(thp->th_di);
541 thp->th_di = DI_NODE_NIL;
542 }
543 if (thp->th_pi != DI_PROM_HANDLE_NIL) {
544 di_prom_fini(thp->th_pi);
545 thp->th_pi = DI_PROM_HANDLE_NIL;
546 }
547
548 if (thp->th_uuid != NULL) {
549 topo_hdl_free(thp, thp->th_uuid, TOPO_UUID_SIZE);
550 thp->th_uuid = NULL;
551 }
552 }
553
554 void
topo_snap_release(topo_hdl_t * thp)555 topo_snap_release(topo_hdl_t *thp)
556 {
557 if (thp == NULL)
558 return;
559
560 topo_hdl_lock(thp);
561 topo_snap_destroy(thp);
562 topo_hdl_unlock(thp);
563 }
564
565 topo_walk_t *
topo_walk_init(topo_hdl_t * thp,const char * scheme,topo_walk_cb_t cb_f,void * pdata,int * errp)566 topo_walk_init(topo_hdl_t *thp, const char *scheme, topo_walk_cb_t cb_f,
567 void *pdata, int *errp)
568 {
569 ttree_t *tp;
570 topo_walk_t *wp;
571
572 for (tp = topo_list_next(&thp->th_trees); tp != NULL;
573 tp = topo_list_next(tp)) {
574 if (strcmp(scheme, tp->tt_scheme) == 0) {
575
576 /*
577 * Hold the root node and start walk at the first
578 * child node
579 */
580 assert(tp->tt_root != NULL);
581
582 if ((wp = topo_node_walk_init(thp, NULL, tp->tt_root,
583 cb_f, pdata, errp)) == NULL) /* errp set */
584 return (NULL);
585
586 return (wp);
587 }
588 }
589
590 *errp = ETOPO_WALK_NOTFOUND;
591 return (NULL);
592 }
593
594 static int
step_child(tnode_t * cnp,topo_walk_t * wp,int flag,int bottomup)595 step_child(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
596 {
597 int status;
598 tnode_t *nnp;
599
600 nnp = topo_child_first(cnp);
601
602 if (nnp == NULL) {
603 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
604 "step_child: TOPO_WALK_TERMINATE for %s=%d\n",
605 cnp->tn_name, cnp->tn_instance);
606 return (TOPO_WALK_TERMINATE);
607 }
608
609 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
610 "step_child: walk through node %s=%d to %s=%d\n",
611 cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
612
613 topo_node_hold(nnp); /* released on return from walk_step */
614 wp->tw_node = nnp;
615 if (bottomup == 1)
616 status = topo_walk_bottomup(wp, flag);
617 else
618 status = topo_walk_step(wp, flag);
619
620 return (status);
621 }
622
623 static int
step_sibling(tnode_t * cnp,topo_walk_t * wp,int flag,int bottomup)624 step_sibling(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
625 {
626 int status;
627 tnode_t *nnp;
628
629 nnp = topo_child_next(cnp->tn_parent, cnp);
630
631 if (nnp == NULL) {
632 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
633 "step_sibling: TOPO_WALK_TERMINATE for %s=%d\n",
634 cnp->tn_name, cnp->tn_instance);
635 return (TOPO_WALK_TERMINATE);
636 }
637
638 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
639 "step_sibling: through sibling node %s=%d to %s=%d\n",
640 cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
641
642 topo_node_hold(nnp); /* released on return from walk_step */
643 wp->tw_node = nnp;
644 if (bottomup == 1)
645 status = topo_walk_bottomup(wp, flag);
646 else
647 status = topo_walk_step(wp, flag);
648
649 return (status);
650 }
651
652 int
topo_walk_byid(topo_walk_t * wp,const char * name,topo_instance_t inst)653 topo_walk_byid(topo_walk_t *wp, const char *name, topo_instance_t inst)
654 {
655 int status;
656 tnode_t *nnp, *cnp;
657
658 cnp = wp->tw_node;
659 nnp = topo_node_lookup(cnp, name, inst);
660 if (nnp == NULL)
661 return (TOPO_WALK_TERMINATE);
662
663 topo_node_hold(nnp);
664 wp->tw_node = nnp;
665 if (wp->tw_mod != NULL)
666 status = wp->tw_cb(wp->tw_mod, nnp, wp->tw_pdata);
667 else
668 status = wp->tw_cb(wp->tw_thp, nnp, wp->tw_pdata);
669 topo_node_rele(nnp);
670 wp->tw_node = cnp;
671
672 return (status);
673 }
674
675 int
topo_walk_bysibling(topo_walk_t * wp,const char * name,topo_instance_t inst)676 topo_walk_bysibling(topo_walk_t *wp, const char *name, topo_instance_t inst)
677 {
678 int status;
679 tnode_t *cnp, *pnp;
680
681 cnp = wp->tw_node;
682 pnp = topo_node_parent(cnp);
683 assert(pnp != NULL);
684
685 topo_node_hold(pnp);
686 wp->tw_node = pnp;
687 status = topo_walk_byid(wp, name, inst);
688 topo_node_rele(pnp);
689 wp->tw_node = cnp;
690
691 return (status);
692 }
693
694 int
topo_walk_step(topo_walk_t * wp,int flag)695 topo_walk_step(topo_walk_t *wp, int flag)
696 {
697 int status;
698 tnode_t *cnp = wp->tw_node;
699
700 if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
701 topo_node_rele(cnp);
702 return (TOPO_WALK_ERR);
703 }
704
705 /*
706 * No more nodes to walk
707 */
708 if (cnp == NULL) {
709 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
710 "walk_step terminated\n");
711 topo_node_rele(cnp);
712 return (TOPO_WALK_TERMINATE);
713 }
714
715
716 if (wp->tw_mod != NULL)
717 status = wp->tw_cb(wp->tw_mod, cnp, wp->tw_pdata);
718 else
719 status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata);
720
721 /*
722 * Walker callback says we're done
723 */
724 if (status != TOPO_WALK_NEXT) {
725 topo_node_rele(cnp);
726 return (status);
727 }
728
729 if (flag == TOPO_WALK_CHILD)
730 status = step_child(cnp, wp, flag, 0);
731 else
732 status = step_sibling(cnp, wp, flag, 0);
733
734 /*
735 * No more nodes in this hash, skip to next node hash by stepping
736 * to next sibling (child-first walk) or next child (sibling-first
737 * walk).
738 */
739 if (status == TOPO_WALK_TERMINATE) {
740 if (flag == TOPO_WALK_CHILD)
741 status = step_sibling(cnp, wp, flag, 0);
742 else
743 status = step_child(cnp, wp, flag, 0);
744 }
745
746 topo_node_rele(cnp); /* done with current node */
747
748 return (status);
749 }
750
751 void
topo_walk_fini(topo_walk_t * wp)752 topo_walk_fini(topo_walk_t *wp)
753 {
754 if (wp == NULL)
755 return;
756
757 topo_node_rele(wp->tw_root);
758
759 topo_hdl_free(wp->tw_thp, wp, sizeof (topo_walk_t));
760 }
761
762 int
topo_walk_bottomup(topo_walk_t * wp,int flag)763 topo_walk_bottomup(topo_walk_t *wp, int flag)
764 {
765 int status;
766 tnode_t *cnp;
767
768 if (wp == NULL)
769 return (TOPO_WALK_ERR);
770
771 cnp = wp->tw_node;
772 if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
773 topo_node_rele(cnp);
774 return (TOPO_WALK_ERR);
775 }
776
777 /*
778 * End of the line
779 */
780 if (cnp == NULL) {
781 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
782 "walk_bottomup terminated\n");
783 topo_node_rele(cnp);
784 return (TOPO_WALK_TERMINATE);
785 }
786
787 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
788 "%s walk_bottomup through node %s=%d\n",
789 (flag == TOPO_WALK_CHILD ? "TOPO_WALK_CHILD" : "TOPO_WALK_SIBLING"),
790 cnp->tn_name, cnp->tn_instance);
791
792 if (flag == TOPO_WALK_CHILD)
793 status = step_child(cnp, wp, flag, 1);
794 else
795 status = step_sibling(cnp, wp, flag, 1);
796
797 /*
798 * At a leaf, run the callback
799 */
800 if (status == TOPO_WALK_TERMINATE) {
801 if ((status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata))
802 != TOPO_WALK_NEXT) {
803 topo_node_rele(cnp);
804 return (status);
805 }
806 }
807
808 /*
809 * Try next child or sibling
810 */
811 if (status == TOPO_WALK_NEXT) {
812 if (flag == TOPO_WALK_CHILD)
813 status = step_sibling(cnp, wp, flag, 1);
814 else
815 status = step_child(cnp, wp, flag, 1);
816 }
817
818 topo_node_rele(cnp); /* done with current node */
819
820 return (status);
821 }
822
823 di_node_t
topo_hdl_devinfo(topo_hdl_t * thp)824 topo_hdl_devinfo(topo_hdl_t *thp)
825 {
826 return (thp == NULL ? DI_NODE_NIL : thp->th_di);
827 }
828
829 di_prom_handle_t
topo_hdl_prominfo(topo_hdl_t * thp)830 topo_hdl_prominfo(topo_hdl_t *thp)
831 {
832 return (thp == NULL ? DI_PROM_HANDLE_NIL : thp->th_pi);
833 }
834
835 int
topo_scheme_walk(topo_hdl_t * thp,topo_scheme_walk_cb_f cb,void * arg)836 topo_scheme_walk(topo_hdl_t *thp, topo_scheme_walk_cb_f cb, void *arg)
837 {
838 for (ttree_t *tp = topo_list_next(&thp->th_trees); tp != NULL;
839 tp = topo_list_next(tp)) {
840 int ret;
841 topo_scheme_info_t info;
842
843 info.tsi_scheme = tp->tt_scheme;
844 info.tsi_type = TOPO_SCHEME_TREE;
845
846 ret = cb(thp, &info, arg);
847 if (ret != TOPO_WALK_NEXT) {
848 return (ret);
849 }
850 }
851
852 for (topo_digraph_t *tdg = topo_list_next(&thp->th_digraphs);
853 tdg != NULL; tdg = topo_list_next(tdg)) {
854 int ret;
855 topo_scheme_info_t info;
856
857 info.tsi_scheme = tdg->tdg_scheme;
858 info.tsi_type = TOPO_SCHEME_DIGRAPH;
859
860 ret = cb(thp, &info, arg);
861 if (ret != TOPO_WALK_NEXT) {
862 return (ret);
863 }
864 }
865
866 return (TOPO_WALK_TERMINATE);
867 }
868