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