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