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
nlm_netbuf_str(char * buf,size_t bufsz,const struct netbuf * nb)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
get_enum(char * obuf,size_t size,const char * type_str,int val,const char * prefix)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
nlm_zone_walk_init(mdb_walk_state_t * wsp)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
nlm_zone_walk_step(mdb_walk_state_t * wsp)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
nlm_zone_help(void)184 nlm_zone_help(void)
185 {
186 mdb_printf("-v verbose information\n");
187 }
188
189 int
nlm_zone_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
nlm_zone_print(uintptr_t addr,const struct nlm_globals * g,uint_t flags)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
nlm_host_walk_init(mdb_walk_state_t * wsp)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
nlm_host_walk_step(mdb_walk_state_t * wsp)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
nlm_host_help(void)310 nlm_host_help(void)
311 {
312 mdb_printf("-v verbose information\n");
313 }
314
315 int
nlm_host_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
nlm_host_print(uintptr_t addr,const struct nlm_host * nh,char * hname,char * haddr,uint_t flags)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
nlm_vhold_walk_init(mdb_walk_state_t * wsp)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
nlm_vhold_walk_step(mdb_walk_state_t * wsp)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
nlm_vhold_help(void)455 nlm_vhold_help(void)
456 {
457 mdb_printf("-v verbose information\n");
458 }
459
460 int
nlm_vhold_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
nlm_vhold_print(uintptr_t addr,const struct nlm_vhold * nv,uint_t flags)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
nlm_slreq_walk_init(mdb_walk_state_t * wsp)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
nlm_slreq_walk_step(mdb_walk_state_t * wsp)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
nlm_slreq_help(void)587 nlm_slreq_help(void)
588 {
589 mdb_printf("-v verbose information\n");
590 }
591
592 int
nlm_slreq_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
nlm_slreq_print(uintptr_t addr,const struct nlm_slreq * nsr,uint_t flags)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
nlm_list_help(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
nlm_list_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
nlm_list_zone_cb(uintptr_t addr,const void * data,void * cb_data)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
nlm_list_host_cb(uintptr_t addr,const void * data,void * cb_data)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
nlm_list_vhold_cb(uintptr_t addr,const void * data,void * cb_data)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
nlm_list_slreq_cb(uintptr_t addr,const void * data,void * cb_data)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
nlm_lockson_help(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
nlm_lockson_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
nlm_locks_zone_cb(uintptr_t addr,const void * data,void * cb_data)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
nlm_locks_host_cb(uintptr_t addr,const void * data,void * cb_data)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
nlm_lockson_cb(uintptr_t addr,const void * data,void * cb_data)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 *
_mdb_init(void)1184 _mdb_init(void)
1185 {
1186 return (&modinfo);
1187 }
1188