xref: /titanic_41/usr/src/cmd/mdb/common/modules/nca/nca.c (revision 70025d765b044c6d8594bb965a2247a61e991a99)
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 2004 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  * NCA mdb module.  Provides a collection of dcmds and walkers that
31  * operate on core NCA data structures.  Dependencies on NCA internals
32  * are described in $SRC/uts/common/inet/nca/nca.h.
33  */
34 
35 #include <mdb/mdb_modapi.h>
36 #include <mdb/mdb_ks.h>
37 
38 #include <ctype.h>
39 #include <sys/types.h>
40 #include <sys/sunddi.h>
41 #include <sys/processor.h>
42 #include <netinet/in.h>
43 #include <netinet/ip6.h>	/* must come before common.h */
44 #include <inet/common.h>	/* must come before led.h */
45 #include <inet/led.h>		/* must come before ip.h */
46 #include <inet/ip.h>		/* must come before tcp.h */
47 #include <inet/tcp.h>		/* must come before nca/nca.h */
48 #include <inet/nca/nca.h>
49 #include <inet/nca/ncadoorhdr.h>
50 
51 #define	NCA_WALK_PLRU	(void *)1
52 #define	NCA_WALK_VLRU	(void *)2
53 #define	NCA_ADDR_WIDTH	11	/* kernel addresses *shouldn't* be wider */
54 #define	YESNO(bool)	((bool) ? 'Y' : 'n')
55 
56 /*
57  * Structure for assigning a name to a region of memory.
58  */
59 typedef struct {
60 	const char	*nm_name;	/* name of region */
61 	int		nm_len;		/* length to region */
62 	uintptr_t	nm_addr;	/* starting address of region */
63 } namedmem_t;
64 
65 /*
66  * Structure for giving a name to a constant.
67  */
68 typedef struct {
69 	const char *const_name;  /* name of constant */
70 	int	    const_value; /* constant itself */
71 } constname_t;
72 
73 /*
74  * Structure for mapping a bit to a name and a description.  Instances
75  * of this datatype should always be arrays which decode bits in a
76  * number, and the index into the array should contain the description
77  * of a bit at position "index" in the number being decoded.  The list
78  * must be terminated by an entry with a NULL `bit_name'.
79  */
80 typedef struct {
81 	const char *bit_name;	/* name of bit */
82 	const char *bit_descr;	/* description of bit's purpose */
83 } bitname_t;
84 
85 /*
86  * Note: These should be defined in upside down order to their
87  * definitions in nca.h
88  * (Assumes that current ordering convention in nca.h will
89  * prevail for future additions)
90  */
91 static const bitname_t node_refs[] = {
92 	{ "REF_UNUSED",		"0x00000001" },
93 	{ "REF_UNUSED",		"0x00000002" },
94 	{ "REF_UNUSED",		"0x00000004" },
95 	{ "REF_UNUSED",		"0x00000008" },
96 	{ "REF_UNUSED",		"0x00000010" },
97 	{ "REF_UNUSED",		"0x00000020" },
98 	{ "REF_UNUSED",		"0x00000040" },
99 	{ "REF_UNUSED",		"0x00000080" },
100 	{ "REF_UNUSED",		"0x00000100" },
101 	{ "REF_UNUSED",		"0x00000200" },
102 	{ "REF_UNUSED",		"0x00000400" },
103 	{ "REF_SEGMAP",		"segmapped (PHYS|VIRT)" },
104 	{ "REF_NCAFS",		"NCAfs required" },
105 	{ "REF_VNODE",		"vnode hashed" },
106 	{ "REF_ERROR",		"errored" },
107 	{ "REF_OWNED",		"owned (won't be freed)" },
108 	{ "REF_UPCALL",		"upcall not completed yet" },
109 	{ "REF_CTAG",		"CTAG hashed" },
110 	{ "REF_PREEMPT",	"processing preempted" },
111 	{ "REF_ONVLRU",		"on virtual memory LRU list" },
112 	{ "REF_ONPLRU",		"on physical memory LRU list" },
113 	{ "REF_MISS",		"in miss processing" },
114 	{ "REF_NOLRU",		"not safe for LRU reclaim" },
115 	{ "REF_RESP",		"done parsing response header" },
116 	{ "REF_FILE",		"reachable through filename hash" },
117 	{ "REF_SAFED",		"not safe for use" },
118 	{ "REF_DONE",		"done with miss processing" },
119 	{ "REF_KMEM",		"content-backed via kmem_alloc()" },
120 	{ "REF_CKSUM",		"checksum mapping in-use" },
121 	{ "REF_VIRT",		"virtually mapped (data valid)" },
122 	{ "REF_PHYS",		"physically mapped (pp valid)" },
123 	{ "REF_URI",		"reachable through URI hash" },
124 	{ NULL }
125 };
126 
127 static const bitname_t advise_types[] = {
128 	{ "ADVISE",		"" },
129 	{ "ADVISE_REPLACE",	"replace cached object with provided object" },
130 	{ "ADVISE_FLUSH",	"flush cached object" },
131 	{ "ADVISE_TEMP",	"return this object; keep cached object" },
132 	{ NULL }
133 };
134 
135 /*
136  * Print `len' bytes of buffer `buf'.  Handle nonprintable characters
137  * specially.
138  */
139 static void
140 printbuf(uint8_t *buf, size_t len)
141 {
142 	size_t	i;
143 
144 	/*
145 	 * TODO: display octal form of unprintable characters in dim mode
146 	 *	 once mdb pager bug is fixed.
147 	 */
148 	for (i = 0; i < len; i++)
149 		mdb_printf(isgraph(buf[i]) ? "%c" : "\\%#o", buf[i]);
150 
151 	mdb_printf("\n");
152 }
153 
154 /*
155  * Convert HTTP method operation `method' to a name.
156  */
157 static const char *
158 method2name(unsigned int method)
159 {
160 	unsigned int i;
161 	static constname_t http_methods[] = {
162 		{ "NCA_UNKNOWN", NCA_UNKNOWN	},
163 		{ "NCA_OPTIONS", NCA_OPTIONS	},
164 		{ "NCA_GET",	 NCA_GET	},
165 		{ "NCA_HEAD",	 NCA_HEAD	},
166 		{ "NCA_POST",	 NCA_POST	},
167 		{ "NCA_PUT",	 NCA_PUT	},
168 		{ "NCA_DELETE",  NCA_DELETE	},
169 		{ "NCA_TRACE",	 NCA_TRACE	},
170 		{ "NCA_RAW",	 NCA_RAW	},
171 		{ NULL }
172 	};
173 
174 	for (i = 0; http_methods[i].const_name != NULL; i++) {
175 		if (method == http_methods[i].const_value)
176 			return (http_methods[i].const_name);
177 	}
178 
179 	return ("<unknown>");
180 }
181 
182 /*
183  * Convert TCP state `state' to a name.
184  */
185 static const char *
186 state2name(int state)
187 {
188 	unsigned int i;
189 	static constname_t tcp_states[] = {
190 		{ "CLOSED",	 TCPS_CLOSED		},
191 		{ "IDLE",	 TCPS_IDLE		},
192 		{ "BOUND",	 TCPS_BOUND		},
193 		{ "LISTEN",	 TCPS_LISTEN		},
194 		{ "SYN_SENT",	 TCPS_SYN_SENT		},
195 		{ "SYN_RCVD",	 TCPS_SYN_RCVD		},
196 		{ "ESTABLISHED", TCPS_ESTABLISHED 	},
197 		{ "CLOSE_WAIT",	 TCPS_CLOSE_WAIT	},
198 		{ "FIN_WAIT1",	 TCPS_FIN_WAIT_1	},
199 		{ "FIN_WAIT2",	 TCPS_FIN_WAIT_2	},
200 		{ "CLOSING",	 TCPS_CLOSING		},
201 		{ "LAST_ACK",	 TCPS_LAST_ACK 		},
202 		{ "TIME_WAIT",	 TCPS_TIME_WAIT		},
203 		{ NULL }
204 	};
205 
206 	for (i = 0; tcp_states[i].const_name != NULL; i++) {
207 		if (state == tcp_states[i].const_value)
208 			return (tcp_states[i].const_name);
209 	}
210 
211 	return ("<unknown>");
212 }
213 
214 /*
215  * Convert an nca_io2_t direct_type into a name.
216  */
217 static const char *
218 direct2name(unsigned int type)
219 {
220 	unsigned int i;
221 	static const constname_t direct_types[] = {
222 		{ "DIRECT_NONE",	NCA_IO_DIRECT_NONE	},
223 		{ "DIRECT_FILENAME",	NCA_IO_DIRECT_FILENAME	},
224 		{ "DIRECT_SHMSEG",	NCA_IO_DIRECT_SHMSEG	},
225 		{ "DIRECT_FILEDESC",	NCA_IO_DIRECT_FILEDESC	},
226 		{ "DIRECT_CTAG",	NCA_IO_DIRECT_CTAG	},
227 		{ "DIRECT_SPLICE",	NCA_IO_DIRECT_SPLICE	},
228 		{ "DIRECT_TEE",		NCA_IO_DIRECT_TEE 	},
229 		{ "DIRECT_FILE_FD",	NCA_IO_DIRECT_FILE_FD 	},
230 		{ NULL,			0			}
231 	};
232 
233 	for (i = 0; direct_types[i].const_name != NULL; i++) {
234 		if (type == direct_types[i].const_value)
235 			return (direct_types[i].const_name);
236 	}
237 
238 	return ("<unknown>");
239 }
240 
241 /*
242  * Convert an nca_io2_t operation into a name.
243  */
244 static const char *
245 op2name(nca_op_t op)
246 {
247 	unsigned int i;
248 	static const constname_t op_types[] = {
249 		{ "http",		http_op		},
250 		{ "error",		error_op	},
251 		{ "error_retry",	error_retry_op	},
252 		{ "resource",		resource_op	},
253 		{ "timeout",		timeout_op	},
254 		{ "door_attach",	door_attach_op	},
255 
256 		{ "log",		log_op		},
257 		{ "log_ok",		log_ok_op	},
258 		{ "log_error",		log_error_op	},
259 		{ "log_op_fiov",	log_op_fiov	},
260 
261 		{ NULL,			0		}
262 	};
263 
264 	for (i = 0; op_types[i].const_name != NULL; i++) {
265 		if (op == op_types[i].const_value)
266 			return (op_types[i].const_name);
267 	}
268 
269 	return ("<unknown>");
270 }
271 
272 /*
273  * Convert from ticks to milliseconds.
274  */
275 static uint64_t
276 tick2msec(uint64_t tick)
277 {
278 	static int tick_per_msec;
279 	static int msec_per_tick;
280 	static int once;
281 
282 	if (once == 0) {
283 		if (mdb_readvar(&tick_per_msec, "tick_per_msec") == -1) {
284 			mdb_warn("cannot read symbol tick_per_msec");
285 			return (0);
286 		}
287 		if (mdb_readvar(&msec_per_tick, "msec_per_tick") == -1) {
288 			mdb_warn("cannot read symbol msec_per_tick");
289 			return (0);
290 		}
291 		once++;
292 	}
293 
294 	return (tick_per_msec ? tick / tick_per_msec : tick * msec_per_tick);
295 }
296 
297 /*
298  * Print the core fields in an nca_io2_t.  With the "-v" argument,
299  * provide more verbose output.  With the "-p" argument, print payload
300  * information.
301  */
302 static int
303 nca_io2(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
304 {
305 	unsigned int	i;
306 	unsigned int	payload_len;
307 	uint64_t	payload_output_max = 0;
308 	unsigned int	verbose = FALSE;
309 	const int	IO2_ADVDELT = NCA_ADDR_WIDTH + 1;
310 	boolean_t	arm;
311 	nca_io2_t	io2;
312 	uint8_t		*buf;
313 	namedmem_t	area[3];
314 
315 	if (!(flags & DCMD_ADDRSPEC))
316 		return (DCMD_USAGE);
317 
318 	if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose,
319 	    'p', MDB_OPT_UINT64, &payload_output_max, NULL) != argc)
320 		return (DCMD_USAGE);
321 
322 	if (!DCMD_HDRSPEC(flags) && verbose)
323 		mdb_printf("\n\n");
324 
325 	if (DCMD_HDRSPEC(flags) || verbose) {
326 		mdb_printf("%<u>%-*s %2s %4s %8s %*s %8s %16s %-12s%</u>\n",
327 		    NCA_ADDR_WIDTH, "ADDR", "AV", "MFNP", "TID",
328 		    NCA_ADDR_WIDTH, "CONN", "CONN_TAG", "CACHE_TAG",
329 		    "OPERATION");
330 	}
331 
332 	if (mdb_vread(&io2, sizeof (nca_io2_t), addr) == -1) {
333 		mdb_warn("cannot read nca_io2_t at %p", addr);
334 		return (DCMD_ERR);
335 	}
336 
337 	if (io2.version != NCA_HTTP_VERSION2)
338 		mdb_warn("nca_io2_t at %p has incorrect version `%u'\n", addr,
339 		    io2.version);
340 
341 	mdb_printf("%0*p %02x %c%c%c%c %08x %0*llx %08x %016llx %s\n",
342 	    NCA_ADDR_WIDTH, addr, io2.advisory, YESNO(io2.more),
343 	    YESNO(io2.first), YESNO(io2.nocache), YESNO(io2.preempt),
344 	    (uint32_t)io2.tid, NCA_ADDR_WIDTH, io2.cid, io2.tag, io2.ctag,
345 	    op2name(io2.op));
346 
347 	if (verbose) {
348 		arm = B_TRUE;
349 		for (i = 0; advise_types[i].bit_name != NULL; i++) {
350 			if ((io2.advisory & (1 << i)) == 0)
351 				continue;
352 
353 			if (arm) {
354 				mdb_printf("%*s|\n", IO2_ADVDELT, "");
355 				mdb_printf("%*s+-->  ", IO2_ADVDELT, "");
356 				arm = B_FALSE;
357 			} else
358 				mdb_printf("%*s      ", IO2_ADVDELT, "");
359 
360 			mdb_printf("%-15s %s\n", advise_types[i].bit_name,
361 			    advise_types[i].bit_descr);
362 		}
363 	}
364 
365 	payload_len = io2.data_len + io2.direct_len + io2.trailer_len;
366 
367 	if (payload_output_max == 0 || payload_len == 0)
368 		return (DCMD_OK);
369 
370 	mdb_inc_indent(4);
371 	mdb_printf("\n%u byte payload consists of:\n", payload_len);
372 	mdb_inc_indent(4);
373 
374 	buf = mdb_alloc(payload_output_max, UM_SLEEP);
375 
376 	area[0].nm_name = "data";
377 	area[0].nm_addr = addr + io2.data;
378 	area[0].nm_len  = io2.data_len;
379 
380 	area[1].nm_name = direct2name(io2.direct_type);
381 	area[1].nm_addr = addr + io2.direct;
382 	area[1].nm_len  = io2.direct_len;
383 
384 	area[2].nm_name = "trailer";
385 	area[2].nm_addr = addr + io2.trailer;
386 	area[2].nm_len  = io2.trailer_len;
387 
388 	for (i = 0; i < sizeof (area) / sizeof (area[0]); i++) {
389 		if (area[i].nm_len <= 0)
390 			continue;
391 
392 		mdb_printf("%d byte %s area at %p (", area[i].nm_len,
393 		    area[i].nm_name, area[i].nm_addr);
394 
395 		if (area[i].nm_len > payload_output_max) {
396 			mdb_printf("first");
397 			area[i].nm_len = (int)payload_output_max;
398 		} else
399 			mdb_printf("all");
400 
401 		mdb_printf(" %u bytes follow):\n", area[i].nm_len);
402 		if (mdb_vread(buf, area[i].nm_len, area[i].nm_addr) == -1)
403 			mdb_warn("cannot read %s area at %p", area[i].nm_name,
404 			    area[i].nm_addr);
405 		else {
406 			mdb_inc_indent(4);
407 			printbuf(buf, area[i].nm_len);
408 			mdb_dec_indent(4);
409 		}
410 	}
411 	mdb_dec_indent(4);
412 	mdb_dec_indent(4);
413 
414 	mdb_free(buf, payload_output_max);
415 
416 	return (DCMD_OK);
417 }
418 
419 static void
420 nca_io2_help(void)
421 {
422 	mdb_printf("Print the core information for a given NCA nca_io2_t.\n");
423 	mdb_printf("Options:\n");
424 	mdb_printf("\t-p N\tshow up to N bytes of payload information from\n");
425 	mdb_printf("\t\teach payload area\n");
426 	mdb_printf("\t\t(reminder: default radix is %<b>hex%</b>)\n");
427 	mdb_printf("\t-v\tbe verbose (more descriptive)\n");
428 }
429 
430 /*
431  * Print the core fields for one or all NCA timers.  If no address is
432  * specified, all NCA timers are printed; otherwise the specified timer
433  * list is printed.  With the "-e" argument, the "encapsulated" pointer
434  * for each te_t in a given tb_t is shown in parentheses.
435  */
436 static int
437 nca_timer(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
438 {
439 	unsigned int	show_encap = FALSE;
440 	void		*tb_addr, *te_addr;
441 	clock_t		lbolt, first_exec = 0;
442 	ti_t		ti;
443 	tb_t		tb;
444 	te_t		te;
445 
446 	if (!(flags & DCMD_ADDRSPEC)) {
447 		if (mdb_walk_dcmd("nca_timer", "nca_timer", argc, argv) == -1) {
448 			mdb_warn("cannot walk timer list");
449 			return (DCMD_ERR);
450 		}
451 		return (DCMD_OK);
452 	}
453 
454 	if (mdb_getopts(argc, argv, 'e', MDB_OPT_SETBITS, TRUE, &show_encap,
455 	    NULL) != argc)
456 		return (DCMD_USAGE);
457 
458 	if (DCMD_HDRSPEC(flags)) {
459 		mdb_printf("%<u>%-*s %-*s %-55s%</u>\n", NCA_ADDR_WIDTH, "TI",
460 		    NCA_ADDR_WIDTH, "SQUEUE", "FIRELIST +MSEC");
461 	}
462 
463 	if (mdb_vread(&ti, sizeof (ti_t), addr) == -1) {
464 		mdb_warn("cannot read ti_t at %p", addr);
465 		return (DCMD_ERR);
466 	}
467 
468 	if (mdb_readvar(&lbolt, "lbolt") == -1) {
469 		mdb_warn("cannot read symbol lbolt");
470 		return (DCMD_ERR);
471 	}
472 
473 	mdb_printf("%0*p %0*p", NCA_ADDR_WIDTH, addr, NCA_ADDR_WIDTH, ti.ep);
474 	mdb_inc_indent(24);
475 	for (tb_addr = ti.head; tb_addr != NULL; tb_addr = tb.next) {
476 		if (mdb_vread(&tb, sizeof (tb_t), (uintptr_t)tb_addr) == -1) {
477 			mdb_warn("cannot read tb_t at %p", tb_addr);
478 			return (DCMD_ERR);
479 		}
480 		if (first_exec == 0) {
481 			mdb_printf(" %ld", tick2msec(tb.exec - lbolt));
482 			first_exec = tb.exec;
483 		} else
484 			mdb_printf(" %+lld", tick2msec(tb.exec - first_exec));
485 
486 		if (!show_encap || tb.head == NULL)
487 			continue;
488 
489 		mdb_printf("(");
490 		for (te_addr = tb.head; te_addr != NULL; te_addr = te.next) {
491 			if (mdb_vread(&te, sizeof (te_t), (uintptr_t)te_addr)
492 			    == -1) {
493 				mdb_warn("cannot read te_t at %p", te_addr);
494 				return (DCMD_ERR);
495 			}
496 			mdb_printf("%0p%s", te.ep, te.next == NULL ? "" : " ");
497 		}
498 		mdb_printf(")");
499 	}
500 	mdb_printf("\n");
501 	mdb_dec_indent(24);
502 
503 	return (DCMD_OK);
504 }
505 
506 static void
507 nca_timer_help(void)
508 {
509 	mdb_printf("Print the core information for one or all NCA timer\n");
510 	mdb_printf("lists.  If no timer list is given, then all timer lists\n");
511 	mdb_printf("are shown.  For each timer list, the list of timers to\n");
512 	mdb_printf("fire on that list are shown, the first in absolute\n");
513 	mdb_printf("ticks and the rest in ticks relative to the first.\n\n");
514 	mdb_printf("Options:\n");
515 	mdb_printf("\t-e\tshow the encapsulating pointer for each event ");
516 	mdb_printf("at each fire time\n");
517 }
518 
519 /*
520  * Print the core fields in an NCA node_t.  With the "-r" argument,
521  * provide additional information about the request; with "-v",
522  * provide more verbose output.
523  */
524 static int
525 nca_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
526 {
527 	unsigned int	i, max;
528 	unsigned int	verbose = FALSE;
529 	unsigned int	request = FALSE;
530 	const int	NODE_REFDELT = NCA_ADDR_WIDTH + 4 + 2;
531 	boolean_t	arm;
532 	node_t		node;
533 	char		*buf;
534 	namedmem_t	hdr[4];
535 
536 	if (!(flags & DCMD_ADDRSPEC))
537 		return (DCMD_USAGE);
538 
539 	if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose,
540 		'r', MDB_OPT_SETBITS, TRUE, &request, 'p', NULL) != argc)
541 		return (DCMD_USAGE);
542 
543 	if (!DCMD_HDRSPEC(flags) && verbose)
544 		mdb_printf("\n\n");
545 
546 	if (DCMD_HDRSPEC(flags) || verbose) {
547 		mdb_printf("%<u>%-*s %4s %5s %8s %-*s %-*s %-*s %-*s%</u>\n",
548 			NCA_ADDR_WIDTH, "ADDR", "REF", "STATE", "DATASIZE",
549 			NCA_ADDR_WIDTH, "SQUEUE", NCA_ADDR_WIDTH, "REQUEST",
550 			NCA_ADDR_WIDTH, "PLRUN", NCA_ADDR_WIDTH, "VLRUN");
551 	}
552 
553 	if (mdb_vread(&node, sizeof (node_t), addr) == -1) {
554 		mdb_warn("cannot read node_t at %p", addr);
555 		return (DCMD_ERR);
556 	}
557 
558 	mdb_printf("%0*p %4d %05x %8d %0*p %0*p %0*p %0*p\n",
559 	    NCA_ADDR_WIDTH, addr, node.cnt, node.ref,
560 	    node.datasz, NCA_ADDR_WIDTH, node.sqp, NCA_ADDR_WIDTH,
561 	    node.req, NCA_ADDR_WIDTH, node.plrunn, NCA_ADDR_WIDTH, node.vlrunn);
562 
563 	if (verbose) {
564 		arm = B_TRUE;
565 		for (i = 0; node_refs[i].bit_name != NULL; i++) {
566 			if ((node.ref & (1 << i)) == 0)
567 				continue;
568 
569 			if (arm) {
570 				mdb_printf("%*s|\n", NODE_REFDELT, "");
571 				mdb_printf("%*s+-->  ", NODE_REFDELT, "");
572 				arm = B_FALSE;
573 			} else
574 				mdb_printf("%*s      ", NODE_REFDELT, "");
575 
576 			mdb_printf("%-12s %s\n", node_refs[i].bit_name,
577 			    node_refs[i].bit_descr);
578 		}
579 	}
580 
581 	if (!request || node.req == NULL)
582 		return (DCMD_OK);
583 
584 	mdb_inc_indent(4);
585 	mdb_printf("\n%u byte HTTP/%u.%u %s request (%u bytes in header, "
586 	    "%u in content)\n", node.reqsz, node.version >> 16,
587 	    node.version & 0xff, method2name(node.method), node.reqhdrsz,
588 	    node.reqcontl);
589 
590 	hdr[0].nm_name = "URI";
591 	hdr[0].nm_addr = (uintptr_t)node.path;
592 	hdr[0].nm_len  = node.pathsz;
593 
594 	hdr[1].nm_name = "Accept";
595 	hdr[1].nm_addr = (uintptr_t)node.reqaccept;
596 	hdr[1].nm_len  = node.reqacceptsz;
597 
598 	hdr[2].nm_name = "Accept-Language";
599 	hdr[2].nm_addr = (uintptr_t)node.reqacceptl;
600 	hdr[2].nm_len  = node.reqacceptlsz;
601 
602 	hdr[3].nm_name = "Host";
603 	hdr[3].nm_addr = (uintptr_t)node.reqhost;
604 	hdr[3].nm_len  = node.reqhostsz;
605 
606 	/*
607 	 * A little optimization.  Allocate all of the necessary memory here,
608 	 * so we don't have to allocate on each loop iteration.
609 	 */
610 
611 	max = node.reqhdrsz;
612 	for (i = 0; i < 4; i++)
613 		max = MAX(max, hdr[i].nm_len);
614 	max++;
615 
616 	buf = mdb_alloc(max, UM_SLEEP);
617 
618 	mdb_inc_indent(4);
619 	for (i = 0; i < sizeof (hdr) / sizeof (hdr[0]); i++) {
620 		if (hdr[i].nm_len <= 0)
621 			continue;
622 
623 		if (mdb_vread(buf, hdr[i].nm_len, hdr[i].nm_addr) == -1) {
624 			mdb_warn("cannot read \"%s\" header field at %p",
625 			    hdr[i].nm_name, hdr[i].nm_addr);
626 			continue;
627 		}
628 		buf[hdr[i].nm_len] = '\0';
629 
630 		mdb_printf("%s: ", hdr[i].nm_name);
631 		mdb_inc_indent(4);
632 		mdb_printf("%s\n", buf);
633 		mdb_dec_indent(4);
634 	}
635 
636 	if (node.reqhdrsz > 0 && verbose) {
637 		if (mdb_vread(buf, node.reqhdrsz, (uintptr_t)node.reqhdr) == -1)
638 			mdb_warn("cannot read header at %p", node.reqhdr);
639 		else {
640 			mdb_printf("Raw header: ");
641 			mdb_inc_indent(4);
642 			printbuf((uint8_t *)buf, node.reqhdrsz);
643 			mdb_dec_indent(4);
644 		}
645 	}
646 	mdb_dec_indent(4);
647 	mdb_dec_indent(4);
648 
649 	mdb_free(buf, max);
650 
651 	return (DCMD_OK);
652 }
653 
654 static void
655 nca_node_help(void)
656 {
657 	mdb_printf("Print the core information for a given NCA node_t.\n\n");
658 	mdb_printf("Options:\n");
659 	mdb_printf("\t-r\tdisplay HTTP request information\n");
660 	mdb_printf("\t-v\tbe verbose (more descriptive)\n");
661 }
662 
663 /*
664  * Print the core fields in an NCA nca_conn_t.  With the "-t" argument, skip
665  * all nca_conn_t's that are in the TIME_WAIT state.  With the "-x" argument,
666  * show the xmit data.
667  */
668 static int
669 nca_conn(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
670 {
671 	unsigned int	i;
672 	nca_conn_t 		conn;
673 	unsigned int	show_timewait = TRUE;
674 	unsigned int	show_xmit = FALSE;
675 
676 	if (!(flags & DCMD_ADDRSPEC))
677 		return (DCMD_USAGE);
678 
679 	if (mdb_getopts(argc, argv, 'x', MDB_OPT_SETBITS, TRUE, &show_xmit,
680 	    't', MDB_OPT_CLRBITS, TRUE, &show_timewait, NULL) != argc)
681 		return (DCMD_USAGE);
682 
683 	if (DCMD_HDRSPEC(flags)) {
684 		mdb_printf("%<u>%-*s %3s %8s %15s %15s %-*s %-10s%</u>\n",
685 		    NCA_ADDR_WIDTH, "ADDR", "REF", "CREATE", "LOCAL_ADDR",
686 		    "REMOTE_ADDR", NCA_ADDR_WIDTH,  "NODE", "STATE");
687 	}
688 
689 	if (mdb_vread(&conn, sizeof (nca_conn_t), addr) == -1) {
690 		mdb_warn("cannot read nca_conn_t at %p", addr);
691 		return (DCMD_ERR);
692 	}
693 
694 	if (!show_timewait && conn.tcp_state == TCPS_TIME_WAIT)
695 		return (DCMD_OK);
696 
697 	mdb_printf("%0*p %3d %8lx %15I %15I %0*p %s\n", NCA_ADDR_WIDTH, addr,
698 	    conn.ref, conn.create, conn.laddr, conn.faddr, NCA_ADDR_WIDTH,
699 	    conn.req_np, state2name(conn.tcp_state));
700 
701 	if (show_xmit) {
702 		mdb_inc_indent(4);
703 
704 		for (i = 0; i < TCP_XMIT_MAX_IX; i++) {
705 			mdb_printf("xmit[%d]\n", i);
706 			mdb_printf("\tref pointer\t\t%p\n", conn.xmit[i].np);
707 			mdb_printf("\tdata pointer\t\t%p\n", conn.xmit[i].dp);
708 			mdb_printf("\tcksum array\t\t%p\n", conn.xmit[i].cp);
709 			mdb_printf("\tremaining xmit data\t%d\n",
710 			    conn.xmit[i].sz);
711 			mdb_printf("\tref to node_t\t\t%p\n",
712 			    conn.xmit[i].refed);
713 			mdb_printf("\tremaining segment data\t%d\n",
714 			    conn.xmit[i].dsz);
715 			mdb_printf("\tvirtual pointer\t\t%p\n",
716 			    conn.xmit[i].dvp);
717 		}
718 
719 		mdb_dec_indent(4);
720 	}
721 
722 	return (DCMD_OK);
723 }
724 
725 static void
726 nca_conn_help(void)
727 {
728 	mdb_printf("Print the core information for a given NCA "
729 	    "nca_conn_t.\n\n");
730 	mdb_printf("Options:\n");
731 	mdb_printf("\t-t\tskip connections in the TIME_WAIT state\n");
732 	mdb_printf("\t-x\tshow TCP XMIT information\n");
733 }
734 
735 /*
736  * Print the core TCP-related fields in an NCA nca_conn_t.  With the "-t"
737  * argument, skips all nca_conn_t's that are in the TIME_WAIT state.
738  */
739 static int
740 nca_tcpconn(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
741 {
742 	nca_conn_t		conn;
743 	unsigned int	show_timewait = TRUE;
744 
745 	if (!(flags & DCMD_ADDRSPEC))
746 		return (DCMD_USAGE);
747 
748 	if (mdb_getopts(argc, argv, 't', MDB_OPT_CLRBITS, TRUE, &show_timewait,
749 	    NULL) != argc)
750 		return (DCMD_USAGE);
751 
752 	if (DCMD_HDRSPEC(flags)) {
753 		mdb_printf("%<u>%-*s %21s %5s %8s %5s %8s %5s %-9s%</u>\n",
754 		    NCA_ADDR_WIDTH, "ADDR", "REMOTE_ADDR", "SWIND", "SUNASEQ",
755 		    "SNSEQ", "RACKSEQ", "RNSEQ", "STATE");
756 	}
757 
758 	if (mdb_vread(&conn, sizeof (nca_conn_t), addr) == -1) {
759 		mdb_warn("cannot read nca_conn_t at %p", addr);
760 		return (DCMD_ERR);
761 	}
762 
763 	if (!show_timewait && conn.tcp_state == TCPS_TIME_WAIT)
764 		return (DCMD_OK);
765 
766 	mdb_nhconvert(&conn.conn_fport, &conn.conn_fport, sizeof (in_port_t));
767 
768 	mdb_printf("%0*p %15I:%05hu %5u %08x %+5d %08x %+5d %-9s\n",
769 	    NCA_ADDR_WIDTH, addr, conn.faddr, conn.conn_fport, conn.tcp_swnd,
770 	    conn.tcp_suna, conn.tcp_snxt - conn.tcp_suna, conn.tcp_rack,
771 	    conn.tcp_rnxt - conn.tcp_rack, state2name(conn.tcp_state));
772 
773 	return (DCMD_OK);
774 }
775 
776 static void
777 nca_tcpconn_help(void)
778 {
779 	mdb_printf("Print the core TCP-related information for a given ");
780 	mdb_printf("NCA nca_conn_t.\n\n");
781 	mdb_printf("Options:\n");
782 	mdb_printf("\t-t\tskip connections in the TIME_WAIT state\n");
783 }
784 
785 /*
786  * Initialize a walk for the NCA connection fanout table.  Note that
787  * local walks are not supported since they're more trouble than
788  * they're worth.
789  */
790 static int
791 nca_connf_walk_init(mdb_walk_state_t *wsp)
792 {
793 	int	fanout_size;
794 
795 	if (wsp->walk_addr != 0) {
796 		mdb_warn("nca_connf_walk does not support local walks\n");
797 		return (WALK_DONE);
798 	}
799 
800 	if (mdb_readvar(&wsp->walk_addr, "nca_conn_fanout") == -1) {
801 		mdb_warn("cannot read symbol nca_conn_fanout");
802 		return (WALK_ERR);
803 	}
804 
805 	if (mdb_readvar(&fanout_size, "nca_conn_fanout_size") == -1) {
806 		mdb_warn("cannot read symbol nca_conn_fanout_size");
807 		return (WALK_ERR);
808 	}
809 
810 	wsp->walk_data = (void *)(uintptr_t)fanout_size;
811 
812 	return (WALK_NEXT);
813 }
814 
815 /*
816  * Walk the NCA connection fanout table; `wsp->walk_data' is used to keep
817  * track of the number of indicies that are left to walk so we know when
818  * to stop.
819  */
820 static int
821 nca_connf_walk_step(mdb_walk_state_t *wsp)
822 {
823 	connf_t		connf;
824 	nca_conn_t		conn;
825 	int		status;
826 	intptr_t	i = (intptr_t)wsp->walk_data;
827 
828 	if (i-- <= 0)
829 		return (WALK_DONE);
830 
831 	if (mdb_vread(&connf, sizeof (connf_t), wsp->walk_addr) == -1) {
832 		mdb_warn("cannot read connf_t at %p", wsp->walk_addr);
833 		return (WALK_ERR);
834 	}
835 
836 	/*
837 	 * No point in walking the fanout if there are no
838 	 * connections in it.
839 	 */
840 	if (connf.head != NULL) {
841 		/*
842 		 * Point to the nca_conn_t instead of the connf_t so that output
843 		 * can be piped to ::nca_conn dcmd.
844 		 */
845 		if (mdb_vread(&conn, sizeof (nca_conn_t),
846 		    (uintptr_t)connf.head) == -1) {
847 			mdb_warn("cannot read nca_conn_t at %p", connf.head);
848 			return (WALK_ERR);
849 		}
850 		status = wsp->walk_callback((uintptr_t)connf.head, &conn,
851 		    wsp->walk_cbdata);
852 	} else {
853 		status = WALK_NEXT;
854 	}
855 
856 	wsp->walk_data = (void *)i;
857 	wsp->walk_addr += sizeof (connf_t);
858 
859 	return (status);
860 }
861 
862 /*
863  * Initialize a walk for the NCA node fanout tables.  Note that local
864  * walks are not supported since they're more trouble than they're
865  * worth.
866  */
867 static int
868 nca_nodef_walk_init(mdb_walk_state_t *wsp)
869 {
870 	char		varname[256];
871 	uint32_t	size;
872 
873 	if (wsp->walk_addr != 0) {
874 		mdb_warn("nca_nodef_walk does not support local walks\n");
875 		return (WALK_DONE);
876 	}
877 
878 	if (mdb_readvar(&wsp->walk_addr, wsp->walk_arg) == -1) {
879 		mdb_warn("cannot read symbol %s", wsp->walk_arg);
880 		return (WALK_ERR);
881 	}
882 
883 	mdb_snprintf(varname, sizeof (varname), "%s_sz", wsp->walk_arg);
884 
885 	if (mdb_readvar(&size, varname) == -1) {
886 		mdb_warn("cannot read symbol %s", varname);
887 		return (WALK_ERR);
888 	}
889 
890 	wsp->walk_data = (void *)(uintptr_t)size;
891 
892 	return (WALK_NEXT);
893 }
894 
895 /*
896  * Walk the NCA node fanout table; `wsp->walk_data' is used to keep
897  * track of the number of indicies that are left to walk so we know
898  * when to stop.
899  */
900 static int
901 nca_nodef_walk_step(mdb_walk_state_t *wsp)
902 {
903 	nodef_t		nodef;
904 	node_t		node;
905 	int		status;
906 	intptr_t	i = (intptr_t)wsp->walk_data;
907 
908 	if (i-- <= 0)
909 		return (WALK_DONE);
910 
911 	if (mdb_vread(&nodef, sizeof (nodef_t), wsp->walk_addr) == -1) {
912 		mdb_warn("cannot read nodef_t at %p", wsp->walk_addr);
913 		return (WALK_ERR);
914 	}
915 
916 	status = wsp->walk_callback(wsp->walk_addr, &nodef, wsp->walk_cbdata);
917 
918 	wsp->walk_data = (void *)i;
919 	wsp->walk_addr += sizeof (nodef_t);
920 
921 	if (nodef.head != NULL) {
922 		/*
923 		 * Point to the node_t instead of the nodef_t so that output
924 		 * can be piped to ::nca_node dcmd.
925 		 */
926 		if (mdb_vread(&node, sizeof (node),
927 		    (uintptr_t)nodef.head) == -1) {
928 			mdb_warn("cannot read node_t at %p", nodef.head);
929 			return (WALK_ERR);
930 		}
931 
932 		status = wsp->walk_callback((uintptr_t)nodef.head,
933 		    &node, wsp->walk_cbdata);
934 	} else {
935 		status = WALK_NEXT;
936 	}
937 
938 	return (status);
939 }
940 
941 /*
942  * Initialize a walk for the NCA CPU table.  Note that local walks
943  * are not supported since they're more trouble than they're worth.
944  */
945 static int
946 nca_cpu_walk_init(mdb_walk_state_t *wsp)
947 {
948 	int	ncpus;
949 
950 	if (wsp->walk_addr != 0) {
951 		mdb_warn("nca_cpu_walk does not support local walks\n");
952 		return (WALK_DONE);
953 	}
954 
955 	if (mdb_readvar(&wsp->walk_addr, "nca_gv") == -1) {
956 		mdb_warn("cannot read symbol nca_gv");
957 		return (WALK_ERR);
958 	}
959 
960 	if (mdb_readvar(&ncpus, "ncpus") == -1) {
961 		mdb_warn("cannot read symbol ncpus");
962 		return (WALK_ERR);
963 	}
964 	wsp->walk_data = (void *)(uintptr_t)ncpus;
965 
966 	return (WALK_NEXT);
967 }
968 
969 /*
970  * Walk the NCA CPU table; `wsp->walk_data' is used to keep track of the
971  * number of CPUs that are left to walk so we know when to stop.
972  */
973 static int
974 nca_cpu_walk_step(mdb_walk_state_t *wsp)
975 {
976 	nca_cpu_t	cpu;
977 	int		status;
978 	intptr_t	curcpu = (intptr_t)wsp->walk_data;
979 
980 	if (curcpu-- <= 0)
981 		return (WALK_DONE);
982 
983 	if (mdb_vread(&cpu, sizeof (nca_cpu_t), wsp->walk_addr) == -1) {
984 		mdb_warn("cannot read nca_cpu_t at %p", wsp->walk_addr);
985 		return (WALK_ERR);
986 	}
987 
988 	status = wsp->walk_callback(wsp->walk_addr, &cpu, wsp->walk_cbdata);
989 
990 	wsp->walk_data = (void *)curcpu;
991 	wsp->walk_addr += sizeof (nca_cpu_t);
992 
993 	return (status);
994 }
995 
996 /*
997  * Initialize a walk for the NCA timer list.  Note that local walks
998  * are not supported since this walk is layered on top of "nca_cpu"
999  * which doesn't support them (and they're not too useful here anyway).
1000  */
1001 static int
1002 nca_timer_walk_init(mdb_walk_state_t *wsp)
1003 {
1004 	if (wsp->walk_addr != 0) {
1005 		mdb_warn("nca_timer_walk does not support local walks\n");
1006 		return (WALK_DONE);
1007 	}
1008 
1009 	if (mdb_layered_walk("nca_cpu", wsp) == -1) {
1010 		mdb_warn("cannot walk nca_cpu");
1011 		return (WALK_ERR);
1012 	}
1013 
1014 	return (WALK_NEXT);
1015 }
1016 
1017 /*
1018  * Walk the NCA timer list; done as a layered walk on top of "nca_cpu".
1019  */
1020 static int
1021 nca_timer_walk_step(mdb_walk_state_t *wsp)
1022 {
1023 	const nca_cpu_t	*nca_cpu = wsp->walk_layer;
1024 	ti_t		ti;
1025 
1026 	/*
1027 	 * Just skip CPUs that don't have any timers running.
1028 	 */
1029 	if (nca_cpu->tcp_ti == NULL)
1030 		return (WALK_NEXT);
1031 
1032 	if (mdb_vread(&ti, sizeof (ti_t), (uintptr_t)nca_cpu->tcp_ti) == -1) {
1033 		mdb_warn("cannot read ti_t at %p", nca_cpu->tcp_ti);
1034 		return (WALK_ERR);
1035 	}
1036 
1037 	return (wsp->walk_callback((uintptr_t)nca_cpu->tcp_ti, &ti,
1038 	    wsp->walk_cbdata));
1039 }
1040 
1041 /*
1042  * Initialize a walk for NCA node LRUs; the type of LRU to walk should
1043  * be specified through `wsp->walk_arg'.  If no starting location for
1044  * the walk is given, `wsp->walk_addr' is set to the head of the
1045  * appropriate LRU.
1046  */
1047 static int
1048 nca_node_lru_walk_init(mdb_walk_state_t *wsp)
1049 {
1050 	GElf_Sym	sym;
1051 	lru_t		lru;
1052 
1053 	if (wsp->walk_addr != 0)
1054 		return (WALK_NEXT);
1055 
1056 	/*
1057 	 * We do this instead of mdb_readvar() so that we catch changes
1058 	 * in the size of the lru_t structure.
1059 	 */
1060 	if (mdb_lookup_by_name("nca_lru", &sym) == -1) {
1061 		mdb_warn("cannot lookup symbol nca_lru");
1062 		return (WALK_ERR);
1063 	}
1064 
1065 	if (sym.st_size != sizeof (lru)) {
1066 		mdb_warn("nca_lru object size mismatch\n");
1067 		return (WALK_ERR);
1068 	}
1069 
1070 	if (mdb_vread(&lru, sym.st_size, (uintptr_t)sym.st_value) == -1) {
1071 		mdb_warn("cannot read nca_lru at %p", sym.st_value);
1072 		return (WALK_ERR);
1073 	}
1074 
1075 	if (wsp->walk_arg == NCA_WALK_PLRU)
1076 		wsp->walk_addr = (uintptr_t)lru.phead;
1077 	else
1078 		wsp->walk_addr = (uintptr_t)lru.vhead;
1079 
1080 	return (WALK_NEXT);
1081 }
1082 
1083 /*
1084  * Walk the NCA node LRUs; the type of LRU to walk should be specified
1085  * through `wsp->walk_arg'.
1086  */
1087 static int
1088 nca_node_lru_walk_step(mdb_walk_state_t *wsp)
1089 {
1090 	node_t		node;
1091 	int		status;
1092 
1093 	if (wsp->walk_addr == 0)
1094 		return (WALK_DONE);
1095 
1096 	if (mdb_vread(&node, sizeof (node_t), wsp->walk_addr) == -1) {
1097 		mdb_warn("cannot read node_t at %p", wsp->walk_addr);
1098 		return (WALK_ERR);
1099 	}
1100 
1101 	status = wsp->walk_callback(wsp->walk_addr, &node, wsp->walk_cbdata);
1102 
1103 	if (wsp->walk_arg == NCA_WALK_PLRU)
1104 		wsp->walk_addr = (uintptr_t)node.plrunn;
1105 	else
1106 		wsp->walk_addr = (uintptr_t)node.vlrunn;
1107 
1108 	return (status);
1109 }
1110 
1111 /*
1112  * Walk the NCA node structures; follows node_t next pointers from a
1113  * given offset, specified through `wsp->walk_arg'.
1114  */
1115 static int
1116 nca_node_walk_step(mdb_walk_state_t *wsp)
1117 {
1118 	node_t		node;
1119 	int		status;
1120 
1121 	if (wsp->walk_addr == 0) {
1122 		mdb_warn("nca_node_walk does not support global walks\n");
1123 		return (WALK_DONE);
1124 	}
1125 
1126 	if (mdb_vread(&node, sizeof (node_t), wsp->walk_addr) == -1) {
1127 		mdb_warn("cannot read node_t at %p", wsp->walk_addr);
1128 		return (WALK_ERR);
1129 	}
1130 
1131 	status = wsp->walk_callback(wsp->walk_addr, &node, wsp->walk_cbdata);
1132 	if (status != WALK_NEXT)
1133 		return (status);
1134 
1135 	/* LINTED */
1136 	wsp->walk_addr = *(uintptr_t *)((caddr_t)&node +
1137 	    (uint_t)(uintptr_t)wsp->walk_arg);
1138 
1139 	if (wsp->walk_addr == 0)
1140 		return (WALK_DONE);
1141 
1142 	return (WALK_NEXT);
1143 }
1144 
1145 /*
1146  * Walk the NCA connection structures; follows nca_conn_t next pointers
1147  * from a given offset, specified through `wsp->walk_arg'.
1148  */
1149 static int
1150 nca_conn_walk_step(mdb_walk_state_t *wsp)
1151 {
1152 	nca_conn_t		conn;
1153 	int		status;
1154 
1155 	if (wsp->walk_addr == 0) {
1156 		mdb_warn("nca_conn_walk does not support global walks\n");
1157 		return (WALK_DONE);
1158 	}
1159 
1160 	if (mdb_vread(&conn, sizeof (nca_conn_t), wsp->walk_addr) == -1) {
1161 		mdb_warn("cannot read nca_conn_t at %p", wsp->walk_addr);
1162 		return (WALK_ERR);
1163 	}
1164 
1165 	status = wsp->walk_callback(wsp->walk_addr, &conn, wsp->walk_cbdata);
1166 	if (status != WALK_NEXT)
1167 		return (status);
1168 
1169 	/* LINTED */
1170 	wsp->walk_addr = *(uintptr_t *)((caddr_t)&conn +
1171 	    (uint_t)(uintptr_t)wsp->walk_arg);
1172 
1173 	if (wsp->walk_addr == 0)
1174 		return (WALK_DONE);
1175 
1176 	return (WALK_NEXT);
1177 }
1178 
1179 static const mdb_dcmd_t dcmds[] = {
1180 	{ "nca_conn",	":[-tx]", "print core NCA nca_conn_t info",   nca_conn,
1181 	    nca_conn_help },
1182 	{ "nca_tcpconn", ":[-t]", "print TCP NCA nca_conn_t info",
1183 	    nca_tcpconn, nca_tcpconn_help },
1184 	{ "nca_io2",	":[-pv]", "print core NCA io2_t info",    nca_io2,
1185 	    nca_io2_help },
1186 	{ "nca_node",	":[-rv]", "print core NCA node_t info",   nca_node,
1187 	    nca_node_help },
1188 	{ "nca_timer",	"?[-e]",  "print core NCA timer info",    nca_timer,
1189 	    nca_timer_help },
1190 	{ NULL }
1191 };
1192 
1193 static const mdb_walker_t walkers[] = {
1194 	{ "nca_conn_hash",	"walk the NCA connection hash chain", 0,
1195 	    nca_conn_walk_step, 0, (void *)OFFSETOF(nca_conn_t, hashnext) },
1196 	{ "nca_conn_bind",	"walk the NCA connection bind chain", 0,
1197 	    nca_conn_walk_step, 0, (void *)OFFSETOF(nca_conn_t, bindnext) },
1198 	{ "nca_conn_miss",	"walk the NCA connection miss chain", 0,
1199 	    nca_conn_walk_step, 0, (void *)OFFSETOF(nca_conn_t, nodenext) },
1200 	{ "nca_conn_tw",	"walk the NCA connection TIME_WAIT chain", 0,
1201 	    nca_conn_walk_step, 0, (void *)OFFSETOF(nca_conn_t, twnext) },
1202 
1203 	{ "nca_node_file",	"walk the NCA node file chain", 0,
1204 	    nca_node_walk_step, 0, (void *)OFFSETOF(node_t, filenext) },
1205 	{ "nca_node_hash",	"walk the NCA node hash chain", 0,
1206 	    nca_node_walk_step, 0, (void *)OFFSETOF(node_t, hashnext) },
1207 	{ "nca_node_chunk",	"walk the NCA node chunk chain", 0,
1208 	    nca_node_walk_step, 0, (void *)OFFSETOF(node_t, next) },
1209 	{ "nca_node_ctag",	"walk the NCA node ctag chain", 0,
1210 	    nca_node_walk_step, 0, (void *)OFFSETOF(node_t, ctagnext) },
1211 
1212 	{ "nca_node_plru",	"walk the NCA node physical LRU chain",
1213 	    nca_node_lru_walk_init, nca_node_lru_walk_step, 0, NCA_WALK_PLRU },
1214 	{ "nca_node_vlru",	"walk the NCA node virtual LRU chain",
1215 	    nca_node_lru_walk_init, nca_node_lru_walk_step, 0, NCA_WALK_VLRU },
1216 
1217 	{ "nca_uri_hash",	"walk the NCA URI node hash table",
1218 	    nca_nodef_walk_init, nca_nodef_walk_step, 0, "ncaurihash" },
1219 	{ "nca_file_hash",	"walk the NCA file node hash table",
1220 	    nca_nodef_walk_init, nca_nodef_walk_step, 0, "ncafilehash" },
1221 	{ "nca_ctag_hash",	"walk the NCA ctag node hash table",
1222 	    nca_nodef_walk_init, nca_nodef_walk_step, 0, "ncactaghash" },
1223 	{ "nca_vnode_hash",	"walk the NCA vnode node hash table",
1224 	    nca_nodef_walk_init, nca_nodef_walk_step, 0, "ncavnodehash" },
1225 
1226 	{ "nca_cpu",		"walk the NCA CPU table",
1227 	    nca_cpu_walk_init,   nca_cpu_walk_step },
1228 	{ "nca_timer",		"walk the NCA timer table",
1229 	    nca_timer_walk_init, nca_timer_walk_step },
1230 	{ "nca_connf",		"walk the NCA connection fanout",
1231 	    nca_connf_walk_init, nca_connf_walk_step },
1232 
1233 	{ NULL }
1234 };
1235 
1236 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
1237 
1238 const mdb_modinfo_t *
1239 _mdb_init(void)
1240 {
1241 	return (&modinfo);
1242 }
1243