#include <sys/types.h>
#include <sys/kstat.h>
#include <sys/mdb_modapi.h>
#include <rpc/clnt.h>
#include <nfs/nfs_clnt.h>
#include <nfs/nfs4_clnt.h>
#include "svc.h"
#include "rfs4.h"
#include "nfssrv.h"
#include "common.h"
#define NFS4_MINOR_VERS_COUNT 0
#define NFS_STAT_NUM_STATS 79
typedef struct nfs_mdb_stats {
struct nfs_stats nfsstats;
struct rpcstat rpcstats;
struct nfs_globals nfsglbls;
uintptr_t clntstat;
uintptr_t clntstat4;
uintptr_t callback_stats;
} nfs_mdb_stats_t;
static int nfs_stat_clnt(nfs_mdb_stats_t *, int, int);
static int nfs_stat_srv(nfs_mdb_stats_t *, int, int);
static int nfs_srvstat(nfs_mdb_stats_t *, int);
static int nfs_srvstat_rpc(nfs_mdb_stats_t *);
static int nfs_srvstat_acl(nfs_mdb_stats_t *, int);
static int nfs_clntstat(nfs_mdb_stats_t *, int);
static int nfs_clntstat_rpc(nfs_mdb_stats_t *);
static int nfs_clntstat_acl(nfs_mdb_stats_t *, int);
static int nfs_srvstat_cb(nfs_mdb_stats_t *);
#define NFS_SRV_STAT 0x1
#define NFS_CLNT_STAT 0x2
#define NFS_CB_STAT 0x4
#define NFS_NFS_STAT 0x1
#define NFS_ACL_STAT 0x2
#define NFS_RPC_STAT 0x4
#define NFS_V2_STAT 0x1
#define NFS_V3_STAT 0x2
#define NFS_V4_STAT 0x4
static int prt_nfs_stats(uintptr_t, char *);
static void kstat_prtout(char *, uint64_t *, int);
void
nfs_stat_help(void)
{
mdb_printf("Switches similar to those of nfsstat command.\n",
" ::nfs_stat -a -> ACL Statistics.\n"
" ::nfs_stat -b -> Callback Stats. (V4 only)\n"
" ::nfs_stat -c -> Client Statistics.\n"
" ::nfs_stat -n -> NFS Statistics.\n"
" ::nfs_stat -r -> RPC Statistics.\n"
" ::nfs_stat -s -> Server Statistics.\n"
" ::nfs_stat -2 -> Version 2.\n"
" ::nfs_stat -3 -> Version 3.\n"
" ::nfs_stat -4 -> Version 4.\n");
}
int
nfs_stat_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int host_flag = 0;
int type_flag = 0;
int vers_flag = 0;
nfs_mdb_stats_t mdb_stats;
uintptr_t glbls;
uintptr_t cb_glbls;
uintptr_t zonep;
if (argc == 1 && argv->a_type == MDB_TYPE_IMMEDIATE) {
kstat_named_t ksts;
int i;
for (i = argv->a_un.a_val; i; i--) {
if (mdb_vread(&ksts, sizeof (ksts), addr) < 0) {
mdb_warn("failed to read kstat_name_t");
return (DCMD_ERR);
}
mdb_printf(" %8s %30d\n", ksts.name, ksts.value.ui64);
addr += sizeof (ksts);
}
return (DCMD_OK);
}
if (mdb_getopts(argc, argv,
'a', MDB_OPT_SETBITS, NFS_ACL_STAT, &type_flag,
'b', MDB_OPT_SETBITS, NFS_CB_STAT, &host_flag,
'c', MDB_OPT_SETBITS, NFS_CLNT_STAT, &host_flag,
'n', MDB_OPT_SETBITS, NFS_NFS_STAT, &type_flag,
'r', MDB_OPT_SETBITS, NFS_RPC_STAT, &type_flag,
's', MDB_OPT_SETBITS, NFS_SRV_STAT, &host_flag,
'2', MDB_OPT_SETBITS, NFS_V2_STAT, &vers_flag,
'3', MDB_OPT_SETBITS, NFS_V3_STAT, &vers_flag,
'4', MDB_OPT_SETBITS, NFS_V4_STAT, &vers_flag,
NULL) != argc) {
return (DCMD_USAGE);
}
if (flags & DCMD_ADDRSPEC) {
zonep = addr;
} else {
if (mdb_readsym(&zonep, sizeof (uintptr_t),
"global_zone") == -1) {
mdb_warn("Failed to find global_zone");
return (DCMD_ERR);
}
}
if (zoned_get_zsd(zonep, "nfssrv_zone_key", &glbls)) {
mdb_warn("Failed to find nfssrv_zone_key");
return (DCMD_ERR);
}
if (mdb_vread(&mdb_stats.nfsglbls, sizeof (struct nfs_globals),
glbls) == -1) {
mdb_warn("Failed to read nfs_stats at %p", glbls);
return (DCMD_ERR);
}
if (zoned_get_zsd(zonep, "nfsstat_zone_key", &glbls)) {
mdb_warn("Failed to find %s", "nfsstat_zone_key");
return (DCMD_ERR);
}
if (mdb_vread(&mdb_stats.nfsstats, sizeof (struct nfs_stats),
glbls) == -1) {
mdb_warn("Failed to read nfs_stats at %p", glbls);
return (DCMD_ERR);
}
if (zoned_get_zsd(zonep, "rpcstat_zone_key", &glbls)) {
mdb_warn("Failed to find %s", "rpcstat_zone_key");
return (DCMD_ERR);
}
if (mdb_vread(&mdb_stats.rpcstats, sizeof (struct rpcstat),
glbls) == -1) {
mdb_warn("Failed to read nfs_stats at %p", glbls);
return (DCMD_ERR);
}
if (zoned_get_zsd(zonep, "nfsclnt_zone_key", &glbls)) {
mdb_warn("Failed to find %s", "nfsclnt_zone_key");
return (DCMD_ERR);
}
mdb_stats.clntstat = glbls + offsetof(struct nfs_clnt, nfscl_stat);
if (zoned_get_zsd(zonep, "nfs4clnt_zone_key", &glbls)) {
mdb_warn("Failed to find %s", "nfs4clnt_zone_key");
return (DCMD_ERR);
}
mdb_stats.clntstat4 = glbls + offsetof(struct nfs4_clnt, nfscl_stat);
if (zoned_get_zsd(zonep, "nfs4_callback_zone_key",
(uintptr_t *)&cb_glbls)) {
mdb_warn("Failed to find %s", "nfs4_callback_zone_key");
return (DCMD_ERR);
}
mdb_stats.callback_stats =
(cb_glbls + offsetof(struct nfs4_callback_globals,
nfs4_callback_stats));
if (host_flag == 0)
host_flag = NFS_SRV_STAT | NFS_CLNT_STAT | NFS_CB_STAT;
if (vers_flag == 0)
vers_flag = NFS_V2_STAT | NFS_V3_STAT | NFS_V4_STAT;
if (type_flag == 0)
type_flag = NFS_NFS_STAT | NFS_ACL_STAT | NFS_RPC_STAT;
if (host_flag & NFS_CB_STAT)
if (nfs_srvstat_cb(&mdb_stats))
return (DCMD_ERR);
if (host_flag & NFS_SRV_STAT)
if (nfs_stat_srv(&mdb_stats, type_flag, vers_flag))
return (DCMD_ERR);
if (host_flag & NFS_CLNT_STAT)
if (nfs_stat_clnt(&mdb_stats, type_flag, vers_flag))
return (DCMD_ERR);
return (DCMD_OK);
}
static int
nfs_srvstat_cb(nfs_mdb_stats_t *stptr)
{
int ret = 0;
mdb_printf("CALLBACK STATISTICS:\n");
ret = prt_nfs_stats(stptr->callback_stats, "nfs4_callback_stats_tmpl");
return (ret);
}
static int
nfs_stat_srv(nfs_mdb_stats_t *stptr, int type_flag, int vers_flag)
{
mdb_printf("NFS SERVER STATS:\n");
if (type_flag & NFS_SRV_STAT) {
if (nfs_srvstat(stptr, vers_flag) != 0)
return (1);
}
if (type_flag & NFS_RPC_STAT) {
if (nfs_srvstat_rpc(stptr) != 0)
return (1);
}
if (type_flag & NFS_ACL_STAT) {
if (nfs_srvstat_acl(stptr, vers_flag) != 0)
return (1);
}
return (0);
}
static int
nfs_stat_clnt(nfs_mdb_stats_t *stptr, int type_flag, int vers_flag)
{
mdb_printf("CLIENT STATISTICS:\n");
if (type_flag & NFS_CLNT_STAT) {
if (nfs_clntstat(stptr, vers_flag))
return (1);
}
if (type_flag & NFS_ACL_STAT) {
if (nfs_clntstat_acl(stptr, vers_flag))
return (1);
}
if (type_flag & NFS_RPC_STAT) {
if (nfs_clntstat_rpc(stptr))
return (1);
}
return (0);
}
static int
nfs_srvstat(nfs_mdb_stats_t *stptr, int flag)
{
mdb_printf("NFS Statistics\n");
if (flag & NFS_V2_STAT) {
mdb_printf("NFSv2\n");
if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.svstat[2],
"svstat_tmpl") ||
prt_nfs_stats((uintptr_t)stptr->nfsglbls.rfsproccnt[2],
"rfsproccnt_v2_tmpl"))
return (-1);
}
if (flag & NFS_V3_STAT) {
mdb_printf("NFSv3\n");
if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.svstat[3],
"svstat_tmpl") ||
prt_nfs_stats((uintptr_t)stptr->nfsglbls.rfsproccnt[3],
"rfsproccnt_v3_tmpl"))
return (-1);
}
if (flag & NFS_V4_STAT) {
mdb_printf("NFSv4\n");
if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.svstat[4],
"svstat_tmpl") ||
prt_nfs_stats((uintptr_t)stptr->nfsglbls.rfsproccnt[4],
"rfsproccnt_v4_tmpl"))
return (-1);
}
return (0);
}
static int
nfs_srvstat_rpc(nfs_mdb_stats_t *stptr)
{
mdb_printf("NFS RPC Statistics\n");
mdb_printf("ConnectionLess\n");
if (prt_nfs_stats((uintptr_t)stptr->rpcstats.rpc_clts_server,
"clts_rsstat_tmpl"))
return (-1);
mdb_printf("ConnectionOriented\n");
if (prt_nfs_stats((uintptr_t)stptr->rpcstats.rpc_cots_server,
"cots_rsstat_tmpl"))
return (-1);
return (0);
}
static int
nfs_srvstat_acl(nfs_mdb_stats_t *stptr, int flags)
{
mdb_printf("NFS ACL Statistics\n");
if (flags & NFS_V2_STAT) {
mdb_printf("NFSv2\n");
if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.aclproccnt[2],
"aclproccnt_v2_tmpl"))
return (-1);
}
if (flags & NFS_V3_STAT) {
mdb_printf("NFSv3\n");
if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.aclproccnt[3],
"aclproccnt_v3_tmpl"))
return (-1);
}
if (flags & NFS_V4_STAT) {
mdb_printf("NFSv4\n");
if (prt_nfs_stats((uintptr_t)stptr->nfsglbls.aclproccnt[4],
"aclreqcnt_v4_tmpl"))
return (-1);
}
return (0);
}
static int
nfs_clntstat(nfs_mdb_stats_t *stptr, int flags)
{
mdb_printf("NFS Statistics\n");
if (prt_nfs_stats((uintptr_t)stptr->clntstat, "clstat_tmpl"))
return (-1);
if (flags & NFS_V2_STAT) {
mdb_printf("Version 2\n");
if (prt_nfs_stats(
(uintptr_t)stptr->nfsstats.nfs_stats_v2.rfsreqcnt_ptr,
"rfsreqcnt_v2_tmpl"))
return (-1);
}
if (flags & NFS_V3_STAT) {
mdb_printf("Version 3\n");
if (prt_nfs_stats(
(uintptr_t)stptr->nfsstats.nfs_stats_v3.rfsreqcnt_ptr,
"rfsreqcnt_v3_tmpl"))
return (-1);
}
if (flags & NFS_V4_STAT) {
mdb_printf("NFSv4 client\n");
if (prt_nfs_stats((uintptr_t)stptr->clntstat, "clstat4_tmpl"))
return (-1);
mdb_printf("Version 4\n");
if (prt_nfs_stats(
(uintptr_t)stptr->nfsstats.nfs_stats_v4.rfsreqcnt_ptr,
"rfsreqcnt_v4_tmpl"))
return (-1);
}
return (0);
}
static int
nfs_clntstat_rpc(nfs_mdb_stats_t *stptr)
{
mdb_printf("NFS RPC Statistics\n");
mdb_printf("ConnectionLess\n");
if (prt_nfs_stats((uintptr_t)stptr->rpcstats.rpc_clts_client,
"clts_rcstat_tmpl"))
return (-1);
mdb_printf("ConnectionOriented\n");
if (prt_nfs_stats((uintptr_t)stptr->rpcstats.rpc_cots_client,
"cots_rcstat_tmpl"))
return (-1);
return (0);
}
static int
nfs_clntstat_acl(nfs_mdb_stats_t *stptr, int flags)
{
mdb_printf("ACL Statistics\n");
if (flags & NFS_V2_STAT) {
mdb_printf("Version 2\n");
if (prt_nfs_stats(
(uintptr_t)stptr->nfsstats.nfs_stats_v2.aclreqcnt_ptr,
"aclreqcnt_v2_tmpl"))
return (-1);
}
if (flags & NFS_V3_STAT) {
mdb_printf("Version 3\n");
if (prt_nfs_stats(
(uintptr_t)stptr->nfsstats.nfs_stats_v3.aclreqcnt_ptr,
"aclreqcnt_v3_tmpl"))
return (-1);
}
if (flags & NFS_V4_STAT) {
mdb_printf("Version 4\n");
if (prt_nfs_stats(
(uintptr_t)stptr->nfsstats.nfs_stats_v4.aclreqcnt_ptr,
"aclreqcnt_v4_tmpl"))
return (-1);
}
return (0);
}
#define NFS_STAT_NUM_CLMNS 16
static int
prt_nfs_stats(uintptr_t addr, char *name)
{
GElf_Sym sym;
kstat_named_t kstats;
char *kstat_line;
uint64_t *value;
uint_t count;
int i = 0, status = 0;
if (mdb_lookup_by_name(name, &sym) != 0) {
mdb_warn("failed to find %s", name);
return (1);
}
count = sym.st_size / sizeof (kstat_named_t);
kstat_line = mdb_alloc(count * NFS_STAT_NUM_CLMNS, UM_SLEEP);
value = mdb_alloc(count * sizeof (uint64_t), UM_SLEEP);
for (i = 0; i < count; i++) {
if (mdb_vread(&kstats, sizeof (kstat_named_t),
addr + (i * sizeof (kstat_named_t))) < 0) {
status = 1;
goto done;
}
mdb_snprintf(&kstat_line[NFS_STAT_NUM_CLMNS * i],
NFS_STAT_NUM_CLMNS, "%s", kstats.name);
value[i] = kstats.value.ui64;
}
kstat_prtout(kstat_line, value, count);
done:
mdb_free(kstat_line, count * NFS_STAT_NUM_CLMNS);
mdb_free(value, count * sizeof (uint64_t));
return (status);
}
static void
kstat_prtout(char *ks_line, uint64_t *values, int count)
{
char val_str[32];
int i = 0, num = 0;
while (i < count) {
mdb_printf("%-*s", NFS_STAT_NUM_CLMNS,
&ks_line[NFS_STAT_NUM_CLMNS * i]);
num++;
if (num == NFS_STAT_NUM_STATS / NFS_STAT_NUM_CLMNS) {
mdb_printf("\n");
while (num > 0) {
mdb_snprintf(val_str, 24, "%ld ",
values[i+1-num]);
mdb_printf("%-*s", NFS_STAT_NUM_CLMNS, val_str);
--num;
}
mdb_printf("\n");
}
i++;
}
mdb_printf("\n");
}