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 /*
13  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
14  */
15 
16 #include <sys/mdb_modapi.h>
17 #include <mdb/mdb_ctf.h>
18 #include <sys/vnode.h>
19 #include <stddef.h>
20 #include <nfs/rnode.h>
21 #include <limits.h>
22 #include <nfs/lm.h>
23 #include <sys/flock_impl.h>
24 #include <mdb/mdb_ks.h>
25 
26 #include <rpcsvc/nlm_prot.h>
27 #include <rpcsvc/sm_inter.h>
28 #include <rpcsvc/nsm_addr.h>
29 
30 #include "klm/nlm_impl.h"
31 
32 #define	NLM_MAXNAMELEN	256
33 #define	NLM_MAXADDRSTR	64
34 
35 /*
36  * ****************************************************************
37  * Helper functions
38  */
39 
40 /*
41  * Helper to get printable IP address into a buffer.
42  * Used by nlm_host_dcmd
43  */
44 static int
45 nlm_netbuf_str(char *buf, size_t bufsz, const struct netbuf *nb)
46 {
47 	struct sockaddr_storage sa;
48 	struct sockaddr_in *s_in;
49 	struct sockaddr_in6 *s_in6;
50 	uint_t salen = nb->len;
51 	in_port_t port;
52 
53 	if (salen < sizeof (sa_family_t))
54 		return (-1);
55 	if (salen > sizeof (sa))
56 		salen = sizeof (sa);
57 	if (mdb_vread(&sa, salen, (uintptr_t)nb->buf) < 0)
58 		return (-1);
59 
60 	switch (sa.ss_family) {
61 	case AF_INET:
62 		s_in = (struct sockaddr_in *)(void *)&sa;
63 		mdb_nhconvert(&port, &s_in->sin_port, sizeof (port));
64 		mdb_snprintf(buf, bufsz, "%I/%d",
65 		    s_in->sin_addr.s_addr, port);
66 		break;
67 
68 	case AF_INET6:
69 		s_in6 = (struct sockaddr_in6 *)(void *)&sa;
70 		mdb_nhconvert(&port, &s_in6->sin6_port, sizeof (port));
71 		mdb_snprintf(buf, bufsz, "%N/%d",
72 		    &(s_in6->sin6_addr), port);
73 		break;
74 
75 	default:
76 		mdb_printf("AF_%d", sa.ss_family);
77 		break;
78 	}
79 
80 	return (0);
81 }
82 
83 /*
84  * Get the name for an enum value
85  */
86 static void
87 get_enum(char *obuf, size_t size, const char *type_str, int val,
88     const char *prefix)
89 {
90 	mdb_ctf_id_t type_id;
91 	const char *cp;
92 
93 	if (mdb_ctf_lookup_by_name(type_str, &type_id) != 0)
94 		goto errout;
95 	if (mdb_ctf_type_resolve(type_id, &type_id) != 0)
96 		goto errout;
97 	if ((cp = mdb_ctf_enum_name(type_id, val)) == NULL)
98 		goto errout;
99 	if (prefix != NULL) {
100 		size_t len = strlen(prefix);
101 		if (strncmp(cp, prefix, len) == 0)
102 			cp += len;
103 	}
104 	(void) strlcpy(obuf, cp, size);
105 	return;
106 
107 errout:
108 	mdb_snprintf(obuf, size, "? (%d)", val);
109 }
110 
111 static const mdb_bitmask_t
112 host_flag_bits[] = {
113 	{
114 		"MONITORED",
115 		NLM_NH_MONITORED,
116 		NLM_NH_MONITORED },
117 	{
118 		"RECLAIM",
119 		NLM_NH_RECLAIM,
120 		NLM_NH_RECLAIM },
121 	{
122 		"INIDLE",
123 		NLM_NH_INIDLE,
124 		NLM_NH_INIDLE },
125 	{
126 		"SUSPEND",
127 		NLM_NH_SUSPEND,
128 		NLM_NH_SUSPEND },
129 	{
130 		NULL, 0, 0 }
131 };
132 
133 /*
134  * ****************************************************************
135  * NLM zones (top level)
136  */
137 
138 /*
139  * nlm_zone walker implementation
140  */
141 
142 int
143 nlm_zone_walk_init(mdb_walk_state_t *wsp)
144 {
145 
146 	/*
147 	 * Technically, this is "cheating" with the knowledge that
148 	 * the TAILQ_HEAD link is at the beginning of this object.
149 	 */
150 	if (wsp->walk_addr == 0 && mdb_readsym(&wsp->walk_addr,
151 	    sizeof (wsp->walk_addr), "nlm_zones_list") == -1) {
152 		mdb_warn("failed to read 'nlm_zones_list'");
153 		return (WALK_ERR);
154 	}
155 
156 	return (WALK_NEXT);
157 }
158 
159 int
160 nlm_zone_walk_step(mdb_walk_state_t *wsp)
161 {
162 	struct nlm_globals g;
163 	uintptr_t addr = wsp->walk_addr;
164 
165 	if (addr == 0)
166 		return (WALK_DONE);
167 
168 	if (mdb_vread(&g, sizeof (g), addr) < 0) {
169 		mdb_warn("failed to read nlm_globals at %p", addr);
170 		return (WALK_ERR);
171 	}
172 
173 	wsp->walk_addr = (uintptr_t)TAILQ_NEXT(&g, nlm_link);
174 	return (wsp->walk_callback(addr, &g, wsp->walk_cbdata));
175 }
176 
177 /*
178  * nlm_zone dcmd implementation
179  */
180 
181 static void nlm_zone_print(uintptr_t, const struct nlm_globals *, uint_t);
182 
183 void
184 nlm_zone_help(void)
185 {
186 	mdb_printf("-v		verbose information\n");
187 }
188 
189 int
190 nlm_zone_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
191 {
192 	struct nlm_globals g;
193 	char enum_val[32];
194 	uint_t opt_v = FALSE;
195 
196 	if (mdb_getopts(argc, argv,
197 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
198 		return (DCMD_USAGE);
199 
200 	if ((flags & DCMD_ADDRSPEC) == 0) {
201 		mdb_warn("requires addr of nlm_zone");
202 		return (DCMD_ERR);
203 	}
204 
205 	if (mdb_vread(&g, sizeof (g), addr) == -1) {
206 		mdb_warn("failed to read nlm_globals at %p", addr);
207 		return (DCMD_ERR);
208 	}
209 
210 	if (opt_v == FALSE) {
211 		nlm_zone_print(addr, &g, flags);
212 		return (DCMD_OK);
213 	}
214 
215 	/*
216 	 * Print verbose format
217 	 */
218 	mdb_printf("%<b>%<u>NLM zone globals (%p):%</u>%</b>\n", addr);
219 	mdb_printf(" Lockd PID: %u\n", g.lockd_pid);
220 	get_enum(enum_val, sizeof (enum_val),
221 	    "nlm_run_status_t", g.run_status, "NLM_S_");
222 	mdb_printf("Run status: %d (%s)\n", g.run_status, enum_val);
223 	mdb_printf(" NSM state: %d\n", g.nsm_state);
224 
225 	return (DCMD_OK);
226 }
227 
228 /*
229  * Shared by nlm_zone_dcmd and nlm_list_zone_cb
230  * Print a zone (nlm_globals) summary line.
231  */
232 static void
233 nlm_zone_print(uintptr_t addr, const struct nlm_globals *g, uint_t flags)
234 {
235 
236 	if (DCMD_HDRSPEC(flags)) {
237 		mdb_printf(
238 		    "%<b>%<u>%?-s  %-16s %</u>%</b>\n",
239 		    "nlm_globals", "pid");
240 	}
241 
242 	mdb_printf("%-?p %6d\n", addr, (int)g->lockd_pid);
243 }
244 
245 /*
246  * ****************************************************************
247  * NLM hosts (under zones)
248  */
249 
250 /*
251  * nlm_host walker implementation
252  */
253 
254 int
255 nlm_host_walk_init(mdb_walk_state_t *wsp)
256 {
257 	static int avl_off = -1;
258 
259 	if (wsp->walk_addr == 0) {
260 		mdb_printf("requires address of struct nlm_globals\n");
261 		return (WALK_ERR);
262 	}
263 
264 	/*
265 	 * Need the address of the nlm_hosts_tree AVL head
266 	 * within the nlm_globals, for the AVL walker.
267 	 */
268 	if (avl_off < 0) {
269 		avl_off = mdb_ctf_offsetof_by_name(
270 		    "struct nlm_globals", "nlm_hosts_tree");
271 	}
272 	if (avl_off < 0) {
273 		mdb_warn("cannot lookup: nlm_globals .nlm_hosts_tree");
274 		return (WALK_ERR);
275 	}
276 	wsp->walk_addr += avl_off;
277 
278 	if (mdb_layered_walk("avl", wsp) == -1) {
279 		mdb_warn("failed to walk nlm_globals .nlm_hosts_tree");
280 		return (WALK_ERR);
281 	}
282 
283 	return (WALK_NEXT);
284 }
285 
286 int
287 nlm_host_walk_step(mdb_walk_state_t *wsp)
288 {
289 	struct nlm_host nh;
290 	uintptr_t addr = wsp->walk_addr;
291 
292 	if (mdb_vread(&nh, sizeof (nh), addr) < 0) {
293 		mdb_warn("failed to read nlm_host at %p", addr);
294 		return (WALK_ERR);
295 	}
296 
297 	/* layered walk avl */
298 	return (wsp->walk_callback(wsp->walk_addr, &nh,
299 	    wsp->walk_cbdata));
300 }
301 
302 /*
303  * nlm_host dcmd implementation
304  */
305 
306 static void nlm_host_print(uintptr_t, const struct nlm_host *,
307     char *, char *, uint_t);
308 
309 void
310 nlm_host_help(void)
311 {
312 	mdb_printf("-v       verbose information\n");
313 }
314 
315 int
316 nlm_host_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
317 {
318 	struct nlm_host nh;
319 	char hname[NLM_MAXNAMELEN];
320 	char haddr[NLM_MAXADDRSTR];
321 	uint_t opt_v = FALSE;
322 
323 	if (mdb_getopts(argc, argv,
324 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
325 		return (DCMD_USAGE);
326 
327 	if ((flags & DCMD_ADDRSPEC) == 0) {
328 		mdb_warn("requires addr of nlm_host");
329 		return (DCMD_ERR);
330 	}
331 
332 	/* Get the nlm_host */
333 	if (mdb_vread(&nh, sizeof (nh), addr) == -1) {
334 		mdb_warn("failed to read nlm_host at %p", addr);
335 		return (DCMD_ERR);
336 	}
337 
338 	/* Get its name and address */
339 	if (mdb_readstr(hname, sizeof (hname),
340 	    (uintptr_t)nh.nh_name) < 0)
341 		strlcpy(hname, "?", sizeof (hname));
342 	if (nlm_netbuf_str(haddr, sizeof (haddr), &nh.nh_addr) < 0)
343 		strlcpy(haddr, "?", sizeof (haddr));
344 
345 	if (opt_v == FALSE) {
346 		nlm_host_print(addr, &nh, hname, haddr, flags);
347 		return (DCMD_OK);
348 	}
349 
350 	/*
351 	 * Print verbose format
352 	 */
353 
354 	mdb_printf("%<b>%<u>NLM host (%p):%</u>%</b>\n", addr);
355 
356 	mdb_printf("Refcnt: %u\n", nh.nh_refs);
357 	mdb_printf(" Sysid: %d\n", (int)nh.nh_sysid);
358 	mdb_printf("  Name: %s\n", hname);
359 	mdb_printf("  Addr: %s\n", haddr);
360 	mdb_printf(" State: %d\n", nh.nh_state);
361 	mdb_printf(" Flags: 0x%x <%b>\n",
362 	    nh.nh_flags, nh.nh_flags, host_flag_bits);
363 	mdb_printf("Vholds: %?p\n", nh.nh_vholds_list.tqh_first);
364 
365 	return (DCMD_OK);
366 }
367 
368 /*
369  * Shared by nlm_host_dcmd and nlm_list_host_cb
370  * Print an nlm_host summary line.
371  */
372 static void
373 nlm_host_print(uintptr_t addr, const struct nlm_host *nh,
374     char *hname, char *haddr, uint_t flags)
375 {
376 	int hname_width = 20;
377 
378 	if (DCMD_HDRSPEC(flags)) {
379 		mdb_printf("%<b>%<u>%-?s %-*s%10s %6s ", "nlm_host",
380 		    hname_width, "name", "refs", "sysid");
381 		mdb_printf("%s%</u>%</b>\n", "net_addr");
382 	}
383 
384 	mdb_printf("%?p %-*s%10i %6hi %s\n",
385 	    addr, hname_width, hname,
386 	    nh->nh_refs, nh->nh_sysid, haddr);
387 }
388 
389 /*
390  * ****************************************************************
391  * NLM vholds (under hosts)
392  */
393 
394 /*
395  * nlm_vhold walker implementation
396  */
397 
398 int
399 nlm_vhold_walk_init(mdb_walk_state_t *wsp)
400 {
401 	struct nlm_vhold_list head;
402 	uintptr_t addr;
403 	static int head_off = -1;
404 
405 	if (wsp->walk_addr == 0) {
406 		mdb_printf("requires address of struct nlm_host\n");
407 		return (WALK_ERR);
408 	}
409 
410 	/* Get offset of the list head and read it. */
411 	if (head_off < 0) {
412 		head_off = mdb_ctf_offsetof_by_name(
413 		    "struct nlm_host", "nh_vholds_list");
414 	}
415 	if (head_off < 0) {
416 		mdb_warn("cannot lookup: nlm_host .nh_vholds_list");
417 		return (WALK_ERR);
418 	}
419 
420 	addr = wsp->walk_addr + head_off;
421 	if (mdb_vread(&head, sizeof (head), addr) < 0) {
422 		mdb_warn("cannot read nlm_host at %p", wsp->walk_addr);
423 		return (WALK_ERR);
424 	}
425 
426 	wsp->walk_addr = (uintptr_t)head.tqh_first;
427 	return (WALK_NEXT);
428 }
429 
430 int
431 nlm_vhold_walk_step(mdb_walk_state_t *wsp)
432 {
433 	struct nlm_vhold nv;
434 	uintptr_t addr = wsp->walk_addr;
435 
436 	if (addr == 0)
437 		return (WALK_DONE);
438 
439 	if (mdb_vread(&nv, sizeof (nv), addr) < 0) {
440 		mdb_warn("failed to read nlm_vhold at %p", addr);
441 		return (WALK_ERR);
442 	}
443 
444 	wsp->walk_addr = (uintptr_t)nv.nv_link.tqe_next;
445 	return (wsp->walk_callback(addr, &nv, wsp->walk_cbdata));
446 }
447 
448 /*
449  * nlm_vhold dcmd implementation
450  */
451 
452 static void nlm_vhold_print(uintptr_t, const struct nlm_vhold *, uint_t);
453 
454 void
455 nlm_vhold_help(void)
456 {
457 	mdb_printf("-v       verbose information\n");
458 }
459 
460 int
461 nlm_vhold_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
462 {
463 	struct nlm_vhold nv;
464 	char path_buf[MAXPATHLEN];
465 	uint_t opt_v = FALSE;
466 
467 	if (mdb_getopts(argc, argv,
468 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
469 		return (DCMD_USAGE);
470 
471 	if ((flags & DCMD_ADDRSPEC) == 0) {
472 		mdb_warn("requires addr of nlm_vhold");
473 		return (DCMD_ERR);
474 	}
475 
476 	if (mdb_vread(&nv, sizeof (nv), addr) == -1) {
477 		mdb_warn("failed to read nlm_vhold at %p", addr);
478 		return (DCMD_ERR);
479 	}
480 
481 	if (opt_v == FALSE) {
482 		nlm_vhold_print(addr, &nv, flags);
483 		return (DCMD_OK);
484 	}
485 
486 	/*
487 	 * Print verbose format
488 	 */
489 
490 	if (nv.nv_vp == NULL || mdb_vnode2path((uintptr_t)nv.nv_vp,
491 	    path_buf, sizeof (path_buf)) != 0)
492 		strlcpy(path_buf, "?", sizeof (path_buf));
493 
494 	mdb_printf("%<b>%<u>NLM vhold (%p):%</u>%</b>\n", addr);
495 
496 	mdb_printf("Refcnt: %u\n", nv.nv_refcnt);
497 	mdb_printf(" Vnode: %?p (%s)\n", nv.nv_vp, path_buf);
498 	mdb_printf(" Slreq: %?p\n", nv.nv_slreqs.tqh_first);
499 
500 	return (DCMD_OK);
501 }
502 
503 /*
504  * Shared by nlm_vhold_dcmd and nlm_list_vnode_cb
505  * Print an nlm_vhold summary line.
506  */
507 static void
508 nlm_vhold_print(uintptr_t addr, const struct nlm_vhold *nv, uint_t flags)
509 {
510 
511 	if (DCMD_HDRSPEC(flags)) {
512 		mdb_printf("%<b>%<u>%-?s %10s %-?s %-?s%</u>%</b>\n",
513 		    "nlm_vhold", "refcnt", "vnode", "slreq");
514 	}
515 
516 	mdb_printf("%?p %10i %?p %?-p\n",
517 	    addr, nv->nv_refcnt, nv->nv_vp,
518 	    nv->nv_slreqs.tqh_first);
519 }
520 
521 /*
522  * ****************************************************************
523  * NLM slreqs (under vhold)
524  */
525 
526 /*
527  * nlm_slreq walker implementation
528  */
529 
530 int
531 nlm_slreq_walk_init(mdb_walk_state_t *wsp)
532 {
533 	struct nlm_slreq_list head;
534 	uintptr_t addr;
535 	static int head_off = -1;
536 
537 	if (wsp->walk_addr == 0) {
538 		mdb_printf("requires address of struct nlm_vhold\n");
539 		return (WALK_ERR);
540 	}
541 
542 	/* Get offset of the list head and read it. */
543 	if (head_off < 0) {
544 		head_off = mdb_ctf_offsetof_by_name(
545 		    "struct nlm_vhold", "nv_slreqs");
546 	}
547 	if (head_off < 0) {
548 		mdb_warn("cannot lookup: nlm_vhold .nv_slreqs");
549 		return (WALK_ERR);
550 	}
551 
552 	addr = wsp->walk_addr + head_off;
553 	if (mdb_vread(&head, sizeof (head), addr) < 0) {
554 		mdb_warn("cannot read nlm_vhold at %p", wsp->walk_addr);
555 		return (WALK_ERR);
556 	}
557 
558 	wsp->walk_addr = (uintptr_t)head.tqh_first;
559 	return (WALK_NEXT);
560 }
561 
562 int
563 nlm_slreq_walk_step(mdb_walk_state_t *wsp)
564 {
565 	struct nlm_slreq nsr;
566 	uintptr_t addr = wsp->walk_addr;
567 
568 	if (addr == 0)
569 		return (WALK_DONE);
570 
571 	if (mdb_vread(&nsr, sizeof (nsr), addr) < 0) {
572 		mdb_warn("failed to read nlm_slreq at %p", addr);
573 		return (WALK_ERR);
574 	}
575 
576 	wsp->walk_addr = (uintptr_t)nsr.nsr_link.tqe_next;
577 	return (wsp->walk_callback(addr, &nsr, wsp->walk_cbdata));
578 }
579 
580 /*
581  * nlm_slreq dcmd implementation
582  */
583 
584 static void nlm_slreq_print(uintptr_t, const struct nlm_slreq *, uint_t);
585 
586 void
587 nlm_slreq_help(void)
588 {
589 	mdb_printf("-v       verbose information\n");
590 }
591 
592 int
593 nlm_slreq_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
594 {
595 	struct nlm_slreq nsr;
596 	uint_t opt_v = FALSE;
597 
598 	if (mdb_getopts(argc, argv,
599 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
600 		return (DCMD_USAGE);
601 
602 	if ((flags & DCMD_ADDRSPEC) == 0) {
603 		mdb_warn("requires addr of nlm_slreq");
604 		return (DCMD_ERR);
605 	}
606 
607 	if (mdb_vread(&nsr, sizeof (nsr), addr) == -1) {
608 		mdb_warn("failed to read nlm_slreq at %p", addr);
609 		return (DCMD_ERR);
610 	}
611 
612 	if (opt_v == FALSE) {
613 		nlm_slreq_print(addr, &nsr, flags);
614 		return (DCMD_OK);
615 	}
616 
617 	/*
618 	 * Print verbose format
619 	 */
620 
621 	mdb_printf("%<b>%<u>NLM slreq (%p):%</u>%</b>\n", addr);
622 
623 	mdb_printf(" type: %d (%s)\n", nsr.nsr_fl.l_type,
624 	    (nsr.nsr_fl.l_type == F_RDLCK) ? "RD" :
625 	    (nsr.nsr_fl.l_type == F_WRLCK) ? "WR" : "??");
626 	mdb_printf("sysid: %d\n", nsr.nsr_fl.l_sysid);
627 	mdb_printf("  pid: %d\n", nsr.nsr_fl.l_pid);
628 	mdb_printf("start: %lld\n", nsr.nsr_fl.l_start);
629 	mdb_printf("  len: %lld\n", nsr.nsr_fl.l_len);
630 
631 	return (DCMD_OK);
632 }
633 
634 /*
635  * Shared by nlm_slreq_dcmd and nlm_list_slreq_cb
636  * Print an nlm_slreq summary line.
637  */
638 static void
639 nlm_slreq_print(uintptr_t addr, const struct nlm_slreq *nsr, uint_t flags)
640 {
641 
642 	if (DCMD_HDRSPEC(flags)) {
643 		mdb_printf("%<b>%<u>%-?s %4s %5s %3s %6s %6s%</u>%</b>\n",
644 		    "nlm_slreq", "type", "sysid", "pid", "start", "len");
645 	}
646 
647 	mdb_printf(
648 	    "%?p %4d %5d %3d %6lld %6lld\n",
649 	    addr,
650 	    nsr->nsr_fl.l_type,
651 	    nsr->nsr_fl.l_sysid,
652 	    nsr->nsr_fl.l_pid,
653 	    nsr->nsr_fl.l_start,
654 	    nsr->nsr_fl.l_len);
655 }
656 
657 /*
658  * ****************************************************************
659  */
660 
661 /*
662  * nlm_list dcmd implementation
663  *
664  * This is a fancy command command to walk the whole NLM
665  * data hierarchy, skipping uninteresting elements.
666  */
667 
668 #define	NLM_LIST_DEPTH_HOSTS	1	/* just hosts */
669 #define	NLM_LIST_DEPTH_VHOLDS	2	/* host and vholds */
670 #define	NLM_LIST_DEPTH_SLREQS	3	/* sleeping lock requests */
671 #define	NLM_LIST_DEPTH_DEFAULT	3	/* default: show all */
672 
673 struct nlm_list_arg {
674 	uint_t	opt_v;
675 	uint_t	opt_a;
676 	uint_t	depth;
677 	int	sysid;
678 	char	*host;
679 	uint_t	zone_flags;
680 	uint_t	host_flags;
681 	uint_t	vhold_flags;
682 	uint_t	slreq_flags;
683 	char	namebuf[NLM_MAXNAMELEN];
684 	char	addrbuf[NLM_MAXADDRSTR];
685 };
686 
687 static int nlm_list_zone_cb(uintptr_t, const void *, void *);
688 static int nlm_list_host_cb(uintptr_t, const void *, void *);
689 static int nlm_list_vhold_cb(uintptr_t, const void *, void *);
690 static int nlm_list_slreq_cb(uintptr_t, const void *, void *);
691 
692 void
693 nlm_list_help(void)
694 {
695 	mdb_printf("-v		verbose information\n");
696 	mdb_printf("-a		include idle hosts\n");
697 	mdb_printf("-d depth	recursion depth (zones, hosts, ...)\n");
698 	mdb_printf("-h host	filter by host name\n");
699 	mdb_printf("-s sysid	filter by sysid (0tnnn for decimal)\n");
700 }
701 
702 int
703 nlm_list_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
704 {
705 	struct nlm_list_arg *arg;
706 	uintptr_t depth = NLM_LIST_DEPTH_DEFAULT;
707 	char *host = NULL;
708 	char *sysid = NULL;
709 
710 	if ((flags & DCMD_ADDRSPEC) != 0)
711 		return (DCMD_USAGE);
712 
713 	arg = mdb_zalloc(sizeof (*arg), UM_SLEEP | UM_GC);
714 
715 	if (mdb_getopts(argc, argv,
716 	    'v', MDB_OPT_SETBITS, TRUE, &arg->opt_v,
717 	    'a', MDB_OPT_SETBITS, TRUE, &arg->opt_a,
718 	    'd', MDB_OPT_UINTPTR, &depth,
719 	    'h', MDB_OPT_STR, &host,
720 	    's', MDB_OPT_STR, &sysid,
721 	    NULL) != argc)
722 		return (DCMD_USAGE);
723 
724 	arg->depth = (uint_t)depth;
725 	arg->sysid = -1;
726 	if (host != NULL)
727 		arg->host = host;
728 	if (sysid != NULL) {
729 		arg->sysid = (int)mdb_strtoull(sysid);
730 		if (arg->sysid < 1) {
731 			mdb_warn("invalid sysid");
732 			arg->sysid = -1;
733 		}
734 	}
735 
736 	/* Specifying host or sysid id implies -a */
737 	if (arg->host != NULL || arg->sysid >= 0)
738 		arg->opt_a = TRUE;
739 
740 	arg->zone_flags = (DCMD_LOOP | DCMD_LOOPFIRST);
741 	if (mdb_pwalk("nlm_zone", nlm_list_zone_cb, arg, 0)) {
742 		mdb_warn("cannot walk nlm_zone list");
743 		return (DCMD_ERR);
744 	}
745 
746 	return (DCMD_OK);
747 }
748 
749 /* Called for each zone's nlm_globals */
750 static int
751 nlm_list_zone_cb(uintptr_t addr, const void *data, void *cb_data)
752 {
753 	struct nlm_list_arg *arg = cb_data;
754 	const struct nlm_globals *g = data;
755 
756 	/* Add zone filtering? */
757 
758 	/*
759 	 * Summary line for a struct nlm_globals
760 	 */
761 	nlm_zone_print(addr, g, 0);
762 	arg->zone_flags &= ~DCMD_LOOPFIRST;
763 
764 	if (arg->depth >= NLM_LIST_DEPTH_HOSTS) {
765 		(void) mdb_inc_indent(2);
766 		arg->host_flags = (DCMD_LOOP | DCMD_LOOPFIRST);
767 		if (mdb_pwalk("nlm_host", nlm_list_host_cb, arg, addr) != 0) {
768 			mdb_warn("failed to walk hosts for zone %p", addr);
769 			/* keep going */
770 		}
771 		(void) mdb_dec_indent(2);
772 	}
773 
774 	return (WALK_NEXT);
775 }
776 
777 /* Called for each nlm_host */
778 static int
779 nlm_list_host_cb(uintptr_t addr, const void *data, void *cb_data)
780 {
781 	struct nlm_list_arg *arg = cb_data;
782 	const struct nlm_host *nh = data;
783 
784 	/* Get the host name and net addr. */
785 	if (mdb_readstr(arg->namebuf, NLM_MAXNAMELEN,
786 	    (uintptr_t)nh->nh_name) < 0)
787 		(void) strlcpy(arg->namebuf, "?", sizeof (char));
788 	if (nlm_netbuf_str(arg->addrbuf, NLM_MAXADDRSTR, &nh->nh_addr) < 0)
789 		(void) strlcpy(arg->addrbuf, "?", sizeof (char));
790 
791 	/* Filter out uninteresting hosts */
792 	if (arg->opt_a == 0 && nh->nh_refs == 0)
793 		return (WALK_NEXT);
794 	if (arg->sysid != -1 && arg->sysid != (nh->nh_sysid & LM_SYSID_MAX))
795 		return (WALK_NEXT);
796 	if (arg->host != NULL && strcmp(arg->host, arg->namebuf) != 0)
797 		return (WALK_NEXT);
798 
799 	/*
800 	 * Summary line for struct nlm_host
801 	 */
802 	nlm_host_print(addr, nh, arg->namebuf, arg->addrbuf,
803 	    arg->host_flags);
804 	arg->host_flags &= ~DCMD_LOOPFIRST;
805 
806 	if (arg->depth >= NLM_LIST_DEPTH_VHOLDS) {
807 		(void) mdb_inc_indent(2);
808 		arg->vhold_flags = (DCMD_LOOP | DCMD_LOOPFIRST);
809 		if (mdb_pwalk("nlm_vhold", nlm_list_vhold_cb, arg, addr)) {
810 			mdb_warn("failed to walk vholds for host %p", addr);
811 			/* keep going */
812 		}
813 		(void) mdb_dec_indent(2);
814 	}
815 
816 	/*
817 	 * We printed some hosts, so tell nlm_list_zone_cb to
818 	 * print its header line again.
819 	 */
820 	arg->zone_flags |= DCMD_LOOPFIRST;
821 
822 	return (WALK_NEXT);
823 }
824 
825 /* Called for each nlm_vhold */
826 static int
827 nlm_list_vhold_cb(uintptr_t addr, const void *data, void *cb_data)
828 {
829 	struct nlm_list_arg *arg = cb_data;
830 	const struct nlm_vhold *nv = data;
831 
832 	/* Filter out uninteresting vholds */
833 	if (arg->opt_a == 0 && nv->nv_refcnt == 0)
834 		return (WALK_NEXT);
835 
836 	/*
837 	 * Summary line for struct nlm_vhold
838 	 */
839 	nlm_vhold_print(addr, nv, arg->vhold_flags);
840 	arg->vhold_flags &= ~DCMD_LOOPFIRST;
841 
842 	if (arg->depth >= NLM_LIST_DEPTH_SLREQS) {
843 		(void) mdb_inc_indent(2);
844 		arg->slreq_flags = (DCMD_LOOP | DCMD_LOOPFIRST);
845 		if (mdb_pwalk("nlm_slreq", nlm_list_slreq_cb, arg, addr)) {
846 			mdb_warn("failed to walk slreqs for vhold %p", addr);
847 			/* keep going */
848 		}
849 		(void) mdb_dec_indent(2);
850 	}
851 
852 	/*
853 	 * We printed some vholds, so tell nlm_list_host_cb to
854 	 * print its header line again.
855 	 */
856 	arg->host_flags |= DCMD_LOOPFIRST;
857 
858 	return (WALK_NEXT);
859 }
860 
861 /* Called for each nlm_slreq */
862 static int
863 nlm_list_slreq_cb(uintptr_t addr, const void *data, void *cb_data)
864 {
865 	struct nlm_list_arg *arg = cb_data;
866 	const struct nlm_slreq *nv = data;
867 
868 	/*
869 	 * Summary line for struct nlm_slreq
870 	 */
871 	nlm_slreq_print(addr, nv, arg->slreq_flags);
872 	arg->slreq_flags &= ~DCMD_LOOPFIRST;
873 
874 	/*
875 	 * We printed some slreqs, so tell nlm_list_vhold_cb to
876 	 * print its header line again.
877 	 */
878 	arg->vhold_flags |= DCMD_LOOPFIRST;
879 
880 	return (WALK_NEXT);
881 }
882 
883 /*
884  * ****************************************************************
885  */
886 
887 /*
888  * nlm_lockson dcmd implementation
889  * Walk the lock_graph, filtered by sysid
890  */
891 
892 struct nlm_locks_arg {
893 	/* dcmd options */
894 	uint_t	opt_v;
895 	int	sysid;
896 	char	*host;
897 	/* callback vars */
898 	uint_t	flags;
899 	int	lg_sysid;
900 	char	namebuf[NLM_MAXNAMELEN];
901 	char	addrbuf[NLM_MAXADDRSTR];
902 	char	pathbuf[PATH_MAX];
903 };
904 
905 static int nlm_locks_zone_cb(uintptr_t, const void *, void *);
906 static int nlm_locks_host_cb(uintptr_t, const void *, void *);
907 static int nlm_lockson_cb(uintptr_t, const void *, void *c);
908 
909 void
910 nlm_lockson_help(void)
911 {
912 	mdb_printf("-v		verbose information\n");
913 	mdb_printf("-h host	filter by host name\n");
914 	mdb_printf("-s sysid	filter by sysid (0tnnn for decimal)\n");
915 }
916 
917 int
918 nlm_lockson_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
919 {
920 	struct nlm_locks_arg *arg;
921 	char *host = NULL;
922 	char *sysid = NULL;
923 
924 	if ((flags & DCMD_ADDRSPEC) != 0)
925 		return (DCMD_USAGE);
926 
927 	arg = mdb_zalloc(sizeof (*arg), UM_SLEEP | UM_GC);
928 
929 	if (mdb_getopts(argc, argv,
930 	    'v', MDB_OPT_SETBITS, TRUE, &arg->opt_v,
931 	    'h', MDB_OPT_STR, &host,
932 	    's', MDB_OPT_STR, &sysid,
933 	    NULL) != argc)
934 		return (DCMD_USAGE);
935 
936 	arg->sysid = -1;
937 	if (host != NULL)
938 		arg->host = host;
939 	if (sysid != NULL) {
940 		arg->sysid = (int)mdb_strtoull(sysid);
941 		if (arg->sysid < 1) {
942 			mdb_warn("invalid sysid");
943 			arg->sysid = -1;
944 		}
945 	}
946 
947 	if (mdb_pwalk("nlm_zone", nlm_locks_zone_cb, arg, 0)) {
948 		mdb_warn("cannot walk nlm_zone list");
949 		return (DCMD_ERR);
950 	}
951 
952 	return (DCMD_OK);
953 }
954 
955 /* Called for each zone's nlm_globals */
956 static int
957 nlm_locks_zone_cb(uintptr_t addr, const void *data, void *cb_data)
958 {
959 	struct nlm_locks_arg *arg = cb_data;
960 	(void) data;
961 
962 	/*
963 	 * No filtering here. Don't even print zone addr.
964 	 * Just run the host list walker.
965 	 */
966 	if (mdb_pwalk("nlm_host", nlm_locks_host_cb, arg, addr) != 0) {
967 		mdb_warn("failed to walk hosts for zone %p", addr);
968 		/* keep going */
969 	}
970 
971 	return (WALK_NEXT);
972 }
973 
974 /* Called for each nlm_host */
975 static int
976 nlm_locks_host_cb(uintptr_t addr, const void *data, void *cb_data)
977 {
978 	struct nlm_locks_arg *arg = cb_data;
979 	const struct nlm_host *nh = data;
980 
981 	/* Get the host name and net addr. */
982 	if (mdb_readstr(arg->namebuf, NLM_MAXNAMELEN,
983 	    (uintptr_t)nh->nh_name) < 0)
984 		(void) strlcpy(arg->namebuf, "?", sizeof (char));
985 	if (nlm_netbuf_str(arg->addrbuf, NLM_MAXADDRSTR, &nh->nh_addr) < 0)
986 		(void) strlcpy(arg->addrbuf, "?", sizeof (char));
987 
988 	/* Filter out uninteresting hosts */
989 	if (arg->sysid != -1 && arg->sysid != (nh->nh_sysid & LM_SYSID_MAX))
990 		return (WALK_NEXT);
991 	if (arg->host != NULL && strcmp(arg->host, arg->namebuf) != 0)
992 		return (WALK_NEXT);
993 
994 	/*
995 	 * Summary line for struct nlm_host
996 	 */
997 	nlm_host_print(addr, nh, arg->namebuf, arg->addrbuf, 0);
998 
999 	/*
1000 	 * We run the lock_graph walker for every sysid, and the callback
1001 	 * uses arg->lg_sysid to filter graph elements.  Set that from
1002 	 * the host we're visiting now.
1003 	 */
1004 	arg->lg_sysid = (int)nh->nh_sysid;
1005 	arg->flags = (DCMD_LOOP | DCMD_LOOPFIRST);
1006 	if (mdb_pwalk("lock_graph", nlm_lockson_cb, arg, 0) < 0) {
1007 		mdb_warn("failed to walk lock_graph");
1008 		return (WALK_ERR);
1009 	}
1010 
1011 	return (WALK_NEXT);
1012 }
1013 
1014 static int
1015 nlm_lockson_cb(uintptr_t addr, const void *data, void *cb_data)
1016 {
1017 	struct nlm_locks_arg *arg = cb_data;
1018 	const lock_descriptor_t *ld = data;
1019 	proc_t p;
1020 	int local, sysid;
1021 	int host_width = 16;
1022 	char *s;
1023 
1024 	local = ld->l_flock.l_sysid & LM_SYSID_CLIENT;
1025 	sysid = ld->l_flock.l_sysid & LM_SYSID_MAX;
1026 
1027 	if (arg->lg_sysid != sysid)
1028 		return (WALK_NEXT);
1029 
1030 	if (DCMD_HDRSPEC(arg->flags)) {
1031 		mdb_printf("%<b>%<u>%-?s %-*s %5s(x) %-?s %-6s %-*s %-*s type",
1032 		    "lock_addr", host_width, "host", "sysid", "vnode", "pid",
1033 		    MAXCOMLEN, "cmd", arg->opt_v ? 9 : 5, "state");
1034 
1035 		if (arg->opt_v)
1036 			mdb_printf("%-11s srvstat %-10s", "(width)", "path");
1037 
1038 		mdb_printf("%</u>%</b>\n");
1039 	}
1040 	arg->flags &= ~DCMD_LOOPFIRST;
1041 
1042 	mdb_printf("%?p %-*s %5hi(%c) %?p %-6i %-*s ",
1043 	    addr, host_width, arg->namebuf,
1044 	    sysid, local ? 'L' : 'R', ld->l_vnode,
1045 	    ld->l_flock.l_pid, MAXCOMLEN,
1046 	    ld->l_flock.l_pid == 0 ? "<kernel>"
1047 	    : !local ? "<remote>"
1048 	    : mdb_pid2proc(ld->l_flock.l_pid, &p) == 0 ? "<defunct>"
1049 	    : p.p_user.u_comm);
1050 
1051 	if (arg->opt_v) {
1052 		switch (ld->l_status) {
1053 		case FLK_INITIAL_STATE:
1054 			s = "init";
1055 			break;
1056 		case FLK_START_STATE:
1057 			s = "execute";
1058 			break;
1059 		case FLK_ACTIVE_STATE:
1060 			s = "active";
1061 			break;
1062 		case FLK_SLEEPING_STATE:
1063 			s = "blocked";
1064 			break;
1065 		case FLK_GRANTED_STATE:
1066 			s = "granted";
1067 			break;
1068 		case FLK_INTERRUPTED_STATE:
1069 			s = "interrupt";
1070 			break;
1071 		case FLK_CANCELLED_STATE:
1072 			s = "cancel";
1073 			break;
1074 		case FLK_DEAD_STATE:
1075 			s = "done";
1076 			break;
1077 		default:
1078 			s = "??";
1079 			break;
1080 		}
1081 		mdb_printf("%-9s", s);
1082 	} else {
1083 		mdb_printf("%-5i", ld->l_status);
1084 	}
1085 
1086 	mdb_printf(" %-2s", ld->l_type == F_RDLCK ? "RD"
1087 	    : ld->l_type == F_WRLCK ? "WR" : "??");
1088 
1089 
1090 	if (!arg->opt_v) {
1091 		mdb_printf("\n");
1092 		return (WALK_NEXT);
1093 	}
1094 
1095 	switch (GET_NLM_STATE(ld)) {
1096 	case FLK_NLM_UP:
1097 		s = "up";
1098 		break;
1099 	case FLK_NLM_SHUTTING_DOWN:
1100 		s = "halting";
1101 		break;
1102 	case FLK_NLM_DOWN:
1103 		s = "down";
1104 		break;
1105 	case FLK_NLM_UNKNOWN:
1106 		s = "unknown";
1107 		break;
1108 	default:
1109 		s = "??";
1110 		break;
1111 	}
1112 
1113 	mdb_printf("(%5i:%-5i) %-7s ", ld->l_start, ld->l_len, s);
1114 	if (mdb_vnode2path((uintptr_t)ld->l_vnode,
1115 	    arg->pathbuf, PATH_MAX) == -1)
1116 		strlcpy(arg->pathbuf, "??", PATH_MAX);
1117 	mdb_printf("%s\n", arg->pathbuf);
1118 
1119 	return (WALK_NEXT);
1120 }
1121 
1122 
1123 static const mdb_walker_t walkers[] = {
1124 	{
1125 		"nlm_zone", "nlm_zone walker",
1126 		nlm_zone_walk_init, nlm_zone_walk_step
1127 	},
1128 	{
1129 		"nlm_host", "nlm_host walker",
1130 		nlm_host_walk_init, nlm_host_walk_step
1131 	},
1132 	{
1133 		"nlm_vhold", "nlm_vhold walker",
1134 		nlm_vhold_walk_init, nlm_vhold_walk_step
1135 	},
1136 	{
1137 		"nlm_slreq", "nlm_slreq walker",
1138 		nlm_slreq_walk_init, nlm_slreq_walk_step
1139 	},
1140 	{NULL, NULL, NULL, NULL}
1141 };
1142 
1143 static const mdb_dcmd_t dcmds[] = {
1144 	{
1145 		"nlm_zone", "?[-v]",
1146 		"dump per-zone nlm_globals",
1147 		nlm_zone_dcmd, nlm_zone_help
1148 	},
1149 	{
1150 		"nlm_host", "?[-v]",
1151 		"dump nlm_host structures (hosts/sysids)",
1152 		nlm_host_dcmd, nlm_host_help
1153 	},
1154 	{
1155 		"nlm_vhold", "?[-v]",
1156 		"dump nlm_vhold structures (vnode holds)",
1157 		nlm_vhold_dcmd, nlm_vhold_help
1158 	},
1159 	{
1160 		"nlm_slreq", "?[-v]",
1161 		"dump nlm_slreq structures (sleeping lock requests)",
1162 		nlm_slreq_dcmd, nlm_slreq_help
1163 	},
1164 	{
1165 		"nlm_list", "[-v][-a][-d depth][-h host][-s 0tSysID]",
1166 		"list all zones, optionally filter hosts ",
1167 		nlm_list_dcmd, nlm_list_help
1168 	},
1169 	{
1170 		"nlm_lockson", "[-v] [-h host] [-s 0tSysID]",
1171 		"dump NLM locks from host (or sysid)",
1172 		nlm_lockson_dcmd, nlm_lockson_help
1173 	},
1174 	{NULL, NULL, NULL, NULL}
1175 };
1176 
1177 static const mdb_modinfo_t modinfo = {
1178 	MDB_API_VERSION,
1179 	dcmds,
1180 	walkers
1181 };
1182 
1183 const mdb_modinfo_t *
1184 _mdb_init(void)
1185 {
1186 	return (&modinfo);
1187 }
1188