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