Skip to content

Commit bd9d6a3

Browse files
LorenzoBianconichucklever
authored andcommitted
NFSD: add rpc_status netlink support
Introduce rpc_status netlink support for NFSD in order to dump pending RPC requests debugging information from userspace. Closes: https://bugzilla.linux-nfs.org/show_bug.cgi?id=366 Tested-by: Jeff Layton <[email protected]> Signed-off-by: Lorenzo Bianconi <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 13727f8 commit bd9d6a3

File tree

5 files changed

+220
-3
lines changed

5 files changed

+220
-3
lines changed

fs/nfsd/nfsctl.c

Lines changed: 187 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1496,19 +1496,200 @@ static int create_proc_exports_entry(void)
14961496

14971497
unsigned int nfsd_net_id;
14981498

1499+
/**
1500+
* nfsd_nl_rpc_status_get_start - Prepare rpc_status_get dumpit
1501+
* @cb: netlink metadata and command arguments
1502+
*
1503+
* Return values:
1504+
* %0: The rpc_status_get command may proceed
1505+
* %-ENODEV: There is no NFSD running in this namespace
1506+
*/
14991507
int nfsd_nl_rpc_status_get_start(struct netlink_callback *cb)
15001508
{
1509+
struct nfsd_net *nn = net_generic(sock_net(cb->skb->sk), nfsd_net_id);
1510+
int ret = -ENODEV;
1511+
1512+
mutex_lock(&nfsd_mutex);
1513+
if (nn->nfsd_serv) {
1514+
svc_get(nn->nfsd_serv);
1515+
ret = 0;
1516+
}
1517+
mutex_unlock(&nfsd_mutex);
1518+
1519+
return ret;
1520+
}
1521+
1522+
static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
1523+
struct netlink_callback *cb,
1524+
struct nfsd_genl_rqstp *rqstp)
1525+
{
1526+
void *hdr;
1527+
u32 i;
1528+
1529+
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
1530+
&nfsd_nl_family, 0, NFSD_CMD_RPC_STATUS_GET);
1531+
if (!hdr)
1532+
return -ENOBUFS;
1533+
1534+
if (nla_put_be32(skb, NFSD_A_RPC_STATUS_XID, rqstp->rq_xid) ||
1535+
nla_put_u32(skb, NFSD_A_RPC_STATUS_FLAGS, rqstp->rq_flags) ||
1536+
nla_put_u32(skb, NFSD_A_RPC_STATUS_PROG, rqstp->rq_prog) ||
1537+
nla_put_u32(skb, NFSD_A_RPC_STATUS_PROC, rqstp->rq_proc) ||
1538+
nla_put_u8(skb, NFSD_A_RPC_STATUS_VERSION, rqstp->rq_vers) ||
1539+
nla_put_s64(skb, NFSD_A_RPC_STATUS_SERVICE_TIME,
1540+
ktime_to_us(rqstp->rq_stime),
1541+
NFSD_A_RPC_STATUS_PAD))
1542+
return -ENOBUFS;
1543+
1544+
switch (rqstp->rq_saddr.sa_family) {
1545+
case AF_INET: {
1546+
const struct sockaddr_in *s_in, *d_in;
1547+
1548+
s_in = (const struct sockaddr_in *)&rqstp->rq_saddr;
1549+
d_in = (const struct sockaddr_in *)&rqstp->rq_daddr;
1550+
if (nla_put_in_addr(skb, NFSD_A_RPC_STATUS_SADDR4,
1551+
s_in->sin_addr.s_addr) ||
1552+
nla_put_in_addr(skb, NFSD_A_RPC_STATUS_DADDR4,
1553+
d_in->sin_addr.s_addr) ||
1554+
nla_put_be16(skb, NFSD_A_RPC_STATUS_SPORT,
1555+
s_in->sin_port) ||
1556+
nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT,
1557+
d_in->sin_port))
1558+
return -ENOBUFS;
1559+
break;
1560+
}
1561+
case AF_INET6: {
1562+
const struct sockaddr_in6 *s_in, *d_in;
1563+
1564+
s_in = (const struct sockaddr_in6 *)&rqstp->rq_saddr;
1565+
d_in = (const struct sockaddr_in6 *)&rqstp->rq_daddr;
1566+
if (nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_SADDR6,
1567+
&s_in->sin6_addr) ||
1568+
nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_DADDR6,
1569+
&d_in->sin6_addr) ||
1570+
nla_put_be16(skb, NFSD_A_RPC_STATUS_SPORT,
1571+
s_in->sin6_port) ||
1572+
nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT,
1573+
d_in->sin6_port))
1574+
return -ENOBUFS;
1575+
break;
1576+
}
1577+
}
1578+
1579+
for (i = 0; i < rqstp->rq_opcnt; i++)
1580+
if (nla_put_u32(skb, NFSD_A_RPC_STATUS_COMPOUND_OPS,
1581+
rqstp->rq_opnum[i]))
1582+
return -ENOBUFS;
1583+
1584+
genlmsg_end(skb, hdr);
15011585
return 0;
15021586
}
15031587

1588+
/**
1589+
* nfsd_nl_rpc_status_get_dumpit - Handle rpc_status_get dumpit
1590+
* @skb: reply buffer
1591+
* @cb: netlink metadata and command arguments
1592+
*
1593+
* Returns the size of the reply or a negative errno.
1594+
*/
15041595
int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
15051596
struct netlink_callback *cb)
15061597
{
1507-
return 0;
1598+
struct nfsd_net *nn = net_generic(sock_net(skb->sk), nfsd_net_id);
1599+
int i, ret, rqstp_index = 0;
1600+
1601+
rcu_read_lock();
1602+
1603+
for (i = 0; i < nn->nfsd_serv->sv_nrpools; i++) {
1604+
struct svc_rqst *rqstp;
1605+
1606+
if (i < cb->args[0]) /* already consumed */
1607+
continue;
1608+
1609+
rqstp_index = 0;
1610+
list_for_each_entry_rcu(rqstp,
1611+
&nn->nfsd_serv->sv_pools[i].sp_all_threads,
1612+
rq_all) {
1613+
struct nfsd_genl_rqstp genl_rqstp;
1614+
unsigned int status_counter;
1615+
1616+
if (rqstp_index++ < cb->args[1]) /* already consumed */
1617+
continue;
1618+
/*
1619+
* Acquire rq_status_counter before parsing the rqst
1620+
* fields. rq_status_counter is set to an odd value in
1621+
* order to notify the consumers the rqstp fields are
1622+
* meaningful.
1623+
*/
1624+
status_counter =
1625+
smp_load_acquire(&rqstp->rq_status_counter);
1626+
if (!(status_counter & 1))
1627+
continue;
1628+
1629+
genl_rqstp.rq_xid = rqstp->rq_xid;
1630+
genl_rqstp.rq_flags = rqstp->rq_flags;
1631+
genl_rqstp.rq_vers = rqstp->rq_vers;
1632+
genl_rqstp.rq_prog = rqstp->rq_prog;
1633+
genl_rqstp.rq_proc = rqstp->rq_proc;
1634+
genl_rqstp.rq_stime = rqstp->rq_stime;
1635+
genl_rqstp.rq_opcnt = 0;
1636+
memcpy(&genl_rqstp.rq_daddr, svc_daddr(rqstp),
1637+
sizeof(struct sockaddr));
1638+
memcpy(&genl_rqstp.rq_saddr, svc_addr(rqstp),
1639+
sizeof(struct sockaddr));
1640+
1641+
#ifdef CONFIG_NFSD_V4
1642+
if (rqstp->rq_vers == NFS4_VERSION &&
1643+
rqstp->rq_proc == NFSPROC4_COMPOUND) {
1644+
/* NFSv4 compound */
1645+
struct nfsd4_compoundargs *args;
1646+
int j;
1647+
1648+
args = rqstp->rq_argp;
1649+
genl_rqstp.rq_opcnt = args->opcnt;
1650+
for (j = 0; j < genl_rqstp.rq_opcnt; j++)
1651+
genl_rqstp.rq_opnum[j] =
1652+
args->ops[j].opnum;
1653+
}
1654+
#endif /* CONFIG_NFSD_V4 */
1655+
1656+
/*
1657+
* Acquire rq_status_counter before reporting the rqst
1658+
* fields to the user.
1659+
*/
1660+
if (smp_load_acquire(&rqstp->rq_status_counter) !=
1661+
status_counter)
1662+
continue;
1663+
1664+
ret = nfsd_genl_rpc_status_compose_msg(skb, cb,
1665+
&genl_rqstp);
1666+
if (ret)
1667+
goto out;
1668+
}
1669+
}
1670+
1671+
cb->args[0] = i;
1672+
cb->args[1] = rqstp_index;
1673+
ret = skb->len;
1674+
out:
1675+
rcu_read_unlock();
1676+
1677+
return ret;
15081678
}
15091679

1680+
/**
1681+
* nfsd_nl_rpc_status_get_done - rpc_status_get dumpit post-processing
1682+
* @cb: netlink metadata and command arguments
1683+
*
1684+
* Return values:
1685+
* %0: Success
1686+
*/
15101687
int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb)
15111688
{
1689+
mutex_lock(&nfsd_mutex);
1690+
nfsd_put(sock_net(cb->skb->sk));
1691+
mutex_unlock(&nfsd_mutex);
1692+
15121693
return 0;
15131694
}
15141695

@@ -1606,6 +1787,10 @@ static int __init init_nfsd(void)
16061787
retval = register_filesystem(&nfsd_fs_type);
16071788
if (retval)
16081789
goto out_free_all;
1790+
retval = genl_register_family(&nfsd_nl_family);
1791+
if (retval)
1792+
goto out_free_all;
1793+
16091794
return 0;
16101795
out_free_all:
16111796
nfsd4_destroy_laundry_wq();
@@ -1630,6 +1815,7 @@ static int __init init_nfsd(void)
16301815

16311816
static void __exit exit_nfsd(void)
16321817
{
1818+
genl_unregister_family(&nfsd_nl_family);
16331819
unregister_filesystem(&nfsd_fs_type);
16341820
nfsd4_destroy_laundry_wq();
16351821
unregister_cld_notifier();

fs/nfsd/nfsd.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ struct readdir_cd {
6262
__be32 err; /* 0, nfserr, or nfserr_eof */
6363
};
6464

65+
/* Maximum number of operations per session compound */
66+
#define NFSD_MAX_OPS_PER_COMPOUND 50
67+
68+
struct nfsd_genl_rqstp {
69+
struct sockaddr rq_daddr;
70+
struct sockaddr rq_saddr;
71+
unsigned long rq_flags;
72+
ktime_t rq_stime;
73+
__be32 rq_xid;
74+
u32 rq_vers;
75+
u32 rq_prog;
76+
u32 rq_proc;
77+
78+
/* NFSv4 compound */
79+
u32 rq_opcnt;
80+
u32 rq_opnum[NFSD_MAX_OPS_PER_COMPOUND];
81+
};
6582

6683
extern struct svc_program nfsd_program;
6784
extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;

fs/nfsd/nfssvc.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,15 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
997997
if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
998998
goto out_decode_err;
999999

1000+
/*
1001+
* Release rq_status_counter setting it to an odd value after the rpc
1002+
* request has been properly parsed. rq_status_counter is used to
1003+
* notify the consumers if the rqstp fields are stable
1004+
* (rq_status_counter is odd) or not meaningful (rq_status_counter
1005+
* is even).
1006+
*/
1007+
smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter | 1);
1008+
10001009
rp = NULL;
10011010
switch (nfsd_cache_lookup(rqstp, &rp)) {
10021011
case RC_DOIT:
@@ -1014,6 +1023,12 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
10141023
if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream))
10151024
goto out_encode_err;
10161025

1026+
/*
1027+
* Release rq_status_counter setting it to an even value after the rpc
1028+
* request has been properly processed.
1029+
*/
1030+
smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1);
1031+
10171032
nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, statp + 1);
10181033
out_cached_reply:
10191034
return 1;

fs/nfsd/state.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,6 @@ static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s)
195195

196196
/* Maximum number of slots per session. 160 is useful for long haul TCP */
197197
#define NFSD_MAX_SLOTS_PER_SESSION 160
198-
/* Maximum number of operations per session compound */
199-
#define NFSD_MAX_OPS_PER_COMPOUND 50
200198
/* Maximum session per slot cache size */
201199
#define NFSD_SLOT_CACHE_SIZE 2048
202200
/* Maximum number of NFSD_SLOT_CACHE_SIZE slots per session */

include/linux/sunrpc/svc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ struct svc_rqst {
251251
* net namespace
252252
*/
253253
void ** rq_lease_breaker; /* The v4 client breaking a lease */
254+
unsigned int rq_status_counter; /* RPC processing counter */
254255
};
255256

256257
/* bits for rq_flags */

0 commit comments

Comments
 (0)