/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #include #include #include #include "zone.h" #include #include #define ZONE_NAMELEN 20 #ifdef _LP64 #define ZONE_PATHLEN 32 #else #define ZONE_PATHLEN 40 #endif /* * Names corresponding to zone_status_t values in sys/zone.h */ char *zone_status_names[] = { "uninitialized", /* ZONE_IS_UNINITIALIZED */ "initialized", /* ZONE_IS_INITIALIZED */ "ready", /* ZONE_IS_READY */ "booting", /* ZONE_IS_BOOTING */ "running", /* ZONE_IS_RUNNING */ "shutting_down", /* ZONE_IS_SHUTTING_DOWN */ "empty", /* ZONE_IS_EMPTY */ "down", /* ZONE_IS_DOWN */ "dying", /* ZONE_IS_DYING */ "dead" /* ZONE_IS_DEAD */ }; static int zid_lookup_cb(uintptr_t addr, const zone_t *zone, void *arg) { zoneid_t zid = *(uintptr_t *)arg; if (zone->zone_id == zid) mdb_printf("%p\n", addr); return (WALK_NEXT); } /*ARGSUSED*/ int zid2zone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { if (!(flags & DCMD_ADDRSPEC) || argc != 0) return (DCMD_USAGE); if (mdb_walk("zone", (mdb_walk_cb_t)zid_lookup_cb, &addr) == -1) { mdb_warn("failed to walk zone"); return (DCMD_ERR); } return (DCMD_OK); } int zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { zone_t zn; char name[ZONE_NAMELEN]; char path[ZONE_PATHLEN]; int len; uint_t vopt_given; uint_t ropt_given; if (argc > 2) return (DCMD_USAGE); if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("zone", "zone", argc, argv) == -1) { mdb_warn("can't walk zones"); return (DCMD_ERR); } return (DCMD_OK); } /* * Get the optional -r (reference counts) and -v (verbose output) * arguments. */ vopt_given = FALSE; ropt_given = FALSE; if (argc > 0 && mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &vopt_given, 'r', MDB_OPT_SETBITS, TRUE, &ropt_given, NULL) != argc) return (DCMD_USAGE); /* * -v can only be specified with -r. */ if (vopt_given == TRUE && ropt_given == FALSE) return (DCMD_USAGE); /* * Print a table header, if necessary. */ if (DCMD_HDRSPEC(flags)) { if (ropt_given == FALSE) mdb_printf("%%?s %6s %-13s %-20s %-s%\n", "ADDR", "ID", "STATUS", "NAME", "PATH"); else mdb_printf("%%?s %6s %10s %10s %-20s%\n", "ADDR", "ID", "REFS", "CREFS", "NAME"); } /* * Read the zone_t structure at the given address and read its name. */ if (mdb_vread(&zn, sizeof (zone_t), addr) == -1) { mdb_warn("can't read zone_t structure at %p", addr); return (DCMD_ERR); } len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zn.zone_name); if (len > 0) { if (len == ZONE_NAMELEN) (void) strcpy(&name[len - 4], "..."); } else { (void) strcpy(name, "??"); } if (ropt_given == FALSE) { char *statusp; /* * Default display * Fetch the zone's path and print the results. */ len = mdb_readstr(path, ZONE_PATHLEN, (uintptr_t)zn.zone_rootpath); if (len > 0) { if (len == ZONE_PATHLEN) (void) strcpy(&path[len - 4], "..."); } else { (void) strcpy(path, "??"); } if (zn.zone_status >= ZONE_IS_UNINITIALIZED && zn.zone_status <= ZONE_IS_DEAD) statusp = zone_status_names[zn.zone_status]; else statusp = "???"; mdb_printf("%0?p %6d %-13s %-20s %s\n", addr, zn.zone_id, statusp, name, path); } else { /* * Display the zone's reference counts. * Display the zone's subsystem-specific reference counts if * the user specified the '-v' option. */ mdb_printf("%0?p %6d %10u %10u %-20s\n", addr, zn.zone_id, zn.zone_ref, zn.zone_cred_ref, name); if (vopt_given == TRUE) { GElf_Sym subsys_names_sym; uintptr_t **zone_ref_subsys_names; uint_t num_subsys; uint_t n; /* * Read zone_ref_subsys_names from the kernel image. */ if (mdb_lookup_by_name("zone_ref_subsys_names", &subsys_names_sym) != 0) { mdb_warn("can't find zone_ref_subsys_names"); return (DCMD_ERR); } if (subsys_names_sym.st_size != ZONE_REF_NUM_SUBSYS * sizeof (char *)) { mdb_warn("number of subsystems in target " "differs from what mdb expects (mismatched" " kernel versions?)"); if (subsys_names_sym.st_size < ZONE_REF_NUM_SUBSYS * sizeof (char *)) num_subsys = subsys_names_sym.st_size / sizeof (char *); else num_subsys = ZONE_REF_NUM_SUBSYS; } else { num_subsys = ZONE_REF_NUM_SUBSYS; } if ((zone_ref_subsys_names = mdb_alloc( subsys_names_sym.st_size, UM_GC)) == NULL) { mdb_warn("out of memory"); return (DCMD_ERR); } if (mdb_readvar(zone_ref_subsys_names, "zone_ref_subsys_names") == -1) { mdb_warn("can't find zone_ref_subsys_names"); return (DCMD_ERR); } /* * Display each subsystem's reference count if it's * nonzero. */ mdb_inc_indent(7); for (n = 0; n < num_subsys; ++n) { char subsys_name[16]; /* * Skip subsystems lacking outstanding * references. */ if (zn.zone_subsys_ref[n] == 0) continue; /* * Each subsystem's name must be read from * the target's image. */ if (mdb_readstr(subsys_name, sizeof (subsys_name), (uintptr_t)zone_ref_subsys_names[n]) == -1) { mdb_warn("unable to read subsystem name" " from zone_ref_subsys_names[%u]", n); return (DCMD_ERR); } mdb_printf("%15s: %10u\n", subsys_name, zn.zone_subsys_ref[n]); } mdb_dec_indent(7); } } return (DCMD_OK); } int zone_walk_init(mdb_walk_state_t *wsp) { GElf_Sym sym; if (wsp->walk_addr == 0) { if (mdb_lookup_by_name("zone_active", &sym) == -1) { mdb_warn("failed to find 'zone_active'"); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)sym.st_value; } if (mdb_layered_walk("list", wsp) == -1) { mdb_warn("couldn't walk 'list'"); return (WALK_ERR); } return (WALK_NEXT); } int zone_walk_step(mdb_walk_state_t *wsp) { return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer, wsp->walk_cbdata)); } int zsd_walk_init(mdb_walk_state_t *wsp) { if (wsp->walk_addr == 0) { mdb_warn("global walk not supported\n"); return (WALK_ERR); } wsp->walk_addr += offsetof(struct zone, zone_zsd); if (mdb_layered_walk("list", wsp) == -1) { mdb_warn("couldn't walk 'list'"); return (WALK_ERR); } return (WALK_NEXT); } int zsd_walk_step(mdb_walk_state_t *wsp) { return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer, wsp->walk_cbdata)); } /* * Helper structure used when walking ZSD entries via zsd(). */ struct zsd_cb_data { uint_t keygiven; /* Was a key specified (are we */ /* searching for a specific ZSD */ /* entry)? */ zone_key_t key; /* Key of ZSD for which we're looking */ uint_t found; /* Was the specific ZSD entry found? */ uint_t voptgiven; /* Display verbose information? */ }; /* * Helper function for zsd() that displays information from a single ZSD struct. * 'datap' must point to a valid zsd_cb_data struct. */ /* ARGSUSED */ static int zsd_print(uintptr_t addrp, const void * datap, void * privatep) { struct zsd_entry entry; struct zsd_cb_data *cbdp; if (mdb_vread(&entry, sizeof (entry), addrp) == -1) { mdb_warn("couldn't read zsd_entry at %p", addrp); return (WALK_ERR); } cbdp = (struct zsd_cb_data *)privatep; /* * Are we looking for a single entry specified by a key? Then make sure * that the current ZSD's key is what we're looking for. */ if (cbdp->keygiven == TRUE && cbdp->key != entry.zsd_key) return (WALK_NEXT); mdb_printf("%?x %0?p %8x\n", entry.zsd_key, entry.zsd_data, entry.zsd_flags); if (cbdp->voptgiven == TRUE) mdb_printf(" Create CB: %a\n Shutdown CB: %a\n" " Destroy CB: %a\n", entry.zsd_create, entry.zsd_shutdown, entry.zsd_destroy); if (cbdp->keygiven == TRUE) { cbdp->found = TRUE; return (WALK_DONE); } return (WALK_NEXT); } int zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { zone_t zone; const mdb_arg_t *argp; int argcindex; struct zsd_cb_data cbd; char name[ZONE_NAMELEN]; int len; /* * Walk all zones if necessary. */ if (argc > 2) return (DCMD_USAGE); if ((flags & DCMD_ADDRSPEC) == 0) { if (mdb_walk_dcmd("zone", "zsd", argc, argv) == -1) { mdb_warn("failed to walk zone\n"); return (DCMD_ERR); } return (DCMD_OK); } /* * Make sure a zone_t can be read from the specified address. */ if (mdb_vread(&zone, sizeof (zone), addr) == -1) { mdb_warn("couldn't read zone_t at %p", (void *)addr); return (DCMD_ERR); } /* * Get the optional arguments (key or -v or both). Note that * mdb_getopts() will not parse a key argument because it is not * preceded by an option letter. We'll get around this by requiring * that all options precede the optional key argument. */ cbd.keygiven = FALSE; cbd.voptgiven = FALSE; if (argc > 0 && (argcindex = mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &cbd.voptgiven, NULL)) != argc) { /* * No options may appear after the key. */ if (argcindex != argc - 1) return (DCMD_USAGE); /* * The missed argument should be a key. */ argp = &argv[argcindex]; if (argp->a_type == MDB_TYPE_IMMEDIATE) cbd.key = argp->a_un.a_val; else cbd.key = mdb_strtoull(argp->a_un.a_str); cbd.keygiven = TRUE; cbd.found = FALSE; } /* * Prepare to output the specified zone's ZSD information. */ if (DCMD_HDRSPEC(flags)) mdb_printf("%%-20s %?s %?s %8s%\n", "ZONE", "KEY", "VALUE", "FLAGS"); len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zone.zone_name); if (len > 0) { if (len == ZONE_NAMELEN) (void) strcpy(&name[len - 4], "..."); } else { (void) strcpy(name, "??"); } mdb_printf("%-20s ", name); /* * Display the requested ZSD entries. */ mdb_inc_indent(21); if (mdb_pwalk("zsd", zsd_print, &cbd, addr) != 0) { mdb_warn("failed to walk zsd\n"); mdb_dec_indent(21); return (DCMD_ERR); } if (cbd.keygiven == TRUE && cbd.found == FALSE) { mdb_printf("no corresponding ZSD entry found\n"); mdb_dec_indent(21); return (DCMD_ERR); } mdb_dec_indent(21); return (DCMD_OK); }