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
zid_lookup_cb(uintptr_t addr,const zone_t * zone,void * arg)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
zid2zone(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
zoneprt(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
zone_walk_init(mdb_walk_state_t * wsp)254 zone_walk_init(mdb_walk_state_t *wsp)
255 {
256 GElf_Sym sym;
257
258 if (wsp->walk_addr == NULL) {
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
zone_walk_step(mdb_walk_state_t * wsp)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
zsd_walk_init(mdb_walk_state_t * wsp)280 zsd_walk_init(mdb_walk_state_t *wsp)
281 {
282 if (wsp->walk_addr == NULL) {
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
zsd_walk_step(mdb_walk_state_t * wsp)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
zsd_print(uintptr_t addrp,const void * datap,void * privatep)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
zsd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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