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/types.h>
16 #include <sys/kstat.h>
17 #include <sys/mdb_modapi.h>
18 #include <rpc/clnt.h>
19 #include <nfs/nfs_clnt.h>
20 #include <nfs/nfs4_clnt.h>
21
22 #include "svc.h"
23 #include "rfs4.h"
24 #include "nfssrv.h"
25 #include "common.h"
26
27 #define NFS4_MINOR_VERS_COUNT 0
28 #define NFS_STAT_NUM_STATS 79
29
30 /*
31 * Structure used to group kstats we want to print.
32 */
33 typedef struct nfs_mdb_stats {
34 struct nfs_stats nfsstats;
35 struct rpcstat rpcstats;
36 struct nfs_globals nfsglbls;
37 uintptr_t clntstat;
38 uintptr_t clntstat4; /* extend this for NFS4.X */
39 uintptr_t callback_stats;
40 } nfs_mdb_stats_t;
41
42 static int nfs_stat_clnt(nfs_mdb_stats_t *, int, int);
43 static int nfs_stat_srv(nfs_mdb_stats_t *, int, int);
44 static int nfs_srvstat(nfs_mdb_stats_t *, int);
45 static int nfs_srvstat_rpc(nfs_mdb_stats_t *);
46 static int nfs_srvstat_acl(nfs_mdb_stats_t *, int);
47 static int nfs_clntstat(nfs_mdb_stats_t *, int);
48 static int nfs_clntstat_rpc(nfs_mdb_stats_t *);
49 static int nfs_clntstat_acl(nfs_mdb_stats_t *, int);
50 static int nfs_srvstat_cb(nfs_mdb_stats_t *);
51
52 #define NFS_SRV_STAT 0x1
53 #define NFS_CLNT_STAT 0x2
54 #define NFS_CB_STAT 0x4
55 #define NFS_NFS_STAT 0x1
56 #define NFS_ACL_STAT 0x2
57 #define NFS_RPC_STAT 0x4
58 #define NFS_V2_STAT 0x1
59 #define NFS_V3_STAT 0x2
60 #define NFS_V4_STAT 0x4
61
62 static int prt_nfs_stats(uintptr_t, char *);
63 static void kstat_prtout(char *, uint64_t *, int);
64
65 void
nfs_stat_help(void)66 nfs_stat_help(void)
67 {
68 mdb_printf("Switches similar to those of nfsstat command.\n",
69 " ::nfs_stat -a -> ACL Statistics.\n"
70 " ::nfs_stat -b -> Callback Stats. (V4 only)\n"
71 " ::nfs_stat -c -> Client Statistics.\n"
72 " ::nfs_stat -n -> NFS Statistics.\n"
73 " ::nfs_stat -r -> RPC Statistics.\n"
74 " ::nfs_stat -s -> Server Statistics.\n"
75 " ::nfs_stat -2 -> Version 2.\n"
76 " ::nfs_stat -3 -> Version 3.\n"
77 " ::nfs_stat -4 -> Version 4.\n");
78 }
79
80 int
nfs_stat_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)81 nfs_stat_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
82 {
83 int host_flag = 0; /* host or client flag */
84 int type_flag = 0; /* type acl, rpc of nfs */
85 int vers_flag = 0; /* NFS version flag */
86 nfs_mdb_stats_t mdb_stats;
87 uintptr_t glbls;
88 uintptr_t cb_glbls;
89 uintptr_t zonep;
90
91 if (argc == 1 && argv->a_type == MDB_TYPE_IMMEDIATE) {
92 kstat_named_t ksts;
93 int i;
94 for (i = argv->a_un.a_val; i; i--) {
95 if (mdb_vread(&ksts, sizeof (ksts), addr) < 0) {
96 mdb_warn("failed to read kstat_name_t");
97 return (DCMD_ERR);
98 }
99 mdb_printf(" %8s %30d\n", ksts.name, ksts.value.ui64);
100 addr += sizeof (ksts);
101 }
102 return (DCMD_OK);
103 }
104
105 if (mdb_getopts(argc, argv,
106 'a', MDB_OPT_SETBITS, NFS_ACL_STAT, &type_flag,
107 'b', MDB_OPT_SETBITS, NFS_CB_STAT, &host_flag,
108 'c', MDB_OPT_SETBITS, NFS_CLNT_STAT, &host_flag,
109 'n', MDB_OPT_SETBITS, NFS_NFS_STAT, &type_flag,
110 'r', MDB_OPT_SETBITS, NFS_RPC_STAT, &type_flag,
111 's', MDB_OPT_SETBITS, NFS_SRV_STAT, &host_flag,
112 '2', MDB_OPT_SETBITS, NFS_V2_STAT, &vers_flag,
113 '3', MDB_OPT_SETBITS, NFS_V3_STAT, &vers_flag,
114 '4', MDB_OPT_SETBITS, NFS_V4_STAT, &vers_flag,
115 NULL) != argc) {
116 return (DCMD_USAGE);
117 }
118
119
120 if (flags & DCMD_ADDRSPEC) {
121 zonep = addr;
122 } else {
123 if (mdb_readsym(&zonep, sizeof (uintptr_t),
124 "global_zone") == -1) {
125 mdb_warn("Failed to find global_zone");
126 return (DCMD_ERR);
127 }
128 }
129
130 if (zoned_get_zsd(zonep, "nfssrv_zone_key", &glbls)) {
131 mdb_warn("Failed to find nfssrv_zone_key");
132 return (DCMD_ERR);
133 }
134
135 if (mdb_vread(&mdb_stats.nfsglbls, sizeof (struct nfs_globals),
136 glbls) == -1) {
137 mdb_warn("Failed to read nfs_stats at %p", glbls);
138 return (DCMD_ERR);
139 }
140
141 if (zoned_get_zsd(zonep, "nfsstat_zone_key", &glbls)) {
142 mdb_warn("Failed to find %s", "nfsstat_zone_key");
143 return (DCMD_ERR);
144 }
145
146 if (mdb_vread(&mdb_stats.nfsstats, sizeof (struct nfs_stats),
147 glbls) == -1) {
148 mdb_warn("Failed to read nfs_stats at %p", glbls);
149 return (DCMD_ERR);
150 }
151
152 if (zoned_get_zsd(zonep, "rpcstat_zone_key", &glbls)) {
153 mdb_warn("Failed to find %s", "rpcstat_zone_key");
154 return (DCMD_ERR);
155 }
156
157 if (mdb_vread(&mdb_stats.rpcstats, sizeof (struct rpcstat),
158 glbls) == -1) {
159 mdb_warn("Failed to read nfs_stats at %p", glbls);
160 return (DCMD_ERR);
161 }
162
163 if (zoned_get_zsd(zonep, "nfsclnt_zone_key", &glbls)) {
164 mdb_warn("Failed to find %s", "nfsclnt_zone_key");
165 return (DCMD_ERR);
166 }
167 mdb_stats.clntstat = glbls + offsetof(struct nfs_clnt, nfscl_stat);
168
169 if (zoned_get_zsd(zonep, "nfs4clnt_zone_key", &glbls)) {
170 mdb_warn("Failed to find %s", "nfs4clnt_zone_key");
171 return (DCMD_ERR);
172 }
173 /*
174 * currently only have NFSv4.0 is availble. When NFSv4.1 and above are
175 * available stats support will need to be added.
176 */
177 mdb_stats.clntstat4 = glbls + offsetof(struct nfs4_clnt, nfscl_stat);
178
179 if (zoned_get_zsd(zonep, "nfs4_callback_zone_key",
180 (uintptr_t *)&cb_glbls)) {
181 mdb_warn("Failed to find %s", "nfs4_callback_zone_key");
182 return (DCMD_ERR);
183 }
184
185 mdb_stats.callback_stats =
186 (cb_glbls + offsetof(struct nfs4_callback_globals,
187 nfs4_callback_stats));
188
189 if (host_flag == 0)
190 host_flag = NFS_SRV_STAT | NFS_CLNT_STAT | NFS_CB_STAT;
191 if (vers_flag == 0)
192 vers_flag = NFS_V2_STAT | NFS_V3_STAT | NFS_V4_STAT;
193 if (type_flag == 0)
194 type_flag = NFS_NFS_STAT | NFS_ACL_STAT | NFS_RPC_STAT;
195
196 if (host_flag & NFS_CB_STAT)
197 if (nfs_srvstat_cb(&mdb_stats))
198 return (DCMD_ERR);
199 if (host_flag & NFS_SRV_STAT)
200 if (nfs_stat_srv(&mdb_stats, type_flag, vers_flag))
201 return (DCMD_ERR);
202 if (host_flag & NFS_CLNT_STAT)
203 if (nfs_stat_clnt(&mdb_stats, type_flag, vers_flag))
204 return (DCMD_ERR);
205 return (DCMD_OK);
206 }
207
208 static int
nfs_srvstat_cb(nfs_mdb_stats_t * stptr)209 nfs_srvstat_cb(nfs_mdb_stats_t *stptr)
210 {
211 int ret = 0;
212 mdb_printf("CALLBACK STATISTICS:\n");
213
214 ret = prt_nfs_stats(stptr->callback_stats, "nfs4_callback_stats_tmpl");
215
216 return (ret);
217 }
218
219 static int
nfs_stat_srv(nfs_mdb_stats_t * stptr,int type_flag,int vers_flag)220 nfs_stat_srv(nfs_mdb_stats_t *stptr, int type_flag, int vers_flag)
221 {
222 mdb_printf("NFS SERVER STATS:\n");
223 if (type_flag & NFS_SRV_STAT) {
224 if (nfs_srvstat(stptr, vers_flag) != 0)
225 return (1);
226 }
227 if (type_flag & NFS_RPC_STAT) {
228 if (nfs_srvstat_rpc(stptr) != 0)
229 return (1);
230 }
231 if (type_flag & NFS_ACL_STAT) {
232 if (nfs_srvstat_acl(stptr, vers_flag) != 0)
233 return (1);
234 }
235 return (0);
236 }
237
238 static int
nfs_stat_clnt(nfs_mdb_stats_t * stptr,int type_flag,int vers_flag)239 nfs_stat_clnt(nfs_mdb_stats_t *stptr, int type_flag, int vers_flag)
240 {
241 mdb_printf("CLIENT STATISTICS:\n");
242 if (type_flag & NFS_CLNT_STAT) {
243 if (nfs_clntstat(stptr, vers_flag))
244 return (1);
245 }
246 if (type_flag & NFS_ACL_STAT) {
247 if (nfs_clntstat_acl(stptr, vers_flag))
248 return (1);
249 }
250 if (type_flag & NFS_RPC_STAT) {
251 if (nfs_clntstat_rpc(stptr))
252 return (1);
253 }
254 return (0);
255 }
256
257
258 static int
nfs_srvstat(nfs_mdb_stats_t * stptr,int flag)259 nfs_srvstat(nfs_mdb_stats_t *stptr, int flag)
260 {
261 mdb_printf("NFS Statistics\n");
262 if (flag & NFS_V2_STAT) {
263 mdb_printf("NFSv2\n");
264 if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.svstat[2],
265 "svstat_tmpl") ||
266 prt_nfs_stats((uintptr_t)stptr->nfsglbls.rfsproccnt[2],
267 "rfsproccnt_v2_tmpl"))
268 return (-1);
269 }
270 if (flag & NFS_V3_STAT) {
271 mdb_printf("NFSv3\n");
272 if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.svstat[3],
273 "svstat_tmpl") ||
274 prt_nfs_stats((uintptr_t)stptr->nfsglbls.rfsproccnt[3],
275 "rfsproccnt_v3_tmpl"))
276 return (-1);
277 }
278 if (flag & NFS_V4_STAT) {
279 mdb_printf("NFSv4\n");
280 if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.svstat[4],
281 "svstat_tmpl") ||
282 prt_nfs_stats((uintptr_t)stptr->nfsglbls.rfsproccnt[4],
283 "rfsproccnt_v4_tmpl"))
284 return (-1);
285 }
286 return (0);
287 }
288
289
290 static int
nfs_srvstat_rpc(nfs_mdb_stats_t * stptr)291 nfs_srvstat_rpc(nfs_mdb_stats_t *stptr)
292 {
293 mdb_printf("NFS RPC Statistics\n");
294 mdb_printf("ConnectionLess\n");
295 if (prt_nfs_stats((uintptr_t)stptr->rpcstats.rpc_clts_server,
296 "clts_rsstat_tmpl"))
297 return (-1);
298 mdb_printf("ConnectionOriented\n");
299 if (prt_nfs_stats((uintptr_t)stptr->rpcstats.rpc_cots_server,
300 "cots_rsstat_tmpl"))
301 return (-1);
302 return (0);
303 }
304
305
306 static int
nfs_srvstat_acl(nfs_mdb_stats_t * stptr,int flags)307 nfs_srvstat_acl(nfs_mdb_stats_t *stptr, int flags)
308 {
309 mdb_printf("NFS ACL Statistics\n");
310 if (flags & NFS_V2_STAT) {
311 mdb_printf("NFSv2\n");
312 if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.aclproccnt[2],
313 "aclproccnt_v2_tmpl"))
314 return (-1);
315 }
316 if (flags & NFS_V3_STAT) {
317 mdb_printf("NFSv3\n");
318 if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.aclproccnt[3],
319 "aclproccnt_v3_tmpl"))
320 return (-1);
321 }
322 if (flags & NFS_V4_STAT) {
323 mdb_printf("NFSv4\n");
324 if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.aclproccnt[4],
325 "aclreqcnt_v4_tmpl"))
326 return (-1);
327 }
328 return (0);
329 }
330
331 static int
nfs_clntstat(nfs_mdb_stats_t * stptr,int flags)332 nfs_clntstat(nfs_mdb_stats_t *stptr, int flags)
333 {
334 mdb_printf("NFS Statistics\n");
335 if (prt_nfs_stats((uintptr_t)stptr->clntstat, "clstat_tmpl"))
336 return (-1);
337 if (flags & NFS_V2_STAT) {
338 mdb_printf("Version 2\n");
339 if (prt_nfs_stats(
340 (uintptr_t)stptr->nfsstats.nfs_stats_v2.rfsreqcnt_ptr,
341 "rfsreqcnt_v2_tmpl"))
342 return (-1);
343 }
344 if (flags & NFS_V3_STAT) {
345 mdb_printf("Version 3\n");
346 if (prt_nfs_stats(
347 (uintptr_t)stptr->nfsstats.nfs_stats_v3.rfsreqcnt_ptr,
348 "rfsreqcnt_v3_tmpl"))
349 return (-1);
350 }
351 if (flags & NFS_V4_STAT) {
352 mdb_printf("NFSv4 client\n");
353 if (prt_nfs_stats((uintptr_t)stptr->clntstat, "clstat4_tmpl"))
354 return (-1);
355 mdb_printf("Version 4\n");
356 if (prt_nfs_stats(
357 (uintptr_t)stptr->nfsstats.nfs_stats_v4.rfsreqcnt_ptr,
358 "rfsreqcnt_v4_tmpl"))
359 return (-1);
360 }
361 return (0);
362 }
363
364 static int
nfs_clntstat_rpc(nfs_mdb_stats_t * stptr)365 nfs_clntstat_rpc(nfs_mdb_stats_t *stptr)
366 {
367 mdb_printf("NFS RPC Statistics\n");
368 mdb_printf("ConnectionLess\n");
369 if (prt_nfs_stats((uintptr_t)stptr->rpcstats.rpc_clts_client,
370 "clts_rcstat_tmpl"))
371 return (-1);
372 mdb_printf("ConnectionOriented\n");
373 if (prt_nfs_stats((uintptr_t)stptr->rpcstats.rpc_cots_client,
374 "cots_rcstat_tmpl"))
375 return (-1);
376 return (0);
377 }
378
379 static int
nfs_clntstat_acl(nfs_mdb_stats_t * stptr,int flags)380 nfs_clntstat_acl(nfs_mdb_stats_t *stptr, int flags)
381 {
382 mdb_printf("ACL Statistics\n");
383 if (flags & NFS_V2_STAT) {
384 mdb_printf("Version 2\n");
385 if (prt_nfs_stats(
386 (uintptr_t)stptr->nfsstats.nfs_stats_v2.aclreqcnt_ptr,
387 "aclreqcnt_v2_tmpl"))
388 return (-1);
389 }
390 if (flags & NFS_V3_STAT) {
391 mdb_printf("Version 3\n");
392 if (prt_nfs_stats(
393 (uintptr_t)stptr->nfsstats.nfs_stats_v3.aclreqcnt_ptr,
394 "aclreqcnt_v3_tmpl"))
395 return (-1);
396 }
397 if (flags & NFS_V4_STAT) {
398 mdb_printf("Version 4\n");
399 if (prt_nfs_stats(
400 (uintptr_t)stptr->nfsstats.nfs_stats_v4.aclreqcnt_ptr,
401 "aclreqcnt_v4_tmpl"))
402 return (-1);
403 }
404 return (0);
405 }
406
407
408 /*
409 * helper functions for printing out the kstat data
410 */
411
412 #define NFS_STAT_NUM_CLMNS 16
413
414 static int
prt_nfs_stats(uintptr_t addr,char * name)415 prt_nfs_stats(uintptr_t addr, char *name)
416 {
417
418 GElf_Sym sym;
419 kstat_named_t kstats;
420 char *kstat_line;
421 uint64_t *value;
422 uint_t count;
423 int i = 0, status = 0;
424
425
426 if (mdb_lookup_by_name(name, &sym) != 0) {
427 mdb_warn("failed to find %s", name);
428 return (1);
429 }
430
431 count = sym.st_size / sizeof (kstat_named_t);
432
433 kstat_line = mdb_alloc(count * NFS_STAT_NUM_CLMNS, UM_SLEEP);
434 value = mdb_alloc(count * sizeof (uint64_t), UM_SLEEP);
435 for (i = 0; i < count; i++) {
436 if (mdb_vread(&kstats, sizeof (kstat_named_t),
437 addr + (i * sizeof (kstat_named_t))) < 0) {
438 status = 1;
439 goto done;
440 }
441 mdb_snprintf(&kstat_line[NFS_STAT_NUM_CLMNS * i],
442 NFS_STAT_NUM_CLMNS, "%s", kstats.name);
443 value[i] = kstats.value.ui64;
444 }
445 kstat_prtout(kstat_line, value, count);
446 done:
447 mdb_free(kstat_line, count * NFS_STAT_NUM_CLMNS);
448 mdb_free(value, count * sizeof (uint64_t));
449 return (status);
450 }
451
452 static void
kstat_prtout(char * ks_line,uint64_t * values,int count)453 kstat_prtout(char *ks_line, uint64_t *values, int count)
454 {
455 char val_str[32];
456 int i = 0, num = 0;
457
458 while (i < count) {
459 mdb_printf("%-*s", NFS_STAT_NUM_CLMNS,
460 &ks_line[NFS_STAT_NUM_CLMNS * i]);
461
462 num++;
463 if (num == NFS_STAT_NUM_STATS / NFS_STAT_NUM_CLMNS) {
464 mdb_printf("\n");
465 while (num > 0) {
466 mdb_snprintf(val_str, 24, "%ld ",
467 values[i+1-num]);
468 mdb_printf("%-*s", NFS_STAT_NUM_CLMNS, val_str);
469 --num;
470 }
471 mdb_printf("\n");
472 }
473 i++;
474 }
475 mdb_printf("\n");
476 }
477