2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Snapshot Library Interfaces
2N/A *
2N/A * Consumers of topology data may use the interfaces in this file to open,
2N/A * snapshot and close a topology exported by FMRI scheme (hc, mem and cpu)
2N/A * builtin plugins and their helper modules. A topology handle is obtained
2N/A * by calling topo_open(). Upon a successful return, the caller may use this
2N/A * handle to open a new snapshot. Each snapshot is assigned a Universally
2N/A * Unique Identifier that in a future enchancement to the libtopo API will be
2N/A * used as the file locator in /var/fm/topo to persist new snapshots or lookup
2N/A * a previously captured snapshot. topo_snap_hold() will capture the current
2N/A * system topology. All consumers of the topo_hdl_t argument will be
2N/A * blocked from accessing the topology trees until the snapshot completes.
2N/A *
2N/A * A snapshot may be cleared by calling topo_snap_rele(). As with
2N/A * topo_snap_hold(), all topology accesses are blocked until the topology
2N/A * trees have been released and deallocated.
2N/A *
2N/A * Walker Library Interfaces
2N/A *
2N/A * Once a snapshot has been taken with topo_snap_hold(), topo_hdl_t holders
2N/A * may initiate topology tree walks on a scheme-tree basis. topo_walk_init()
2N/A * will initiate the data structures required to walk any one one of the
2N/A * FMRI scheme trees. The walker data structure, topo_walk_t, is an opaque
2N/A * handle passed to topo_walk_step to begin the walk. At each node in the
2N/A * topology tree, a callback function is called with access to the node at
2N/A * which our current walk falls. The callback function is passed in during
2N/A * calls to topo_walk_init() and used throughout the walk_step of the
2N/A * scheme tree. At any time, the callback may terminate the walk by returning
2N/A * TOPO_WALK_TERMINATE or TOPO_WALK_ERR. TOPO_WALK_NEXT will continue the walk.
2N/A *
2N/A * The type of walk through the tree may be sibling first or child first by
2N/A * respectively passing in TOPO_WALK_SIBLING or TOPO_WALK_CHILD to
2N/A * the topo_walk_step() function. Topology nodes
2N/A * associated with an outstanding walk are held in place and will not be
2N/A * deallocated until the walk through that node completes.
2N/A *
2N/A * Once the walk has terminated, the walking process should call
2N/A * topo_walk_fini() to clean-up resources created in topo_walk_init()
2N/A * and release nodes that may be still held.
2N/A */
2N/A
2N/A#include <alloca.h>
2N/A#include <ctype.h>
2N/A#include <pthread.h>
2N/A#include <limits.h>
2N/A#include <assert.h>
2N/A#include <fcntl.h>
2N/A#include <smbios.h>
2N/A#include <sys/param.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <sys/utsname.h>
2N/A#include <uuid/uuid.h>
2N/A#include <unistd.h>
2N/A#include <zone.h>
2N/A
2N/A#include <fm/libtopo.h>
2N/A#include <sys/fm/protocol.h>
2N/A
2N/A#include <topo_alloc.h>
2N/A#include <topo_builtin.h>
2N/A#include <topo_string.h>
2N/A#include <topo_error.h>
2N/A#include <topo_subr.h>
2N/A
2N/Astatic void topo_snap_destroy(topo_hdl_t *);
2N/A
2N/Astatic topo_hdl_t *
2N/Aset_open_errno(topo_hdl_t *thp, int *errp, int err)
2N/A{
2N/A if (thp != NULL) {
2N/A topo_close(thp);
2N/A }
2N/A if (errp != NULL)
2N/A *errp = err;
2N/A return (NULL);
2N/A}
2N/A
2N/Astatic void
2N/Atopo_set_product(topo_hdl_t *thp)
2N/A{
2N/A char *product;
2N/A char *bufp;
2N/A int noclean = 0;
2N/A smbios_hdl_t *shp = NULL;
2N/A smbios_system_t s1;
2N/A smbios_info_t s2;
2N/A id_t id;
2N/A
2N/A /* get SMBIOS handle */
2N/A shp = topo_hdl_smbios(thp);
2N/A
2N/A /* determine product name */
2N/A product = NULL;
2N/A if (shp &&
2N/A ((id = smbios_info_system(shp, &s1)) != SMB_ERR) &&
2N/A (smbios_info_common(shp, id, &s2) != SMB_ERR) &&
2N/A (strcmp(s2.smbi_product, SMB_DEFAULT1) != 0) &&
2N/A (strcmp(s2.smbi_product, SMB_DEFAULT2) != 0))
2N/A product = (char *)s2.smbi_product;
2N/A if ((product == NULL) && thp->th_pi && thp->th_di &&
2N/A (di_prom_prop_lookup_bytes(thp->th_pi, thp->th_di, "name",
2N/A (unsigned char **)&bufp) != -1)) {
2N/A product = bufp;
2N/A }
2N/A if (product == NULL) {
2N/A product = thp->th_platform;
2N/A noclean = 1;
2N/A }
2N/A
2N/A if (product && !noclean) {
2N/A /*
2N/A * Cleanup the product name we found. Note that
2N/A * topo_cleanup_auth_str imposes a MAXNAMELEN limit
2N/A * on the input/result.
2N/A */
2N/A thp->th_product = topo_cleanup_auth_str(thp, product);
2N/A }
2N/A}
2N/A
2N/Atopo_hdl_t *
2N/Atopo_open(int version, const char *rootdir, int *errp)
2N/A{
2N/A struct stat st;
2N/A topo_hdl_t *thp = NULL;
2N/A topo_alloc_t *tap;
2N/A char platform[MAXNAMELEN];
2N/A char isa[MAXNAMELEN];
2N/A struct utsname uts;
2N/A char *dbflags;
2N/A char *dbout;
2N/A
2N/A if (version != TOPO_VERSION)
2N/A return (set_open_errno(thp, errp, ETOPO_HDL_ABIVER));
2N/A
2N/A if ((rootdir != NULL) && (stat(rootdir, &st) < 0))
2N/A return (set_open_errno(thp, errp, ETOPO_HDL_INVAL));
2N/A
2N/A if ((thp = topo_zalloc(sizeof (topo_hdl_t), 0)) == NULL)
2N/A return (set_open_errno(thp, errp, ETOPO_NOMEM));
2N/A
2N/A (void) pthread_mutex_init(&thp->th_lock, NULL);
2N/A
2N/A dbflags = getenv("TOPO_DEBUG");
2N/A dbout = getenv("TOPO_DEBUG_OUT");
2N/A if (dbflags != NULL)
2N/A topo_debug_set(thp, dbflags, dbout);
2N/A
2N/A if ((tap = topo_zalloc(sizeof (topo_alloc_t), 0)) == NULL)
2N/A return (set_open_errno(thp, errp, ETOPO_NOMEM));
2N/A
2N/A /*
2N/A * Install default allocators
2N/A */
2N/A tap->ta_flags = 0;
2N/A tap->ta_alloc = topo_alloc;
2N/A tap->ta_zalloc = topo_zalloc;
2N/A tap->ta_free = topo_free;
2N/A tap->ta_nvops.nv_ao_alloc = topo_nv_alloc;
2N/A tap->ta_nvops.nv_ao_free = topo_nv_free;
2N/A (void) nv_alloc_init(&tap->ta_nva, &tap->ta_nvops);
2N/A thp->th_alloc = tap;
2N/A
2N/A if ((thp->th_modhash = topo_modhash_create(thp)) == NULL)
2N/A return (set_open_errno(thp, errp, ETOPO_NOMEM));
2N/A
2N/A /*
2N/A * Set-up system information and search paths for modules
2N/A * and topology map files
2N/A */
2N/A if (rootdir == NULL) {
2N/A rootdir = topo_hdl_strdup(thp, "/");
2N/A thp->th_rootdir = (char *)rootdir;
2N/A } else {
2N/A int len;
2N/A char *rpath;
2N/A
2N/A len = strlen(rootdir);
2N/A if (len >= PATH_MAX)
2N/A return (set_open_errno(thp, errp, EINVAL));
2N/A
2N/A if (rootdir[len - 1] != '/') {
2N/A rpath = alloca(len + 2);
2N/A (void) snprintf(rpath, len + 2, "%s/", rootdir);
2N/A } else {
2N/A rpath = (char *)rootdir;
2N/A }
2N/A thp->th_rootdir = topo_hdl_strdup(thp, rpath);
2N/A }
2N/A
2N/A /* setup sysinfo based authority */
2N/A platform[0] = '\0';
2N/A isa[0] = '\0';
2N/A uts.machine[0] = '\0';
2N/A uts.nodename[0] = '\0';
2N/A (void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
2N/A (void) sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
2N/A (void) uname(&uts);
2N/A thp->th_platform = topo_hdl_strdup(thp, platform);
2N/A thp->th_isa = topo_hdl_strdup(thp, isa);
2N/A thp->th_machine = topo_hdl_strdup(thp, uts.machine);
2N/A thp->th_server = topo_cleanup_auth_str(thp, uts.nodename);
2N/A
2N/A if (thp->th_rootdir == NULL || thp->th_platform == NULL ||
2N/A thp->th_machine == NULL)
2N/A return (set_open_errno(thp, errp, ETOPO_NOMEM));
2N/A
2N/A if (topo_builtin_create(thp, thp->th_rootdir) != 0) {
2N/A topo_dprintf(thp, TOPO_DBG_ERR,
2N/A "%s: failed to load builtin modules: %s\n",
2N/A __func__, topo_hdl_errmsg(thp));
2N/A return (set_open_errno(thp, errp, topo_hdl_errno(thp)));
2N/A }
2N/A
2N/A return (thp);
2N/A}
2N/A
2N/Avoid
2N/Atopo_close(topo_hdl_t *thp)
2N/A{
2N/A ttree_t *tp;
2N/A
2N/A topo_hdl_lock(thp);
2N/A
2N/A if (thp->th_rootdir != NULL)
2N/A topo_hdl_strfree(thp, thp->th_rootdir);
2N/A if (thp->th_platform != NULL)
2N/A topo_hdl_strfree(thp, thp->th_platform);
2N/A if (thp->th_isa != NULL)
2N/A topo_hdl_strfree(thp, thp->th_isa);
2N/A if (thp->th_machine != NULL)
2N/A topo_hdl_strfree(thp, thp->th_machine);
2N/A if (thp->th_server != NULL)
2N/A topo_hdl_strfree(thp, thp->th_server);
2N/A if (thp->th_product != NULL)
2N/A topo_hdl_strfree(thp, thp->th_product);
2N/A if (thp->th_ipmi != NULL)
2N/A ipmi_close(thp->th_ipmi);
2N/A if (thp->th_auth != NULL)
2N/A nvlist_free(thp->th_auth);
2N/A
2N/A /*
2N/A * Clean-up snapshot
2N/A */
2N/A topo_snap_destroy(thp);
2N/A
2N/A /*
2N/A * Clean-up trees
2N/A */
2N/A while ((tp = topo_list_next(&thp->th_trees)) != NULL) {
2N/A topo_list_delete(&thp->th_trees, tp);
2N/A topo_tree_destroy(tp);
2N/A }
2N/A
2N/A /*
2N/A * Unload all plugins
2N/A */
2N/A topo_modhash_unload_all(thp);
2N/A
2N/A if (thp->th_modhash != NULL)
2N/A topo_modhash_destroy(thp);
2N/A if (thp->th_alloc != NULL)
2N/A topo_free(thp->th_alloc, sizeof (topo_alloc_t));
2N/A
2N/A topo_hdl_unlock(thp);
2N/A
2N/A topo_free(thp, sizeof (topo_hdl_t));
2N/A}
2N/A
2N/Astatic char *
2N/Atopo_snap_create(topo_hdl_t *thp, int *errp, boolean_t need_force)
2N/A{
2N/A uuid_t uuid;
2N/A char *ustr = NULL;
2N/A boolean_t load_snapshot;
2N/A
2N/A topo_hdl_lock(thp);
2N/A if (thp->th_uuid != NULL) {
2N/A *errp = ETOPO_HDL_UUID;
2N/A topo_dprintf(thp, TOPO_DBG_ERR,
2N/A "%s: th_uuid should be NULL\n", __func__);
2N/A topo_hdl_unlock(thp);
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * If we're loading a topo snapshot rather than generating
2N/A * a new one from scratch, then thp->th_snap_dir will be
2N/A * non-null and the name of a sub-directory under the top
2N/A * level topo snapshots directory.
2N/A */
2N/A load_snapshot = ((thp->th_snap_dir == NULL) ? B_FALSE : B_TRUE);
2N/A
2N/A topo_dprintf(thp, TOPO_DBG_SNAP, "%s: thp=0x%p, load_snapshot=%s\n",
2N/A __func__, (void *)thp,
2N/A (load_snapshot == B_TRUE) ? "TRUE" : "FALSE");
2N/A
2N/A /*
2N/A * If we're not loading a topo snapshot then generate a uuid,
2N/A * otherwise, one will be read in from the snapshot.
2N/A * We also don't need the devinfo or prominfo handles
2N/A * when loading a snapshot since those are only used
2N/A * by enumerators.
2N/A */
2N/A if (load_snapshot == B_FALSE) {
2N/A
2N/A thp->th_timestamp = time(NULL);
2N/A
2N/A if ((thp->th_uuid = topo_hdl_zalloc(thp, TOPO_UUID_SIZE))
2N/A == NULL) {
2N/A *errp = ETOPO_NOMEM;
2N/A topo_dprintf(thp, TOPO_DBG_ERR, "%s: unable to "
2N/A "allocate uuid\n", __func__);
2N/A topo_hdl_unlock(thp);
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * Use uuid_generate_time() if debug option is set.
2N/A */
2N/A if (thp->th_debug & TOPO_DBG_UUID) {
2N/A struct timeval uuid_timeval;
2N/A time_t time;
2N/A
2N/A uuid_generate_time(uuid);
2N/A time = uuid_time(uuid, &uuid_timeval);
2N/A topo_dprintf(thp, TOPO_DBG_SNAP,
2N/A "%s: using uuid_generate_time(), time=0x%lx(%s)\n",
2N/A __func__, (ulong_t)time, ctime(&time));
2N/A } else {
2N/A uuid_generate(uuid);
2N/A }
2N/A
2N/A uuid_unparse(uuid, thp->th_uuid);
2N/A
2N/A topo_dprintf(thp, TOPO_DBG_SNAP, "%s: new snapshot uuid=%s, "
2N/A "timestamp=0x%lx(%s)\n", __func__, thp->th_uuid,
2N/A (ulong_t)thp->th_timestamp, ctime(&(thp->th_timestamp)));
2N/A
2N/A if (need_force) {
2N/A topo_dprintf(thp, TOPO_DBG_FORCE,
2N/A "%s: taking a DINFOFORCE snapshot\n", __func__);
2N/A thp->th_di = di_init("/", DINFOFORCE |
2N/A DINFOSUBTREE | DINFOMINOR | DINFOPROP | DINFOPATH);
2N/A } else {
2N/A thp->th_di = di_init("/", DINFOCACHE);
2N/A }
2N/A
2N/A /*
2N/A * the /dev links are probed by the disk enumerator and the ses
2N/A * enumetator to find /dev public name for a device that the
2N/A * enumerator is interested in. The devlink handle used to
2N/A * be acquired in disk_common.c but /dev links are only probed
2N/A * for the devices that are pulled from the devinfo snapshot
2N/A * taken above so it is okay to acquire the handle here since
2N/A * the /dev link repository will include the same set of
2N/A * the devices as the devinfo snapshot.
2N/A *
2N/A * NOTE from disk_common.c:
2N/A * Until we can clean up (i.e. remove) topo dependencies
2N/A * on /dev public names, we ensure that name creation has
2N/A * completed by passing the DI_MAKE_LINK flag. Doing this
2N/A * lets us ignore EC_DEV_ADD and EC_DEV_REMOVE events
2N/A * in fmd_dr.c code.
2N/A */
2N/A thp->th_dli = di_devlink_init(NULL, DI_MAKE_LINK);
2N/A thp->th_pi = di_prom_init();
2N/A thp->th_smbios = smbios_open(NULL, SMB_VERSION, 0, NULL);
2N/A
2N/A topo_set_product(thp);
2N/A } else {
2N/A topo_dprintf(thp, TOPO_DBG_SNAP, "%s: th_snap_dir=%s\n",
2N/A __func__, thp->th_snap_dir);
2N/A }
2N/A
2N/A if (topo_tree_enum_all(thp) < 0) {
2N/A /*
2N/A * Note that nothing returns ETOPO_ENUM_FATAL today.
2N/A */
2N/A if (topo_hdl_errno(thp) == ETOPO_ENUM_FATAL) {
2N/A topo_dprintf(thp, TOPO_DBG_ERR, "%s: fatal "
2N/A "enumeration error\n", __func__);
2N/A *errp = ETOPO_ENUM_FATAL;
2N/A topo_hdl_unlock(thp);
2N/A return (NULL);
2N/A } else {
2N/A topo_dprintf(thp, TOPO_DBG_ERR, "%s: non-fatal "
2N/A "enumeration error\n", __func__);
2N/A }
2N/A }
2N/A
2N/A if (thp->th_ipmi != NULL &&
2N/A ipmi_sdr_changed(thp->th_ipmi) &&
2N/A ipmi_sdr_refresh(thp->th_ipmi) != 0) {
2N/A topo_dprintf(thp, TOPO_DBG_ERR,
2N/A "%s: failed to refresh IPMI sdr repository: %s\n",
2N/A __func__, ipmi_errmsg(thp->th_ipmi));
2N/A }
2N/A
2N/A if (thp->th_uuid == NULL) {
2N/A topo_dprintf(thp, TOPO_DBG_ERR,
2N/A "%s: enumeration failed, th_uuid is NULL\n", __func__);
2N/A *errp = thp->th_errno;
2N/A topo_hdl_unlock(thp);
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((ustr = topo_hdl_strdup(thp, thp->th_uuid)) == NULL) {
2N/A topo_dprintf(thp, TOPO_DBG_ERR,
2N/A "%s: unable to allocate ustr\n", __func__);
2N/A (void) topo_hdl_seterrno(thp, ETOPO_NOMEM);
2N/A *errp = ETOPO_NOMEM;
2N/A topo_hdl_unlock(thp);
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * Create topo handle authority if enumerator has not.
2N/A */
2N/A if (thp->th_auth == NULL) {
2N/A topo_dprintf(thp, TOPO_DBG_SNAP, "%s: create authority\n",
2N/A __func__);
2N/A topo_auth_create(thp);
2N/A }
2N/A
2N/A topo_hdl_unlock(thp);
2N/A topo_dprintf(thp, TOPO_DBG_SNAP, "%s: returning ustr=%s\n",
2N/A __func__, ustr);
2N/A return (ustr);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic char *
2N/Atopo_snap_log_create(topo_hdl_t *thp, const char *uuid, int *errp)
2N/A{
2N/A return ((char *)uuid);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Afac_walker(topo_hdl_t *thp, tnode_t *node, void *arg)
2N/A{
2N/A int err;
2N/A nvlist_t *out;
2N/A
2N/A if (topo_method_supported(node, TOPO_METH_FAC_ENUM, 0)) {
2N/A /*
2N/A * If the facility enumeration method fails, note the failure,
2N/A * but continue on with the walk.
2N/A */
2N/A if (topo_method_invoke(node, TOPO_METH_FAC_ENUM, 0, NULL, &out,
2N/A &err) != 0) {
2N/A topo_dprintf(thp, TOPO_DBG_ERR,
2N/A "%s: facility enumeration method failed on "
2N/A "node %s=%d (%s)\n", __func__, topo_node_name(node),
2N/A topo_node_instance(node), topo_strerror(err));
2N/A }
2N/A }
2N/A return (TOPO_WALK_NEXT);
2N/A}
2N/A
2N/A/*
2N/A * Return snapshot id
2N/A */
2N/Achar *
2N/Atopo_snap_hold(topo_hdl_t *thp, const char *uuid, int *errp)
2N/A{
2N/A char *ret;
2N/A
2N/A if ((ret = topo_snap_hold_flag(thp, uuid, errp, 0)) == NULL)
2N/A topo_snap_release(thp);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Achar *
2N/Atopo_snap_hold_flag(topo_hdl_t *thp, const char *uuid, int *errp, int flag)
2N/A{
2N/A topo_walk_t *twp;
2N/A char *ret;
2N/A
2N/A if (thp == NULL)
2N/A return (NULL);
2N/A
2N/A if (uuid)
2N/A return (topo_snap_log_create(thp, uuid, errp));
2N/A
2N/A /* In global zone, bracket snapshot with di_cromk_begin/end. */
2N/A if ((getzoneid() == 0) && (flag & TOPO_SNAP_HOLD_CROMK))
2N/A thp->th_cromk_hdl = di_cromk_begin(0);
2N/A
2N/A ret = topo_snap_create(thp, errp,
2N/A (thp->th_debug & TOPO_DBG_FORCE) ? B_TRUE : B_FALSE);
2N/A
2N/A if (ret == NULL) {
2N/A if (thp->th_cromk_hdl) {
2N/A di_cromk_end(thp->th_cromk_hdl, DI_CROMK_END_ABANDON,
2N/A NULL, NULL, NULL, 0);
2N/A thp->th_cromk_hdl = NULL;
2N/A }
2N/A return (NULL);
2N/A }
2N/A
2N/A if (getzoneid())
2N/A return (ret);
2N/A
2N/A /* global zone: walk and invoke facility enumeration methods. */
2N/A twp = topo_walk_init(thp,
2N/A FM_FMRI_SCHEME_HC, fac_walker, (void *)0, errp);
2N/A if (twp) {
2N/A (void) topo_walk_step(twp, TOPO_WALK_CHILD);
2N/A topo_walk_fini(twp);
2N/A }
2N/A
2N/A if (thp->th_cromk_hdl) {
2N/A char *csn = NULL;
2N/A (void) nvlist_lookup_string(thp->th_auth,
2N/A FM_FMRI_AUTH_V1_CHASSIS_SN, &csn);
2N/A di_cromk_end(thp->th_cromk_hdl, DI_CROMK_END_COMMIT,
2N/A thp->th_server, thp->th_product, csn,
2N/A thp->th_di ? di_cna_dev(thp->th_di) : 0);
2N/A thp->th_cromk_hdl = NULL;
2N/A }
2N/A return (ret);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Atopo_walk_destroy(topo_hdl_t *thp, tnode_t *node, void *notused)
2N/A{
2N/A tnode_t *cnode;
2N/A
2N/A cnode = topo_child_first(node);
2N/A
2N/A if (cnode != NULL)
2N/A return (TOPO_WALK_NEXT);
2N/A
2N/A topo_node_unbind(node);
2N/A
2N/A return (TOPO_WALK_NEXT);
2N/A}
2N/A
2N/Astatic void
2N/Atopo_snap_destroy(topo_hdl_t *thp)
2N/A{
2N/A int i;
2N/A ttree_t *tp;
2N/A topo_walk_t *twp;
2N/A tnode_t *root;
2N/A topo_nodehash_t *nhp;
2N/A topo_mod_t *mod;
2N/A
2N/A for (tp = topo_list_next(&thp->th_trees); tp != NULL;
2N/A tp = topo_list_next(tp)) {
2N/A
2N/A root = tp->tt_root;
2N/A twp = tp->tt_walk;
2N/A /*
2N/A * Clean-up tree nodes from the bottom-up
2N/A */
2N/A if ((twp->tw_node = topo_child_first(root)) != NULL) {
2N/A twp->tw_cb = topo_walk_destroy;
2N/A topo_node_hold(root);
2N/A topo_node_hold(twp->tw_node); /* released at walk end */
2N/A (void) topo_walk_bottomup(twp, TOPO_WALK_CHILD);
2N/A topo_node_rele(root);
2N/A }
2N/A
2N/A /*
2N/A * Tidy-up the root node
2N/A */
2N/A while ((nhp = topo_list_next(&root->tn_children)) != NULL) {
2N/A for (i = 0; i < nhp->th_arrlen; i++) {
2N/A assert(nhp->th_nodearr[i] == NULL);
2N/A }
2N/A mod = nhp->th_enum;
2N/A topo_mod_strfree(mod, nhp->th_name);
2N/A topo_mod_free(mod, nhp->th_nodearr,
2N/A nhp->th_arrlen * sizeof (tnode_t *));
2N/A topo_list_delete(&root->tn_children, nhp);
2N/A topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
2N/A topo_mod_rele(mod);
2N/A }
2N/A
2N/A }
2N/A
2N/A /*
2N/A * Clean-up our cached devinfo, devlink, prom tree, and smbios handles.
2N/A */
2N/A if (thp->th_di != DI_NODE_NIL) {
2N/A di_fini(thp->th_di);
2N/A thp->th_di = DI_NODE_NIL;
2N/A }
2N/A if (thp->th_dli != NULL) {
2N/A (void) di_devlink_fini(&thp->th_dli);
2N/A thp->th_dli = NULL;
2N/A }
2N/A if (thp->th_pi != DI_PROM_HANDLE_NIL) {
2N/A di_prom_fini(thp->th_pi);
2N/A thp->th_pi = DI_PROM_HANDLE_NIL;
2N/A }
2N/A if (thp->th_smbios != NULL) {
2N/A smbios_close(thp->th_smbios);
2N/A thp->th_smbios = NULL;
2N/A }
2N/A
2N/A if (thp->th_uuid != NULL) {
2N/A topo_hdl_free(thp, thp->th_uuid, TOPO_UUID_SIZE);
2N/A thp->th_uuid = NULL;
2N/A }
2N/A
2N/A topo_hdl_strfree(thp, thp->th_snap_dir);
2N/A thp->th_snap_dir = NULL;
2N/A}
2N/A
2N/Avoid
2N/Atopo_snap_release(topo_hdl_t *thp)
2N/A{
2N/A if (thp == NULL)
2N/A return;
2N/A
2N/A topo_hdl_lock(thp);
2N/A topo_snap_destroy(thp);
2N/A topo_hdl_unlock(thp);
2N/A}
2N/A
2N/Atopo_walk_t *
2N/Atopo_walk_init(topo_hdl_t *thp, const char *scheme, topo_walk_cb_t cb_f,
2N/A void *pdata, int *errp)
2N/A{
2N/A ttree_t *tp;
2N/A topo_walk_t *wp;
2N/A
2N/A topo_dprintf(thp, TOPO_DBG_SNAP, "%s: thp=0x%p, uuid=%s, "
2N/A "scheme=%s\n", __func__, (void *)thp, thp->th_uuid, scheme);
2N/A
2N/A for (tp = topo_list_next(&thp->th_trees); tp != NULL;
2N/A tp = topo_list_next(tp)) {
2N/A if (strcmp(scheme, tp->tt_scheme) == 0) {
2N/A
2N/A /*
2N/A * Hold the root node and start walk at the first
2N/A * child node
2N/A */
2N/A assert(tp->tt_root != NULL);
2N/A
2N/A if ((wp = topo_node_walk_init(thp, NULL, tp->tt_root,
2N/A cb_f, pdata, errp)) == NULL) /* errp set */
2N/A return (NULL);
2N/A
2N/A return (wp);
2N/A }
2N/A }
2N/A
2N/A *errp = ETOPO_WALK_NOTFOUND;
2N/A return (NULL);
2N/A}
2N/A
2N/Astatic int
2N/Astep_child(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
2N/A{
2N/A int status;
2N/A tnode_t *nnp;
2N/A
2N/A nnp = topo_child_first(cnp);
2N/A
2N/A if (nnp == NULL) {
2N/A topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
2N/A "%s: TOPO_WALK_TERMINATE for %s=%d\n",
2N/A __func__, cnp->tn_name, cnp->tn_instance);
2N/A return (TOPO_WALK_TERMINATE);
2N/A }
2N/A
2N/A topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
2N/A "%s: walk through node %s=%d to %s=%d\n", __func__,
2N/A cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
2N/A
2N/A topo_node_hold(nnp); /* released on return from walk_step */
2N/A wp->tw_node = nnp;
2N/A if (bottomup == 1)
2N/A status = topo_walk_bottomup(wp, flag);
2N/A else
2N/A status = topo_walk_step(wp, flag);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Astatic int
2N/Astep_sibling(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
2N/A{
2N/A int status;
2N/A tnode_t *nnp;
2N/A
2N/A nnp = topo_child_next(cnp->tn_parent, cnp);
2N/A
2N/A if (nnp == NULL) {
2N/A topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
2N/A "%s: TOPO_WALK_TERMINATE for %s=%d\n", __func__,
2N/A cnp->tn_name, cnp->tn_instance);
2N/A return (TOPO_WALK_TERMINATE);
2N/A }
2N/A
2N/A topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
2N/A "%s: through sibling node %s=%d to %s=%d\n", __func__,
2N/A cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
2N/A
2N/A topo_node_hold(nnp); /* released on return from walk_step */
2N/A wp->tw_node = nnp;
2N/A if (bottomup == 1)
2N/A status = topo_walk_bottomup(wp, flag);
2N/A else
2N/A status = topo_walk_step(wp, flag);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Aint
2N/Atopo_walk_byid(topo_walk_t *wp, const char *name, topo_instance_t inst)
2N/A{
2N/A int status;
2N/A tnode_t *nnp, *cnp;
2N/A
2N/A cnp = wp->tw_node;
2N/A nnp = topo_node_lookup(cnp, name, inst);
2N/A if (nnp == NULL)
2N/A return (TOPO_WALK_TERMINATE);
2N/A
2N/A topo_node_hold(nnp);
2N/A wp->tw_node = nnp;
2N/A if (wp->tw_mod != NULL)
2N/A status = wp->tw_cb(wp->tw_mod, nnp, wp->tw_pdata);
2N/A else
2N/A status = wp->tw_cb(wp->tw_thp, nnp, wp->tw_pdata);
2N/A topo_node_rele(nnp);
2N/A wp->tw_node = cnp;
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Aint
2N/Atopo_walk_bysibling(topo_walk_t *wp, const char *name, topo_instance_t inst)
2N/A{
2N/A int status;
2N/A tnode_t *cnp, *pnp;
2N/A
2N/A cnp = wp->tw_node;
2N/A pnp = topo_node_parent(cnp);
2N/A assert(pnp != NULL);
2N/A
2N/A topo_node_hold(pnp);
2N/A wp->tw_node = pnp;
2N/A status = topo_walk_byid(wp, name, inst);
2N/A topo_node_rele(pnp);
2N/A wp->tw_node = cnp;
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Aint
2N/Atopo_walk_step(topo_walk_t *wp, int flag)
2N/A{
2N/A int status;
2N/A tnode_t *cnp = wp->tw_node;
2N/A
2N/A if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
2N/A topo_node_rele(cnp);
2N/A return (TOPO_WALK_ERR);
2N/A }
2N/A
2N/A /*
2N/A * No more nodes to walk
2N/A */
2N/A if (cnp == NULL) {
2N/A topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
2N/A "%s: walk terminated\n", __func__);
2N/A topo_node_rele(cnp);
2N/A return (TOPO_WALK_TERMINATE);
2N/A }
2N/A
2N/A
2N/A if (wp->tw_mod != NULL)
2N/A status = wp->tw_cb(wp->tw_mod, cnp, wp->tw_pdata);
2N/A else
2N/A status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata);
2N/A
2N/A /*
2N/A * Walker callback says we're done
2N/A */
2N/A if (status != TOPO_WALK_NEXT) {
2N/A topo_node_rele(cnp);
2N/A return (status);
2N/A }
2N/A
2N/A if (flag == TOPO_WALK_CHILD)
2N/A status = step_child(cnp, wp, flag, 0);
2N/A else
2N/A status = step_sibling(cnp, wp, flag, 0);
2N/A
2N/A /*
2N/A * No more nodes in this hash, skip to next node hash by stepping
2N/A * to next sibling (child-first walk) or next child (sibling-first
2N/A * walk).
2N/A */
2N/A if (status == TOPO_WALK_TERMINATE) {
2N/A if (flag == TOPO_WALK_CHILD)
2N/A status = step_sibling(cnp, wp, flag, 0);
2N/A else
2N/A status = step_child(cnp, wp, flag, 0);
2N/A }
2N/A
2N/A topo_node_rele(cnp); /* done with current node */
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Avoid
2N/Atopo_walk_fini(topo_walk_t *wp)
2N/A{
2N/A if (wp == NULL)
2N/A return;
2N/A
2N/A topo_node_rele(wp->tw_root);
2N/A
2N/A topo_hdl_free(wp->tw_thp, wp, sizeof (topo_walk_t));
2N/A}
2N/A
2N/Aint
2N/Atopo_walk_bottomup(topo_walk_t *wp, int flag)
2N/A{
2N/A int status;
2N/A tnode_t *cnp;
2N/A
2N/A if (wp == NULL)
2N/A return (TOPO_WALK_ERR);
2N/A
2N/A cnp = wp->tw_node;
2N/A if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
2N/A topo_node_rele(cnp);
2N/A return (TOPO_WALK_ERR);
2N/A }
2N/A
2N/A /*
2N/A * End of the line
2N/A */
2N/A if (cnp == NULL) {
2N/A topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
2N/A "%s: walk terminated\n", __func__);
2N/A topo_node_rele(cnp);
2N/A return (TOPO_WALK_TERMINATE);
2N/A }
2N/A
2N/A topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
2N/A "%s: %s through node %s=%d\n", __func__,
2N/A (flag == TOPO_WALK_CHILD ? "TOPO_WALK_CHILD" : "TOPO_WALK_SIBLING"),
2N/A cnp->tn_name, cnp->tn_instance);
2N/A
2N/A if (flag == TOPO_WALK_CHILD)
2N/A status = step_child(cnp, wp, flag, 1);
2N/A else
2N/A status = step_sibling(cnp, wp, flag, 1);
2N/A
2N/A /*
2N/A * At a leaf, run the callback
2N/A */
2N/A if (status == TOPO_WALK_TERMINATE) {
2N/A if ((status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata))
2N/A != TOPO_WALK_NEXT) {
2N/A topo_node_rele(cnp);
2N/A return (status);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Try next child or sibling
2N/A */
2N/A if (status == TOPO_WALK_NEXT) {
2N/A if (flag == TOPO_WALK_CHILD)
2N/A status = step_sibling(cnp, wp, flag, 1);
2N/A else
2N/A status = step_child(cnp, wp, flag, 1);
2N/A }
2N/A
2N/A topo_node_rele(cnp); /* done with current node */
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Adi_node_t
2N/Atopo_hdl_devinfo(topo_hdl_t *thp)
2N/A{
2N/A return (thp == NULL ? DI_NODE_NIL : thp->th_di);
2N/A}
2N/A
2N/Adi_devlink_handle_t
2N/Atopo_hdl_devlink(topo_hdl_t *thp)
2N/A{
2N/A return (thp == NULL ? NULL : thp->th_dli);
2N/A}
2N/A
2N/Adi_prom_handle_t
2N/Atopo_hdl_prominfo(topo_hdl_t *thp)
2N/A{
2N/A return (thp == NULL ? DI_PROM_HANDLE_NIL : thp->th_pi);
2N/A}
2N/A
2N/Asmbios_hdl_t *
2N/Atopo_hdl_smbios(topo_hdl_t *thp)
2N/A{
2N/A return (thp == NULL ? NULL : thp->th_smbios);
2N/A}
2N/A
2N/A/*
2N/A * Load in a saved topology snapshot.
2N/A */
2N/Achar *
2N/Atopo_snap_load(topo_hdl_t *thp, const char *snap_dir, int *errp)
2N/A{
2N/A char *ustr;
2N/A
2N/A if (thp == NULL) {
2N/A topo_dprintf(thp, TOPO_DBG_SNAP, "%s: NULL handle\n",
2N/A __func__);
2N/A *errp = ETOPO_HDL_INVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A if (snap_dir == NULL) {
2N/A topo_dprintf(thp, TOPO_DBG_SNAP, "%s: NULL directory\n",
2N/A __func__);
2N/A (void) topo_hdl_seterrno(thp, ETOPO_HDL_INVAL);
2N/A *errp = ETOPO_HDL_INVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A topo_dprintf(thp, TOPO_DBG_SNAP, "%s: thp=0x%p, snap_dir=%s\n",
2N/A __func__, (void *)thp, snap_dir);
2N/A
2N/A thp->th_snap_dir = topo_hdl_strdup(thp, snap_dir);
2N/A ustr = topo_snap_create(thp, errp, B_FALSE);
2N/A
2N/A return (ustr);
2N/A}
2N/A
2N/Adi_cromk_hdl_t
2N/Atopo_hdl_cromk_hdl(topo_hdl_t *thp)
2N/A{
2N/A return (thp == NULL ? NULL : thp->th_cromk_hdl);
2N/A}
2N/A
2N/Avoid
2N/Atopo_cleanup()
2N/A{
2N/A /* fmd(1M) is about to exit, call di_cromk_cleanup(). */
2N/A di_cromk_cleanup();
2N/A}