Commit 3dedbb5c authored by Jason Baron's avatar Jason Baron Committed by Trond Myklebust

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: default avatarYigong Lou <ylou@akamai.com>
Signed-off-by: default avatarJason Baron <jbaron@akamai.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
parent f279cd00
...@@ -1913,6 +1913,7 @@ call_transmit_status(struct rpc_task *task) ...@@ -1913,6 +1913,7 @@ call_transmit_status(struct rpc_task *task)
case -EHOSTDOWN: case -EHOSTDOWN:
case -EHOSTUNREACH: case -EHOSTUNREACH:
case -ENETUNREACH: case -ENETUNREACH:
case -EPERM:
if (RPC_IS_SOFTCONN(task)) { if (RPC_IS_SOFTCONN(task)) {
xprt_end_transmit(task); xprt_end_transmit(task);
rpc_exit(task, task->tk_status); rpc_exit(task, task->tk_status);
...@@ -2018,6 +2019,7 @@ call_status(struct rpc_task *task) ...@@ -2018,6 +2019,7 @@ call_status(struct rpc_task *task)
case -EHOSTDOWN: case -EHOSTDOWN:
case -EHOSTUNREACH: case -EHOSTUNREACH:
case -ENETUNREACH: case -ENETUNREACH:
case -EPERM:
if (RPC_IS_SOFTCONN(task)) { if (RPC_IS_SOFTCONN(task)) {
rpc_exit(task, status); rpc_exit(task, status);
break; break;
......
...@@ -644,6 +644,10 @@ static int xs_udp_send_request(struct rpc_task *task) ...@@ -644,6 +644,10 @@ static int xs_udp_send_request(struct rpc_task *task)
dprintk("RPC: xs_udp_send_request(%u) = %d\n", dprintk("RPC: xs_udp_send_request(%u) = %d\n",
xdr->len - req->rq_bytes_sent, status); xdr->len - req->rq_bytes_sent, status);
/* firewall is blocking us, don't return -EAGAIN or we end up looping */
if (status == -EPERM)
goto process_status;
if (sent > 0 || status == 0) { if (sent > 0 || status == 0) {
req->rq_xmit_bytes_sent += sent; req->rq_xmit_bytes_sent += sent;
if (sent >= req->rq_slen) if (sent >= req->rq_slen)
...@@ -652,6 +656,7 @@ static int xs_udp_send_request(struct rpc_task *task) ...@@ -652,6 +656,7 @@ static int xs_udp_send_request(struct rpc_task *task)
status = -EAGAIN; status = -EAGAIN;
} }
process_status:
switch (status) { switch (status) {
case -ENOTSOCK: case -ENOTSOCK:
status = -ENOTCONN; status = -ENOTCONN;
...@@ -667,6 +672,7 @@ static int xs_udp_send_request(struct rpc_task *task) ...@@ -667,6 +672,7 @@ static int xs_udp_send_request(struct rpc_task *task)
case -ENOBUFS: case -ENOBUFS:
case -EPIPE: case -EPIPE:
case -ECONNREFUSED: case -ECONNREFUSED:
case -EPERM:
/* When the server has died, an ICMP port unreachable message /* When the server has died, an ICMP port unreachable message
* prompts ECONNREFUSED. */ * prompts ECONNREFUSED. */
clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags); clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment