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