xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_snap.c (revision aedf2b3bb56b025fcaf87b49ec6c8aeea07f16d7)
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