xref: /titanic_50/usr/src/cmd/mdb/common/modules/genunix/mmd.c (revision b3700b074e637f8c6991b70754c88a2cfffb246b)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Multidata dcmds and walkers, part of the genunix mdb module,
31  * and operate on core Multidata structures.
32  */
33 
34 #include <mdb/mdb_modapi.h>
35 #include <mdb/mdb_ks.h>
36 
37 #include <sys/types.h>
38 #include <sys/strsubr.h>
39 #include <sys/strsun.h>
40 #include <sys/stream.h>
41 #include <sys/modctl.h>
42 #include <sys/strft.h>
43 #include <sys/sysmacros.h>
44 
45 #include <sys/multidata.h>
46 #include <sys/multidata_impl.h>
47 #include <sys/pattr.h>
48 
49 #include "mmd.h"
50 
51 /*
52  * Structure for passing internal variables.
53  */
54 typedef struct mmd_data_s {
55 	uint_t	flags;		/* see flags values below */
56 	uint_t	counter;	/* scratch counter */
57 } mmd_data_t;
58 
59 #define	MMD_VERBOSE	0x1	/* multidata: provide more info */
60 #define	MMD_STATS	0x2	/* multidata: provide statistics */
61 #define	PD_HDR		0x4	/* pdesc: count header region */
62 #define	PD_PLD		0x8	/* pdesc: count payload region(s) */
63 #define	PD_ATTR		0x10	/* pdesc: count local attributes */
64 #define	PD_REM_NOCNT	0x20	/* pdesc: do not count removed pdesc */
65 
66 /*
67  * Structure to support circular, doubly-linked list (ql_t) walker.
68  */
69 typedef struct q_walk_s {
70 	char *qw_name;		/* name of opaque list structure */
71 	uintptr_t qw_head;	/* address of list head */
72 	void *qw_data;		/* opaque data structure */
73 	uint_t qw_sz;		/* size of opaque data structure */
74 	uint_t qw_off;		/* ql_t offset in opaque data structure */
75 	uint_t qw_step;		/* walk_step has been called */
76 	uint_t qw_iprint;	/* initial print */
77 } q_walk_t;
78 
79 static int pdesc_slab_print(uintptr_t, q_walk_t *, mmd_data_t *);
80 static int pdesc_print(uintptr_t, q_walk_t *, mmd_data_t *);
81 static int pdesc_count(uintptr_t, q_walk_t *, mmd_data_t *);
82 static int pattr_print(uintptr_t, q_walk_t *, mmd_data_t *);
83 static int pattr_count(uintptr_t, q_walk_t *, mmd_data_t *);
84 static int multidata_stats(uintptr_t addr, multidata_t *);
85 
86 #define	VA_OFF(x, o)	(((uchar_t *)(x) + (o)))
87 
88 /*
89  * A dcmd which prints a summary of a multidata_t structure.
90  */
91 /* ARGSUSED */
92 int
93 multidata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
94 {
95 	mmd_data_t data;
96 	multidata_t mmd;
97 	char str[32] = "-";
98 	int i = 0;
99 
100 	bzero(&data, sizeof (data));
101 	if (!(flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
102 	    'v', MDB_OPT_SETBITS, MMD_VERBOSE, &data.flags,
103 	    's', MDB_OPT_SETBITS, MMD_STATS, &data.flags, NULL) != argc)
104 		return (DCMD_USAGE);
105 
106 	if (mdb_vread(&mmd, sizeof (mmd), addr) == -1) {
107 		mdb_warn("failed to read multidata_t structure at %p", addr);
108 		return (DCMD_ERR);
109 	}
110 
111 	if (mmd.mmd_magic != MULTIDATA_MAGIC)
112 		mdb_printf("Incorrect Multidata magic number at %p\n",
113 		    VA_OFF(addr, offsetof(multidata_t, mmd_magic)));
114 
115 	mdb_printf("\n");
116 	if (data.flags & MMD_STATS) {
117 		if ((i = multidata_stats(addr, &mmd)) != DCMD_OK)
118 			return (i);
119 	}
120 
121 	mdb_printf("%<b>%-5s %-?s %-4s %-?s %-4s %-4s %-4s %-?s%</b>",
122 	    "PDESC", "PATTBL", "HBUF", "HBUF", "PBUF", "PBUF", "PBUF", "PBUF");
123 	mdb_printf("\n");
124 	mdb_printf("%<b>%<u>%-5s %-?s %-4s %-?s %-4s %-4s %-4s %-?s%</u>%</b>",
125 	    "CNT", "ADDRESS", "REF", "ADDRESS", "REF", "CNT", "IDX",
126 	    "ADDRESS(ES)");
127 	mdb_printf("\n");
128 
129 	if (mmd.mmd_pattbl != 0)
130 		mdb_snprintf(str, sizeof (str), "%016p", mmd.mmd_pattbl);
131 
132 	i = 0;
133 	mdb_printf("%-5d %-16s %-4d %016p %-4d %-4d %-4d %016p\n",
134 	    mmd.mmd_pd_cnt, str, mmd.mmd_hbuf_ref, mmd.mmd_hbuf,
135 	    mmd.mmd_pbuf_ref, mmd.mmd_pbuf_cnt, i, mmd.mmd_pbuf[i]);
136 
137 	for (++i; i < mmd.mmd_pbuf_cnt; i++)
138 		mdb_printf("%-54s %-4d %016p\n", "", i, mmd.mmd_pbuf[i]);
139 
140 	if (!(data.flags & MMD_VERBOSE))
141 		return (DCMD_OK);
142 
143 	/* Walk packet descriptor slab list */
144 	if (mdb_pwalk("pdesc_slab", (mdb_walk_cb_t)pdesc_slab_print,
145 	    &data, (uintptr_t)VA_OFF(addr, offsetof(multidata_t,
146 	    mmd_pd_slab_q))) == -1) {
147 		mdb_warn("couldn't walk pdesc_slab_t list");
148 		return (DCMD_ERR);
149 	}
150 
151 	/* Walk packet descriptor list */
152 	data.counter = 0;
153 	if (mdb_pwalk("pdesc", (mdb_walk_cb_t)pdesc_print,
154 	    &data, (uintptr_t)VA_OFF(addr, offsetof(multidata_t,
155 	    mmd_pd_q))) == -1) {
156 		mdb_warn("couldn't walk pdesc_t list");
157 		return (DCMD_ERR);
158 	}
159 
160 	return (DCMD_OK);
161 }
162 
163 /*
164  * Print additional Multidata statistics
165  */
166 static int
167 multidata_stats(uintptr_t addr, multidata_t *mmd)
168 {
169 	mblk_t mp;
170 	uint_t i = 0, j = 0, k = 0, sz = 0;
171 	mmd_data_t data;
172 	uintptr_t patbkt;
173 
174 	bzero(&data, sizeof (data));
175 
176 	if (mmd->mmd_hbuf != 0) {
177 		if (mdb_vread(&mp, sizeof (mp),
178 		    (uintptr_t)mmd->mmd_hbuf) == -1) {
179 			mdb_warn("couldn't read mblk_t at %p", mmd->mmd_hbuf);
180 			return (DCMD_ERR);
181 		}
182 
183 		i++;
184 		sz = MBLKL(&mp);
185 	}
186 
187 	k += sz;	/* total bytes */
188 	j += i;		/* total buffers */
189 
190 	mdb_printf("%<b>%<u>BUFFER STATS%</b>%</u>\n");
191 	mdb_printf("Header:\t\t\t%-4d% buffer,\t%-12d bytes\n", i, sz);
192 
193 	for (i = 0, sz = 0; i < mmd->mmd_pbuf_cnt; i++) {
194 		if (mdb_vread(&mp, sizeof (mp),
195 		    (uintptr_t)mmd->mmd_pbuf[i]) == -1) {
196 			mdb_warn("couldn't read mblk_t at %p",
197 			    mmd->mmd_pbuf[i]);
198 			return (DCMD_ERR);
199 		}
200 		sz += MBLKL(&mp);
201 	}
202 
203 	k += sz;	/* total bytes */
204 	j += i;		/* total buffers */
205 
206 	mdb_printf("%<u>Payload:\t\t%-4d buffers,\t%-12d bytes%</u>\n", i, sz);
207 	mdb_printf("Total:\t\t\t%-4d buffers,\t%-12d bytes\n\n", j, k);
208 
209 	mdb_printf("%<b>%<u>PACKET DESCRIPTOR STATS%</u>%</b>\n");
210 
211 	/*
212 	 * Total claimed packet descriptors
213 	 */
214 	data.flags = 0;
215 	data.counter = 0;
216 	if (mdb_pwalk("pdesc", (mdb_walk_cb_t)pdesc_count, &data,
217 	    (uintptr_t)VA_OFF(addr, offsetof(multidata_t, mmd_pd_q))) == -1) {
218 		mdb_warn("couldn't walk pdesc_t list");
219 		return (DCMD_ERR);
220 	}
221 	i = data.counter;	/* claimed */
222 	mdb_printf("Total claimed:\t\t%-4d", i);
223 
224 	/*
225 	 * Total active header references
226 	 */
227 	data.flags = (PD_HDR | PD_REM_NOCNT);
228 	data.counter = 0;
229 	if (mdb_pwalk("pdesc", (mdb_walk_cb_t)pdesc_count, &data,
230 	    (uintptr_t)VA_OFF(addr, offsetof(multidata_t, mmd_pd_q))) == -1) {
231 		mdb_warn("couldn't walk pdesc_t list");
232 		return (DCMD_ERR);
233 	}
234 	mdb_printf("\tActive header refs:\t%-12d bytes\n", data.counter);
235 
236 	/*
237 	 * Total active packet descriptors
238 	 */
239 	data.flags = PD_REM_NOCNT;
240 	data.counter = 0;
241 	if (mdb_pwalk("pdesc", (mdb_walk_cb_t)pdesc_count, &data,
242 	    (uintptr_t)VA_OFF(addr, offsetof(multidata_t, mmd_pd_q))) == -1) {
243 		mdb_warn("couldn't walk pdesc_t list");
244 		return (DCMD_ERR);
245 	}
246 	k = data.counter;	/* active */
247 	mdb_printf("Active:\t\t\t%-4d", data.counter);
248 
249 	/*
250 	 * Total active payload references
251 	 */
252 	data.flags = (PD_PLD | PD_REM_NOCNT);
253 	data.counter = 0;
254 	if (mdb_pwalk("pdesc", (mdb_walk_cb_t)pdesc_count, &data,
255 	    (uintptr_t)VA_OFF(addr, offsetof(multidata_t, mmd_pd_q))) == -1) {
256 		mdb_warn("couldn't walk pdesc_t list");
257 		return (DCMD_ERR);
258 	}
259 	mdb_printf("\t%<u>Active payload refs:\t%-12d bytes%</u>\n",
260 	    data.counter);
261 
262 	/*
263 	 * Number of removed packet descriptors (claimed - active)
264 	 */
265 	mdb_printf("Removed:\t\t%-4d", i - k);
266 
267 	/*
268 	 * Total active header and payload references
269 	 */
270 	data.flags = (PD_PLD | PD_HDR | PD_REM_NOCNT);
271 	data.counter = 0;
272 	if (mdb_pwalk("pdesc", (mdb_walk_cb_t)pdesc_count, &data,
273 	    (uintptr_t)VA_OFF(addr, offsetof(multidata_t, mmd_pd_q))) == -1) {
274 		mdb_warn("couldn't walk pdesc_t list");
275 		return (DCMD_ERR);
276 	}
277 	mdb_printf("\tTotal:\t\t\t%-12d bytes\n\n", data.counter);
278 
279 	mdb_printf("%<b>%<u>ACTIVE ATTRIBUTE STATS%</u>%</b>\n");
280 
281 	/*
282 	 * Count local attributes
283 	 */
284 	data.flags = (PD_ATTR | PD_REM_NOCNT);
285 	data.counter = 0;
286 	if (mdb_pwalk("pdesc", (mdb_walk_cb_t)pdesc_count, &data,
287 	    (uintptr_t)VA_OFF(addr, offsetof(multidata_t, mmd_pd_q))) == -1) {
288 		mdb_warn("couldn't walk pdesc_t list");
289 		return (DCMD_ERR);
290 	}
291 	mdb_printf("Local:\t\t\t%-4d", data.counter);
292 
293 	/*
294 	 * Count global attributes
295 	 */
296 	data.counter = 0;
297 	patbkt = (uintptr_t)mmd->mmd_pattbl;
298 	if (patbkt != 0) {
299 		uint_t pattbl_sz;
300 
301 		/* Figure out the size of hash table */
302 		mdb_readvar(&pattbl_sz, "pattbl_sz");
303 
304 		/* Walk each bucket and count its contents */
305 		for (i = 0; i < (pattbl_sz * sizeof (patbkt_t));
306 		    i += sizeof (patbkt_t)) {
307 			if (mdb_pwalk("pattr",
308 			    (mdb_walk_cb_t)pattr_count, &data,
309 			    patbkt + i + offsetof(patbkt_t,
310 			    pbkt_pattr_q)) == -1) {
311 				mdb_warn("couldn't walk pattr_t list");
312 				return (DCMD_ERR);
313 			}
314 		}
315 	}
316 	mdb_printf("\tGlobal:\t\t\t%-4d\n", data.counter);
317 	mdb_printf("\n");
318 
319 	return (DCMD_OK);
320 }
321 
322 /*
323  * Print the contents of a packet descriptor slab (pdesc_slab_t) structure.
324  */
325 /* ARGSUSED */
326 static int
327 pdesc_slab_print(uintptr_t addr, q_walk_t *qwp, mmd_data_t *data)
328 {
329 	pdesc_slab_t *slab;
330 	uint_t pdslab_sz, slab_sz;
331 
332 	/* Figure out how many descriptors in a slab */
333 	mdb_readvar(&pdslab_sz, "pdslab_sz");
334 
335 	/* This shouldn't be true, unless something awful has happened */
336 	if (pdslab_sz < 1) {
337 		mdb_warn("incorrect pdslab_sz (0)");
338 		pdslab_sz = 1;
339 	}
340 
341 	/* Read in the entire slab chunk; may be of use one day */
342 	slab_sz = PDESC_SLAB_SIZE(pdslab_sz);
343 	slab = mdb_alloc(slab_sz, UM_SLEEP);
344 
345 	if (mdb_vread(slab, slab_sz, addr) == -1) {
346 		mdb_free(slab, slab_sz);
347 		mdb_warn("failed to read pdesc_slab_t at %p", addr);
348 		return (WALK_ERR);
349 	}
350 
351 	if (!qwp->qw_step)
352 		mdb_printf("\n%<b>%<u>%-?s %7s %7s%</u>%</b>\n",
353 		    "PDESC SLAB ADDR", "SIZE", "CLAIMED");
354 
355 	mdb_printf("%016p %7d %7d\n", addr, slab->pds_sz, slab->pds_used);
356 
357 	mdb_free(slab, slab_sz);
358 
359 	return (WALK_NEXT);
360 }
361 
362 /*
363  * Generic packet descriptor (pdesc_t) counting routine.
364  */
365 /* ARGSUSED */
366 static int
367 pdesc_count(uintptr_t addr, q_walk_t *qwp, mmd_data_t *data)
368 {
369 	pdesc_t pd;
370 	int i;
371 	uint_t f = data->flags;
372 
373 	if (mdb_vread(&pd, sizeof (pd), addr) == -1) {
374 		mdb_warn("failed to read pdesc_t at %p", addr);
375 		return (WALK_ERR);
376 	}
377 
378 	if (pd.pd_magic != PDESC_MAGIC)
379 		mdb_printf("Incorrect pdesc magic number at %p\n",
380 		    VA_OFF(addr, offsetof(pdesc_t, pd_magic)));
381 
382 	if (f == 0) {
383 		/* No flags set, count all pdescs */
384 		data->counter++;
385 	} else if (f == PD_REM_NOCNT && !(pd.pd_pdi.flags & PDESC_REM_DEFER)) {
386 		/* Count only active (skip removed) pdescs */
387 		data->counter++;
388 	} else if (f & PD_ATTR) {
389 		uint_t pattbl_sz;
390 		uintptr_t patbkt = (uintptr_t)pd.pd_pattbl;
391 		mmd_data_t attr_data;
392 
393 		/* Count local attributes */
394 		if ((!(f & PD_REM_NOCNT) || ((f & PD_REM_NOCNT) &&
395 		    !(pd.pd_pdi.flags & PDESC_REM_DEFER))) && patbkt != 0) {
396 
397 			/* Figure out the size of hash table */
398 			mdb_readvar(&pattbl_sz, "pattbl_sz");
399 
400 			attr_data.counter = 0;
401 			/* Walk each bucket and count its contents */
402 			for (i = 0; i < (pattbl_sz * sizeof (patbkt_t));
403 			    i += sizeof (patbkt_t)) {
404 				if (mdb_pwalk("pattr",
405 				    (mdb_walk_cb_t)pattr_count, &attr_data,
406 				    patbkt + i + offsetof(patbkt_t,
407 				    pbkt_pattr_q)) == -1) {
408 					mdb_warn("couldn't walk pattr_t list");
409 					return (WALK_ERR);
410 				}
411 			}
412 			data->counter += attr_data.counter;
413 		}
414 	} else {
415 		if (f & PD_HDR) {
416 			/* Count header span referenced by pdesc */
417 			if (!(f & PD_REM_NOCNT) || ((f & PD_REM_NOCNT) &&
418 			    !(pd.pd_pdi.flags & PDESC_REM_DEFER)))
419 				data->counter += PDESC_HDRL(&pd.pd_pdi);
420 		}
421 
422 		if (f & PD_PLD) {
423 			/* Count payload span referenced by pdesc */
424 			if (!(f & PD_REM_NOCNT) || ((f & PD_REM_NOCNT) &&
425 			    !(pd.pd_pdi.flags & PDESC_REM_DEFER))) {
426 				for (i = 0; i < pd.pd_pdi.pld_cnt; i++)
427 					data->counter += PDESC_PLD_SPAN_SIZE(
428 					    &pd.pd_pdi, i);
429 			}
430 		}
431 	}
432 
433 	return (WALK_NEXT);
434 }
435 
436 /*
437  * Print the contents of a packet descriptor (pdesc_t) structure.
438  */
439 /* ARGSUSED */
440 static int
441 pdesc_print(uintptr_t addr, q_walk_t *qwp, mmd_data_t *data)
442 {
443 	pdesc_t pd;
444 	int i = 0;
445 	char str[32] = "-";
446 	static const mdb_bitmask_t pd_flags_bits[] = {
447 		{ "H", PDESC_HBUF_REF, PDESC_HBUF_REF },
448 		{ "P", PDESC_PBUF_REF, PDESC_PBUF_REF },
449 		{ "R", PDESC_REM_DEFER, PDESC_REM_DEFER },
450 		{ NULL, 0, 0 }
451 	};
452 
453 	if (mdb_vread(&pd, sizeof (pd), addr) == -1) {
454 		mdb_warn("failed to read pdesc_t at %p", addr);
455 		return (WALK_ERR);
456 	}
457 
458 	if (pd.pd_magic != PDESC_MAGIC)
459 		mdb_printf("Incorrect pdesc magic number at %p\n",
460 		    VA_OFF(addr, offsetof(pdesc_t, pd_magic)));
461 
462 	if (!qwp->qw_step) {
463 		mdb_printf("\n");
464 		mdb_printf("%<b>%-3s %-16s %-16s %-4s %-4s %-4s %-4s %-4s %-4s "
465 		    "%-4s %-6s%</b>",
466 		    "", "PDESC", "PATTBL", "HDR", "HDR",
467 		    "HDR", "HDR", "PLD", "PBUF", "PLD", "");
468 		mdb_printf("\n");
469 		mdb_printf(
470 		    "%<b>%<u>%-3s %-16s %-16s %-4s %-4s %-4s %-4s %-4s %-4s "
471 		    "%-4s %-6s%</u>%</b>",
472 		    "NO.", "ADDRESS", "ADDRESS", "SIZE", "HEAD",
473 		    "LEN", "TAIL", "CNT", "IDX", "SIZE", "FLAGS");
474 		mdb_printf("\n");
475 	}
476 
477 	if (pd.pd_pattbl != 0)
478 		mdb_snprintf(str, sizeof (str), "%016p", pd.pd_pattbl);
479 
480 	mdb_printf("%-3d %016p %-16s %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-6b\n",
481 	    ++data->counter, addr, str,
482 	    PDESC_HDRSIZE(&pd.pd_pdi), PDESC_HDRHEAD(&pd.pd_pdi),
483 	    PDESC_HDRL(&pd.pd_pdi), PDESC_HDRTAIL(&pd.pd_pdi),
484 	    pd.pd_pdi.pld_cnt, pd.pd_pdi.pld_ary[i].pld_pbuf_idx,
485 	    PDESC_PLD_SPAN_SIZE(&pd.pd_pdi, i), pd.pd_pdi.flags, pd_flags_bits);
486 
487 	for (++i; i < pd.pd_pdi.pld_cnt; i++)
488 		mdb_printf("%-62s %-4d %-4d\n",
489 		    "", pd.pd_pdi.pld_ary[i].pld_pbuf_idx,
490 		    PDESC_PLD_SPAN_SIZE(&pd.pd_pdi, i));
491 
492 	return (WALK_NEXT);
493 }
494 
495 /*
496  * General purpose ql_t walk_init routine.
497  */
498 static int
499 mmdq_walk_init(mdb_walk_state_t *wsp, char *name, uintptr_t qh,
500     uint_t sz, uint_t ql_off)
501 {
502 	q_walk_t *qwp;
503 	ql_t ql;
504 
505 	/* Caller must have supplied an address */
506 	if (wsp->walk_addr == NULL)
507 		return (WALK_ERR);
508 
509 	qwp = mdb_alloc(sizeof (*qwp), UM_SLEEP);
510 	qwp->qw_name = name;
511 	qwp->qw_head = qh;
512 	qwp->qw_data = sz > 0 ? mdb_alloc(sz, UM_SLEEP) : NULL;
513 	qwp->qw_sz = sz;
514 	qwp->qw_off = ql_off;
515 	qwp->qw_step = FALSE;
516 	qwp->qw_iprint = TRUE;
517 
518 	wsp->walk_data = qwp;
519 
520 	if (mdb_vread(qwp->qw_data, qwp->qw_sz, wsp->walk_addr) == -1) {
521 		mdb_warn("failed to read %s at %p", qwp->qw_name,
522 		    wsp->walk_addr);
523 		mmdq_walk_fini(wsp);
524 		return (WALK_ERR);
525 	}
526 
527 	bcopy((uchar_t *)qwp->qw_data + qwp->qw_off, &ql, sizeof (ql));
528 	if (qh == (uintptr_t)ql.ql_next) {
529 		mmdq_walk_fini(wsp);
530 		return (WALK_DONE);
531 	}
532 
533 	wsp->walk_addr = (uintptr_t)ql.ql_next;
534 
535 	return (WALK_NEXT);
536 }
537 
538 /*
539  * General purpose ql_t walk_step routine.
540  */
541 int
542 mmdq_walk_step(mdb_walk_state_t *wsp)
543 {
544 	q_walk_t *qwp = (q_walk_t *)wsp->walk_data;
545 	int status = WALK_NEXT;
546 	ql_t ql;
547 
548 	/* We've wrapped around the circular list */
549 	if (qwp->qw_step && wsp->walk_addr == qwp->qw_head)
550 		return (WALK_DONE);
551 
552 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
553 	    wsp->walk_cbdata);
554 
555 	if (mdb_vread(qwp->qw_data, qwp->qw_sz, wsp->walk_addr) == -1) {
556 		mdb_warn("failed to read %s at %p", qwp->qw_name,
557 		    wsp->walk_addr);
558 		return (WALK_ERR);
559 	}
560 
561 	/* Go forward to the next one */
562 	bcopy((uchar_t *)qwp->qw_data + qwp->qw_off, &ql, sizeof (ql));
563 	wsp->walk_addr = (uintptr_t)ql.ql_next;
564 
565 	/* We've done the first walk */
566 	qwp->qw_step = TRUE;
567 
568 	return (status);
569 }
570 
571 /*
572  * General purpose ql_t walk_fini routine.
573  */
574 void
575 mmdq_walk_fini(mdb_walk_state_t *wsp)
576 {
577 	q_walk_t *qwp = (q_walk_t *)wsp->walk_data;
578 
579 	if (qwp->qw_data != NULL)
580 		mdb_free(qwp->qw_data, qwp->qw_sz);
581 
582 	mdb_free(qwp, sizeof (*qwp));
583 }
584 
585 /*
586  * Packet descriptor slab (pdesc_slab_t) walker initialization routine.
587  */
588 int
589 pdesc_slab_walk_init(mdb_walk_state_t *wsp)
590 {
591 	uintptr_t q_head;
592 
593 	if (wsp->walk_addr == NULL)
594 		return (WALK_DONE);
595 
596 	/*
597 	 * If we're called from multidata dcmd, then we're passed in
598 	 * the address of ql_t head; otherwise we'd have to get the
599 	 * address ourselves.
600 	 */
601 	if (wsp->walk_cbdata == NULL) {
602 		pdesc_slab_t slab;
603 
604 		/* Read in pdesc_slab_t */
605 		if (mdb_vread(&slab, sizeof (slab), wsp->walk_addr) == -1) {
606 			mdb_warn("failed to read pdesc_slab_t at %p",
607 			    wsp->walk_addr);
608 			return (WALK_ERR);
609 		}
610 
611 		/* pdesc_slab_t head is inside multidata_t */
612 		q_head = (uintptr_t)VA_OFF(slab.pds_mmd,
613 		    offsetof(multidata_t, mmd_pd_slab_q));
614 	} else
615 		q_head = wsp->walk_addr;
616 
617 	/* Pass it on to our generic ql_t walker init */
618 	return (mmdq_walk_init(wsp, "pdesc_slab_t", q_head,
619 	    sizeof (pdesc_slab_t), offsetof(pdesc_slab_t, pds_next)));
620 }
621 
622 /*
623  * A dcmd which returns a multidata_t pointer from a pdesc_slab_t structure.
624  */
625 /* ARGSUSED */
626 int
627 slab2multidata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
628 {
629 	pdesc_slab_t slab;
630 
631 	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
632 		return (DCMD_USAGE);
633 
634 	if (mdb_vread(&slab, sizeof (slab), addr) == -1) {
635 		mdb_warn("couldn't read pdesc_slab_t at %p", addr);
636 		return (DCMD_ERR);
637 	}
638 
639 	mdb_printf("%p\n", slab.pds_mmd);
640 
641 	return (DCMD_OK);
642 }
643 
644 /*
645  * Packet descriptor (pdesc_t) walker initialization routine.
646  */
647 int
648 pdesc_walk_init(mdb_walk_state_t *wsp)
649 {
650 	uintptr_t q_head;
651 
652 	if (wsp->walk_addr == NULL)
653 		return (WALK_DONE);
654 
655 	/*
656 	 * If we're called from multidata dcmd, then we're passed in
657 	 * the address of ql_t head; otherwise we'd have to get the
658 	 * address ourselves.
659 	 */
660 	if (wsp->walk_cbdata == NULL) {
661 		pdesc_t pd;
662 		pdesc_slab_t slab;
663 
664 		/* First we get pdsec_t */
665 		if (mdb_vread(&pd, sizeof (pd), wsp->walk_addr) == -1) {
666 			mdb_warn("failed to read pdesc_t at %p",
667 			    wsp->walk_addr);
668 			return (WALK_ERR);
669 		}
670 
671 		/* And then the pdesc_slab_t */
672 		if (mdb_vread(&slab, sizeof (slab),
673 		    (uintptr_t)pd.pd_slab) == -1) {
674 			mdb_warn("failed to read pdesc_slab_t at %p",
675 			    (uintptr_t)pd.pd_slab);
676 			return (WALK_ERR);
677 		}
678 
679 		/* pdesc_t head is inside multidata_t */
680 		q_head = (uintptr_t)VA_OFF(slab.pds_mmd,
681 		    offsetof(multidata_t, mmd_pd_q));
682 	} else
683 		q_head = wsp->walk_addr;
684 
685 	/* Pass it on to our generic ql_t walker init */
686 	return (mmdq_walk_init(wsp, "pdesc_t", q_head,
687 	    sizeof (pdesc_t), offsetof(pdesc_t, pd_next)));
688 }
689 
690 /*
691  * A dcmd which prints the attribute hash table.
692  */
693 /* ARGSUSED */
694 int
695 pattbl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
696 {
697 	mmd_data_t data;
698 	uint_t pattbl_sz;
699 	int i, j;
700 
701 	bzero(&data, sizeof (data));
702 	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
703 		return (DCMD_USAGE);
704 
705 	/* Figure out the size of hash table */
706 	mdb_readvar(&pattbl_sz, "pattbl_sz");
707 
708 	mdb_printf("\n");
709 	mdb_printf("%<b>%<u>%-3s %-16s %-16s %-12s %-3s %-16s %-5s%</u>%</b>\n",
710 	    "BKT", "PATBKT ADDR", "PATTR ADDR", "TYPE", "LEN", "BUF ADDR",
711 	    "FLAGS");
712 
713 	/* Walk each bucket and print its contents */
714 	for (i = 0, j = 0; i < (pattbl_sz * sizeof (patbkt_t));
715 	    i += sizeof (patbkt_t)) {
716 
717 		mdb_printf("%-3d %016p ", j++, addr + i);
718 
719 		if (mdb_pwalk("pattr", (mdb_walk_cb_t)pattr_print, &data,
720 		    addr + i + offsetof(patbkt_t, pbkt_pattr_q)) == -1) {
721 			mdb_warn("couldn't walk pattr_t list");
722 			return (DCMD_ERR);
723 		}
724 		mdb_printf("\n");
725 	}
726 	mdb_printf("\n");
727 
728 	return (DCMD_OK);
729 }
730 
731 typedef struct pattr_type_s {
732 	char *name;	/* attribute name */
733 	uint_t type;	/* attribute type value */
734 } pattr_type_t;
735 
736 /*
737  * Generic packet attribute (pattr_t) counting routine.
738  */
739 /* ARGSUSED */
740 static int
741 pattr_count(uintptr_t addr, q_walk_t *qwp, mmd_data_t *data)
742 {
743 	pattr_t pattr;
744 
745 	if (mdb_vread(&pattr, sizeof (pattr), addr) == -1) {
746 		mdb_warn("failed to read pattr_t at %p", addr);
747 		return (WALK_ERR);
748 	}
749 
750 	if (pattr.pat_magic != PATTR_MAGIC)
751 		mdb_printf("Incorrect pattr magic number at %p\n",
752 		    VA_OFF(addr, offsetof(pattr_t, pat_magic)));
753 
754 	data->counter++;
755 
756 	return (WALK_NEXT);
757 }
758 
759 /*
760  * Print the contents of a packet attribute (pattr_t) structure.
761  */
762 /* ARGSUSED */
763 static int
764 pattr_print(uintptr_t addr, q_walk_t *qwp, mmd_data_t *data)
765 {
766 	pattr_t pattr;
767 	int i;
768 	char *pa_name = "UNKNOWN";
769 	static const pattr_type_t pa_type[] = {
770 		{ "DSTADDRSAP", PATTR_DSTADDRSAP },
771 		{ "SRCADDRSAP", PATTR_SRCADDRSAP },
772 		{ "HCKSUM", PATTR_HCKSUM }
773 	};
774 	static const mdb_bitmask_t pa_flags_bits[] = {
775 		{ "R", PATTR_REM_DEFER, PATTR_REM_DEFER },
776 		{ "P", PATTR_PERSIST, PATTR_PERSIST },
777 		{ NULL, 0, 0 }
778 	};
779 
780 	if (mdb_vread(&pattr, sizeof (pattr), addr) == -1) {
781 		mdb_warn("failed to read pattr_t at %p", addr);
782 		return (WALK_ERR);
783 	}
784 
785 	if (pattr.pat_magic != PATTR_MAGIC)
786 		mdb_printf("Incorrect pattr magic number at %p\n",
787 		    VA_OFF(addr, offsetof(pattr_t, pat_magic)));
788 
789 	/* Find a matching string */
790 	for (i = 0; i < (sizeof (pa_type) / sizeof (*pa_type)); i++) {
791 		if (pa_type[i].type == pattr.pat_type)
792 			pa_name = pa_type[i].name;
793 	}
794 
795 	if (!qwp->qw_iprint) {
796 		mdb_printf("\n");
797 		mdb_inc_indent(21);
798 	}
799 
800 	mdb_printf("%016p %x:%-10s %-3d %016p %-5b", addr, pattr.pat_type,
801 	    pa_name, pattr.pat_buflen - sizeof (pattr), addr + sizeof (pattr),
802 	    pattr.pat_flags, pa_flags_bits);
803 
804 	if (!qwp->qw_iprint)
805 		mdb_dec_indent(21);
806 	else
807 		qwp->qw_iprint = FALSE;
808 
809 	return (WALK_NEXT);
810 }
811 
812 /*
813  * Packet attribute (pattr_t) walker initialization routine.
814  */
815 int
816 pattr_walk_init(mdb_walk_state_t *wsp)
817 {
818 	uintptr_t q_head;
819 
820 	if (wsp->walk_addr == NULL)
821 		return (WALK_DONE);
822 
823 	/*
824 	 * If we're called from pattbl dcmd, then we're passed in
825 	 * the address of ql_t head; otherwise we'd have to get the
826 	 * address ourselves.
827 	 */
828 	if (wsp->walk_cbdata == NULL) {
829 		pattr_t pattr;
830 
831 		if (mdb_vread(&pattr, sizeof (pattr), wsp->walk_addr) == -1) {
832 			mdb_warn("failed to read pattr_t at %p",
833 			    wsp->walk_addr);
834 			return (WALK_ERR);
835 		}
836 
837 		q_head = (uintptr_t)VA_OFF(pattr.pat_lock,
838 		    -offsetof(patbkt_t, pbkt_lock)) +
839 		    offsetof(patbkt_t, pbkt_pattr_q);
840 	} else
841 		q_head = wsp->walk_addr;
842 
843 	/* Pass it on to our generic ql_t walker init */
844 	return (mmdq_walk_init(wsp, "pattr_t", q_head,
845 	    sizeof (pattr_t), offsetof(pattr_t, pat_next)));
846 }
847 
848 /*
849  * A dcmd which returns a multidata_t pointer from a pattr_t.
850  */
851 /* ARGSUSED */
852 int
853 pattr2multidata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
854 {
855 	pattr_t pattr;
856 
857 	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
858 		return (DCMD_USAGE);
859 
860 	if (mdb_vread(&pattr, sizeof (pattr), addr) == -1) {
861 		mdb_warn("couldn't read pattr_t at %p", addr);
862 		return (DCMD_ERR);
863 	}
864 
865 	if (pattr.pat_magic != PATTR_MAGIC) {
866 		mdb_warn("Incorrect pattr magic number at %p",
867 		    VA_OFF(addr, offsetof(pattr_t, pat_magic)));
868 		return (DCMD_ERR);
869 	}
870 
871 	mdb_printf("%p\n", pattr.pat_mmd);
872 
873 	return (DCMD_OK);
874 }
875 
876 /*
877  * A dcmd which returns a pdesc_slab_t from a pdesc_t.
878  */
879 /* ARGSUSED */
880 int
881 pdesc2slab(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
882 {
883 	pdesc_t pd;
884 
885 	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
886 		return (DCMD_USAGE);
887 
888 	if (mdb_vread(&pd, sizeof (pd), addr) == -1) {
889 		mdb_warn("couldn't read pdesc_t at %p", addr);
890 		return (DCMD_ERR);
891 	}
892 
893 	if (pd.pd_magic != PDESC_MAGIC) {
894 		mdb_warn("Incorrect pdesc magic number at %p",
895 		    VA_OFF(addr, offsetof(pdesc_t, pd_magic)));
896 		return (DCMD_ERR);
897 	}
898 
899 	mdb_printf("%p\n", pd.pd_slab);
900 
901 	return (DCMD_OK);
902 }
903 
904 /*
905  * A dcmd which verifies the integrity of a pdesc_t.
906  */
907 /* ARGSUSED */
908 int
909 pdesc_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
910 {
911 	multidata_t mmd;
912 	pdesc_t pd;
913 	pdescinfo_t *pdi = &pd.pd_pdi;
914 	pdesc_slab_t slab;
915 	mblk_t hbuf, pbuf[MULTIDATA_MAX_PBUFS];
916 	uint_t i, idx;
917 	boolean_t valid = B_TRUE;
918 	struct pld_ary_s *pa;
919 
920 	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
921 		return (DCMD_USAGE);
922 
923 	if (mdb_vread(&pd, sizeof (pd), addr) == -1) {
924 		mdb_warn("couldn't read pdesc_t at %p", addr);
925 		return (DCMD_ERR);
926 	}
927 
928 	if (pd.pd_magic != PDESC_MAGIC) {
929 		mdb_warn("Incorrect pdesc magic number at %p\n",
930 		    VA_OFF(addr, offsetof(pdesc_t, pd_magic)));
931 		return (DCMD_ERR);
932 	}
933 
934 	if (mdb_vread(&slab, sizeof (slab), (uintptr_t)pd.pd_slab) == -1) {
935 		mdb_warn("couldn't read pdesc_slab_t at %p", pd.pd_slab);
936 		return (DCMD_ERR);
937 	}
938 
939 	if (mdb_vread(&mmd, sizeof (mmd), (uintptr_t)slab.pds_mmd) == -1) {
940 		mdb_warn("couldn't read multidata_t at %p", slab.pds_mmd);
941 		return (DCMD_ERR);
942 	}
943 
944 	if (mmd.mmd_magic != MULTIDATA_MAGIC)
945 		mdb_printf("Incorrect Multidata magic number at %p\n",
946 		    VA_OFF(slab.pds_mmd, offsetof(multidata_t, mmd_magic)));
947 
948 	if (mmd.mmd_hbuf != 0 &&
949 	    mdb_vread(&hbuf, sizeof (hbuf), (uintptr_t)mmd.mmd_hbuf) == -1) {
950 		mdb_warn("couldn't read mblk_t at %p", mmd.mmd_hbuf);
951 		return (DCMD_ERR);
952 	}
953 
954 	if (mmd.mmd_pbuf_cnt > MULTIDATA_MAX_PBUFS) {
955 		mdb_warn("Multidata pbuf count exceeds %d\n",
956 		    MULTIDATA_MAX_PBUFS);
957 		return (DCMD_ERR);
958 	} else if (pdi->pld_cnt > mmd.mmd_pbuf_cnt) {
959 		mdb_warn("descriptor pbuf count exceeds Multidata "
960 		    "pbuf count %d\n", mmd.mmd_pbuf_cnt);
961 		return (DCMD_ERR);
962 	}
963 
964 	if (mmd.mmd_pbuf_cnt > 0) {
965 		for (i = 0; i < mmd.mmd_pbuf_cnt; i++) {
966 			if (mdb_vread(&pbuf[i], sizeof (mblk_t),
967 			    (uintptr_t)mmd.mmd_pbuf[i]) == -1) {
968 				mdb_warn("couldn't read mblk_t at %p",
969 				    mmd.mmd_pbuf[i]);
970 				return (DCMD_ERR);
971 			}
972 		}
973 	}
974 
975 	/* It should have at least one buffer reference */
976 	if (!(pdi->flags & PDESC_HAS_REF)) {
977 		mdb_warn("descriptor has no buffer reference indicator "
978 		    "in flags (0x%x)\n", pdi->flags);
979 		return (DCMD_ERR);
980 	} else if (!(pdi->flags & PDESC_PBUF_REF) && pdi->pld_cnt > 0) {
981 		mdb_warn("descriptor has no pbuf reference indicator in "
982 		    "flags (0x%x); but pld_cnt is %d\n", pdi->flags,
983 		    pdi->pld_cnt);
984 		return (DCMD_ERR);
985 	}
986 
987 	/* Bounds check the header fragment, if any */
988 	if (!((pdi->flags & PDESC_HBUF_REF) && pdi->hdr_rptr != 0 &&
989 	    pdi->hdr_wptr != 0 && pdi->hdr_base != 0 &&
990 	    pdi->hdr_lim != 0 && pdi->hdr_lim >= pdi->hdr_base &&
991 	    pdi->hdr_wptr >= pdi->hdr_rptr && pdi->hdr_base <= pdi->hdr_rptr &&
992 	    pdi->hdr_lim >= pdi->hdr_wptr && pdi->hdr_base >= hbuf.b_rptr &&
993 	    MBLKIN(&hbuf, (pdi->hdr_base - hbuf.b_rptr),
994 	    PDESC_HDRSIZE(pdi)))) {
995 		mdb_warn("descriptor has invalid header fragment\n");
996 		return (DCMD_ERR);
997 	}
998 
999 	i = 0;
1000 	pa = &pdi->pld_ary[0];
1001 	/* Bounds check the payload fragment, if any */
1002 	while (valid && i < pdi->pld_cnt) {
1003 		valid = (((idx = pa->pld_pbuf_idx) < mmd.mmd_pbuf_cnt) &&
1004 		    pa->pld_rptr != NULL && pa->pld_wptr != NULL &&
1005 		    pa->pld_wptr >= pa->pld_rptr &&
1006 		    pa->pld_rptr >= pbuf[idx].b_rptr &&
1007 		    MBLKIN(&pbuf[idx], (pa->pld_rptr - pbuf[idx].b_rptr),
1008 			PDESC_PLD_SPAN_SIZE(pdi, i)));
1009 
1010 		if (!valid) {
1011 			mdb_warn("descriptor has invalid payload fragment\n");
1012 			return (DCMD_ERR);
1013 		}
1014 
1015 		/* advance to next entry */
1016 		i++;
1017 		pa++;
1018 	}
1019 
1020 	return (DCMD_OK);
1021 }
1022