Skip to content

Commit 3dedbb5

Browse files
almostivantrondmypd
authored andcommitted
rpc: Add -EPERM processing for xs_udp_send_request()
If an iptables drop rule is added for an nfs server, the client can end up in a softlockup. Because of the way that xs_sendpages() is structured, the -EPERM is ignored since the prior bits of the packet may have been successfully queued and thus xs_sendpages() returns a non-zero value. Then, xs_udp_send_request() thinks that because some bits were queued it should return -EAGAIN. We then try the request again and again, resulting in cpu spinning. Reproducer: 1) open a file on the nfs server '/nfs/foo' (mounted using udp) 2) iptables -A OUTPUT -d <nfs server ip> -j DROP 3) write to /nfs/foo 4) close /nfs/foo 5) iptables -D OUTPUT -d <nfs server ip> -j DROP The softlockup occurs in step 4 above. The previous patch, allows xs_sendpages() to return both a sent count and any error values that may have occurred. Thus, if we get an -EPERM, return that to the higher level code. With this patch in place we can successfully abort the above sequence and avoid the softlockup. I also tried the above test case on an nfs mount on tcp and although the system does not softlockup, I still ended up with the 'hung_task' firing after 120 seconds, due to the i/o being stuck. The tcp case appears a bit harder to fix, since -EPERM appears to get ignored much lower down in the stack and does not propogate up to xs_sendpages(). This case is not quite as insidious as the softlockup and it is not addressed here. Reported-by: Yigong Lou <ylou@akamai.com> Signed-off-by: Jason Baron <jbaron@akamai.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
1 parent f279cd0 commit 3dedbb5

File tree

2 files changed

+8
-0
lines changed

2 files changed

+8
-0
lines changed

net/sunrpc/clnt.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,6 +1913,7 @@ call_transmit_status(struct rpc_task *task)
19131913
case -EHOSTDOWN:
19141914
case -EHOSTUNREACH:
19151915
case -ENETUNREACH:
1916+
case -EPERM:
19161917
if (RPC_IS_SOFTCONN(task)) {
19171918
xprt_end_transmit(task);
19181919
rpc_exit(task, task->tk_status);
@@ -2018,6 +2019,7 @@ call_status(struct rpc_task *task)
20182019
case -EHOSTDOWN:
20192020
case -EHOSTUNREACH:
20202021
case -ENETUNREACH:
2022+
case -EPERM:
20212023
if (RPC_IS_SOFTCONN(task)) {
20222024
rpc_exit(task, status);
20232025
break;

net/sunrpc/xprtsock.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,10 @@ static int xs_udp_send_request(struct rpc_task *task)
644644
dprintk("RPC: xs_udp_send_request(%u) = %d\n",
645645
xdr->len - req->rq_bytes_sent, status);
646646

647+
/* firewall is blocking us, don't return -EAGAIN or we end up looping */
648+
if (status == -EPERM)
649+
goto process_status;
650+
647651
if (sent > 0 || status == 0) {
648652
req->rq_xmit_bytes_sent += sent;
649653
if (sent >= req->rq_slen)
@@ -652,6 +656,7 @@ static int xs_udp_send_request(struct rpc_task *task)
652656
status = -EAGAIN;
653657
}
654658

659+
process_status:
655660
switch (status) {
656661
case -ENOTSOCK:
657662
status = -ENOTCONN;
@@ -667,6 +672,7 @@ static int xs_udp_send_request(struct rpc_task *task)
667672
case -ENOBUFS:
668673
case -EPIPE:
669674
case -ECONNREFUSED:
675+
case -EPERM:
670676
/* When the server has died, an ICMP port unreachable message
671677
* prompts ECONNREFUSED. */
672678
clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);

0 commit comments

Comments
 (0)