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
zoneprt(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
zone_walk_init(mdb_walk_state_t * wsp)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
zone_walk_step(mdb_walk_state_t * wsp)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
zsd_walk_init(mdb_walk_state_t * wsp)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
zsd_walk_step(mdb_walk_state_t * wsp)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
zsd_print(uintptr_t addrp,const void * datap,void * privatep)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
zsd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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