xref: /illumos-gate/usr/src/cmd/mdb/common/modules/nfs/nfs_clnt.c (revision ddb365bfc9e868ad24ccdcb0dc91af18b10df082)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 /*
12  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
13  */
14 
15 #include <sys/mdb_modapi.h>
16 #include <sys/list.h>
17 #include <nfs/rnode.h>
18 #include <nfs/rnode4.h>
19 #include <nfs/nfs_clnt.h>
20 #include <nfs/nfs4_clnt.h>
21 #include <mdb/mdb_ks.h>
22 #include <mdb/mdb_ctf.h>
23 
24 #include "nfs_clnt.h"
25 #include "common.h"
26 
27 /*
28  * Common functions
29  */
30 
31 static void
32 nfs_print_io_stat(uintptr_t kstat_addr)
33 {
34 	kstat_t kstat;
35 	kstat_io_t kstat_io;
36 
37 	mdb_printf("IO statistics for this mount:\n");
38 	mdb_inc_indent(2);
39 
40 	if (mdb_vread(&kstat, sizeof (kstat), kstat_addr) == -1 ||
41 	    mdb_vread(&kstat_io, sizeof (kstat_io),
42 	    (uintptr_t)KSTAT_IO_PTR(&kstat)) == -1) {
43 		mdb_printf("No. of bytes read:       %9s\n", "??");
44 		mdb_printf("No. of read operations:  %9s\n", "??");
45 		mdb_printf("No. of bytes written:    %9s\n", "??");
46 		mdb_printf("No. of write operations: %9s\n", "??");
47 	} else {
48 		mdb_printf("No. of bytes read:       %9llu\n", kstat_io.nread);
49 		mdb_printf("No. of read operations:  %9lu\n", kstat_io.reads);
50 		mdb_printf("No. of bytes written:    %9llu\n",
51 		    kstat_io.nwritten);
52 		mdb_printf("No. of write operations: %9lu\n", kstat_io.writes);
53 	}
54 
55 	mdb_dec_indent(2);
56 }
57 
58 static int
59 walk_count_cb(uintptr_t addr, const void *data, void *cb_data)
60 {
61 	(*(size_t *)cb_data)++;
62 	return (WALK_NEXT);
63 }
64 
65 #define	TBL_ENTRY(e)	{#e, e}
66 
67 static const struct {
68 	const char *str;
69 	nfs_opnum4 op;
70 } nfs4_op_tbl[] = {
71 	TBL_ENTRY(OP_ACCESS),
72 	TBL_ENTRY(OP_CLOSE),
73 	TBL_ENTRY(OP_COMMIT),
74 	TBL_ENTRY(OP_CREATE),
75 	TBL_ENTRY(OP_DELEGPURGE),
76 	TBL_ENTRY(OP_DELEGRETURN),
77 	TBL_ENTRY(OP_GETATTR),
78 	TBL_ENTRY(OP_GETFH),
79 	TBL_ENTRY(OP_LINK),
80 	TBL_ENTRY(OP_LOCK),
81 	TBL_ENTRY(OP_LOCKT),
82 	TBL_ENTRY(OP_LOCKU),
83 	TBL_ENTRY(OP_LOOKUP),
84 	TBL_ENTRY(OP_LOOKUPP),
85 	TBL_ENTRY(OP_NVERIFY),
86 	TBL_ENTRY(OP_OPEN),
87 	TBL_ENTRY(OP_OPENATTR),
88 	TBL_ENTRY(OP_OPEN_CONFIRM),
89 	TBL_ENTRY(OP_OPEN_DOWNGRADE),
90 	TBL_ENTRY(OP_PUTFH),
91 	TBL_ENTRY(OP_PUTPUBFH),
92 	TBL_ENTRY(OP_PUTROOTFH),
93 	TBL_ENTRY(OP_READ),
94 	TBL_ENTRY(OP_READDIR),
95 	TBL_ENTRY(OP_READLINK),
96 	TBL_ENTRY(OP_REMOVE),
97 	TBL_ENTRY(OP_RENAME),
98 	TBL_ENTRY(OP_RENEW),
99 	TBL_ENTRY(OP_RESTOREFH),
100 	TBL_ENTRY(OP_SAVEFH),
101 	TBL_ENTRY(OP_SECINFO),
102 	TBL_ENTRY(OP_SETATTR),
103 	TBL_ENTRY(OP_SETCLIENTID),
104 	TBL_ENTRY(OP_SETCLIENTID_CONFIRM),
105 	TBL_ENTRY(OP_VERIFY),
106 	TBL_ENTRY(OP_WRITE),
107 	TBL_ENTRY(OP_RELEASE_LOCKOWNER),
108 	TBL_ENTRY(OP_ILLEGAL),
109 	TBL_ENTRY(OP_CCREATE),
110 	TBL_ENTRY(OP_CLINK),
111 	TBL_ENTRY(OP_CLOOKUP),
112 	TBL_ENTRY(OP_COPEN),
113 	TBL_ENTRY(OP_CPUTFH),
114 	TBL_ENTRY(OP_CREMOVE),
115 	TBL_ENTRY(OP_CRENAME),
116 	TBL_ENTRY(OP_CSECINFO),
117 	{NULL}
118 };
119 
120 static const char *
121 nfs4_op_str(nfs_opnum4 op)
122 {
123 	int i;
124 
125 	for (i = 0; nfs4_op_tbl[i].str != NULL; i++)
126 		if (nfs4_op_tbl[i].op == op)
127 			return (nfs4_op_tbl[i].str);
128 
129 	return ("??");
130 }
131 
132 static const struct {
133 	const char *str;
134 	nfs4_recov_t action;
135 } nfs4_recov_tbl[] = {
136 	TBL_ENTRY(NR_UNUSED),
137 	TBL_ENTRY(NR_CLIENTID),
138 	TBL_ENTRY(NR_OPENFILES),
139 	TBL_ENTRY(NR_FHEXPIRED),
140 	TBL_ENTRY(NR_FAILOVER),
141 	TBL_ENTRY(NR_WRONGSEC),
142 	TBL_ENTRY(NR_EXPIRED),
143 	TBL_ENTRY(NR_BAD_STATEID),
144 	TBL_ENTRY(NR_BADHANDLE),
145 	TBL_ENTRY(NR_BAD_SEQID),
146 	TBL_ENTRY(NR_OLDSTATEID),
147 	TBL_ENTRY(NR_GRACE),
148 	TBL_ENTRY(NR_DELAY),
149 	TBL_ENTRY(NR_LOST_LOCK),
150 	TBL_ENTRY(NR_LOST_STATE_RQST),
151 	TBL_ENTRY(NR_STALE),
152 	TBL_ENTRY(NR_MOVED),
153 	{NULL}
154 };
155 
156 static const char *
157 nfs4_recov_str(nfs4_recov_t action)
158 {
159 	int i;
160 
161 	for (i = 0; nfs4_recov_tbl[i].str != NULL; i++)
162 		if (nfs4_recov_tbl[i].action == action)
163 			return (nfs4_recov_tbl[i].str);
164 
165 	return ("??");
166 }
167 
168 static const struct {
169 	const char *str;
170 	nfsstat4 stat;
171 } nfs4_stat_tbl[] = {
172 	TBL_ENTRY(NFS4_OK),
173 	TBL_ENTRY(NFS4ERR_PERM),
174 	TBL_ENTRY(NFS4ERR_NOENT),
175 	TBL_ENTRY(NFS4ERR_IO),
176 	TBL_ENTRY(NFS4ERR_NXIO),
177 	TBL_ENTRY(NFS4ERR_ACCESS),
178 	TBL_ENTRY(NFS4ERR_EXIST),
179 	TBL_ENTRY(NFS4ERR_XDEV),
180 	TBL_ENTRY(NFS4ERR_NOTDIR),
181 	TBL_ENTRY(NFS4ERR_ISDIR),
182 	TBL_ENTRY(NFS4ERR_INVAL),
183 	TBL_ENTRY(NFS4ERR_FBIG),
184 	TBL_ENTRY(NFS4ERR_NOSPC),
185 	TBL_ENTRY(NFS4ERR_ROFS),
186 	TBL_ENTRY(NFS4ERR_MLINK),
187 	TBL_ENTRY(NFS4ERR_NAMETOOLONG),
188 	TBL_ENTRY(NFS4ERR_NOTEMPTY),
189 	TBL_ENTRY(NFS4ERR_DQUOT),
190 	TBL_ENTRY(NFS4ERR_STALE),
191 	TBL_ENTRY(NFS4ERR_BADHANDLE),
192 	TBL_ENTRY(NFS4ERR_BAD_COOKIE),
193 	TBL_ENTRY(NFS4ERR_NOTSUPP),
194 	TBL_ENTRY(NFS4ERR_TOOSMALL),
195 	TBL_ENTRY(NFS4ERR_SERVERFAULT),
196 	TBL_ENTRY(NFS4ERR_BADTYPE),
197 	TBL_ENTRY(NFS4ERR_DELAY),
198 	TBL_ENTRY(NFS4ERR_SAME),
199 	TBL_ENTRY(NFS4ERR_DENIED),
200 	TBL_ENTRY(NFS4ERR_EXPIRED),
201 	TBL_ENTRY(NFS4ERR_LOCKED),
202 	TBL_ENTRY(NFS4ERR_GRACE),
203 	TBL_ENTRY(NFS4ERR_FHEXPIRED),
204 	TBL_ENTRY(NFS4ERR_SHARE_DENIED),
205 	TBL_ENTRY(NFS4ERR_WRONGSEC),
206 	TBL_ENTRY(NFS4ERR_CLID_INUSE),
207 	TBL_ENTRY(NFS4ERR_RESOURCE),
208 	TBL_ENTRY(NFS4ERR_MOVED),
209 	TBL_ENTRY(NFS4ERR_NOFILEHANDLE),
210 	TBL_ENTRY(NFS4ERR_MINOR_VERS_MISMATCH),
211 	TBL_ENTRY(NFS4ERR_STALE_CLIENTID),
212 	TBL_ENTRY(NFS4ERR_STALE_STATEID),
213 	TBL_ENTRY(NFS4ERR_OLD_STATEID),
214 	TBL_ENTRY(NFS4ERR_BAD_STATEID),
215 	TBL_ENTRY(NFS4ERR_BAD_SEQID),
216 	TBL_ENTRY(NFS4ERR_NOT_SAME),
217 	TBL_ENTRY(NFS4ERR_LOCK_RANGE),
218 	TBL_ENTRY(NFS4ERR_SYMLINK),
219 	TBL_ENTRY(NFS4ERR_RESTOREFH),
220 	TBL_ENTRY(NFS4ERR_LEASE_MOVED),
221 	TBL_ENTRY(NFS4ERR_ATTRNOTSUPP),
222 	TBL_ENTRY(NFS4ERR_NO_GRACE),
223 	TBL_ENTRY(NFS4ERR_RECLAIM_BAD),
224 	TBL_ENTRY(NFS4ERR_RECLAIM_CONFLICT),
225 	TBL_ENTRY(NFS4ERR_BADXDR),
226 	TBL_ENTRY(NFS4ERR_LOCKS_HELD),
227 	TBL_ENTRY(NFS4ERR_OPENMODE),
228 	TBL_ENTRY(NFS4ERR_BADOWNER),
229 	TBL_ENTRY(NFS4ERR_BADCHAR),
230 	TBL_ENTRY(NFS4ERR_BADNAME),
231 	TBL_ENTRY(NFS4ERR_BAD_RANGE),
232 	TBL_ENTRY(NFS4ERR_LOCK_NOTSUPP),
233 	TBL_ENTRY(NFS4ERR_OP_ILLEGAL),
234 	TBL_ENTRY(NFS4ERR_DEADLOCK),
235 	TBL_ENTRY(NFS4ERR_FILE_OPEN),
236 	TBL_ENTRY(NFS4ERR_ADMIN_REVOKED),
237 	TBL_ENTRY(NFS4ERR_CB_PATH_DOWN),
238 	{NULL}
239 };
240 
241 static const char *
242 nfs4_stat_str(nfsstat4 stat)
243 {
244 	int i;
245 
246 	for (i = 0; nfs4_stat_tbl[i].str != NULL; i++)
247 		if (nfs4_stat_tbl[i].stat == stat)
248 			return (nfs4_stat_tbl[i].str);
249 
250 	return ("??");
251 }
252 
253 static const struct {
254 	const char *str;
255 	nfs4_tag_type_t tt;
256 } nfs4_tag_tbl[] = {
257 	{"", TAG_NONE},
258 	{"access", TAG_ACCESS},
259 	{"close", TAG_CLOSE},
260 	{"lost close", TAG_CLOSE_LOST},
261 	{"undo close", TAG_CLOSE_UNDO},
262 	{"commit", TAG_COMMIT},
263 	{"delegreturn", TAG_DELEGRETURN},
264 	{"fsinfo", TAG_FSINFO},
265 	{"get symlink text", TAG_GET_SYMLINK},
266 	{"getattr", TAG_GETATTR},
267 	{"getattr fslocation", TAG_GETATTR_FSLOCATION},
268 	{"inactive", TAG_INACTIVE},
269 	{"link", TAG_LINK},
270 	{"lock", TAG_LOCK},
271 	{"reclaim lock", TAG_LOCK_RECLAIM},
272 	{"resend lock", TAG_LOCK_RESEND},
273 	{"reinstate lock", TAG_LOCK_REINSTATE},
274 	{"unknown lock", TAG_LOCK_UNKNOWN},
275 	{"lock test", TAG_LOCKT},
276 	{"unlock", TAG_LOCKU},
277 	{"resend locku", TAG_LOCKU_RESEND},
278 	{"reinstate unlock", TAG_LOCKU_REINSTATE},
279 	{"lookup", TAG_LOOKUP},
280 	{"lookup parent", TAG_LOOKUP_PARENT},
281 	{"lookup valid", TAG_LOOKUP_VALID},
282 	{"lookup valid parent", TAG_LOOKUP_VPARENT},
283 	{"mkdir", TAG_MKDIR},
284 	{"mknod", TAG_MKNOD},
285 	{"mount", TAG_MOUNT},
286 	{"open", TAG_OPEN},
287 	{"open confirm", TAG_OPEN_CONFIRM},
288 	{"lost open confirm", TAG_OPEN_CONFIRM_LOST},
289 	{"open downgrade", TAG_OPEN_DG},
290 	{"lost open downgrade", TAG_OPEN_DG_LOST},
291 	{"lost open", TAG_OPEN_LOST},
292 	{"openattr", TAG_OPENATTR},
293 	{"pathconf", TAG_PATHCONF},
294 	{"putrootfh", TAG_PUTROOTFH},
295 	{"read", TAG_READ},
296 	{"readahead", TAG_READAHEAD},
297 	{"readdir", TAG_READDIR},
298 	{"readlink", TAG_READLINK},
299 	{"relock", TAG_RELOCK},
300 	{"remap lookup", TAG_REMAP_LOOKUP},
301 	{"remap lookup attr dir", TAG_REMAP_LOOKUP_AD},
302 	{"remap lookup named attrs", TAG_REMAP_LOOKUP_NA},
303 	{"remap mount", TAG_REMAP_MOUNT},
304 	{"rmdir", TAG_RMDIR},
305 	{"remove", TAG_REMOVE},
306 	{"rename", TAG_RENAME},
307 	{"rename volatile fh", TAG_RENAME_VFH},
308 	{"renew", TAG_RENEW},
309 	{"reopen", TAG_REOPEN},
310 	{"lost reopen", TAG_REOPEN_LOST},
311 	{"secinfo", TAG_SECINFO},
312 	{"setattr", TAG_SETATTR},
313 	{"setclientid", TAG_SETCLIENTID},
314 	{"setclientid_confirm", TAG_SETCLIENTID_CF},
315 	{"symlink", TAG_SYMLINK},
316 	{"write", TAG_WRITE},
317 	{NULL, 0}
318 };
319 
320 static const char *
321 nfs4_tag_str(nfs4_tag_type_t tt)
322 {
323 	int i;
324 
325 	for (i = 0; nfs4_tag_tbl[i].str != NULL; i++)
326 		if (nfs4_tag_tbl[i].tt == tt)
327 			return (nfs4_tag_tbl[i].str);
328 
329 	return ("??");
330 }
331 
332 /*
333  * nfs_mntinfo dcmd implementation
334  */
335 
336 static const mdb_bitmask_t nfs_mi_flags[] = {
337 	{"MI_HARD", MI_HARD, MI_HARD},
338 	{"MI_PRINTED", MI_PRINTED, MI_PRINTED},
339 	{"MI_INT", MI_INT, MI_INT},
340 	{"MI_DOWN", MI_DOWN, MI_DOWN},
341 	{"MI_NOAC", MI_NOAC, MI_NOAC},
342 	{"MI_NOCTO", MI_NOCTO, MI_NOCTO},
343 	{"MI_DYNAMIC", MI_DYNAMIC, MI_DYNAMIC},
344 	{"MI_LLOCK", MI_LLOCK, MI_LLOCK},
345 	{"MI_GRPID", MI_GRPID, MI_GRPID},
346 	{"MI_RPCTIMESYNC", MI_RPCTIMESYNC, MI_RPCTIMESYNC},
347 	{"MI_LINK", MI_LINK, MI_LINK},
348 	{"MI_SYMLINK", MI_SYMLINK, MI_SYMLINK},
349 	{"MI_READDIRONLY", MI_READDIRONLY, MI_READDIRONLY},
350 	{"MI_ACL", MI_ACL, MI_ACL},
351 	{"MI_BINDINPROG", MI_BINDINPROG, MI_BINDINPROG},
352 	{"MI_LOOPBACK", MI_LOOPBACK, MI_LOOPBACK},
353 	{"MI_SEMISOFT", MI_SEMISOFT, MI_SEMISOFT},
354 	{"MI_NOPRINT", MI_NOPRINT, MI_NOPRINT},
355 	{"MI_DIRECTIO", MI_DIRECTIO, MI_DIRECTIO},
356 	{"MI_EXTATTR", MI_EXTATTR, MI_EXTATTR},
357 	{"MI_ASYNC_MGR_STOP", MI_ASYNC_MGR_STOP, MI_ASYNC_MGR_STOP},
358 	{"MI_DEAD", MI_DEAD, MI_DEAD},
359 	{NULL, 0, 0}
360 };
361 
362 static int
363 nfs_print_mntinfo_cb(uintptr_t addr, const void *data, void *cb_data)
364 {
365 	const mntinfo_t *mi = data;
366 	uintptr_t nfs3_ops;
367 	vfs_t vfs;
368 	char buf[MAXPATHLEN];
369 	uint_t opt_v = *(uint_t *)cb_data;
370 	int i;
371 
372 	if (mdb_readvar(&nfs3_ops, "nfs3_vfsops") == -1) {
373 		mdb_warn("failed to read %s", "nfs3_vfsops");
374 		return (WALK_ERR);
375 	}
376 
377 	if (mdb_vread(&vfs, sizeof (vfs), (uintptr_t)mi->mi_vfsp) == -1) {
378 		mdb_warn("failed to read vfs_t at %p", mi->mi_vfsp);
379 		return (WALK_ERR);
380 	}
381 
382 	mdb_printf("NFS Version: %d\n",
383 	    nfs3_ops == (uintptr_t)vfs.vfs_op ? 3 : 2);
384 	mdb_inc_indent(2);
385 
386 	mdb_printf("mi_flags:    %b\n", mi->mi_flags, nfs_mi_flags);
387 	if (mdb_read_refstr((uintptr_t)vfs.vfs_mntpt, buf,
388 	    sizeof (buf)) == -1)
389 		strcpy(buf, "??");
390 	mdb_printf("mount point: %s\n", buf);
391 	if (mdb_read_refstr((uintptr_t)vfs.vfs_resource, buf,
392 	    sizeof (buf)) == -1)
393 		strcpy(buf, "??");
394 	mdb_printf("mount from:  %s\n", buf);
395 
396 	mdb_dec_indent(2);
397 	mdb_printf("\n");
398 
399 	if (!opt_v)
400 		return (WALK_NEXT);
401 
402 	mdb_inc_indent(2);
403 
404 	mdb_printf("mi_zone = %p\n", mi->mi_zone);
405 	mdb_printf("mi_curread = %i, mi_curwrite = %i, mi_retrans = %i, "
406 	    "mi_timeo = %i\n", mi->mi_curread, mi->mi_curwrite, mi->mi_retrans,
407 	    mi->mi_timeo);
408 	mdb_printf("mi_acregmin = %lu, mi_acregmax = %lu, mi_acdirmin = %lu, "
409 	    "mi_acdirmax = %lu\n", mi->mi_acregmin, mi->mi_acregmax,
410 	    mi->mi_acdirmin, mi->mi_acdirmax);
411 
412 	mdb_printf("\nServer list: %p\n", mi->mi_servers);
413 	mdb_inc_indent(2);
414 	if (mdb_pwalk_dcmd("nfs_serv", "nfs_servinfo", 0, NULL,
415 	    (uintptr_t)mi->mi_servers) == -1)
416 		mdb_printf("??\n");
417 	mdb_dec_indent(2);
418 
419 	mdb_printf("Current Server: %p ", mi->mi_curr_serv);
420 	if (mdb_call_dcmd("nfs_servinfo", (uintptr_t)mi->mi_curr_serv,
421 	    DCMD_ADDRSPEC, 0, NULL) == -1)
422 		mdb_printf("??\n");
423 
424 	mdb_printf(
425 	    "\nTotal: Server Non-responses = %u, Server Failovers = %u\n",
426 	    mi->mi_noresponse, mi->mi_failover);
427 
428 	if (mi->mi_io_kstats != NULL)
429 		nfs_print_io_stat((uintptr_t)mi->mi_io_kstats);
430 
431 	mdb_printf("\nAsync Request queue:\n");
432 	mdb_inc_indent(2);
433 	mdb_printf("max threads = %u, active threads = %u\n",
434 	    mi->mi_max_threads, mi->mi_threads[NFS_ASYNC_QUEUE]);
435 	mdb_printf("Async reserved page operation only active threads = %u\n",
436 	    mi->mi_threads[NFS_ASYNC_PGOPS_QUEUE]);
437 	mdb_printf("number requests queued:\n");
438 	for (i = 0; i < NFS_ASYNC_TYPES; i++) {
439 		const char *opname;
440 		size_t count = 0;
441 
442 		switch (i) {
443 		case NFS_PUTAPAGE:
444 			opname = "PUTAPAGE";
445 			break;
446 		case NFS_PAGEIO:
447 			opname = "PAGEIO";
448 			break;
449 		case NFS_COMMIT:
450 			opname = "COMMIT";
451 			break;
452 		case NFS_READ_AHEAD:
453 			opname = "READ_AHEAD";
454 			break;
455 		case NFS_READDIR:
456 			opname = "READDIR";
457 			break;
458 		case NFS_INACTIVE:
459 			opname = "INACTIVE";
460 			break;
461 		default:
462 			opname = "??";
463 			break;
464 		}
465 
466 		if (mi->mi_async_reqs[i] == NULL || mdb_pwalk("nfs_async",
467 		    walk_count_cb, &count, (uintptr_t)mi->mi_async_reqs[i])
468 		    == -1)
469 			mdb_printf("\t%s = ??", opname);
470 		else
471 			mdb_printf("\t%s = %llu", opname, count);
472 	}
473 	mdb_printf("\n");
474 	mdb_dec_indent(2);
475 
476 	if (mi->mi_printftime)
477 		mdb_printf("\nLast error report time = %Y\n",
478 		    mi->mi_printftime);
479 
480 	mdb_dec_indent(2);
481 	mdb_printf("\n");
482 
483 	return (WALK_NEXT);
484 }
485 
486 int
487 nfs_mntinfo_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
488 {
489 	mntinfo_t mi;
490 	uint_t opt_v = FALSE;
491 
492 	if (mdb_getopts(argc, argv,
493 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
494 		return (DCMD_USAGE);
495 
496 	if ((flags & DCMD_ADDRSPEC) == 0) {
497 		if (mdb_walk("nfs_mnt", nfs_print_mntinfo_cb, &opt_v) == -1) {
498 			mdb_warn("failed to walk nfs_mnt");
499 			return (DCMD_ERR);
500 		}
501 		return (DCMD_OK);
502 	}
503 
504 	if (mdb_vread(&mi, sizeof (mi), addr) == -1) {
505 		mdb_warn("failed to read mntinfo_t");
506 		return (DCMD_ERR);
507 	}
508 
509 	return (nfs_print_mntinfo_cb(addr, &mi, &opt_v) == WALK_ERR ? DCMD_ERR
510 	    : DCMD_OK);
511 }
512 
513 void
514 nfs_mntinfo_help(void)
515 {
516 	mdb_printf("-v       verbose information\n");
517 }
518 
519 /*
520  * nfs_servinfo dcmd implementation
521  */
522 
523 int
524 nfs_servinfo_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
525 {
526 	servinfo_t si;
527 	uint_t opt_v = FALSE;
528 	const char *addr_str;
529 	struct knetconfig knconf;
530 	char *hostname;
531 	int i;
532 
533 	if ((flags & DCMD_ADDRSPEC) == 0) {
534 		mdb_printf("requires address of servinfo_t\n");
535 		return (DCMD_USAGE);
536 	}
537 
538 	if (mdb_getopts(argc, argv,
539 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
540 		return (DCMD_USAGE);
541 
542 	if (mdb_vread(&si, sizeof (si), addr) == -1) {
543 		mdb_warn("can't read servinfo_t");
544 		return (DCMD_ERR);
545 	}
546 
547 	addr_str = common_netbuf_str(&si.sv_addr);
548 
549 	if (!opt_v) {
550 		mdb_printf("%s\n", addr_str);
551 		return (DCMD_OK);
552 	}
553 
554 	mdb_printf("secdata ptr = %p\n", si.sv_secdata);
555 
556 	mdb_printf("address = ");
557 	if (mdb_vread(&knconf, sizeof (knconf),
558 	    (uintptr_t)si.sv_knconf) == -1) {
559 		mdb_printf("?\?/?\?/??");
560 	} else {
561 		char knc_str[KNC_STRSIZE];
562 
563 		mdb_printf("%u", knconf.knc_semantics);
564 
565 		if (mdb_readstr(knc_str, sizeof (knc_str),
566 		    (uintptr_t)knconf.knc_protofmly) == -1)
567 			mdb_printf("/??");
568 		else
569 			mdb_printf("/%s", knc_str);
570 
571 		if (mdb_readstr(knc_str, sizeof (knc_str),
572 		    (uintptr_t)knconf.knc_proto) == -1)
573 			mdb_printf("/??");
574 		else
575 			mdb_printf("/%s", knc_str);
576 	}
577 	mdb_printf("/%s\n", addr_str);
578 
579 	if (si.sv_hostnamelen <= 0 || (hostname = mdb_alloc(si.sv_hostnamelen,
580 	    UM_NOSLEEP | UM_GC)) == NULL || mdb_readstr(hostname,
581 	    si.sv_hostnamelen, (uintptr_t)si.sv_hostname) == -1)
582 		mdb_printf("hostname = ??\n");
583 	else
584 		mdb_printf("hostname = %s\n", hostname);
585 
586 	mdb_printf("filehandle = ");
587 	if (si.sv_fhandle.fh_len >= 0 &&
588 	    si.sv_fhandle.fh_len <= NFS_FHANDLE_LEN)
589 		for (i = 0; i < si.sv_fhandle.fh_len; i++)
590 			mdb_printf("%02x",
591 			    (unsigned char)si.sv_fhandle.fh_buf[i]);
592 	else
593 		mdb_printf("??");
594 	mdb_printf("\n\n");
595 
596 	return (DCMD_OK);
597 }
598 
599 void
600 nfs_servinfo_help(void)
601 {
602 	mdb_printf("-v       verbose information\n");
603 }
604 
605 /*
606  * nfs4_mntinfo dcmd implementation
607  */
608 
609 static const mdb_bitmask_t nfs_mi4_flags[] = {
610 	{"MI4_HARD", MI4_HARD, MI4_HARD},
611 	{"MI4_PRINTED", MI4_PRINTED, MI4_PRINTED},
612 	{"MI4_INT", MI4_INT, MI4_INT},
613 	{"MI4_DOWN", MI4_DOWN, MI4_DOWN},
614 	{"MI4_NOAC", MI4_NOAC, MI4_NOAC},
615 	{"MI4_NOCTO", MI4_NOCTO, MI4_NOCTO},
616 	{"MI4_LLOCK", MI4_LLOCK, MI4_LLOCK},
617 	{"MI4_GRPID", MI4_GRPID, MI4_GRPID},
618 	{"MI4_SHUTDOWN", MI4_SHUTDOWN, MI4_SHUTDOWN},
619 	{"MI4_LINK", MI4_LINK, MI4_LINK},
620 	{"MI4_SYMLINK", MI4_SYMLINK, MI4_SYMLINK},
621 	{"MI4_EPHEMERAL_RECURSED", MI4_EPHEMERAL_RECURSED,
622 		MI4_EPHEMERAL_RECURSED},
623 	{"MI4_ACL", MI4_ACL, MI4_ACL},
624 	{"MI4_MIRRORMOUNT", MI4_MIRRORMOUNT, MI4_MIRRORMOUNT},
625 	{"MI4_REFERRAL", MI4_REFERRAL, MI4_REFERRAL},
626 	{"MI4_EPHEMERAL", MI4_EPHEMERAL, MI4_EPHEMERAL},
627 	{"MI4_NOPRINT", MI4_NOPRINT, MI4_NOPRINT},
628 	{"MI4_DIRECTIO", MI4_DIRECTIO, MI4_DIRECTIO},
629 	{"MI4_RECOV_ACTIV", MI4_RECOV_ACTIV, MI4_RECOV_ACTIV},
630 	{"MI4_REMOVE_ON_LAST_CLOSE", MI4_REMOVE_ON_LAST_CLOSE,
631 		MI4_REMOVE_ON_LAST_CLOSE},
632 	{"MI4_RECOV_FAIL", MI4_RECOV_FAIL, MI4_RECOV_FAIL},
633 	{"MI4_PUBLIC", MI4_PUBLIC, MI4_PUBLIC},
634 	{"MI4_MOUNTING", MI4_MOUNTING, MI4_MOUNTING},
635 	{"MI4_POSIX_LOCK", MI4_POSIX_LOCK, MI4_POSIX_LOCK},
636 	{"MI4_LOCK_DEBUG", MI4_LOCK_DEBUG, MI4_LOCK_DEBUG},
637 	{"MI4_DEAD", MI4_DEAD, MI4_DEAD},
638 	{"MI4_INACTIVE_IDLE", MI4_INACTIVE_IDLE, MI4_INACTIVE_IDLE},
639 	{"MI4_BADOWNER_DEBUG", MI4_BADOWNER_DEBUG, MI4_BADOWNER_DEBUG},
640 	{"MI4_ASYNC_MGR_STOP", MI4_ASYNC_MGR_STOP, MI4_ASYNC_MGR_STOP},
641 	{"MI4_TIMEDOUT", MI4_TIMEDOUT, MI4_TIMEDOUT},
642 	{NULL, 0, 0}
643 };
644 
645 static const mdb_bitmask_t nfs_mi4_recovflags[] = {
646 	{"MI4R_NEED_CLIENTID", MI4R_NEED_CLIENTID, MI4R_NEED_CLIENTID},
647 	{"MI4R_REOPEN_FILES", MI4R_REOPEN_FILES, MI4R_REOPEN_FILES},
648 	{"MI4R_NEED_SECINFO", MI4R_NEED_SECINFO, MI4R_NEED_SECINFO},
649 	{"MI4R_NEED_NEW_SERVER", MI4R_NEED_NEW_SERVER, MI4R_NEED_NEW_SERVER},
650 	{"MI4R_REMAP_FILES", MI4R_REMAP_FILES, MI4R_REMAP_FILES},
651 	{"MI4R_SRV_REBOOT", MI4R_SRV_REBOOT, MI4R_SRV_REBOOT},
652 	{"MI4R_LOST_STATE", MI4R_LOST_STATE, MI4R_LOST_STATE},
653 	{"MI4R_BAD_SEQID", MI4R_BAD_SEQID, MI4R_BAD_SEQID},
654 	{"MI4R_MOVED", MI4R_MOVED, MI4R_MOVED},
655 	{NULL, 0, 0}
656 };
657 
658 int
659 nfs4_mntinfo_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
660 {
661 	mntinfo4_t mi;
662 	vfs_t vfs;
663 	char buf[MAXPATHLEN];
664 	uint_t opt_m = FALSE;
665 	uint_t opt_v = FALSE;
666 
667 	if ((flags & DCMD_ADDRSPEC) == 0) {
668 		if (mdb_walk_dcmd("nfs4_mnt", "nfs4_mntinfo", argc,
669 		    argv) == -1) {
670 			mdb_warn("failed to walk nfs4_mnt");
671 			return (DCMD_ERR);
672 		}
673 		return (DCMD_OK);
674 	}
675 
676 	if (mdb_getopts(argc, argv,
677 	    'm', MDB_OPT_SETBITS, TRUE, &opt_m,
678 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
679 		return (DCMD_USAGE);
680 
681 	if (mdb_vread(&mi, sizeof (mi), addr) == -1) {
682 		mdb_warn("failed to read mntinfo4_t at %p", addr);
683 		return (DCMD_ERR);
684 	}
685 
686 	if (mdb_vread(&vfs, sizeof (vfs), (uintptr_t)mi.mi_vfsp) == -1) {
687 		mdb_warn("failed to read vfs_t at %p", mi.mi_vfsp);
688 		return (DCMD_ERR);
689 	}
690 
691 	mdb_printf("+--------------------------------------+\n");
692 	mdb_printf("    mntinfo4_t: 0x%p\n", addr);
693 	mdb_printf("   NFS Version: 4\n");
694 	mdb_printf("      mi_flags: %b\n", mi.mi_flags, nfs_mi4_flags);
695 	mdb_printf("      mi_error: %u\n", mi.mi_error);
696 	mdb_printf(" mi_open_files: %i\n", mi.mi_open_files);
697 	mdb_printf("  mi_msg_count: %i\n", mi.mi_msg_count);
698 	mdb_printf(" mi_recovflags: %b\n", mi.mi_recovflags,
699 	    nfs_mi4_recovflags);
700 	mdb_printf("mi_recovthread: 0x%p\n", mi.mi_recovthread);
701 	mdb_printf("mi_in_recovery: %i\n", mi.mi_in_recovery);
702 
703 	if (mdb_read_refstr((uintptr_t)vfs.vfs_mntpt, buf,
704 	    sizeof (buf)) == -1)
705 		strcpy(buf, "??");
706 	mdb_printf("   mount point: %s\n", buf);
707 	if (mdb_read_refstr((uintptr_t)vfs.vfs_resource, buf,
708 	    sizeof (buf)) == -1)
709 		strcpy(buf, "??");
710 	mdb_printf("    mount from: %s\n", buf);
711 
712 	if (opt_v) {
713 		int i;
714 
715 		mdb_printf("\n");
716 		mdb_inc_indent(2);
717 
718 		mdb_printf("mi_zone = %p\n", mi.mi_zone);
719 		mdb_printf("mi_curread = %i, mi_curwrite = %i, "
720 		    "mi_retrans = %i, mi_timeo = %i\n", mi.mi_curread,
721 		    mi.mi_curwrite, mi.mi_retrans, mi.mi_timeo);
722 		mdb_printf("mi_acregmin = %lu, mi_acregmax = %lu, "
723 		    "mi_acdirmin = %lu, mi_acdirmax = %lu\n", mi.mi_acregmin,
724 		    mi.mi_acregmax, mi.mi_acdirmin, mi.mi_acdirmax);
725 
726 		mdb_printf("\nServer list: %p\n", mi.mi_servers);
727 		mdb_inc_indent(2);
728 		if (mdb_pwalk_dcmd("nfs4_serv", "nfs4_servinfo", 0, NULL,
729 		    (uintptr_t)mi.mi_servers) == -1)
730 			mdb_printf("??\n");
731 		mdb_dec_indent(2);
732 
733 		mdb_printf("Current Server: %p ", mi.mi_curr_serv);
734 		if (mdb_call_dcmd("nfs4_servinfo", (uintptr_t)mi.mi_curr_serv,
735 		    DCMD_ADDRSPEC, 0, NULL) == -1)
736 			mdb_printf("??\n");
737 
738 		mdb_printf("\nTotal: Server Non-responses = %u, "
739 		    "Server Failovers = %u\n", mi.mi_noresponse,
740 		    mi.mi_failover);
741 
742 		if (mi.mi_io_kstats != NULL)
743 			nfs_print_io_stat((uintptr_t)mi.mi_io_kstats);
744 
745 		mdb_printf("\nAsync Request queue:\n");
746 		mdb_inc_indent(2);
747 		mdb_printf("max threads = %u, active threads = %u\n",
748 		    mi.mi_max_threads, mi.mi_threads[NFS4_ASYNC_QUEUE]);
749 		mdb_printf("Async reserved page operation only active "
750 		    "threads = %u\n", mi.mi_threads[NFS4_ASYNC_PGOPS_QUEUE]);
751 		mdb_printf("number requests queued:\n");
752 		for (i = 0; i < NFS4_ASYNC_TYPES; i++) {
753 			const char *opname;
754 			size_t count = 0;
755 
756 			switch (i) {
757 			case NFS4_PUTAPAGE:
758 				opname = "PUTAPAGE";
759 				break;
760 			case NFS4_PAGEIO:
761 				opname = "PAGEIO";
762 				break;
763 			case NFS4_COMMIT:
764 				opname = "COMMIT";
765 				break;
766 			case NFS4_READ_AHEAD:
767 				opname = "READ_AHEAD";
768 				break;
769 			case NFS4_READDIR:
770 				opname = "READDIR";
771 				break;
772 			case NFS4_INACTIVE:
773 				opname = "INACTIVE";
774 				break;
775 			default:
776 				opname = "??";
777 				break;
778 			}
779 
780 			if (mi.mi_async_reqs[i] != NULL &&
781 			    mdb_pwalk("nfs4_async", walk_count_cb, &count,
782 			    (uintptr_t)mi.mi_async_reqs[i]) == -1)
783 				mdb_printf("\t%s = ??", opname);
784 			else
785 				mdb_printf("\t%s = %llu", opname, count);
786 		}
787 		mdb_printf("\n");
788 		mdb_dec_indent(2);
789 
790 		mdb_dec_indent(2);
791 	}
792 
793 	return (DCMD_OK);
794 }
795 
796 void
797 nfs4_mntinfo_help(void)
798 {
799 	mdb_printf("<mntinfo4>::nfs4_mntinfo  -> gives mntinfo4_t information\n"
800 	    "          ::nfs4_mntinfo  -> walks thru all NFSv4 mntinfo4_t\n"
801 	    "Each of these formats also takes the following argument\n"
802 	    "        -v      -> Verbose output\n");
803 }
804 
805 /*
806  * nfs4_servinfo dcmd implementation
807  */
808 
809 int
810 nfs4_servinfo_dcmd(uintptr_t addr, uint_t flags, int argc,
811     const mdb_arg_t *argv)
812 {
813 	servinfo4_t si;
814 	uint_t opt_v = FALSE;
815 	const char *addr_str;
816 	struct knetconfig knconf;
817 	char *hostname;
818 	int i;
819 
820 	if ((flags & DCMD_ADDRSPEC) == 0) {
821 		mdb_printf("requires address of servinfo4_t\n");
822 		return (DCMD_USAGE);
823 	}
824 
825 	if (mdb_getopts(argc, argv,
826 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
827 		return (DCMD_USAGE);
828 
829 	if (mdb_vread(&si, sizeof (si), addr) == -1) {
830 		mdb_warn("can't read servinfo_t");
831 		return (DCMD_ERR);
832 	}
833 
834 	addr_str = common_netbuf_str(&si.sv_addr);
835 
836 	if (!opt_v) {
837 		mdb_printf("%s\n", addr_str);
838 		return (DCMD_OK);
839 	}
840 
841 	mdb_printf("secdata ptr = %p\n", si.sv_secdata);
842 
843 	mdb_printf("address = ");
844 	if (mdb_vread(&knconf, sizeof (knconf),
845 	    (uintptr_t)si.sv_knconf) == -1) {
846 		mdb_printf("?\?/?\?/??");
847 	} else {
848 		char knc_str[KNC_STRSIZE];
849 
850 		mdb_printf("%u", knconf.knc_semantics);
851 
852 		if (mdb_readstr(knc_str, sizeof (knc_str),
853 		    (uintptr_t)knconf.knc_protofmly) == -1)
854 			mdb_printf("/??");
855 		else
856 			mdb_printf("/%s", knc_str);
857 
858 		if (mdb_readstr(knc_str, sizeof (knc_str),
859 		    (uintptr_t)knconf.knc_proto) == -1)
860 			mdb_printf("/??");
861 		else
862 			mdb_printf("/%s", knc_str);
863 	}
864 	mdb_printf("/%s\n", addr_str);
865 
866 	if (si.sv_hostnamelen <= 0 || (hostname = mdb_alloc(si.sv_hostnamelen,
867 	    UM_NOSLEEP | UM_GC)) == NULL || mdb_readstr(hostname,
868 	    si.sv_hostnamelen, (uintptr_t)si.sv_hostname) == -1)
869 		mdb_printf("hostname = ??\n");
870 	else
871 		mdb_printf("hostname = %s\n", hostname);
872 
873 	mdb_printf("server filehandle = ");
874 	if (si.sv_fhandle.fh_len >= 0 && si.sv_fhandle.fh_len <= NFS4_FHSIZE)
875 		for (i = 0; i < si.sv_fhandle.fh_len; i++)
876 			mdb_printf("%02x",
877 			    (unsigned char)si.sv_fhandle.fh_buf[i]);
878 	else
879 		mdb_printf("??");
880 
881 	mdb_printf("\nparent dir filehandle = ");
882 	if (si.sv_pfhandle.fh_len >= 0 && si.sv_pfhandle.fh_len <= NFS4_FHSIZE)
883 		for (i = 0; i < si.sv_pfhandle.fh_len; i++)
884 			mdb_printf("%02x",
885 			    (unsigned char)si.sv_pfhandle.fh_buf[i]);
886 	else
887 		mdb_printf("??");
888 	mdb_printf("\n\n");
889 
890 	return (DCMD_OK);
891 }
892 
893 void
894 nfs4_servinfo_help(void)
895 {
896 	mdb_printf("-v       verbose information\n");
897 }
898 
899 /*
900  * nfs4_server_info dcmd implementation
901  */
902 
903 static const mdb_bitmask_t nfs4_si_flags[] = {
904 	{"N4S_CLIENTID_SET", N4S_CLIENTID_SET, N4S_CLIENTID_SET},
905 	{"N4S_CLIENTID_PEND", N4S_CLIENTID_PEND, N4S_CLIENTID_PEND},
906 	{"N4S_CB_PINGED", N4S_CB_PINGED, N4S_CB_PINGED},
907 	{"N4S_CB_WAITER", N4S_CB_WAITER, N4S_CB_WAITER},
908 	{"N4S_INSERTED", N4S_INSERTED, N4S_INSERTED},
909 	{"N4S_BADOWNER_DEBUG", N4S_BADOWNER_DEBUG, N4S_BADOWNER_DEBUG},
910 	{NULL, 0, 0}
911 };
912 
913 int
914 nfs4_server_info_dcmd(uintptr_t addr, uint_t flags, int argc,
915     const mdb_arg_t *argv)
916 {
917 	nfs4_server_t srv;
918 	char *id_val;
919 	uint_t opt_c = FALSE;
920 	uint_t opt_s = FALSE;
921 
922 	if ((flags & DCMD_ADDRSPEC) == 0) {
923 		if (mdb_walk_dcmd("nfs4_server", "nfs4_server_info", argc, argv)
924 		    == -1) {
925 			mdb_warn("nfs4_server walker failed");
926 			return (DCMD_ERR);
927 		}
928 		return (DCMD_OK);
929 	}
930 
931 	if (mdb_getopts(argc, argv,
932 	    'c', MDB_OPT_SETBITS, TRUE, &opt_c,
933 	    's', MDB_OPT_SETBITS, TRUE, &opt_s, NULL) != argc)
934 		return (DCMD_USAGE);
935 
936 	if (mdb_vread(&srv, sizeof (srv), addr) == -1) {
937 		mdb_warn("failed to read nfs4_server_t at %p", addr);
938 		return (DCMD_ERR);
939 	}
940 
941 	if (srv.saddr.len == 0)
942 		return (DCMD_OK);
943 
944 	mdb_printf("Address: %p Zone: %i Server: %s\n", addr, srv.zoneid,
945 	    common_netbuf_str(&srv.saddr));
946 	mdb_printf("Program: %x Flags: %b\n", srv.s_program, srv.s_flags,
947 	    nfs4_si_flags);
948 	mdb_printf("Client ID: %#llx", srv.clientid);
949 	if (opt_s) {
950 		struct {
951 			uint32_t start_time;
952 			uint32_t c_id;
953 		} *impl_id = (void *)&srv.clientid;
954 		mdb_printf(" (srvrboot: %Y, c_id: %u)", impl_id->start_time,
955 		    impl_id->c_id);
956 	}
957 
958 	mdb_printf("\nCLIDtoSend: [verifier: %llx", srv.clidtosend.verifier);
959 	if (opt_c) {
960 		struct {
961 			uint32_t sec;
962 			uint32_t subsec;
963 		} *curtime = (void *)&srv.clidtosend.verifier;
964 		mdb_printf(" (%Y + %u nsec)", curtime->sec, curtime->subsec);
965 	}
966 
967 	mdb_printf(", client identifier: ");
968 	id_val = mdb_alloc(srv.clidtosend.id_len, UM_NOSLEEP | UM_GC);
969 	if (id_val != NULL && mdb_vread(id_val, srv.clidtosend.id_len,
970 	    (uintptr_t)srv.clidtosend.id_val) == srv.clidtosend.id_len) {
971 		uint_t i;
972 
973 		if (opt_c) {
974 			size_t l;
975 			struct netbuf nb;
976 
977 			l = strlen(id_val) + 1;
978 			nb.len = nb.maxlen = srv.clidtosend.id_len - l;
979 			nb.buf = srv.clidtosend.id_val + l;
980 			mdb_printf("(%s/%s) ", id_val, common_netbuf_str(&nb));
981 		}
982 
983 		for (i = 0; i < srv.clidtosend.id_len; i++)
984 			mdb_printf("%02x", (unsigned char)id_val[i]);
985 	} else {
986 		mdb_printf("??");
987 	}
988 	mdb_printf(" ]\n");
989 
990 	mdb_printf("mntinfo4 list: %p\n", srv.mntinfo4_list);
991 	mdb_printf("Deleg list: %p ::walk list\n", addr +
992 	    OFFSETOF(nfs4_server_t, s_deleg_list));
993 	mdb_printf("Lease Valid: ");
994 	switch (srv.lease_valid) {
995 	case NFS4_LEASE_INVALID:
996 		mdb_printf("INVALID\n");
997 		break;
998 	case NFS4_LEASE_VALID:
999 		mdb_printf("VALID\n");
1000 		break;
1001 	case NFS4_LEASE_UNINITIALIZED:
1002 		mdb_printf("UNINIT\n");
1003 		break;
1004 	case NFS4_LEASE_NOT_STARTED:
1005 		mdb_printf("NOT_STARTED\n");
1006 		break;
1007 	default:
1008 		mdb_printf("??\n");
1009 		break;
1010 	}
1011 
1012 	mdb_printf("Lease Time: %i sec\n", srv.s_lease_time);
1013 	mdb_printf("Last renewal: %Y\n", srv.last_renewal_time);
1014 	mdb_printf("Propgn Delay: %li sec : %li nsec\n",
1015 	    srv.propagation_delay.tv_sec, srv.propagation_delay.tv_nsec);
1016 	mdb_printf("Credential: %p\n\n", srv.s_cred);
1017 
1018 	return (DCMD_OK);
1019 }
1020 
1021 void
1022 nfs4_server_info_help(void)
1023 {
1024 	mdb_printf(
1025 	    "-c       assumes client is an illumos NFSv4 Client\n"
1026 	    "-s       assumes server is an illumos NFSv4 Server\n"
1027 	    "\n"
1028 	    "The -c option enables the dcmd to decode the client generated\n"
1029 	    "structure CLIDtoSend that is normally opaque to the server.\n"
1030 	    "The -s option enables the dcmd to decode the server generated\n"
1031 	    "structure Client ID that is normally opaque to the client.\n");
1032 }
1033 
1034 /*
1035  * nfs4_mimsg dcmd implementation
1036  */
1037 
1038 static const struct {
1039 	const char *str;
1040 	nfs4_event_type_t et;
1041 } nfs4_event_type_tbl[] = {
1042 	TBL_ENTRY(RE_BAD_SEQID),
1043 	TBL_ENTRY(RE_BADHANDLE),
1044 	TBL_ENTRY(RE_CLIENTID),
1045 	TBL_ENTRY(RE_DEAD_FILE),
1046 	TBL_ENTRY(RE_END),
1047 	TBL_ENTRY(RE_FAIL_RELOCK),
1048 	TBL_ENTRY(RE_FAIL_REMAP_LEN),
1049 	TBL_ENTRY(RE_FAIL_REMAP_OP),
1050 	TBL_ENTRY(RE_FAILOVER),
1051 	TBL_ENTRY(RE_FILE_DIFF),
1052 	TBL_ENTRY(RE_LOST_STATE),
1053 	TBL_ENTRY(RE_OPENS_CHANGED),
1054 	TBL_ENTRY(RE_SIGLOST),
1055 	TBL_ENTRY(RE_SIGLOST_NO_DUMP),
1056 	TBL_ENTRY(RE_START),
1057 	TBL_ENTRY(RE_UNEXPECTED_ACTION),
1058 	TBL_ENTRY(RE_UNEXPECTED_ERRNO),
1059 	TBL_ENTRY(RE_UNEXPECTED_STATUS),
1060 	TBL_ENTRY(RE_WRONGSEC),
1061 	TBL_ENTRY(RE_LOST_STATE_BAD_OP),
1062 	TBL_ENTRY(RE_REFERRAL),
1063 	{NULL}
1064 };
1065 
1066 static const struct {
1067 	const char *str;
1068 	nfs4_fact_type_t ft;
1069 } nfs4_fact_type_tbl[] = {
1070 	TBL_ENTRY(RF_BADOWNER),
1071 	TBL_ENTRY(RF_ERR),
1072 	TBL_ENTRY(RF_RENEW_EXPIRED),
1073 	TBL_ENTRY(RF_SRV_NOT_RESPOND),
1074 	TBL_ENTRY(RF_SRV_OK),
1075 	TBL_ENTRY(RF_SRVS_NOT_RESPOND),
1076 	TBL_ENTRY(RF_SRVS_OK),
1077 	TBL_ENTRY(RF_DELMAP_CB_ERR),
1078 	TBL_ENTRY(RF_SENDQ_FULL),
1079 	{NULL}
1080 };
1081 
1082 static void
1083 mimsg_print_event(const nfs4_debug_msg_t *msg)
1084 {
1085 	const nfs4_revent_t *ep = &msg->rmsg_u.msg_event;
1086 	char msg_srv[MAXPATHLEN];
1087 	char msg_mntpt[MAXPATHLEN];
1088 	char char1[MAXPATHLEN];
1089 	char *char1p = char1;
1090 	char char2[MAXPATHLEN];
1091 	char *char2p = char2;
1092 
1093 	if (mdb_readstr(msg_srv, sizeof (msg_srv),
1094 	    (uintptr_t)msg->msg_srv) == -1)
1095 		strcpy(msg_srv, "??");
1096 
1097 	if (mdb_readstr(msg_mntpt, sizeof (msg_mntpt),
1098 	    (uintptr_t)msg->msg_mntpt) == -1)
1099 		strcpy(msg_mntpt, "??");
1100 
1101 	if (ep->re_char1 != NULL) {
1102 		if (mdb_readstr(char1, sizeof (char1),
1103 		    (uintptr_t)ep->re_char1) == -1)
1104 			strcpy(char1, "??");
1105 	} else {
1106 		char1[0] = '\0';
1107 		char1p = NULL;
1108 	}
1109 
1110 	if (ep->re_char2 != NULL) {
1111 		if (mdb_readstr(char2, sizeof (char2),
1112 		    (uintptr_t)ep->re_char2) == -1)
1113 			strcpy(char2, "??");
1114 	} else {
1115 		char2[0] = '\0';
1116 		char2p = NULL;
1117 	}
1118 
1119 	switch (ep->re_type) {
1120 	case RE_BAD_SEQID:
1121 		mdb_printf("Operation %s for file %s (rnode_pt 0x%p), pid %d "
1122 		    "using seqid %u got %s on server %s.  Last good seqid was "
1123 		    "%u for operation %s.", nfs4_tag_str(ep->re_tag1), char1p,
1124 		    ep->re_rp1, ep->re_pid, ep->re_seqid1,
1125 		    nfs4_stat_str(ep->re_stat4), msg_srv, ep->re_seqid2,
1126 		    nfs4_tag_str(ep->re_tag2));
1127 		break;
1128 	case RE_BADHANDLE:
1129 		if (ep->re_char1 != NULL) {
1130 			mdb_printf("server %s said filehandle was invalid for "
1131 			    "file: %s (rnode_pt %p) on mount %s", msg_srv,
1132 			    char1, ep->re_rp1, msg_mntpt);
1133 		} else {
1134 			mdb_printf("server %s said filehandle was invalid for "
1135 			    "file: (rnode_pt %p) on mount %s", msg_srv,
1136 			    ep->re_rp1, msg_mntpt);
1137 		}
1138 		break;
1139 	case RE_CLIENTID:
1140 		mdb_printf("Can't recover clientid on mi 0x%p due to error %u "
1141 		    "(%s), for server %s.  Marking file system as unusable.",
1142 		    ep->re_mi, ep->re_uint, nfs4_stat_str(ep->re_stat4),
1143 		    msg_srv);
1144 		break;
1145 	case RE_DEAD_FILE:
1146 		mdb_printf("File %s (rnode_pt %p) on server %s could not be "
1147 		    "recovered and was closed.  %s %s.", char1p, ep->re_rp1,
1148 		    msg_srv, char2,
1149 		    ep->re_stat4 ? nfs4_stat_str(ep->re_stat4) : "");
1150 		break;
1151 	case RE_END:
1152 		mdb_printf("Recovery done for mount %s (0x%p) on server %s, "
1153 		    "rnode_pt1 %s (0x%p), rnode_pt2 %s (0x%p)", msg_mntpt,
1154 		    ep->re_mi, msg_srv, char1p, ep->re_rp1, char2p, ep->re_rp2);
1155 		break;
1156 	case RE_FAIL_RELOCK:
1157 		mdb_printf("Couldn't reclaim lock for pid %d for file %s "
1158 		    "(rnode_pt %p) on (server %s): error %u", ep->re_pid,
1159 		    char1p, ep->re_rp1, msg_srv,
1160 		    ep->re_uint ? ep->re_uint : ep->re_stat4);
1161 		break;
1162 	case RE_FAIL_REMAP_LEN:
1163 		mdb_printf("remap_lookup: server %s returned bad fhandle "
1164 		    "length (%u)", msg_srv, ep->re_uint);
1165 		break;
1166 	case RE_FAIL_REMAP_OP:
1167 		mdb_printf("remap_lookup: didn't get expected OP_GETFH for "
1168 		    "server %s", msg_srv);
1169 		break;
1170 	case RE_FAILOVER:
1171 		if (ep->re_char1)
1172 			mdb_printf("Failing over from %s to %s", msg_srv,
1173 			    char1p);
1174 		else
1175 			mdb_printf("Failing over: selecting original server %s",
1176 			    msg_srv);
1177 		break;
1178 	case RE_FILE_DIFF:
1179 		mdb_printf("Replicas %s and %s: file %s(%p) not same",
1180 		    msg_srv, msg_mntpt, ep->re_char1, (void *)ep->re_rp1);
1181 		break;
1182 	case RE_LOST_STATE:
1183 		/*
1184 		 * if char1 is null you should use ::nfs4_fname for re_rp1
1185 		 */
1186 		mdb_printf("client has a lost %s request for rnode_pt1 %s "
1187 		    "(0x%p), rnode_pt2 %s (0x%p) on fs %s, server %s.",
1188 		    nfs4_op_str(ep->re_uint), char1p, ep->re_rp1, char2p,
1189 		    ep->re_rp2, msg_mntpt, msg_srv);
1190 		break;
1191 	case RE_OPENS_CHANGED:
1192 		mdb_printf("Recovery: number of open files changed "
1193 		    "for mount %s (0x%p) (old %d, new %d) on server %s\n",
1194 		    msg_mntpt, (void *)ep->re_mi, ep->re_uint, ep->re_pid,
1195 		    msg_srv);
1196 		break;
1197 	case RE_SIGLOST:
1198 	case RE_SIGLOST_NO_DUMP:
1199 		mdb_printf("Process %d lost its locks on file %s (rnode_pt %p) "
1200 		    "due to a NFS error (%u) on server %s", ep->re_pid, char1p,
1201 		    ep->re_rp1, ep->re_uint ? ep->re_uint : ep->re_stat4,
1202 		    msg_srv);
1203 		break;
1204 	case RE_START:
1205 		mdb_printf("Starting recovery for mount %s (0x%p, flags %#x) "
1206 		    "on server %s, rnode_pt1 %s (0x%p), rnode_pt2 %s (0x%p)",
1207 		    msg_mntpt, ep->re_mi, ep->re_uint, msg_srv, char1p,
1208 		    ep->re_rp1, char2p, ep->re_rp2);
1209 		break;
1210 	case RE_UNEXPECTED_ACTION:
1211 		mdb_printf("Recovery, unexpected action (%d) on server %s\n",
1212 		    ep->re_uint, msg_srv);
1213 		break;
1214 	case RE_UNEXPECTED_ERRNO:
1215 		mdb_printf("Recovery, unexpected errno (%d) on server %s\n",
1216 		    ep->re_uint, msg_srv);
1217 		break;
1218 	case RE_UNEXPECTED_STATUS:
1219 		mdb_printf("Recovery, unexpected NFS status code (%s) "
1220 		    "on server %s\n",
1221 		    nfs4_stat_str(ep->re_stat4), msg_srv);
1222 		break;
1223 	case RE_WRONGSEC:
1224 		mdb_printf("Recovery, can't recover from NFS4ERR_WRONGSEC."
1225 		    " error %d for mount %s server %s: rnode_pt1 %s (0x%p)"
1226 		    " rnode_pt2 %s (0x%p)", ep->re_uint, msg_mntpt, msg_srv,
1227 		    ep->re_char1, (void *)ep->re_rp1, ep->re_char2,
1228 		    (void *)ep->re_rp2);
1229 		break;
1230 	case RE_LOST_STATE_BAD_OP:
1231 		mdb_printf("NFS lost state with unrecognized op (%d)."
1232 		    " fs %s, server %s, pid %d, file %s (rnode_pt: 0x%p), "
1233 		    "dir %s (0x%p)", ep->re_uint, msg_mntpt, msg_srv,
1234 		    ep->re_pid, ep->re_char1, (void *)ep->re_rp1,
1235 		    ep->re_char2, (void *)ep->re_rp2);
1236 		break;
1237 	case RE_REFERRAL:
1238 		if (ep->re_char1)
1239 			mdb_printf("Referal, Server: %s on Mntpt: %s"
1240 			    "being referred from %s to %s", msg_srv,
1241 			    msg_mntpt, msg_srv, ep->re_char1);
1242 		else
1243 			mdb_printf("Referal, Server: %s on Mntpt: %s"
1244 			    "NFS4: being referred from %s to unknown server",
1245 			    msg_srv, msg_mntpt, msg->msg_srv);
1246 		break;
1247 	default:
1248 		mdb_printf("illegal event %d", ep->re_type);
1249 		break;
1250 	}
1251 }
1252 
1253 static void
1254 mimsg_print_fact(const nfs4_debug_msg_t *msg)
1255 {
1256 	const nfs4_rfact_t *fp = &msg->rmsg_u.msg_fact;
1257 	char msg_srv[MAXPATHLEN];
1258 	char file[MAXPATHLEN];
1259 
1260 	if (mdb_readstr(msg_srv, sizeof (msg_srv),
1261 	    (uintptr_t)msg->msg_srv) == -1)
1262 		strcpy(msg_srv, "??");
1263 
1264 	switch (fp->rf_type) {
1265 	case RF_BADOWNER:
1266 		mdb_printf("NFSMAPID_DOMAIN does not match server: %s's "
1267 		    "domain.", msg_srv);
1268 		break;
1269 	case RF_ERR:
1270 		mdb_printf("Op %s got error ", nfs4_op_str(fp->rf_op));
1271 		if (fp->rf_error)
1272 			mdb_printf("%d", fp->rf_error);
1273 		else
1274 			mdb_printf("%s", nfs4_stat_str(fp->rf_stat4));
1275 		mdb_printf(" causing recovery action %s.",
1276 		    nfs4_recov_str(fp->rf_action));
1277 		if (fp->rf_reboot)
1278 			mdb_printf("  Client also suspects server rebooted");
1279 		break;
1280 	case RF_RENEW_EXPIRED:
1281 		mdb_printf("Client's lease expired on server %s.", msg_srv);
1282 		break;
1283 	case RF_SRV_NOT_RESPOND:
1284 		mdb_printf("Server %s not responding, still trying", msg_srv);
1285 		break;
1286 	case RF_SRV_OK:
1287 		mdb_printf("Server %s ok", msg_srv);
1288 		break;
1289 	case RF_SRVS_NOT_RESPOND:
1290 		mdb_printf("Servers %s not responding, still trying", msg_srv);
1291 		break;
1292 	case RF_SRVS_OK:
1293 		mdb_printf("Servers %s ok", msg_srv);
1294 		break;
1295 	case RF_DELMAP_CB_ERR:
1296 		if (mdb_readstr(file, sizeof (file),
1297 		    (uintptr_t)fp->rf_char1) == -1)
1298 			strcpy(file, "??");
1299 		mdb_printf("Op %s got error %s when executing delmap on file "
1300 		    "%s (rnode_pt 0x%p).", nfs4_op_str(fp->rf_op),
1301 		    nfs4_stat_str(fp->rf_stat4), file, fp->rf_rp1);
1302 		break;
1303 	case RF_SENDQ_FULL:
1304 		mdb_printf("Send queue to NFS server %s is full; still trying",
1305 		    msg_srv);
1306 		break;
1307 	default:
1308 		mdb_printf("??");
1309 		break;
1310 	}
1311 }
1312 
1313 static int
1314 print_mimsg_cb(uintptr_t addr, const void *data, void *cb_data)
1315 {
1316 	nfs4_debug_msg_t msg;
1317 	uint_t opt_s = *(uint_t *)cb_data;
1318 
1319 	if (mdb_vread(&msg, sizeof (msg), addr) == -1) {
1320 		mdb_warn("failed to read nfs4_debug_msg_t at %p", addr);
1321 		return (WALK_ERR);
1322 	}
1323 
1324 	if (opt_s) {
1325 		const char *msg_type = "??";
1326 		const char *ef_type = "??";
1327 		int i;
1328 
1329 		switch (msg.msg_type) {
1330 		case RM_EVENT:
1331 			msg_type = "event";
1332 			for (i = 0; nfs4_event_type_tbl[i].str != NULL; i++)
1333 				if (nfs4_event_type_tbl[i].et ==
1334 				    msg.rmsg_u.msg_event.re_type) {
1335 					ef_type = nfs4_event_type_tbl[i].str;
1336 					break;
1337 				}
1338 			break;
1339 		case RM_FACT:
1340 			msg_type = "fact";
1341 			for (i = 0; nfs4_fact_type_tbl[i].str != NULL; i++)
1342 				if (nfs4_fact_type_tbl[i].ft ==
1343 				    msg.rmsg_u.msg_fact.rf_type) {
1344 					ef_type = nfs4_fact_type_tbl[i].str;
1345 					break;
1346 				}
1347 			break;
1348 		}
1349 
1350 		mdb_printf("%Y: %s %s\n", msg.msg_time.tv_sec, msg_type,
1351 		    ef_type);
1352 
1353 		return (WALK_NEXT);
1354 	}
1355 
1356 	mdb_printf("[NFS4]%Y: ", msg.msg_time.tv_sec);
1357 	switch (msg.msg_type) {
1358 	case RM_EVENT:
1359 		mimsg_print_event(&msg);
1360 		break;
1361 	case RM_FACT:
1362 		mimsg_print_fact(&msg);
1363 		break;
1364 	default:
1365 		mdb_printf("??");
1366 		break;
1367 	}
1368 	mdb_printf("\n");
1369 
1370 	return (WALK_NEXT);
1371 }
1372 
1373 int
1374 nfs4_mimsg_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1375 {
1376 	uint_t opt_s = FALSE;
1377 
1378 	if ((flags & DCMD_ADDRSPEC) == 0) {
1379 		mdb_printf("requires address of mi_msg_list\n");
1380 		return (DCMD_USAGE);
1381 	}
1382 
1383 	if (mdb_getopts(argc, argv,
1384 	    's', MDB_OPT_SETBITS, TRUE, &opt_s, NULL) != argc)
1385 		return (DCMD_USAGE);
1386 
1387 	if (mdb_pwalk("list", print_mimsg_cb, &opt_s, addr) == -1) {
1388 		mdb_warn("failed to walk mi_msg_list list");
1389 		return (DCMD_ERR);
1390 	}
1391 
1392 	return (DCMD_OK);
1393 }
1394 
1395 void
1396 nfs4_mimsg_help(void)
1397 {
1398 	mdb_printf(
1399 	    "-c       assumes client is an illumos NFSv4 Client\n"
1400 	    "-s       assumes server is an illumos NFSv4 Server\n"
1401 	    "\n"
1402 	    "The -c option enables the dcmd to decode the client generated\n"
1403 	    "structure CLIDtoSend that is normally opaque to the server.\n"
1404 	    "The -s option enables the dcmd to decode the server generated\n"
1405 	    "structure Client ID that is normally opaque to the client.\n");
1406 }
1407 
1408 /*
1409  * nfs4_fname dcmd implementation
1410  */
1411 
1412 static void
1413 print_nfs4_fname(uintptr_t addr)
1414 {
1415 	char path[MAXPATHLEN];
1416 	char *p = path + sizeof (path) - 1;
1417 
1418 	*p = '\0';
1419 	while (addr != 0) {
1420 		nfs4_fname_t fn;
1421 		char name[MAXNAMELEN];
1422 
1423 		if (mdb_vread(&fn, sizeof (fn), addr) == -1 ||
1424 		    fn.fn_len >= sizeof (name) || fn.fn_len < 0 ||
1425 		    p - fn.fn_len - 1 < path || mdb_readstr(name, sizeof (name),
1426 		    (uintptr_t)fn.fn_name) != fn.fn_len) {
1427 			mdb_printf("??");
1428 			break;
1429 		}
1430 
1431 		bcopy(name, p -= fn.fn_len, fn.fn_len);
1432 
1433 		if ((addr = (uintptr_t)fn.fn_parent) != 0)
1434 			*--p = '/';
1435 	}
1436 	mdb_printf("%s", p);
1437 }
1438 
1439 int
1440 nfs4_fname_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1441 {
1442 	if ((flags & DCMD_ADDRSPEC) == 0) {
1443 		mdb_printf("requires address of nfs4_fname_t \n");
1444 		return (DCMD_USAGE);
1445 	}
1446 
1447 	if (argc != 0)
1448 		return (DCMD_USAGE);
1449 
1450 	print_nfs4_fname(addr);
1451 	mdb_printf("\n");
1452 
1453 	return (DCMD_OK);
1454 }
1455 
1456 /*
1457  * Open Owner commands and walkers
1458  */
1459 int
1460 nfs4_oo_cb(uintptr_t addr, const void *data, void *varg)
1461 {
1462 	nfs4_open_owner_t	oop;
1463 
1464 	if (mdb_vread(&oop, sizeof (nfs4_open_owner_t), addr) == -1) {
1465 		mdb_warn("failed to read nfs4_open_onwer at %p", addr);
1466 		return (WALK_ERR);
1467 	}
1468 	mdb_printf("%p %p %d %d %s %s\n", addr, oop.oo_cred,
1469 	    oop.oo_ref_count, oop.oo_seqid,
1470 	    oop.oo_just_created ? "True" : "False",
1471 	    oop.oo_seqid_inuse  ? "True" : "False");
1472 
1473 	return (WALK_NEXT);
1474 }
1475 
1476 /*
1477  * nfs4_foo dcmd implementation
1478  */
1479 int
1480 nfs4_foo_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1481 {
1482 	int			foo_off;
1483 	uintptr_t		list_addr;
1484 
1485 	mntinfo4_t		mi;
1486 	foo_off = offsetof(mntinfo4_t, mi_foo_list);
1487 
1488 	if ((flags & DCMD_ADDRSPEC) == 0) {
1489 		mdb_printf("requires address of mntinfo4_t\n");
1490 		return (DCMD_USAGE);
1491 	}
1492 
1493 	if (argc != 0)
1494 		return (DCMD_USAGE);
1495 
1496 	if (mdb_vread(&mi, sizeof (mntinfo4_t), addr) == -1) {
1497 		mdb_warn("Failed to read mntinfo at %p", addr);
1498 		return (DCMD_ERR);
1499 	}
1500 
1501 	list_addr =  addr + foo_off;
1502 
1503 	mdb_printf("mntinfo4: %p, mi_foo_num=%d, mi_foo_max=%d \n",
1504 	    addr, mi.mi_foo_num, mi.mi_foo_max);
1505 	mdb_printf("Address       Cred             RefCnt   SeqID    ");
1506 	mdb_printf("JustCre SeqInUse BadSeqid\n");
1507 
1508 	if (mdb_pwalk("list", nfs4_oo_cb, NULL, list_addr) == -1) {
1509 		mdb_warn("failed to walk 'nfs4_foo'");
1510 		return (DCMD_ERR);
1511 	}
1512 	return (DCMD_OK);
1513 }
1514 
1515 /*
1516  * nfs4_oob_dcmd dcmd
1517  */
1518 int
1519 nfs4_oob_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1520 {
1521 	int			oo_off;
1522 	uintptr_t		list_addr;
1523 	uintptr_t		list_inst;
1524 	int			i;
1525 
1526 
1527 	if ((flags & DCMD_ADDRSPEC) == 0) {
1528 		mdb_printf("requires address of mntinfo4_t\n");
1529 		return (DCMD_USAGE);
1530 	}
1531 
1532 	if (argc != 0)
1533 		return (DCMD_USAGE);
1534 
1535 	oo_off = offsetof(mntinfo4_t, mi_oo_list);
1536 	list_addr =  addr + oo_off;
1537 
1538 	mdb_printf("Address       Cred             RefCnt   SeqID    ");
1539 	mdb_printf("JustCre SeqInUse BadSeqid\n");
1540 
1541 	for (i = 0; i < NFS4_NUM_OO_BUCKETS; i++) {
1542 		list_inst = list_addr + (sizeof (nfs4_oo_hash_bucket_t) * i);
1543 
1544 		if (mdb_pwalk("list", nfs4_oo_cb, NULL, list_inst) == -1) {
1545 			mdb_warn("failed to walk 'nfs4_oob'");
1546 			return (DCMD_ERR);
1547 		}
1548 	}
1549 
1550 	return (DCMD_OK);
1551 }
1552 
1553 /*
1554  * print out open stream entry
1555  */
1556 int
1557 nfs4_openstream_print(uintptr_t addr, void *buf, int *opts)
1558 {
1559 	nfs4_open_stream_t	os;
1560 
1561 	if (mdb_vread(&os, sizeof (nfs4_open_stream_t), addr) == -1) {
1562 		mdb_warn("Failed to read open stream at %p", addr);
1563 		return (DCMD_ERR);
1564 	}
1565 
1566 	mdb_printf("%p\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t"
1567 	    "%d\t%d\t%d\n", addr, os.os_ref_count, os.os_share_acc_read,
1568 	    os.os_share_acc_write, os.os_mmap_read, os.os_mmap_write,
1569 	    os.os_share_deny_none, os.os_share_deny_read,
1570 	    os.os_share_deny_write, os.os_open_ref_count, os.os_dc_openacc,
1571 	    os.os_mapcnt);
1572 
1573 	if (opts && *opts & TRUE) {
1574 		mdb_printf("  ");
1575 		if (os.os_valid)
1576 			mdb_printf("os_valid ");
1577 		if (os.os_delegation)
1578 			mdb_printf("os_delegation ");
1579 		if (os.os_final_close)
1580 			mdb_printf("os_final_close ");
1581 		if (os.os_pending_close)
1582 			mdb_printf("os_pending_close ");
1583 		if (os.os_failed_reopen)
1584 			mdb_printf("os_failed_reopen ");
1585 		if (os.os_force_close)
1586 			mdb_printf("os_force_close ");
1587 		mdb_printf("os_orig_oo_name: %s\n",
1588 		    (uchar_t *)&os.os_orig_oo_name);
1589 	}
1590 	return (DCMD_OK);
1591 }
1592 
1593 /*
1594  * nfs4_svnode dcmd implementation
1595  */
1596 int
1597 nfs4_openstreams_cb(uintptr_t addr, void *private, int *opts)
1598 {
1599 	mdb_ctf_id_t ctfid;
1600 	ulong_t offset;
1601 	uintptr_t os_list_ptr;
1602 
1603 	/*
1604 	 * Walk the rnode4 ptr's r_open_streams list.
1605 	 */
1606 	if ((mdb_ctf_lookup_by_name("rnode4_t", &ctfid) == 0) &&
1607 	    (mdb_ctf_offsetof(ctfid, "r_open_streams", &offset) == 0) &&
1608 	    (offset % (sizeof (uintptr_t) * NBBY) == 0)) {
1609 		offset /= NBBY;
1610 	} else {
1611 		offset = offsetof(rnode4_t, r_open_streams);
1612 	}
1613 
1614 	os_list_ptr = addr + offset;
1615 
1616 	if (mdb_pwalk("list", (mdb_walk_cb_t)nfs4_openstream_print, opts,
1617 	    os_list_ptr) == -1) {
1618 		mdb_warn("Failed to walk r_open_streams");
1619 		return (DCMD_ERR);
1620 	}
1621 	return (DCMD_OK);
1622 }
1623 
1624 /*
1625  * nfs4_os_dcmd list open streams
1626  */
1627 int
1628 nfs4_os_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1629 {
1630 	int opts = 0;
1631 
1632 	if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &opts,
1633 	    NULL) != argc) {
1634 		return (DCMD_USAGE);
1635 	}
1636 
1637 	mdb_printf("    ref\t|    os_share   |    os_mmap   |       "
1638 	    "os_share_deny       |    open    |    deleg    |\t|\n");
1639 
1640 	mdb_printf("%<u>%-?s %-s|%s %s|%s  %s|%s %s %s|"
1641 	    "%s |%s |%s |%</u>\n", "Address", "count", "acc_read",
1642 	    "acc_write", "read", "write", "none", "read", "write", "count",
1643 	    "access", "mapcnt");
1644 
1645 	/*
1646 	 * Walk the rnode4 cache if no address is specified
1647 	 */
1648 	if (!(flags & DCMD_ADDRSPEC)) {
1649 		if (mdb_walk("nfs_rtable4", (mdb_walk_cb_t)nfs4_openstreams_cb,
1650 		    &opts) == -1) {
1651 			mdb_warn("unable to walk nfs_rtable4");
1652 			return (DCMD_ERR);
1653 		}
1654 		return (DCMD_OK);
1655 	}
1656 	return (nfs4_openstreams_cb(addr, NULL, &opts));
1657 }
1658 
1659 
1660 int
1661 nfs4_svnode_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1662 {
1663 	svnode_t sn;
1664 
1665 	if ((flags & DCMD_ADDRSPEC) == 0) {
1666 		mdb_printf("requires address of svnode_t\n");
1667 		return (DCMD_USAGE);
1668 	}
1669 
1670 	if (argc != 0)
1671 		return (DCMD_USAGE);
1672 
1673 	if (mdb_vread(&sn, sizeof (sn), addr) == -1) {
1674 		mdb_warn("can't read svnode_t at %p", addr);
1675 		return (DCMD_ERR);
1676 	}
1677 
1678 	if (DCMD_HDRSPEC(flags))
1679 		mdb_printf("%<b>%<u>%-?s %-?s %-20s%</u>%</b>\n", "SVNODE",
1680 		    "VNODE", "PATH");
1681 
1682 	mdb_printf("%-?p %-?p ", addr, sn.sv_r_vnode);
1683 	print_nfs4_fname((uintptr_t)sn.sv_name);
1684 	mdb_printf("\n");
1685 
1686 	return (DCMD_OK);
1687 }
1688 
1689 /*
1690  * nfs_rtable walker implementation
1691  */
1692 
1693 hash_table_walk_arg_t nfs_rtable_arg = {
1694 	0,		/* will be set in the init */
1695 	0,		/* will be set in the init */
1696 	sizeof (rhashq_t),
1697 	"r_hashf",
1698 	OFFSETOF(rhashq_t, r_hashf),
1699 	"rnode_t",
1700 	sizeof (rnode_t),
1701 	OFFSETOF(struct rnode, r_hashf)
1702 };
1703 
1704 static int
1705 nfs_rtable_common_init(mdb_walk_state_t *wsp, const char *tabname,
1706     const char *sizename)
1707 {
1708 	hash_table_walk_arg_t *arg = wsp->walk_arg;
1709 	int rtsize;
1710 	uintptr_t rtaddr;
1711 
1712 	if (mdb_readsym(&rtsize, sizeof (rtsize), sizename) == -1) {
1713 		mdb_warn("failed to get %s", sizename);
1714 		return (WALK_ERR);
1715 	}
1716 
1717 	if (rtsize < 0) {
1718 		mdb_warn("%s is negative: %d", sizename, rtsize);
1719 		return (WALK_ERR);
1720 	}
1721 
1722 	if (mdb_readsym(&rtaddr, sizeof (rtaddr), tabname) == -1) {
1723 		mdb_warn("failed to get %s", tabname);
1724 		return (WALK_ERR);
1725 	}
1726 
1727 	arg->array_addr = rtaddr;
1728 	arg->array_len = rtsize;
1729 
1730 	return (hash_table_walk_init(wsp));
1731 }
1732 
1733 int
1734 nfs_rtable_walk_init(mdb_walk_state_t *wsp)
1735 {
1736 	if (wsp->walk_addr != 0) {
1737 		mdb_warn("nfs_rtable supports only global walks");
1738 		return (WALK_ERR);
1739 	}
1740 
1741 	return (nfs_rtable_common_init(wsp, "rtable", "rtablesize"));
1742 }
1743 
1744 /*
1745  * nfs_rtable4 walker implementation
1746  */
1747 
1748 hash_table_walk_arg_t nfs_rtable4_arg = {
1749 	0,		/* will be set in the init */
1750 	0,		/* will be set in the init */
1751 	sizeof (r4hashq_t),
1752 	"r_hashf",
1753 	OFFSETOF(r4hashq_t, r_hashf),
1754 	"rnode4_t",
1755 	sizeof (rnode4_t),
1756 	OFFSETOF(struct rnode4, r_hashf)
1757 };
1758 
1759 int
1760 nfs_rtable4_walk_init(mdb_walk_state_t *wsp)
1761 {
1762 	if (wsp->walk_addr != 0) {
1763 		mdb_warn("nfs_rtable4 supports only global walks");
1764 		return (WALK_ERR);
1765 	}
1766 
1767 	return (nfs_rtable_common_init(wsp, "rtable4", "rtable4size"));
1768 }
1769 
1770 /*
1771  * nfs_vfs walker implementation
1772  */
1773 
1774 typedef struct nfs_vfs_walk {
1775 	uintptr_t nfs2_ops;
1776 	uintptr_t nfs3_ops;
1777 	uintptr_t nfs4_ops;
1778 	void *data;		/* walker specific data */
1779 } nfs_vfs_walk_t;
1780 
1781 int
1782 nfs_vfs_walk_init(mdb_walk_state_t *wsp)
1783 {
1784 	nfs_vfs_walk_t data;
1785 	nfs_vfs_walk_t *datap;
1786 
1787 	if (mdb_readvar(&data.nfs2_ops, "nfs_vfsops") == -1) {
1788 		mdb_warn("failed to read %s", "nfs_vfsops");
1789 		return (WALK_ERR);
1790 	}
1791 
1792 	if (mdb_readvar(&data.nfs3_ops, "nfs3_vfsops") == -1) {
1793 		mdb_warn("failed to read %s", "nfs3_vfsops");
1794 		return (WALK_ERR);
1795 	}
1796 
1797 	if (mdb_readvar(&data.nfs4_ops, "nfs4_vfsops") == -1) {
1798 		mdb_warn("failed to read %s", "nfs4_vfsops");
1799 		return (WALK_ERR);
1800 	}
1801 
1802 	if (mdb_layered_walk("genunix`vfs", wsp) == -1) {
1803 		mdb_warn("failed to walk vfs");
1804 		return (WALK_ERR);
1805 	}
1806 
1807 	datap = mdb_alloc(sizeof (data), UM_SLEEP);
1808 	*datap = data;
1809 	wsp->walk_data = datap;
1810 
1811 	return (WALK_NEXT);
1812 }
1813 
1814 int
1815 nfs_vfs_walk_step(mdb_walk_state_t *wsp)
1816 {
1817 	nfs_vfs_walk_t *data = (nfs_vfs_walk_t *)wsp->walk_data;
1818 	vfs_t *vfs = (vfs_t *)wsp->walk_layer;
1819 
1820 	if (data->nfs2_ops != (uintptr_t)vfs->vfs_op &&
1821 	    data->nfs3_ops != (uintptr_t)vfs->vfs_op &&
1822 	    data->nfs4_ops != (uintptr_t)vfs->vfs_op)
1823 		return (WALK_NEXT);
1824 
1825 	return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
1826 	    wsp->walk_cbdata));
1827 }
1828 
1829 void
1830 nfs_vfs_walk_fini(mdb_walk_state_t *wsp)
1831 {
1832 	mdb_free(wsp->walk_data, sizeof (nfs_vfs_walk_t));
1833 }
1834 
1835 /*
1836  * nfs_mnt walker implementation
1837  */
1838 
1839 int
1840 nfs_mnt_walk_init(mdb_walk_state_t *wsp)
1841 {
1842 	int status;
1843 	nfs_vfs_walk_t *data;
1844 
1845 	status = nfs_vfs_walk_init(wsp);
1846 	if (status != WALK_NEXT)
1847 		return (status);
1848 
1849 	data = wsp->walk_data;
1850 	data->data = mdb_alloc(sizeof (mntinfo_t), UM_SLEEP);
1851 
1852 	return (WALK_NEXT);
1853 }
1854 
1855 int
1856 nfs_mnt_walk_step(mdb_walk_state_t *wsp)
1857 {
1858 	nfs_vfs_walk_t *data = (nfs_vfs_walk_t *)wsp->walk_data;
1859 	vfs_t *vfs = (vfs_t *)wsp->walk_layer;
1860 
1861 	if (data->nfs2_ops != (uintptr_t)vfs->vfs_op &&
1862 	    data->nfs3_ops != (uintptr_t)vfs->vfs_op)
1863 		return (WALK_NEXT);
1864 
1865 	if (mdb_vread(data->data, sizeof (mntinfo_t), (uintptr_t)VFTOMI(vfs))
1866 	    == -1) {
1867 		mdb_warn("can't read mntinfo");
1868 		return (WALK_ERR);
1869 	}
1870 
1871 	return (wsp->walk_callback((uintptr_t)VFTOMI(vfs), data->data,
1872 	    wsp->walk_cbdata));
1873 }
1874 
1875 void
1876 nfs_mnt_walk_fini(mdb_walk_state_t *wsp)
1877 {
1878 	nfs_vfs_walk_t *data = (nfs_vfs_walk_t *)wsp->walk_data;
1879 
1880 	mdb_free(data->data, sizeof (mntinfo_t));
1881 	nfs_vfs_walk_fini(wsp);
1882 }
1883 
1884 /*
1885  * nfs4_mnt walker implementation
1886  */
1887 
1888 int
1889 nfs4_mnt_walk_init(mdb_walk_state_t *wsp)
1890 {
1891 	int status;
1892 	nfs_vfs_walk_t *data;
1893 
1894 	status = nfs_vfs_walk_init(wsp);
1895 	if (status != WALK_NEXT)
1896 		return (status);
1897 
1898 	data = wsp->walk_data;
1899 	data->data = mdb_alloc(sizeof (mntinfo4_t), UM_SLEEP);
1900 
1901 	return (WALK_NEXT);
1902 }
1903 
1904 int
1905 nfs4_mnt_walk_step(mdb_walk_state_t *wsp)
1906 {
1907 	nfs_vfs_walk_t *data = (nfs_vfs_walk_t *)wsp->walk_data;
1908 	vfs_t *vfs = (vfs_t *)wsp->walk_layer;
1909 
1910 	if (data->nfs4_ops != (uintptr_t)vfs->vfs_op)
1911 		return (WALK_NEXT);
1912 
1913 	if (mdb_vread(data->data, sizeof (mntinfo4_t), (uintptr_t)VFTOMI4(vfs))
1914 	    == -1) {
1915 		mdb_warn("can't read mntinfo4");
1916 		return (WALK_ERR);
1917 	}
1918 
1919 	return (wsp->walk_callback((uintptr_t)VFTOMI4(vfs), data->data,
1920 	    wsp->walk_cbdata));
1921 }
1922 
1923 void
1924 nfs4_mnt_walk_fini(mdb_walk_state_t *wsp)
1925 {
1926 	nfs_vfs_walk_t *data = (nfs_vfs_walk_t *)wsp->walk_data;
1927 
1928 	mdb_free(data->data, sizeof (mntinfo4_t));
1929 	nfs_vfs_walk_fini(wsp);
1930 }
1931 
1932 /*
1933  * nfs_serv walker implementation
1934  */
1935 
1936 int
1937 nfs_serv_walk_init(mdb_walk_state_t *wsp)
1938 {
1939 	if (wsp->walk_addr == 0) {
1940 		mdb_warn("global walk not supported");
1941 		return (WALK_ERR);
1942 	}
1943 
1944 	return (WALK_NEXT);
1945 }
1946 
1947 int
1948 nfs_serv_walk_step(mdb_walk_state_t *wsp)
1949 {
1950 	servinfo_t si;
1951 	uintptr_t addr = wsp->walk_addr;
1952 
1953 	if (addr == 0)
1954 		return (WALK_DONE);
1955 
1956 	if (mdb_vread(&si, sizeof (si), addr) == -1) {
1957 		mdb_warn("can't read servinfo_t");
1958 		return (WALK_ERR);
1959 	}
1960 
1961 	wsp->walk_addr = (uintptr_t)si.sv_next;
1962 	return (wsp->walk_callback(addr, &si, wsp->walk_cbdata));
1963 }
1964 
1965 /*
1966  * nfs4_serv walker implementation
1967  */
1968 
1969 int
1970 nfs4_serv_walk_init(mdb_walk_state_t *wsp)
1971 {
1972 	if (wsp->walk_addr == 0) {
1973 		mdb_warn("global walk not supported");
1974 		return (WALK_ERR);
1975 	}
1976 
1977 	return (WALK_NEXT);
1978 }
1979 
1980 int
1981 nfs4_serv_walk_step(mdb_walk_state_t *wsp)
1982 {
1983 	servinfo4_t si;
1984 	uintptr_t addr = wsp->walk_addr;
1985 
1986 	if (addr == 0)
1987 		return (WALK_DONE);
1988 
1989 	if (mdb_vread(&si, sizeof (si), addr) == -1) {
1990 		mdb_warn("can't read servinfo4_t");
1991 		return (WALK_ERR);
1992 	}
1993 
1994 	wsp->walk_addr = (uintptr_t)si.sv_next;
1995 	return (wsp->walk_callback(addr, &si, wsp->walk_cbdata));
1996 }
1997 
1998 /*
1999  * nfs4_svnode walker implementation
2000  */
2001 
2002 int
2003 nfs4_svnode_walk_init(mdb_walk_state_t *wsp)
2004 {
2005 	if (wsp->walk_addr == 0) {
2006 		mdb_warn("global walk not supported");
2007 		return (WALK_ERR);
2008 	}
2009 
2010 	wsp->walk_data = (void *)wsp->walk_addr;
2011 
2012 	return (WALK_NEXT);
2013 }
2014 
2015 int
2016 nfs4_svnode_walk_step(mdb_walk_state_t *wsp)
2017 {
2018 	svnode_t sn;
2019 	uintptr_t addr = wsp->walk_addr;
2020 	int status;
2021 
2022 	if (mdb_vread(&sn, sizeof (sn), addr) == -1) {
2023 		mdb_warn("can't read svnode_t");
2024 		return (WALK_ERR);
2025 	}
2026 
2027 	wsp->walk_addr = (uintptr_t)sn.sv_forw;
2028 
2029 	status = wsp->walk_callback(addr, &sn, wsp->walk_cbdata);
2030 	if (status != WALK_NEXT)
2031 		return (status);
2032 
2033 	return (((void *)wsp->walk_addr == wsp->walk_data) ? WALK_DONE
2034 	    : WALK_NEXT);
2035 }
2036 
2037 /*
2038  * nfs4_server walker implementation
2039  */
2040 
2041 int
2042 nfs4_server_walk_init(mdb_walk_state_t *wsp)
2043 {
2044 	if (wsp->walk_addr == 0) {
2045 		GElf_Sym sym;
2046 
2047 		if (mdb_lookup_by_name("nfs4_server_lst", &sym) == -1) {
2048 			mdb_warn("failed to find 'nfs4_server_lst'");
2049 			return (WALK_ERR);
2050 		}
2051 
2052 		wsp->walk_addr = sym.st_value;
2053 	}
2054 
2055 	wsp->walk_data = (void *)wsp->walk_addr;
2056 
2057 	return (WALK_NEXT);
2058 }
2059 
2060 int
2061 nfs4_server_walk_step(mdb_walk_state_t *wsp)
2062 {
2063 	nfs4_server_t srv;
2064 	uintptr_t addr = wsp->walk_addr;
2065 	int status;
2066 
2067 	if (mdb_vread(&srv, sizeof (srv), addr) == -1) {
2068 		mdb_warn("can't read nfs4_server_t");
2069 		return (WALK_ERR);
2070 	}
2071 
2072 	wsp->walk_addr = (uintptr_t)srv.forw;
2073 
2074 	status = wsp->walk_callback(addr, &srv, wsp->walk_cbdata);
2075 	if (status != WALK_NEXT)
2076 		return (status);
2077 
2078 	return (((void *)wsp->walk_addr == wsp->walk_data) ? WALK_DONE
2079 	    : WALK_NEXT);
2080 }
2081 
2082 /*
2083  * nfs_async walker implementation
2084  */
2085 
2086 int
2087 nfs_async_walk_init(mdb_walk_state_t *wsp)
2088 {
2089 	if (wsp->walk_addr == 0) {
2090 		mdb_warn("global walk not supported");
2091 		return (WALK_ERR);
2092 	}
2093 
2094 	return (WALK_NEXT);
2095 }
2096 
2097 int
2098 nfs_async_walk_step(mdb_walk_state_t *wsp)
2099 {
2100 	struct nfs_async_reqs areq;
2101 	uintptr_t addr = wsp->walk_addr;
2102 
2103 	if (addr == 0)
2104 		return (WALK_DONE);
2105 
2106 	if (mdb_vread(&areq, sizeof (areq), addr) == -1) {
2107 		mdb_warn("can't read struct nfs_async_reqs");
2108 		return (WALK_ERR);
2109 	}
2110 
2111 	wsp->walk_addr = (uintptr_t)areq.a_next;
2112 
2113 	return (wsp->walk_callback(addr, &areq, wsp->walk_cbdata));
2114 }
2115 
2116 /*
2117  * nfs4_async walker implementation
2118  */
2119 
2120 int
2121 nfs4_async_walk_init(mdb_walk_state_t *wsp)
2122 {
2123 	if (wsp->walk_addr == 0) {
2124 		mdb_warn("global walk not supported");
2125 		return (WALK_ERR);
2126 	}
2127 
2128 	return (WALK_NEXT);
2129 }
2130 
2131 int
2132 nfs4_async_walk_step(mdb_walk_state_t *wsp)
2133 {
2134 	struct nfs4_async_reqs areq;
2135 	uintptr_t addr = wsp->walk_addr;
2136 
2137 	if (addr == 0)
2138 		return (WALK_DONE);
2139 
2140 	if (mdb_vread(&areq, sizeof (areq), addr) == -1) {
2141 		mdb_warn("can't read struct nfs4_async_reqs");
2142 		return (WALK_ERR);
2143 	}
2144 
2145 	wsp->walk_addr = (uintptr_t)areq.a_next;
2146 
2147 	return (wsp->walk_callback(addr, &areq, wsp->walk_cbdata));
2148 }
2149 
2150 /*
2151  * nfs_acache_rnode walker implementation
2152  */
2153 
2154 int
2155 nfs_acache_rnode_walk_init(mdb_walk_state_t *wsp)
2156 {
2157 	rnode_t rn;
2158 
2159 	if (wsp->walk_addr == 0) {
2160 		mdb_warn("global walk not supported");
2161 		return (WALK_ERR);
2162 	}
2163 
2164 	if (mdb_vread(&rn, sizeof (rn), wsp->walk_addr) == -1) {
2165 		mdb_warn("can't read rnode_t at %p", wsp->walk_addr);
2166 		return (WALK_ERR);
2167 	}
2168 
2169 	wsp->walk_addr = (uintptr_t)rn.r_acache;
2170 
2171 	return (WALK_NEXT);
2172 }
2173 
2174 int
2175 nfs_acache_rnode_walk_step(mdb_walk_state_t *wsp)
2176 {
2177 	acache_t ac;
2178 	uintptr_t addr = wsp->walk_addr;
2179 
2180 	if (addr == 0)
2181 		return (WALK_DONE);
2182 
2183 	if (mdb_vread(&ac, sizeof (ac), addr) == -1) {
2184 		mdb_warn("can't read acache_t at %p", addr);
2185 		return (WALK_ERR);
2186 	}
2187 
2188 	wsp->walk_addr = (uintptr_t)ac.list;
2189 
2190 	return (wsp->walk_callback(addr, &ac, wsp->walk_cbdata));
2191 }
2192 
2193 /*
2194  * nfs_acache walker implementation
2195  */
2196 
2197 static const hash_table_walk_arg_t nfs_acache_arg = {
2198 	0,		/* placeholder */
2199 	0,		/* placeholder */
2200 	sizeof (acache_hash_t),
2201 	"next",
2202 	OFFSETOF(acache_hash_t, next),
2203 	"acache_t",
2204 	sizeof (acache_t),
2205 	OFFSETOF(acache_t, next)
2206 };
2207 
2208 int
2209 nfs_acache_walk_init(mdb_walk_state_t *wsp)
2210 {
2211 	hash_table_walk_arg_t *arg;
2212 	int size;
2213 	uintptr_t addr;
2214 	int status;
2215 
2216 	if (wsp->walk_addr != 0) {
2217 		mdb_warn("local walk not supported");
2218 		return (WALK_ERR);
2219 	}
2220 
2221 	if (mdb_readsym(&size, sizeof (size), "acachesize") == -1) {
2222 		mdb_warn("failed to get %s", "acachesize");
2223 		return (WALK_ERR);
2224 	}
2225 
2226 	if (size < 0) {
2227 		mdb_warn("%s is negative: %d", "acachesize", size);
2228 		return (WALK_ERR);
2229 	}
2230 
2231 	if (mdb_readsym(&addr, sizeof (addr), "acache") == -1) {
2232 		mdb_warn("failed to get %s", "acache");
2233 		return (WALK_ERR);
2234 	}
2235 
2236 	arg = mdb_alloc(sizeof (*arg), UM_SLEEP);
2237 	bcopy(&nfs_acache_arg, arg, sizeof (*arg));
2238 
2239 	arg->array_addr = addr;
2240 	arg->array_len = size;
2241 
2242 	wsp->walk_arg = arg;
2243 
2244 	status = hash_table_walk_init(wsp);
2245 	if (status != WALK_NEXT)
2246 		mdb_free(wsp->walk_arg, sizeof (hash_table_walk_arg_t));
2247 	return (status);
2248 }
2249 
2250 void
2251 nfs_acache_walk_fini(mdb_walk_state_t *wsp)
2252 {
2253 	hash_table_walk_fini(wsp);
2254 	mdb_free(wsp->walk_arg, sizeof (hash_table_walk_arg_t));
2255 }
2256 
2257 /*
2258  * nfs_acache4_rnode walker implementation
2259  */
2260 
2261 int
2262 nfs_acache4_rnode_walk_init(mdb_walk_state_t *wsp)
2263 {
2264 	rnode4_t rn;
2265 
2266 	if (wsp->walk_addr == 0) {
2267 		mdb_warn("global walk not supported");
2268 		return (WALK_ERR);
2269 	}
2270 
2271 	if (mdb_vread(&rn, sizeof (rn), wsp->walk_addr) == -1) {
2272 		mdb_warn("can't read rnode4_t at %p", wsp->walk_addr);
2273 		return (WALK_ERR);
2274 	}
2275 
2276 	wsp->walk_addr = (uintptr_t)rn.r_acache;
2277 
2278 	return (WALK_NEXT);
2279 }
2280 
2281 int
2282 nfs_acache4_rnode_walk_step(mdb_walk_state_t *wsp)
2283 {
2284 	acache4_t ac;
2285 	uintptr_t addr = wsp->walk_addr;
2286 
2287 	if (addr == 0)
2288 		return (WALK_DONE);
2289 
2290 	if (mdb_vread(&ac, sizeof (ac), addr) == -1) {
2291 		mdb_warn("can't read acache4_t at %p", addr);
2292 		return (WALK_ERR);
2293 	}
2294 
2295 	wsp->walk_addr = (uintptr_t)ac.list;
2296 
2297 	return (wsp->walk_callback(addr, &ac, wsp->walk_cbdata));
2298 }
2299 
2300 /*
2301  * nfs_acache4 walker implementation
2302  */
2303 
2304 static const hash_table_walk_arg_t nfs_acache4_arg = {
2305 	0,		/* placeholder */
2306 	0,		/* placeholder */
2307 	sizeof (acache4_hash_t),
2308 	"next",
2309 	OFFSETOF(acache4_hash_t, next),
2310 	"acache4_t",
2311 	sizeof (acache4_t),
2312 	OFFSETOF(acache4_t, next)
2313 };
2314 
2315 int
2316 nfs_acache4_walk_init(mdb_walk_state_t *wsp)
2317 {
2318 	hash_table_walk_arg_t *arg;
2319 	int size;
2320 	uintptr_t addr;
2321 	int status;
2322 
2323 	if (wsp->walk_addr != 0) {
2324 		mdb_warn("local walk not supported");
2325 		return (WALK_ERR);
2326 	}
2327 
2328 	if (mdb_readsym(&size, sizeof (size), "acache4size") == -1) {
2329 		mdb_warn("failed to get %s", "acache4size");
2330 		return (WALK_ERR);
2331 	}
2332 
2333 	if (size < 0) {
2334 		mdb_warn("%s is negative: %d\n", "acache4size", size);
2335 		return (WALK_ERR);
2336 	}
2337 
2338 	if (mdb_readsym(&addr, sizeof (addr), "acache4") == -1) {
2339 		mdb_warn("failed to get %s", "acache4");
2340 		return (WALK_ERR);
2341 	}
2342 
2343 	arg = mdb_alloc(sizeof (*arg), UM_SLEEP);
2344 	bcopy(&nfs_acache4_arg, arg, sizeof (*arg));
2345 
2346 	arg->array_addr = addr;
2347 	arg->array_len = size;
2348 
2349 	wsp->walk_arg = arg;
2350 
2351 	status = hash_table_walk_init(wsp);
2352 	if (status != WALK_NEXT)
2353 		mdb_free(wsp->walk_arg, sizeof (hash_table_walk_arg_t));
2354 	return (status);
2355 }
2356 
2357 void
2358 nfs_acache4_walk_fini(mdb_walk_state_t *wsp)
2359 {
2360 	hash_table_walk_fini(wsp);
2361 	mdb_free(wsp->walk_arg, sizeof (hash_table_walk_arg_t));
2362 }
2363