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