Home | History | Annotate | Download | only in common
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     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 
     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 	thp->th_di = di_init("/", (need_force ? DINFOFORCE : 0) |
    296 	    DINFOSUBTREE | DINFOMINOR | DINFOPROP | DINFOPATH);
    297 	thp->th_pi = di_prom_init();
    298 
    299 	if (topo_tree_enum_all(thp) < 0) {
    300 		topo_dprintf(thp, TOPO_DBG_ERR, "enumeration failure: %s\n",
    301 		    topo_hdl_errmsg(thp));
    302 		if (topo_hdl_errno(thp) == ETOPO_ENUM_FATAL) {
    303 			*errp = thp->th_errno;
    304 
    305 			if (thp->th_di != DI_NODE_NIL) {
    306 				di_fini(thp->th_di);
    307 				thp->th_di = DI_NODE_NIL;
    308 			}
    309 			if (thp->th_pi != DI_PROM_HANDLE_NIL) {
    310 				di_prom_fini(thp->th_pi);
    311 				thp->th_pi = DI_PROM_HANDLE_NIL;
    312 			}
    313 
    314 			topo_hdl_strfree(thp, ustr);
    315 			topo_hdl_unlock(thp);
    316 			return (NULL);
    317 		}
    318 	}
    319 
    320 	if (thp->th_ipmi != NULL &&
    321 	    ipmi_sdr_changed(thp->th_ipmi) &&
    322 	    ipmi_sdr_refresh(thp->th_ipmi) != 0) {
    323 		topo_dprintf(thp, TOPO_DBG_ERR,
    324 		    "failed to refresh IPMI sdr repository: %s\n",
    325 		    ipmi_errmsg(thp->th_ipmi));
    326 	}
    327 
    328 	topo_hdl_unlock(thp);
    329 
    330 	return (ustr);
    331 }
    332 
    333 /*ARGSUSED*/
    334 static char *
    335 topo_snap_log_create(topo_hdl_t *thp, const char *uuid, int *errp)
    336 {
    337 	return ((char *)uuid);
    338 }
    339 
    340 /*ARGSUSED*/
    341 static int
    342 fac_walker(topo_hdl_t *thp, tnode_t *node, void *arg)
    343 {
    344 	int err;
    345 	nvlist_t *out;
    346 
    347 	if (topo_method_supported(node, TOPO_METH_FAC_ENUM, 0)) {
    348 		/*
    349 		 * If the facility enumeration method fails, note the failure,
    350 		 * but continue on with the walk.
    351 		 */
    352 		if (topo_method_invoke(node, TOPO_METH_FAC_ENUM, 0, NULL, &out,
    353 		    &err) != 0) {
    354 			topo_dprintf(thp, TOPO_DBG_ERR,
    355 			    "facility enumeration method failed on node %s=%d "
    356 			    "(%s)\n", topo_node_name(node),
    357 			    topo_node_instance(node), topo_strerror(err));
    358 		}
    359 	}
    360 	return (TOPO_WALK_NEXT);
    361 }
    362 
    363 /*
    364  * Return snapshot id
    365  */
    366 char *
    367 topo_snap_hold(topo_hdl_t *thp, const char *uuid, int *errp)
    368 {
    369 	topo_walk_t *twp;
    370 
    371 	if (thp == NULL)
    372 		return (NULL);
    373 
    374 	if (uuid == NULL) {
    375 		char *ret;
    376 
    377 		ret = topo_snap_create(thp, errp, B_TRUE);
    378 
    379 		/*
    380 		 * Now walk the tree and invoke any facility enumeration methods
    381 		 */
    382 		if (ret != NULL) {
    383 			if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
    384 			    fac_walker, (void *)0, errp)) == NULL) {
    385 				return (ret);
    386 			}
    387 			(void) topo_walk_step(twp, TOPO_WALK_CHILD);
    388 			topo_walk_fini(twp);
    389 		}
    390 		return (ret);
    391 	}
    392 	return (topo_snap_log_create(thp, uuid, errp));
    393 }
    394 
    395 /*
    396  * Return snapshot id. This variant calls di_init() without DINFOFORCE,
    397  * and is intended for use on DR events where there is no need to force
    398  * attach of all devices.
    399  */
    400 char *
    401 topo_snap_hold_no_forceload(topo_hdl_t *thp, const char *uuid, int *errp)
    402 {
    403 	topo_walk_t *twp;
    404 
    405 	if (thp == NULL)
    406 		return (NULL);
    407 
    408 	if (uuid == NULL) {
    409 		char *ret;
    410 
    411 		ret = topo_snap_create(thp, errp, B_FALSE);
    412 
    413 		/*
    414 		 * Now walk the tree and invoke any facility enumeration methods
    415 		 */
    416 		if (ret != NULL) {
    417 			if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
    418 			    fac_walker, (void *)0, errp)) == NULL) {
    419 				return (ret);
    420 			}
    421 			(void) topo_walk_step(twp, TOPO_WALK_CHILD);
    422 			topo_walk_fini(twp);
    423 		}
    424 		return (ret);
    425 	}
    426 	return (topo_snap_log_create(thp, uuid, errp));
    427 }
    428 
    429 /*ARGSUSED*/
    430 static int
    431 topo_walk_destroy(topo_hdl_t *thp, tnode_t *node, void *notused)
    432 {
    433 	tnode_t *cnode;
    434 
    435 	cnode = topo_child_first(node);
    436 
    437 	if (cnode != NULL)
    438 		return (TOPO_WALK_NEXT);
    439 
    440 	topo_node_unbind(node);
    441 
    442 	return (TOPO_WALK_NEXT);
    443 }
    444 
    445 static void
    446 topo_snap_destroy(topo_hdl_t *thp)
    447 {
    448 	int i;
    449 	ttree_t *tp;
    450 	topo_walk_t *twp;
    451 	tnode_t *root;
    452 	topo_nodehash_t *nhp;
    453 	topo_mod_t *mod;
    454 
    455 	for (tp = topo_list_next(&thp->th_trees); tp != NULL;
    456 	    tp = topo_list_next(tp)) {
    457 
    458 		root = tp->tt_root;
    459 		twp = tp->tt_walk;
    460 		/*
    461 		 * Clean-up tree nodes from the bottom-up
    462 		 */
    463 		if ((twp->tw_node = topo_child_first(root)) != NULL) {
    464 			twp->tw_cb = topo_walk_destroy;
    465 			topo_node_hold(root);
    466 			topo_node_hold(twp->tw_node); /* released at walk end */
    467 			(void) topo_walk_bottomup(twp, TOPO_WALK_CHILD);
    468 			topo_node_rele(root);
    469 		}
    470 
    471 		/*
    472 		 * Tidy-up the root node
    473 		 */
    474 		while ((nhp = topo_list_next(&root->tn_children)) != NULL) {
    475 			for (i = 0; i < nhp->th_arrlen; i++) {
    476 				assert(nhp->th_nodearr[i] == NULL);
    477 			}
    478 			mod = nhp->th_enum;
    479 			topo_mod_strfree(mod, nhp->th_name);
    480 			topo_mod_free(mod, nhp->th_nodearr,
    481 			    nhp->th_arrlen * sizeof (tnode_t *));
    482 			topo_list_delete(&root->tn_children, nhp);
    483 			topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
    484 			topo_mod_rele(mod);
    485 		}
    486 
    487 	}
    488 
    489 	/*
    490 	 * Clean-up our cached devinfo and prom tree handles.
    491 	 */
    492 	if (thp->th_di != DI_NODE_NIL) {
    493 		di_fini(thp->th_di);
    494 		thp->th_di = DI_NODE_NIL;
    495 	}
    496 	if (thp->th_pi != DI_PROM_HANDLE_NIL) {
    497 		di_prom_fini(thp->th_pi);
    498 		thp->th_pi = DI_PROM_HANDLE_NIL;
    499 	}
    500 
    501 
    502 	if (thp->th_uuid != NULL) {
    503 		topo_hdl_free(thp, thp->th_uuid, TOPO_UUID_SIZE);
    504 		thp->th_uuid = NULL;
    505 	}
    506 }
    507 
    508 void
    509 topo_snap_release(topo_hdl_t *thp)
    510 {
    511 	if (thp == NULL)
    512 		return;
    513 
    514 	topo_hdl_lock(thp);
    515 	topo_snap_destroy(thp);
    516 	topo_hdl_unlock(thp);
    517 }
    518 
    519 topo_walk_t *
    520 topo_walk_init(topo_hdl_t *thp, const char *scheme, topo_walk_cb_t cb_f,
    521     void *pdata, int *errp)
    522 {
    523 	ttree_t *tp;
    524 	topo_walk_t *wp;
    525 
    526 	for (tp = topo_list_next(&thp->th_trees); tp != NULL;
    527 	    tp = topo_list_next(tp)) {
    528 		if (strcmp(scheme, tp->tt_scheme) == 0) {
    529 
    530 			/*
    531 			 * Hold the root node and start walk at the first
    532 			 * child node
    533 			 */
    534 			assert(tp->tt_root != NULL);
    535 
    536 			if ((wp = topo_node_walk_init(thp, NULL, tp->tt_root,
    537 			    cb_f, pdata, errp)) == NULL) /* errp set */
    538 				return (NULL);
    539 
    540 			return (wp);
    541 		}
    542 	}
    543 
    544 	*errp = ETOPO_WALK_NOTFOUND;
    545 	return (NULL);
    546 }
    547 
    548 static int
    549 step_child(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
    550 {
    551 	int status;
    552 	tnode_t *nnp;
    553 
    554 	nnp = topo_child_first(cnp);
    555 
    556 	if (nnp == NULL) {
    557 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
    558 		    "step_child: TOPO_WALK_TERMINATE for %s=%d\n",
    559 		    cnp->tn_name, cnp->tn_instance);
    560 		return (TOPO_WALK_TERMINATE);
    561 	}
    562 
    563 	topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
    564 	    "step_child: walk through node %s=%d to %s=%d\n",
    565 	    cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
    566 
    567 	topo_node_hold(nnp); /* released on return from walk_step */
    568 	wp->tw_node = nnp;
    569 	if (bottomup == 1)
    570 		status = topo_walk_bottomup(wp, flag);
    571 	else
    572 		status = topo_walk_step(wp, flag);
    573 
    574 	return (status);
    575 }
    576 
    577 static int
    578 step_sibling(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
    579 {
    580 	int status;
    581 	tnode_t *nnp;
    582 
    583 	nnp = topo_child_next(cnp->tn_parent, cnp);
    584 
    585 	if (nnp == NULL) {
    586 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
    587 		    "step_sibling: TOPO_WALK_TERMINATE for %s=%d\n",
    588 		    cnp->tn_name, cnp->tn_instance);
    589 		return (TOPO_WALK_TERMINATE);
    590 	}
    591 
    592 	topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
    593 	    "step_sibling: through sibling node %s=%d to %s=%d\n",
    594 	    cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
    595 
    596 	topo_node_hold(nnp); /* released on return from walk_step */
    597 	wp->tw_node = nnp;
    598 	if (bottomup == 1)
    599 		status = topo_walk_bottomup(wp, flag);
    600 	else
    601 		status = topo_walk_step(wp, flag);
    602 
    603 	return (status);
    604 }
    605 
    606 int
    607 topo_walk_byid(topo_walk_t *wp, const char *name, topo_instance_t inst)
    608 {
    609 	int status;
    610 	tnode_t *nnp, *cnp;
    611 
    612 	cnp = wp->tw_node;
    613 	nnp = topo_node_lookup(cnp, name, inst);
    614 	if (nnp == NULL)
    615 		return (TOPO_WALK_TERMINATE);
    616 
    617 	topo_node_hold(nnp);
    618 	wp->tw_node = nnp;
    619 	if (wp->tw_mod != NULL)
    620 		status = wp->tw_cb(wp->tw_mod, nnp, wp->tw_pdata);
    621 	else
    622 		status = wp->tw_cb(wp->tw_thp, nnp, wp->tw_pdata);
    623 	topo_node_rele(nnp);
    624 	wp->tw_node = cnp;
    625 
    626 	return (status);
    627 }
    628 
    629 int
    630 topo_walk_bysibling(topo_walk_t *wp, const char *name, topo_instance_t inst)
    631 {
    632 	int status;
    633 	tnode_t *cnp, *pnp;
    634 
    635 	cnp = wp->tw_node;
    636 	pnp = topo_node_parent(cnp);
    637 	assert(pnp != NULL);
    638 
    639 	topo_node_hold(pnp);
    640 	wp->tw_node = pnp;
    641 	status = topo_walk_byid(wp, name, inst);
    642 	topo_node_rele(pnp);
    643 	wp->tw_node = cnp;
    644 
    645 	return (status);
    646 }
    647 
    648 int
    649 topo_walk_step(topo_walk_t *wp, int flag)
    650 {
    651 	int status;
    652 	tnode_t *cnp = wp->tw_node;
    653 
    654 	if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
    655 		topo_node_rele(cnp);
    656 		return (TOPO_WALK_ERR);
    657 	}
    658 
    659 	/*
    660 	 * No more nodes to walk
    661 	 */
    662 	if (cnp == NULL) {
    663 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
    664 		    "walk_step terminated\n");
    665 		topo_node_rele(cnp);
    666 		return (TOPO_WALK_TERMINATE);
    667 	}
    668 
    669 
    670 	if (wp->tw_mod != NULL)
    671 		status = wp->tw_cb(wp->tw_mod, cnp, wp->tw_pdata);
    672 	else
    673 		status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata);
    674 
    675 	/*
    676 	 * Walker callback says we're done
    677 	 */
    678 	if (status != TOPO_WALK_NEXT) {
    679 		topo_node_rele(cnp);
    680 		return (status);
    681 	}
    682 
    683 	if (flag == TOPO_WALK_CHILD)
    684 		status = step_child(cnp, wp, flag, 0);
    685 	else
    686 		status = step_sibling(cnp, wp, flag, 0);
    687 
    688 	/*
    689 	 * No more nodes in this hash, skip to next node hash by stepping
    690 	 * to next sibling (child-first walk) or next child (sibling-first
    691 	 * walk).
    692 	 */
    693 	if (status == TOPO_WALK_TERMINATE) {
    694 		if (flag == TOPO_WALK_CHILD)
    695 			status = step_sibling(cnp, wp, flag, 0);
    696 		else
    697 			status = step_child(cnp, wp, flag, 0);
    698 	}
    699 
    700 	topo_node_rele(cnp); /* done with current node */
    701 
    702 	return (status);
    703 }
    704 
    705 void
    706 topo_walk_fini(topo_walk_t *wp)
    707 {
    708 	if (wp == NULL)
    709 		return;
    710 
    711 	topo_node_rele(wp->tw_root);
    712 
    713 	topo_hdl_free(wp->tw_thp, wp, sizeof (topo_walk_t));
    714 }
    715 
    716 int
    717 topo_walk_bottomup(topo_walk_t *wp, int flag)
    718 {
    719 	int status;
    720 	tnode_t *cnp;
    721 
    722 	if (wp == NULL)
    723 		return (TOPO_WALK_ERR);
    724 
    725 	cnp = wp->tw_node;
    726 	if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
    727 		topo_node_rele(cnp);
    728 		return (TOPO_WALK_ERR);
    729 	}
    730 
    731 	/*
    732 	 * End of the line
    733 	 */
    734 	if (cnp == NULL) {
    735 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
    736 		    "walk_bottomup terminated\n");
    737 		topo_node_rele(cnp);
    738 		return (TOPO_WALK_TERMINATE);
    739 	}
    740 
    741 	topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
    742 	    "%s walk_bottomup through node %s=%d\n",
    743 	    (flag == TOPO_WALK_CHILD ? "TOPO_WALK_CHILD" : "TOPO_WALK_SIBLING"),
    744 	    cnp->tn_name, cnp->tn_instance);
    745 
    746 	if (flag == TOPO_WALK_CHILD)
    747 		status = step_child(cnp, wp, flag, 1);
    748 	else
    749 		status = step_sibling(cnp, wp, flag, 1);
    750 
    751 	/*
    752 	 * At a leaf, run the callback
    753 	 */
    754 	if (status == TOPO_WALK_TERMINATE) {
    755 		if ((status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata))
    756 		    != TOPO_WALK_NEXT) {
    757 			topo_node_rele(cnp);
    758 			return (status);
    759 		}
    760 	}
    761 
    762 	/*
    763 	 * Try next child or sibling
    764 	 */
    765 	if (status == TOPO_WALK_NEXT) {
    766 		if (flag == TOPO_WALK_CHILD)
    767 			status = step_sibling(cnp, wp, flag, 1);
    768 		else
    769 			status = step_child(cnp, wp, flag, 1);
    770 	}
    771 
    772 	topo_node_rele(cnp); /* done with current node */
    773 
    774 	return (status);
    775 }
    776 
    777 di_node_t
    778 topo_hdl_devinfo(topo_hdl_t *thp)
    779 {
    780 	return (thp == NULL ? DI_NODE_NIL : thp->th_di);
    781 }
    782 
    783 di_prom_handle_t
    784 topo_hdl_prominfo(topo_hdl_t *thp)
    785 {
    786 	return (thp == NULL ? DI_PROM_HANDLE_NIL : thp->th_pi);
    787 }
    788