1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <mdb/mdb_param.h> 26 #include <mdb/mdb_modapi.h> 27 #include <mdb/mdb_ks.h> 28 29 #include "zone.h" 30 31 #include <stddef.h> 32 #include <sys/zone.h> 33 34 #define ZONE_NAMELEN 20 35 #ifdef _LP64 36 #define ZONE_PATHLEN 32 37 #else 38 #define ZONE_PATHLEN 40 39 #endif 40 41 /* 42 * Names corresponding to zone_status_t values in sys/zone.h 43 */ 44 char *zone_status_names[] = { 45 "uninitialized", /* ZONE_IS_UNINITIALIZED */ 46 "initialized", /* ZONE_IS_INITIALIZED */ 47 "ready", /* ZONE_IS_READY */ 48 "booting", /* ZONE_IS_BOOTING */ 49 "running", /* ZONE_IS_RUNNING */ 50 "shutting_down", /* ZONE_IS_SHUTTING_DOWN */ 51 "empty", /* ZONE_IS_EMPTY */ 52 "down", /* ZONE_IS_DOWN */ 53 "dying", /* ZONE_IS_DYING */ 54 "dead" /* ZONE_IS_DEAD */ 55 }; 56 57 int 58 zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 59 { 60 zone_t zn; 61 char name[ZONE_NAMELEN]; 62 char path[ZONE_PATHLEN]; 63 int len; 64 uint_t vopt_given; 65 uint_t ropt_given; 66 67 if (argc > 2) 68 return (DCMD_USAGE); 69 70 if (!(flags & DCMD_ADDRSPEC)) { 71 if (mdb_walk_dcmd("zone", "zone", argc, argv) == -1) { 72 mdb_warn("can't walk zones"); 73 return (DCMD_ERR); 74 } 75 return (DCMD_OK); 76 } 77 78 /* 79 * Get the optional -r (reference counts) and -v (verbose output) 80 * arguments. 81 */ 82 vopt_given = FALSE; 83 ropt_given = FALSE; 84 if (argc > 0 && mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, 85 &vopt_given, 'r', MDB_OPT_SETBITS, TRUE, &ropt_given, NULL) != argc) 86 return (DCMD_USAGE); 87 88 /* 89 * -v can only be specified with -r. 90 */ 91 if (vopt_given == TRUE && ropt_given == FALSE) 92 return (DCMD_USAGE); 93 94 /* 95 * Print a table header, if necessary. 96 */ 97 if (DCMD_HDRSPEC(flags)) { 98 if (ropt_given == FALSE) 99 mdb_printf("%<u>%?s %6s %-13s %-20s %-s%</u>\n", 100 "ADDR", "ID", "STATUS", "NAME", "PATH"); 101 else 102 mdb_printf("%<u>%?s %6s %10s %10s %-20s%</u>\n", 103 "ADDR", "ID", "REFS", "CREFS", "NAME"); 104 } 105 106 /* 107 * Read the zone_t structure at the given address and read its name. 108 */ 109 if (mdb_vread(&zn, sizeof (zone_t), addr) == -1) { 110 mdb_warn("can't read zone_t structure at %p", addr); 111 return (DCMD_ERR); 112 } 113 len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zn.zone_name); 114 if (len > 0) { 115 if (len == ZONE_NAMELEN) 116 (void) strcpy(&name[len - 4], "..."); 117 } else { 118 (void) strcpy(name, "??"); 119 } 120 121 if (ropt_given == FALSE) { 122 char *statusp; 123 124 /* 125 * Default display 126 * Fetch the zone's path and print the results. 127 */ 128 len = mdb_readstr(path, ZONE_PATHLEN, 129 (uintptr_t)zn.zone_rootpath); 130 if (len > 0) { 131 if (len == ZONE_PATHLEN) 132 (void) strcpy(&path[len - 4], "..."); 133 } else { 134 (void) strcpy(path, "??"); 135 } 136 if (zn.zone_status >= ZONE_IS_UNINITIALIZED && zn.zone_status <= 137 ZONE_IS_DEAD) 138 statusp = zone_status_names[zn.zone_status]; 139 else 140 statusp = "???"; 141 mdb_printf("%0?p %6d %-13s %-20s %s\n", addr, zn.zone_id, 142 statusp, name, path); 143 } else { 144 /* 145 * Display the zone's reference counts. 146 * Display the zone's subsystem-specific reference counts if 147 * the user specified the '-v' option. 148 */ 149 mdb_printf("%0?p %6d %10u %10u %-20s\n", addr, zn.zone_id, 150 zn.zone_ref, zn.zone_cred_ref, name); 151 if (vopt_given == TRUE) { 152 GElf_Sym subsys_names_sym; 153 uintptr_t **zone_ref_subsys_names; 154 uint_t num_subsys; 155 uint_t n; 156 157 /* 158 * Read zone_ref_subsys_names from the kernel image. 159 */ 160 if (mdb_lookup_by_name("zone_ref_subsys_names", 161 &subsys_names_sym) != 0) { 162 mdb_warn("can't find zone_ref_subsys_names"); 163 return (DCMD_ERR); 164 } 165 if (subsys_names_sym.st_size != ZONE_REF_NUM_SUBSYS * 166 sizeof (char *)) { 167 mdb_warn("number of subsystems in target " 168 "differs from what mdb expects (mismatched" 169 " kernel versions?)"); 170 if (subsys_names_sym.st_size < 171 ZONE_REF_NUM_SUBSYS * sizeof (char *)) 172 num_subsys = subsys_names_sym.st_size / 173 sizeof (char *); 174 else 175 num_subsys = ZONE_REF_NUM_SUBSYS; 176 } else { 177 num_subsys = ZONE_REF_NUM_SUBSYS; 178 } 179 if ((zone_ref_subsys_names = mdb_alloc( 180 subsys_names_sym.st_size, UM_GC)) == NULL) { 181 mdb_warn("out of memory"); 182 return (DCMD_ERR); 183 } 184 if (mdb_readvar(zone_ref_subsys_names, 185 "zone_ref_subsys_names") == -1) { 186 mdb_warn("can't find zone_ref_subsys_names"); 187 return (DCMD_ERR); 188 } 189 190 /* 191 * Display each subsystem's reference count if it's 192 * nonzero. 193 */ 194 mdb_inc_indent(7); 195 for (n = 0; n < num_subsys; ++n) { 196 char subsys_name[16]; 197 198 /* 199 * Skip subsystems lacking outstanding 200 * references. 201 */ 202 if (zn.zone_subsys_ref[n] == 0) 203 continue; 204 205 /* 206 * Each subsystem's name must be read from 207 * the target's image. 208 */ 209 if (mdb_readstr(subsys_name, 210 sizeof (subsys_name), 211 (uintptr_t)zone_ref_subsys_names[n]) == 212 -1) { 213 mdb_warn("unable to read subsystem name" 214 " from zone_ref_subsys_names[%u]", 215 n); 216 return (DCMD_ERR); 217 } 218 mdb_printf("%15s: %10u\n", subsys_name, 219 zn.zone_subsys_ref[n]); 220 } 221 mdb_dec_indent(7); 222 } 223 } 224 return (DCMD_OK); 225 } 226 227 int 228 zone_walk_init(mdb_walk_state_t *wsp) 229 { 230 GElf_Sym sym; 231 232 if (wsp->walk_addr == NULL) { 233 if (mdb_lookup_by_name("zone_active", &sym) == -1) { 234 mdb_warn("failed to find 'zone_active'"); 235 return (WALK_ERR); 236 } 237 wsp->walk_addr = (uintptr_t)sym.st_value; 238 } 239 if (mdb_layered_walk("list", wsp) == -1) { 240 mdb_warn("couldn't walk 'list'"); 241 return (WALK_ERR); 242 } 243 return (WALK_NEXT); 244 } 245 246 int 247 zone_walk_step(mdb_walk_state_t *wsp) 248 { 249 return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer, 250 wsp->walk_cbdata)); 251 } 252 253 int 254 zsd_walk_init(mdb_walk_state_t *wsp) 255 { 256 if (wsp->walk_addr == NULL) { 257 mdb_warn("global walk not supported\n"); 258 return (WALK_ERR); 259 } 260 wsp->walk_addr += offsetof(struct zone, zone_zsd); 261 if (mdb_layered_walk("list", wsp) == -1) { 262 mdb_warn("couldn't walk 'list'"); 263 return (WALK_ERR); 264 } 265 return (WALK_NEXT); 266 } 267 268 int 269 zsd_walk_step(mdb_walk_state_t *wsp) 270 { 271 return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer, 272 wsp->walk_cbdata)); 273 } 274 275 /* 276 * Helper structure used when walking ZSD entries via zsd(). 277 */ 278 struct zsd_cb_data { 279 uint_t keygiven; /* Was a key specified (are we */ 280 /* searching for a specific ZSD */ 281 /* entry)? */ 282 zone_key_t key; /* Key of ZSD for which we're looking */ 283 uint_t found; /* Was the specific ZSD entry found? */ 284 uint_t voptgiven; /* Display verbose information? */ 285 }; 286 287 /* 288 * Helper function for zsd() that displays information from a single ZSD struct. 289 * 'datap' must point to a valid zsd_cb_data struct. 290 */ 291 /* ARGSUSED */ 292 static int 293 zsd_print(uintptr_t addrp, const void * datap, void * privatep) 294 { 295 struct zsd_entry entry; 296 struct zsd_cb_data *cbdp; 297 298 if (mdb_vread(&entry, sizeof (entry), addrp) == -1) { 299 mdb_warn("couldn't read zsd_entry at %p", addrp); 300 return (WALK_ERR); 301 } 302 cbdp = (struct zsd_cb_data *)privatep; 303 304 /* 305 * Are we looking for a single entry specified by a key? Then make sure 306 * that the current ZSD's key is what we're looking for. 307 */ 308 if (cbdp->keygiven == TRUE && cbdp->key != entry.zsd_key) 309 return (WALK_NEXT); 310 311 mdb_printf("%?x %0?p %8x\n", entry.zsd_key, entry.zsd_data, 312 entry.zsd_flags); 313 if (cbdp->voptgiven == TRUE) 314 mdb_printf(" Create CB: %a\n Shutdown CB: %a\n" 315 " Destroy CB: %a\n", entry.zsd_create, 316 entry.zsd_shutdown, entry.zsd_destroy); 317 if (cbdp->keygiven == TRUE) { 318 cbdp->found = TRUE; 319 return (WALK_DONE); 320 } 321 return (WALK_NEXT); 322 } 323 324 int 325 zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 326 { 327 zone_t zone; 328 const mdb_arg_t *argp; 329 int argcindex; 330 struct zsd_cb_data cbd; 331 char name[ZONE_NAMELEN]; 332 int len; 333 334 /* 335 * Walk all zones if necessary. 336 */ 337 if (argc > 2) 338 return (DCMD_USAGE); 339 if ((flags & DCMD_ADDRSPEC) == 0) { 340 if (mdb_walk_dcmd("zone", "zsd", argc, argv) == -1) { 341 mdb_warn("failed to walk zone\n"); 342 return (DCMD_ERR); 343 } 344 return (DCMD_OK); 345 } 346 347 /* 348 * Make sure a zone_t can be read from the specified address. 349 */ 350 if (mdb_vread(&zone, sizeof (zone), addr) == -1) { 351 mdb_warn("couldn't read zone_t at %p", (void *)addr); 352 return (DCMD_ERR); 353 } 354 355 /* 356 * Get the optional arguments (key or -v or both). Note that 357 * mdb_getopts() will not parse a key argument because it is not 358 * preceded by an option letter. We'll get around this by requiring 359 * that all options precede the optional key argument. 360 */ 361 cbd.keygiven = FALSE; 362 cbd.voptgiven = FALSE; 363 if (argc > 0 && (argcindex = mdb_getopts(argc, argv, 'v', 364 MDB_OPT_SETBITS, TRUE, &cbd.voptgiven, NULL)) != argc) { 365 /* 366 * No options may appear after the key. 367 */ 368 if (argcindex != argc - 1) 369 return (DCMD_USAGE); 370 371 /* 372 * The missed argument should be a key. 373 */ 374 argp = &argv[argcindex]; 375 if (argp->a_type == MDB_TYPE_IMMEDIATE) 376 cbd.key = argp->a_un.a_val; 377 else 378 cbd.key = mdb_strtoull(argp->a_un.a_str); 379 cbd.keygiven = TRUE; 380 cbd.found = FALSE; 381 } 382 383 /* 384 * Prepare to output the specified zone's ZSD information. 385 */ 386 if (DCMD_HDRSPEC(flags)) 387 mdb_printf("%<u>%-20s %?s %?s %8s%</u>\n", "ZONE", "KEY", 388 "VALUE", "FLAGS"); 389 len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zone.zone_name); 390 if (len > 0) { 391 if (len == ZONE_NAMELEN) 392 (void) strcpy(&name[len - 4], "..."); 393 } else { 394 (void) strcpy(name, "??"); 395 } 396 mdb_printf("%-20s ", name); 397 398 /* 399 * Display the requested ZSD entries. 400 */ 401 mdb_inc_indent(21); 402 if (mdb_pwalk("zsd", zsd_print, &cbd, addr) != 0) { 403 mdb_warn("failed to walk zsd\n"); 404 mdb_dec_indent(21); 405 return (DCMD_ERR); 406 } 407 if (cbd.keygiven == TRUE && cbd.found == FALSE) { 408 mdb_printf("no corresponding ZSD entry found\n"); 409 mdb_dec_indent(21); 410 return (DCMD_ERR); 411 } 412 mdb_dec_indent(21); 413 return (DCMD_OK); 414 } 415