xref: /onnv-gate/usr/src/lib/fm/topo/libtopo/common/topo_snap.c (revision 13131:87d7bfd32811)
11414Scindi /*
21414Scindi  * CDDL HEADER START
31414Scindi  *
41414Scindi  * The contents of this file are subject to the terms of the
53062Scindi  * Common Development and Distribution License (the "License").
63062Scindi  * You may not use this file except in compliance with the License.
71414Scindi  *
81414Scindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91414Scindi  * or http://www.opensolaris.org/os/licensing.
101414Scindi  * See the License for the specific language governing permissions
111414Scindi  * and limitations under the License.
121414Scindi  *
131414Scindi  * When distributing Covered Code, include this CDDL HEADER in each
141414Scindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151414Scindi  * If applicable, add the following below this CDDL HEADER, with the
161414Scindi  * fields enclosed by brackets "[]" replaced with your own identifying
171414Scindi  * information: Portions Copyright [yyyy] [name of copyright owner]
181414Scindi  *
191414Scindi  * CDDL HEADER END
201414Scindi  */
211414Scindi /*
2212967Sgavin.maltby@oracle.com  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
231414Scindi  */
241414Scindi 
251414Scindi /*
261414Scindi  * Snapshot Library Interfaces
271414Scindi  *
281414Scindi  * Consumers of topology data may use the interfaces in this file to open,
291414Scindi  * snapshot and close a topology exported by FMRI scheme (hc, mem and cpu)
301414Scindi  * builtin plugins and their helper modules.  A topology handle is obtained
311414Scindi  * by calling topo_open().  Upon a successful return, the caller may use this
321414Scindi  * handle to open a new snapshot.  Each snapshot is assigned a Universally
331414Scindi  * Unique Identifier that in a future enchancement to the libtopo API will be
341414Scindi  * used as the file locator in /var/fm/topo to persist new snapshots or lookup
351414Scindi  * a previously captured snapshot.  topo_snap_hold() will capture the current
361414Scindi  * system topology.  All consumers of the topo_hdl_t argument will be
371414Scindi  * blocked from accessing the topology trees until the snapshot completes.
381414Scindi  *
391414Scindi  * A snapshot may be cleared by calling topo_snap_rele().  As with
401414Scindi  * topo_snap_hold(), all topology accesses are blocked until the topology
411414Scindi  * trees have been released and deallocated.
421414Scindi  *
431414Scindi  * Walker Library Interfaces
441414Scindi  *
451414Scindi  * Once a snapshot has been taken with topo_snap_hold(), topo_hdl_t holders
461414Scindi  * may initiate topology tree walks on a scheme-tree basis.  topo_walk_init()
471414Scindi  * will initiate the data structures required to walk any one one of the
481414Scindi  * FMRI scheme trees.  The walker data structure, topo_walk_t, is an opaque
491414Scindi  * handle passed to topo_walk_step to begin the walk.  At each node in the
501414Scindi  * topology tree, a callback function is called with access to the node at
511414Scindi  * which our current walk falls.  The callback function is passed in during
521414Scindi  * calls to topo_walk_init() and used throughout the walk_step of the
531414Scindi  * scheme tree.  At any time, the callback may terminate the walk by returning
544087Scindi  * TOPO_WALK_TERMINATE or TOPO_WALK_ERR.  TOPO_WALK_NEXT will continue the walk.
551414Scindi  *
566341Scy152378  * The type of walk through the tree may be sibling first or child first by
571414Scindi  * respectively passing in TOPO_WALK_SIBLING or TOPO_WALK_CHILD to
584087Scindi  * the topo_walk_step() function.  Topology nodes
594087Scindi  * associated with an outstanding walk are held in place and will not be
604087Scindi  * deallocated until the walk through that node completes.
611414Scindi  *
621414Scindi  * Once the walk has terminated, the walking process should call
631414Scindi  * topo_walk_fini() to clean-up resources created in topo_walk_init()
641414Scindi  * and release nodes that may be still held.
651414Scindi  */
661414Scindi 
673062Scindi #include <alloca.h>
683062Scindi #include <ctype.h>
691414Scindi #include <pthread.h>
701414Scindi #include <limits.h>
711414Scindi #include <assert.h>
721414Scindi #include <fcntl.h>
733062Scindi #include <smbios.h>
743062Scindi #include <sys/param.h>
751414Scindi #include <sys/types.h>
761414Scindi #include <sys/stat.h>
773062Scindi #include <sys/systeminfo.h>
783062Scindi #include <sys/utsname.h>
791414Scindi #include <uuid/uuid.h>
8012967Sgavin.maltby@oracle.com #include <zone.h>
811414Scindi 
821414Scindi #include <fm/libtopo.h>
837585SRobert.Johnston@Sun.COM #include <sys/fm/protocol.h>
841414Scindi 
851414Scindi #include <topo_alloc.h>
861414Scindi #include <topo_builtin.h>
871414Scindi #include <topo_string.h>
881414Scindi #include <topo_error.h>
891414Scindi #include <topo_subr.h>
901414Scindi 
911414Scindi static void topo_snap_destroy(topo_hdl_t *);
921414Scindi 
931414Scindi static topo_hdl_t *
set_open_errno(topo_hdl_t * thp,int * errp,int err)941414Scindi set_open_errno(topo_hdl_t *thp, int *errp, int err)
951414Scindi {
961414Scindi 	if (thp != NULL) {
971414Scindi 		topo_close(thp);
981414Scindi 	}
991414Scindi 	if (errp != NULL)
1001414Scindi 		*errp = err;
1011414Scindi 	return (NULL);
1021414Scindi }
1031414Scindi 
1041414Scindi topo_hdl_t *
topo_open(int version,const char * rootdir,int * errp)1051414Scindi topo_open(int version, const char *rootdir, int *errp)
1061414Scindi {
1071414Scindi 	topo_hdl_t *thp = NULL;
1081414Scindi 	topo_alloc_t *tap;
1093062Scindi 
1103062Scindi 	char platform[MAXNAMELEN];
1113062Scindi 	char isa[MAXNAMELEN];
1123062Scindi 	struct utsname uts;
1131414Scindi 	struct stat st;
1141414Scindi 
1153062Scindi 	smbios_hdl_t *shp;
1163062Scindi 	smbios_system_t s1;
1173062Scindi 	smbios_info_t s2;
1183062Scindi 	id_t id;
1191414Scindi 
1203062Scindi 	char *dbflags, *dbout;
1213062Scindi 
1223062Scindi 	if (version != TOPO_VERSION)
1233062Scindi 		return (set_open_errno(thp, errp, ETOPO_HDL_ABIVER));
1241414Scindi 
1251414Scindi 	if (rootdir != NULL && stat(rootdir, &st) < 0)
1261414Scindi 		return (set_open_errno(thp, errp, ETOPO_HDL_INVAL));
1271414Scindi 
1281414Scindi 	if ((thp = topo_zalloc(sizeof (topo_hdl_t), 0)) == NULL)
1291414Scindi 		return (set_open_errno(thp, errp, ETOPO_NOMEM));
1301414Scindi 
1313062Scindi 	(void) pthread_mutex_init(&thp->th_lock, NULL);
1323062Scindi 
1334087Scindi 	if ((tap = topo_zalloc(sizeof (topo_alloc_t), 0)) == NULL)
1341414Scindi 		return (set_open_errno(thp, errp, ETOPO_NOMEM));
1351414Scindi 
1361414Scindi 	/*
1371414Scindi 	 * Install default allocators
1381414Scindi 	 */
1391414Scindi 	tap->ta_flags = 0;
1401414Scindi 	tap->ta_alloc = topo_alloc;
1411414Scindi 	tap->ta_zalloc = topo_zalloc;
1421414Scindi 	tap->ta_free = topo_free;
1431414Scindi 	tap->ta_nvops.nv_ao_alloc = topo_nv_alloc;
1441414Scindi 	tap->ta_nvops.nv_ao_free = topo_nv_free;
1451414Scindi 	(void) nv_alloc_init(&tap->ta_nva, &tap->ta_nvops);
1461414Scindi 	thp->th_alloc = tap;
1471414Scindi 
1481414Scindi 	if ((thp->th_modhash = topo_modhash_create(thp)) == NULL)
1491414Scindi 		return (set_open_errno(thp, errp, ETOPO_NOMEM));
1501414Scindi 
1513062Scindi 	/*
1523062Scindi 	 * Set-up system information and search paths for modules
1533062Scindi 	 * and topology map files
1543062Scindi 	 */
1551414Scindi 	if (rootdir == NULL) {
1561414Scindi 		rootdir = topo_hdl_strdup(thp, "/");
1571414Scindi 		thp->th_rootdir = (char *)rootdir;
1581414Scindi 	} else {
1593062Scindi 		int len;
1603062Scindi 		char *rpath;
1613062Scindi 
1623062Scindi 		len = strlen(rootdir);
1633062Scindi 		if (len >= PATH_MAX)
1641414Scindi 			return (set_open_errno(thp, errp, EINVAL));
1651414Scindi 
1666360Srobj 		if (rootdir[len - 1] != '/') {
1676360Srobj 			rpath = alloca(len + 2);
1686360Srobj 			(void) snprintf(rpath, len + 2, "%s/", rootdir);
1693062Scindi 		} else {
1703062Scindi 			rpath = (char *)rootdir;
1713062Scindi 		}
1723062Scindi 		thp->th_rootdir = topo_hdl_strdup(thp, rpath);
1731414Scindi 	}
1741414Scindi 
1753062Scindi 	platform[0] = '\0';
1763062Scindi 	isa[0] = '\0';
1773062Scindi 	(void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
1783062Scindi 	(void) sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
1793062Scindi 	(void) uname(&uts);
1803062Scindi 	thp->th_platform = topo_hdl_strdup(thp, platform);
1813062Scindi 	thp->th_isa = topo_hdl_strdup(thp, isa);
1823062Scindi 	thp->th_machine = topo_hdl_strdup(thp, uts.machine);
1833062Scindi 	if ((shp = smbios_open(NULL, SMB_VERSION, 0, NULL)) != NULL) {
1843062Scindi 		if ((id = smbios_info_system(shp, &s1)) != SMB_ERR &&
1853062Scindi 		    smbios_info_common(shp, id, &s2) != SMB_ERR) {
1863062Scindi 
1873062Scindi 			if (strcmp(s2.smbi_product, SMB_DEFAULT1) != 0 &&
1883062Scindi 			    strcmp(s2.smbi_product, SMB_DEFAULT2) != 0) {
1894198Seschrock 				thp->th_product = topo_cleanup_auth_str(thp,
1908526SRobert.Johnston@Sun.COM 				    (char *)s2.smbi_product);
1913062Scindi 			}
1923062Scindi 		}
1933062Scindi 		smbios_close(shp);
1943062Scindi 	} else {
1953062Scindi 		thp->th_product = topo_hdl_strdup(thp, thp->th_platform);
1963062Scindi 	}
1973062Scindi 
1984328Scindi 	if (thp->th_rootdir == NULL || thp->th_platform == NULL ||
1994328Scindi 	    thp->th_machine == NULL)
2001414Scindi 		return (set_open_errno(thp, errp, ETOPO_NOMEM));
2013062Scindi 
2023062Scindi 	dbflags	 = getenv("TOPO_DEBUG");
2033062Scindi 	dbout = getenv("TOPO_DEBUG_OUT");
2043062Scindi 	if (dbflags != NULL)
2053062Scindi 		topo_debug_set(thp, dbflags, dbout);
2061414Scindi 
2071414Scindi 	if (topo_builtin_create(thp, thp->th_rootdir) != 0) {
2083062Scindi 		topo_dprintf(thp, TOPO_DBG_ERR,
2093062Scindi 		    "failed to load builtin modules: %s\n",
2103062Scindi 		    topo_hdl_errmsg(thp));
2119874SStephen.Hanson@Sun.COM 		return (set_open_errno(thp, errp, topo_hdl_errno(thp)));
2121414Scindi 	}
2131414Scindi 
2141414Scindi 	return (thp);
2151414Scindi }
2161414Scindi 
2171414Scindi void
topo_close(topo_hdl_t * thp)2181414Scindi topo_close(topo_hdl_t *thp)
2191414Scindi {
2201414Scindi 	ttree_t *tp;
2211414Scindi 
2221414Scindi 	topo_hdl_lock(thp);
2233062Scindi 	if (thp->th_platform != NULL)
2243062Scindi 		topo_hdl_strfree(thp, thp->th_platform);
2253062Scindi 	if (thp->th_isa != NULL)
2263062Scindi 		topo_hdl_strfree(thp, thp->th_isa);
2273062Scindi 	if (thp->th_machine != NULL)
2283062Scindi 		topo_hdl_strfree(thp, thp->th_machine);
2293062Scindi 	if (thp->th_product != NULL)
2303062Scindi 		topo_hdl_strfree(thp, thp->th_product);
2311414Scindi 	if (thp->th_rootdir != NULL)
2321414Scindi 		topo_hdl_strfree(thp, thp->th_rootdir);
2336070Srobj 	if (thp->th_ipmi != NULL)
2346070Srobj 		ipmi_close(thp->th_ipmi);
2359791SEric.Schrock@Sun.COM 	if (thp->th_smbios != NULL)
2369791SEric.Schrock@Sun.COM 		smbios_close(thp->th_smbios);
2371414Scindi 
2381414Scindi 	/*
2391414Scindi 	 * Clean-up snapshot
2401414Scindi 	 */
2411414Scindi 	topo_snap_destroy(thp);
2421414Scindi 
2431414Scindi 	/*
2441414Scindi 	 * Clean-up trees
2451414Scindi 	 */
2461414Scindi 	while ((tp = topo_list_next(&thp->th_trees)) != NULL) {
2471414Scindi 		topo_list_delete(&thp->th_trees, tp);
2483062Scindi 		topo_tree_destroy(tp);
2491414Scindi 	}
2501414Scindi 
2511414Scindi 	/*
2521414Scindi 	 * Unload all plugins
2531414Scindi 	 */
2541414Scindi 	topo_modhash_unload_all(thp);
2551414Scindi 
2561414Scindi 	if (thp->th_modhash != NULL)
2571414Scindi 		topo_modhash_destroy(thp);
2581414Scindi 	if (thp->th_alloc != NULL)
2591414Scindi 		topo_free(thp->th_alloc, sizeof (topo_alloc_t));
2601414Scindi 
2611414Scindi 	topo_hdl_unlock(thp);
2621414Scindi 
2631414Scindi 	topo_free(thp, sizeof (topo_hdl_t));
2641414Scindi }
2651414Scindi 
2661414Scindi static char *
topo_snap_create(topo_hdl_t * thp,int * errp,boolean_t need_force)26710145SStephen.Hanson@Sun.COM topo_snap_create(topo_hdl_t *thp, int *errp, boolean_t need_force)
2681414Scindi {
2691414Scindi 	uuid_t uuid;
2701414Scindi 	char *ustr = NULL;
2711414Scindi 
2721414Scindi 	topo_hdl_lock(thp);
2731414Scindi 	if (thp->th_uuid != NULL) {
2741414Scindi 		*errp = ETOPO_HDL_UUID;
2751414Scindi 		topo_hdl_unlock(thp);
2761414Scindi 		return (NULL);
2771414Scindi 	}
2781414Scindi 
2791414Scindi 	if ((thp->th_uuid = topo_hdl_zalloc(thp, TOPO_UUID_SIZE)) == NULL) {
2801414Scindi 		*errp = ETOPO_NOMEM;
2813062Scindi 		topo_dprintf(thp, TOPO_DBG_ERR, "unable to allocate uuid: %s\n",
2821414Scindi 		    topo_strerror(*errp));
2831414Scindi 		topo_hdl_unlock(thp);
2841414Scindi 		return (NULL);
2851414Scindi 	}
2861414Scindi 
2871414Scindi 	uuid_generate(uuid);
2881414Scindi 	uuid_unparse(uuid, thp->th_uuid);
2898740SSean.Ye@Sun.COM 	if ((ustr = topo_hdl_strdup(thp, thp->th_uuid)) == NULL) {
2908740SSean.Ye@Sun.COM 		*errp = ETOPO_NOMEM;
2918740SSean.Ye@Sun.COM 		topo_hdl_unlock(thp);
2928740SSean.Ye@Sun.COM 		return (NULL);
2938740SSean.Ye@Sun.COM 	}
2948740SSean.Ye@Sun.COM 
295*13131SHyon.Kim@Sun.COM 	if (need_force) {
296*13131SHyon.Kim@Sun.COM 		topo_dprintf(thp, TOPO_DBG_FORCE,
297*13131SHyon.Kim@Sun.COM 		    "taking a DINFOFORCE snapshot\n");
298*13131SHyon.Kim@Sun.COM 		thp->th_di = di_init("/", DINFOFORCE |
299*13131SHyon.Kim@Sun.COM 		    DINFOSUBTREE | DINFOMINOR | DINFOPROP | DINFOPATH);
300*13131SHyon.Kim@Sun.COM 	} else {
301*13131SHyon.Kim@Sun.COM 		thp->th_di = di_init("/", DINFOCACHE);
302*13131SHyon.Kim@Sun.COM 	}
3038740SSean.Ye@Sun.COM 	thp->th_pi = di_prom_init();
3041414Scindi 
3051414Scindi 	if (topo_tree_enum_all(thp) < 0) {
3063062Scindi 		topo_dprintf(thp, TOPO_DBG_ERR, "enumeration failure: %s\n",
3071414Scindi 		    topo_hdl_errmsg(thp));
3083062Scindi 		if (topo_hdl_errno(thp) == ETOPO_ENUM_FATAL) {
3091414Scindi 			*errp = thp->th_errno;
3108740SSean.Ye@Sun.COM 
3118740SSean.Ye@Sun.COM 			if (thp->th_di != DI_NODE_NIL) {
3128740SSean.Ye@Sun.COM 				di_fini(thp->th_di);
3138740SSean.Ye@Sun.COM 				thp->th_di = DI_NODE_NIL;
3148740SSean.Ye@Sun.COM 			}
3158740SSean.Ye@Sun.COM 			if (thp->th_pi != DI_PROM_HANDLE_NIL) {
3168740SSean.Ye@Sun.COM 				di_prom_fini(thp->th_pi);
3178740SSean.Ye@Sun.COM 				thp->th_pi = DI_PROM_HANDLE_NIL;
3188740SSean.Ye@Sun.COM 			}
3198740SSean.Ye@Sun.COM 
3208740SSean.Ye@Sun.COM 			topo_hdl_strfree(thp, ustr);
3211414Scindi 			topo_hdl_unlock(thp);
3221414Scindi 			return (NULL);
3231414Scindi 		}
3241414Scindi 	}
3251414Scindi 
3266070Srobj 	if (thp->th_ipmi != NULL &&
3276070Srobj 	    ipmi_sdr_changed(thp->th_ipmi) &&
3286070Srobj 	    ipmi_sdr_refresh(thp->th_ipmi) != 0) {
3296070Srobj 		topo_dprintf(thp, TOPO_DBG_ERR,
3306070Srobj 		    "failed to refresh IPMI sdr repository: %s\n",
3316070Srobj 		    ipmi_errmsg(thp->th_ipmi));
3326070Srobj 	}
3336070Srobj 
3341414Scindi 	topo_hdl_unlock(thp);
3351414Scindi 
3361414Scindi 	return (ustr);
3371414Scindi }
3381414Scindi 
3391414Scindi /*ARGSUSED*/
3401414Scindi static char *
topo_snap_log_create(topo_hdl_t * thp,const char * uuid,int * errp)3411414Scindi topo_snap_log_create(topo_hdl_t *thp, const char *uuid, int *errp)
3421414Scindi {
3431414Scindi 	return ((char *)uuid);
3441414Scindi }
3451414Scindi 
3467585SRobert.Johnston@Sun.COM /*ARGSUSED*/
3477585SRobert.Johnston@Sun.COM static int
fac_walker(topo_hdl_t * thp,tnode_t * node,void * arg)3487585SRobert.Johnston@Sun.COM fac_walker(topo_hdl_t *thp, tnode_t *node, void *arg)
3497585SRobert.Johnston@Sun.COM {
3507585SRobert.Johnston@Sun.COM 	int err;
3517585SRobert.Johnston@Sun.COM 	nvlist_t *out;
3527585SRobert.Johnston@Sun.COM 
3537585SRobert.Johnston@Sun.COM 	if (topo_method_supported(node, TOPO_METH_FAC_ENUM, 0)) {
3547585SRobert.Johnston@Sun.COM 		/*
3557585SRobert.Johnston@Sun.COM 		 * If the facility enumeration method fails, note the failure,
3567585SRobert.Johnston@Sun.COM 		 * but continue on with the walk.
3577585SRobert.Johnston@Sun.COM 		 */
3587585SRobert.Johnston@Sun.COM 		if (topo_method_invoke(node, TOPO_METH_FAC_ENUM, 0, NULL, &out,
3597585SRobert.Johnston@Sun.COM 		    &err) != 0) {
3607585SRobert.Johnston@Sun.COM 			topo_dprintf(thp, TOPO_DBG_ERR,
3617585SRobert.Johnston@Sun.COM 			    "facility enumeration method failed on node %s=%d "
3627585SRobert.Johnston@Sun.COM 			    "(%s)\n", topo_node_name(node),
3637585SRobert.Johnston@Sun.COM 			    topo_node_instance(node), topo_strerror(err));
3647585SRobert.Johnston@Sun.COM 		}
3657585SRobert.Johnston@Sun.COM 	}
3667585SRobert.Johnston@Sun.COM 	return (TOPO_WALK_NEXT);
3677585SRobert.Johnston@Sun.COM }
3687585SRobert.Johnston@Sun.COM 
3691414Scindi /*
3701414Scindi  * Return snapshot id
3711414Scindi  */
3721414Scindi char *
topo_snap_hold(topo_hdl_t * thp,const char * uuid,int * errp)3731414Scindi topo_snap_hold(topo_hdl_t *thp, const char *uuid, int *errp)
3741414Scindi {
3757585SRobert.Johnston@Sun.COM 	topo_walk_t *twp;
3767585SRobert.Johnston@Sun.COM 
3771414Scindi 	if (thp == NULL)
3781414Scindi 		return (NULL);
3791414Scindi 
3807585SRobert.Johnston@Sun.COM 	if (uuid == NULL) {
3817585SRobert.Johnston@Sun.COM 		char *ret;
3827585SRobert.Johnston@Sun.COM 
383*13131SHyon.Kim@Sun.COM 		if (thp->th_debug & TOPO_DBG_FORCE) {
384*13131SHyon.Kim@Sun.COM 			ret = topo_snap_create(thp, errp, B_TRUE);
385*13131SHyon.Kim@Sun.COM 		} else {
386*13131SHyon.Kim@Sun.COM 			ret = topo_snap_create(thp, errp, B_FALSE);
387*13131SHyon.Kim@Sun.COM 		}
38810145SStephen.Hanson@Sun.COM 
38910145SStephen.Hanson@Sun.COM 		/*
39010145SStephen.Hanson@Sun.COM 		 * Now walk the tree and invoke any facility enumeration methods
39110145SStephen.Hanson@Sun.COM 		 */
39212967Sgavin.maltby@oracle.com 		if (ret != NULL && getzoneid() == 0) {
39310145SStephen.Hanson@Sun.COM 			if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
39410145SStephen.Hanson@Sun.COM 			    fac_walker, (void *)0, errp)) == NULL) {
39510145SStephen.Hanson@Sun.COM 				return (ret);
39610145SStephen.Hanson@Sun.COM 			}
39710145SStephen.Hanson@Sun.COM 			(void) topo_walk_step(twp, TOPO_WALK_CHILD);
39810145SStephen.Hanson@Sun.COM 			topo_walk_fini(twp);
39910145SStephen.Hanson@Sun.COM 		}
40010145SStephen.Hanson@Sun.COM 		return (ret);
40110145SStephen.Hanson@Sun.COM 	}
40210145SStephen.Hanson@Sun.COM 	return (topo_snap_log_create(thp, uuid, errp));
40310145SStephen.Hanson@Sun.COM }
40410145SStephen.Hanson@Sun.COM 
4051414Scindi /*ARGSUSED*/
4061414Scindi static int
topo_walk_destroy(topo_hdl_t * thp,tnode_t * node,void * notused)4071414Scindi topo_walk_destroy(topo_hdl_t *thp, tnode_t *node, void *notused)
4081414Scindi {
4091414Scindi 	tnode_t *cnode;
4101414Scindi 
4111414Scindi 	cnode = topo_child_first(node);
4121414Scindi 
4131414Scindi 	if (cnode != NULL)
4141414Scindi 		return (TOPO_WALK_NEXT);
4151414Scindi 
4161414Scindi 	topo_node_unbind(node);
4171414Scindi 
4181414Scindi 	return (TOPO_WALK_NEXT);
4191414Scindi }
4201414Scindi 
4211414Scindi static void
topo_snap_destroy(topo_hdl_t * thp)4221414Scindi topo_snap_destroy(topo_hdl_t *thp)
4231414Scindi {
4241414Scindi 	int i;
4251414Scindi 	ttree_t *tp;
4261414Scindi 	topo_walk_t *twp;
4271414Scindi 	tnode_t *root;
4281414Scindi 	topo_nodehash_t *nhp;
4291414Scindi 	topo_mod_t *mod;
4301414Scindi 
4311414Scindi 	for (tp = topo_list_next(&thp->th_trees); tp != NULL;
4321414Scindi 	    tp = topo_list_next(tp)) {
4331414Scindi 
4341414Scindi 		root = tp->tt_root;
4351414Scindi 		twp = tp->tt_walk;
4361414Scindi 		/*
4371414Scindi 		 * Clean-up tree nodes from the bottom-up
4381414Scindi 		 */
4391414Scindi 		if ((twp->tw_node = topo_child_first(root)) != NULL) {
4401414Scindi 			twp->tw_cb = topo_walk_destroy;
4411414Scindi 			topo_node_hold(root);
4421414Scindi 			topo_node_hold(twp->tw_node); /* released at walk end */
4431414Scindi 			(void) topo_walk_bottomup(twp, TOPO_WALK_CHILD);
4441414Scindi 			topo_node_rele(root);
4451414Scindi 		}
4461414Scindi 
4471414Scindi 		/*
4481414Scindi 		 * Tidy-up the root node
4491414Scindi 		 */
4501414Scindi 		while ((nhp = topo_list_next(&root->tn_children)) != NULL) {
4511414Scindi 			for (i = 0; i < nhp->th_arrlen; i++) {
4521414Scindi 				assert(nhp->th_nodearr[i] == NULL);
4531414Scindi 			}
4541414Scindi 			mod = nhp->th_enum;
4551414Scindi 			topo_mod_strfree(mod, nhp->th_name);
4561414Scindi 			topo_mod_free(mod, nhp->th_nodearr,
4571414Scindi 			    nhp->th_arrlen * sizeof (tnode_t *));
4581414Scindi 			topo_list_delete(&root->tn_children, nhp);
4591414Scindi 			topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
4601414Scindi 			topo_mod_rele(mod);
4611414Scindi 		}
4621414Scindi 
4631414Scindi 	}
4641414Scindi 
4658740SSean.Ye@Sun.COM 	/*
4668740SSean.Ye@Sun.COM 	 * Clean-up our cached devinfo and prom tree handles.
4678740SSean.Ye@Sun.COM 	 */
4688740SSean.Ye@Sun.COM 	if (thp->th_di != DI_NODE_NIL) {
4698740SSean.Ye@Sun.COM 		di_fini(thp->th_di);
4708740SSean.Ye@Sun.COM 		thp->th_di = DI_NODE_NIL;
4718740SSean.Ye@Sun.COM 	}
4728740SSean.Ye@Sun.COM 	if (thp->th_pi != DI_PROM_HANDLE_NIL) {
4738740SSean.Ye@Sun.COM 		di_prom_fini(thp->th_pi);
4748740SSean.Ye@Sun.COM 		thp->th_pi = DI_PROM_HANDLE_NIL;
4758740SSean.Ye@Sun.COM 	}
4768740SSean.Ye@Sun.COM 
4778740SSean.Ye@Sun.COM 
4783062Scindi 	if (thp->th_uuid != NULL) {
4793062Scindi 		topo_hdl_free(thp, thp->th_uuid, TOPO_UUID_SIZE);
4803062Scindi 		thp->th_uuid = NULL;
4813062Scindi 	}
4821414Scindi }
4831414Scindi 
4841414Scindi void
topo_snap_release(topo_hdl_t * thp)4851414Scindi topo_snap_release(topo_hdl_t *thp)
4861414Scindi {
4871414Scindi 	if (thp == NULL)
4881414Scindi 		return;
4891414Scindi 
4901414Scindi 	topo_hdl_lock(thp);
4913062Scindi 	topo_snap_destroy(thp);
4921414Scindi 	topo_hdl_unlock(thp);
4931414Scindi }
4941414Scindi 
4951414Scindi topo_walk_t *
topo_walk_init(topo_hdl_t * thp,const char * scheme,topo_walk_cb_t cb_f,void * pdata,int * errp)4961414Scindi topo_walk_init(topo_hdl_t *thp, const char *scheme, topo_walk_cb_t cb_f,
4971414Scindi     void *pdata, int *errp)
4981414Scindi {
4991414Scindi 	ttree_t *tp;
5001414Scindi 	topo_walk_t *wp;
5011414Scindi 
5021414Scindi 	for (tp = topo_list_next(&thp->th_trees); tp != NULL;
5031414Scindi 	    tp = topo_list_next(tp)) {
5041414Scindi 		if (strcmp(scheme, tp->tt_scheme) == 0) {
5051414Scindi 
5061414Scindi 			/*
5071414Scindi 			 * Hold the root node and start walk at the first
5081414Scindi 			 * child node
5091414Scindi 			 */
5101414Scindi 			assert(tp->tt_root != NULL);
5111414Scindi 
5124087Scindi 			if ((wp = topo_node_walk_init(thp, NULL, tp->tt_root,
5134087Scindi 			    cb_f, pdata, errp)) == NULL) /* errp set */
5141414Scindi 				return (NULL);
5151414Scindi 
5161414Scindi 			return (wp);
5171414Scindi 		}
5181414Scindi 	}
5191414Scindi 
5201414Scindi 	*errp = ETOPO_WALK_NOTFOUND;
5211414Scindi 	return (NULL);
5221414Scindi }
5231414Scindi 
5241414Scindi static int
step_child(tnode_t * cnp,topo_walk_t * wp,int flag,int bottomup)5256341Scy152378 step_child(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
5261414Scindi {
5271414Scindi 	int status;
5281414Scindi 	tnode_t *nnp;
5291414Scindi 
5301414Scindi 	nnp = topo_child_first(cnp);
5311414Scindi 
5324087Scindi 	if (nnp == NULL) {
5334087Scindi 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
5344087Scindi 		    "step_child: TOPO_WALK_TERMINATE for %s=%d\n",
5354087Scindi 		    cnp->tn_name, cnp->tn_instance);
5361414Scindi 		return (TOPO_WALK_TERMINATE);
5374087Scindi 	}
5381414Scindi 
5393062Scindi 	topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
5404087Scindi 	    "step_child: walk through node %s=%d to %s=%d\n",
5414087Scindi 	    cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
5421414Scindi 
5431414Scindi 	topo_node_hold(nnp); /* released on return from walk_step */
5441414Scindi 	wp->tw_node = nnp;
5451414Scindi 	if (bottomup == 1)
5466341Scy152378 		status = topo_walk_bottomup(wp, flag);
5471414Scindi 	else
5486341Scy152378 		status = topo_walk_step(wp, flag);
5491414Scindi 
5501414Scindi 	return (status);
5511414Scindi }
5521414Scindi 
5531414Scindi static int
step_sibling(tnode_t * cnp,topo_walk_t * wp,int flag,int bottomup)5546341Scy152378 step_sibling(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
5551414Scindi {
5561414Scindi 	int status;
5571414Scindi 	tnode_t *nnp;
5581414Scindi 
5591414Scindi 	nnp = topo_child_next(cnp->tn_parent, cnp);
5601414Scindi 
5614087Scindi 	if (nnp == NULL) {
5624087Scindi 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
5634087Scindi 		    "step_sibling: TOPO_WALK_TERMINATE for %s=%d\n",
5644087Scindi 		    cnp->tn_name, cnp->tn_instance);
5651414Scindi 		return (TOPO_WALK_TERMINATE);
5664087Scindi 	}
5671414Scindi 
5683062Scindi 	topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
5694087Scindi 	    "step_sibling: through sibling node %s=%d to %s=%d\n",
5704087Scindi 	    cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
5711414Scindi 
5721414Scindi 	topo_node_hold(nnp); /* released on return from walk_step */
5731414Scindi 	wp->tw_node = nnp;
5741414Scindi 	if (bottomup == 1)
5756341Scy152378 		status = topo_walk_bottomup(wp, flag);
5761414Scindi 	else
5776341Scy152378 		status = topo_walk_step(wp, flag);
5781414Scindi 
5791414Scindi 	return (status);
5801414Scindi }
5811414Scindi 
5821414Scindi int
topo_walk_byid(topo_walk_t * wp,const char * name,topo_instance_t inst)5834087Scindi topo_walk_byid(topo_walk_t *wp, const char *name, topo_instance_t inst)
5844087Scindi {
5854087Scindi 	int status;
5864087Scindi 	tnode_t *nnp, *cnp;
5874087Scindi 
5884087Scindi 	cnp = wp->tw_node;
5894087Scindi 	nnp = topo_node_lookup(cnp, name, inst);
5904087Scindi 	if (nnp == NULL)
5914087Scindi 		return (TOPO_WALK_TERMINATE);
5924087Scindi 
5934087Scindi 	topo_node_hold(nnp);
5944087Scindi 	wp->tw_node = nnp;
5954087Scindi 	if (wp->tw_mod != NULL)
5964087Scindi 		status = wp->tw_cb(wp->tw_mod, nnp, wp->tw_pdata);
5974087Scindi 	else
5984087Scindi 		status = wp->tw_cb(wp->tw_thp, nnp, wp->tw_pdata);
5994087Scindi 	topo_node_rele(nnp);
6004087Scindi 	wp->tw_node = cnp;
6014087Scindi 
6024087Scindi 	return (status);
6034087Scindi }
6044087Scindi 
6054087Scindi int
topo_walk_bysibling(topo_walk_t * wp,const char * name,topo_instance_t inst)6066341Scy152378 topo_walk_bysibling(topo_walk_t *wp, const char *name, topo_instance_t inst)
6076341Scy152378 {
6086341Scy152378 	int status;
6096341Scy152378 	tnode_t *cnp, *pnp;
6106341Scy152378 
6116341Scy152378 	cnp = wp->tw_node;
6126341Scy152378 	pnp = topo_node_parent(cnp);
6136341Scy152378 	assert(pnp != NULL);
6146341Scy152378 
6156341Scy152378 	topo_node_hold(pnp);
6166341Scy152378 	wp->tw_node = pnp;
6176341Scy152378 	status = topo_walk_byid(wp, name, inst);
6186341Scy152378 	topo_node_rele(pnp);
6196341Scy152378 	wp->tw_node = cnp;
6206341Scy152378 
6216341Scy152378 	return (status);
6226341Scy152378 }
6236341Scy152378 
6246341Scy152378 int
topo_walk_step(topo_walk_t * wp,int flag)6251414Scindi topo_walk_step(topo_walk_t *wp, int flag)
6261414Scindi {
6271414Scindi 	int status;
6281414Scindi 	tnode_t *cnp = wp->tw_node;
6291414Scindi 
6301414Scindi 	if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
6311414Scindi 		topo_node_rele(cnp);
6321414Scindi 		return (TOPO_WALK_ERR);
6331414Scindi 	}
6341414Scindi 
6351414Scindi 	/*
6364087Scindi 	 * No more nodes to walk
6371414Scindi 	 */
6381414Scindi 	if (cnp == NULL) {
6393062Scindi 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
6403062Scindi 		    "walk_step terminated\n");
6411414Scindi 		topo_node_rele(cnp);
6421414Scindi 		return (TOPO_WALK_TERMINATE);
6431414Scindi 	}
6441414Scindi 
6451414Scindi 
6464087Scindi 	if (wp->tw_mod != NULL)
6474087Scindi 		status = wp->tw_cb(wp->tw_mod, cnp, wp->tw_pdata);
6484087Scindi 	else
6494087Scindi 		status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata);
6504087Scindi 
6514087Scindi 	/*
6524087Scindi 	 * Walker callback says we're done
6534087Scindi 	 */
6544087Scindi 	if (status != TOPO_WALK_NEXT) {
6551414Scindi 		topo_node_rele(cnp);
6561414Scindi 		return (status);
6571414Scindi 	}
6581414Scindi 
6596341Scy152378 	if (flag == TOPO_WALK_CHILD)
6606341Scy152378 		status = step_child(cnp, wp, flag, 0);
6616341Scy152378 	else
6626341Scy152378 		status = step_sibling(cnp, wp, flag, 0);
6631414Scindi 
6641414Scindi 	/*
6654087Scindi 	 * No more nodes in this hash, skip to next node hash by stepping
6666341Scy152378 	 * to next sibling (child-first walk) or next child (sibling-first
6674087Scindi 	 * walk).
6681414Scindi 	 */
6691414Scindi 	if (status == TOPO_WALK_TERMINATE) {
6706341Scy152378 		if (flag == TOPO_WALK_CHILD)
6716341Scy152378 			status = step_sibling(cnp, wp, flag, 0);
6726341Scy152378 		else
6736341Scy152378 			status = step_child(cnp, wp, flag, 0);
6741414Scindi 	}
6751414Scindi 
6761414Scindi 	topo_node_rele(cnp); /* done with current node */
6771414Scindi 
6781414Scindi 	return (status);
6791414Scindi }
6801414Scindi 
6811414Scindi void
topo_walk_fini(topo_walk_t * wp)6821414Scindi topo_walk_fini(topo_walk_t *wp)
6831414Scindi {
6841414Scindi 	if (wp == NULL)
6851414Scindi 		return;
6861414Scindi 
6871414Scindi 	topo_node_rele(wp->tw_root);
6881414Scindi 
6891414Scindi 	topo_hdl_free(wp->tw_thp, wp, sizeof (topo_walk_t));
6901414Scindi }
6911414Scindi 
6921414Scindi int
topo_walk_bottomup(topo_walk_t * wp,int flag)6931414Scindi topo_walk_bottomup(topo_walk_t *wp, int flag)
6941414Scindi {
6951414Scindi 	int status;
6961414Scindi 	tnode_t *cnp;
6971414Scindi 
6981414Scindi 	if (wp == NULL)
6991414Scindi 		return (TOPO_WALK_ERR);
7001414Scindi 
7011414Scindi 	cnp = wp->tw_node;
7021414Scindi 	if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
7031414Scindi 		topo_node_rele(cnp);
7041414Scindi 		return (TOPO_WALK_ERR);
7051414Scindi 	}
7061414Scindi 
7071414Scindi 	/*
7081414Scindi 	 * End of the line
7091414Scindi 	 */
7101414Scindi 	if (cnp == NULL) {
7113062Scindi 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
7123062Scindi 		    "walk_bottomup terminated\n");
7131414Scindi 		topo_node_rele(cnp);
7141414Scindi 		return (TOPO_WALK_TERMINATE);
7151414Scindi 	}
7161414Scindi 
7173062Scindi 	topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
7183062Scindi 	    "%s walk_bottomup through node %s=%d\n",
7191414Scindi 	    (flag == TOPO_WALK_CHILD ? "TOPO_WALK_CHILD" : "TOPO_WALK_SIBLING"),
7201414Scindi 	    cnp->tn_name, cnp->tn_instance);
7211414Scindi 
7221414Scindi 	if (flag == TOPO_WALK_CHILD)
7236341Scy152378 		status = step_child(cnp, wp, flag, 1);
7241414Scindi 	else
7256341Scy152378 		status = step_sibling(cnp, wp, flag, 1);
7261414Scindi 
7271414Scindi 	/*
7281414Scindi 	 * At a leaf, run the callback
7291414Scindi 	 */
7301414Scindi 	if (status == TOPO_WALK_TERMINATE) {
7311414Scindi 		if ((status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata))
7321414Scindi 		    != TOPO_WALK_NEXT) {
7331414Scindi 			topo_node_rele(cnp);
7341414Scindi 			return (status);
7351414Scindi 		}
7361414Scindi 	}
7371414Scindi 
7381414Scindi 	/*
7391414Scindi 	 * Try next child or sibling
7401414Scindi 	 */
7411414Scindi 	if (status == TOPO_WALK_NEXT) {
7421414Scindi 		if (flag == TOPO_WALK_CHILD)
7436341Scy152378 			status = step_sibling(cnp, wp, flag, 1);
7441414Scindi 		else
7456341Scy152378 			status = step_child(cnp, wp, flag, 1);
7461414Scindi 	}
7471414Scindi 
7481414Scindi 	topo_node_rele(cnp); /* done with current node */
7491414Scindi 
7501414Scindi 	return (status);
7511414Scindi }
7528740SSean.Ye@Sun.COM 
7538740SSean.Ye@Sun.COM di_node_t
topo_hdl_devinfo(topo_hdl_t * thp)7548740SSean.Ye@Sun.COM topo_hdl_devinfo(topo_hdl_t *thp)
7558740SSean.Ye@Sun.COM {
7568740SSean.Ye@Sun.COM 	return (thp == NULL ? DI_NODE_NIL : thp->th_di);
7578740SSean.Ye@Sun.COM }
7588740SSean.Ye@Sun.COM 
7598740SSean.Ye@Sun.COM di_prom_handle_t
topo_hdl_prominfo(topo_hdl_t * thp)7608740SSean.Ye@Sun.COM topo_hdl_prominfo(topo_hdl_t *thp)
7618740SSean.Ye@Sun.COM {
7628740SSean.Ye@Sun.COM 	return (thp == NULL ? DI_PROM_HANDLE_NIL : thp->th_pi);
7638740SSean.Ye@Sun.COM }
764