SUNRPC: Fix suspicious enobufs issues.
authorTrond Myklebust <trond.myklebust@primarydata.com>
Sun, 29 May 2016 04:42:03 +0000 (00:42 -0400)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Mon, 13 Jun 2016 16:35:51 +0000 (12:35 -0400)
The current test is racy when dealing with fast NICs.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
net/sunrpc/xprtsock.c

index 646170d0cb86839359f9ece30543d225321f1f68..6b3efeb3edc57e166057dedf3755bc6cc8be518e 100644 (file)
@@ -642,6 +642,7 @@ static int xs_tcp_send_request(struct rpc_task *task)
        struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
        struct xdr_buf *xdr = &req->rq_snd_buf;
        bool zerocopy = true;
+       bool vm_wait = false;
        int status;
        int sent;
 
@@ -677,15 +678,33 @@ static int xs_tcp_send_request(struct rpc_task *task)
                        return 0;
                }
 
+               WARN_ON_ONCE(sent == 0 && status == 0);
+
+               if (status == -EAGAIN ) {
+                       /*
+                        * Return EAGAIN if we're sure we're hitting the
+                        * socket send buffer limits.
+                        */
+                       if (test_bit(SOCK_NOSPACE, &transport->sock->flags))
+                               break;
+                       /*
+                        * Did we hit a memory allocation failure?
+                        */
+                       if (sent == 0) {
+                               status = -ENOBUFS;
+                               if (vm_wait)
+                                       break;
+                               /* Retry, knowing now that we're below the
+                                * socket send buffer limit
+                                */
+                               vm_wait = true;
+                       }
+                       continue;
+               }
                if (status < 0)
                        break;
-               if (sent == 0) {
-                       status = -EAGAIN;
-                       break;
-               }
+               vm_wait = false;
        }
-       if (status == -EAGAIN && sk_stream_is_writeable(transport->inet))
-               status = -ENOBUFS;
 
        switch (status) {
        case -ENOTSOCK: