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 * 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 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 * 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 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 * 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 * 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 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 * 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 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 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 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 * 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 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 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 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 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 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 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 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 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 830 topo_hdl_prominfo(topo_hdl_t *thp) 831 { 832 return (thp == NULL ? DI_PROM_HANDLE_NIL : thp->th_pi); 833 } 834 835 int 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