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