SUNRPC: don't pause on incomplete allocation
authorNeilBrown <neilb@suse.de>
Sun, 29 Aug 2021 22:36:34 +0000 (08:36 +1000)
committerChuck Lever <chuck.lever@oracle.com>
Wed, 1 Sep 2021 15:05:07 +0000 (11:05 -0400)
alloc_pages_bulk_array() attempts to allocate at least one page based on
the provided pages, and then opportunistically allocates more if that
can be done without dropping the spinlock.

So if it returns fewer than requested, that could just mean that it
needed to drop the lock.  In that case, try again immediately.

Only pause for a time if no progress could be made.

Reported-and-tested-by: Mike Javorski <mike.javorski@gmail.com>
Reported-and-tested-by: Lothar Paltins <lopa@mailbox.org>
Fixes: f6e70aab9dfe ("SUNRPC: refresh rq_pages using a bulk page allocator")
Signed-off-by: NeilBrown <neilb@suse.de>
Acked-by: Mel Gorman <mgorman@suse.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
net/sunrpc/svc_xprt.c

index 682058a5ec13d179a7f21de76b562894bfbb8df3..796eebf1787db2da63b2c18b39130aae51db0979 100644 (file)
@@ -663,7 +663,7 @@ static int svc_alloc_arg(struct svc_rqst *rqstp)
 {
        struct svc_serv *serv = rqstp->rq_server;
        struct xdr_buf *arg = &rqstp->rq_arg;
-       unsigned long pages, filled;
+       unsigned long pages, filled, ret;
 
        pagevec_init(&rqstp->rq_pvec);
 
@@ -675,11 +675,12 @@ static int svc_alloc_arg(struct svc_rqst *rqstp)
                pages = RPCSVC_MAXPAGES;
        }
 
-       for (;;) {
-               filled = alloc_pages_bulk_array(GFP_KERNEL, pages,
-                                               rqstp->rq_pages);
-               if (filled == pages)
-                       break;
+       for (filled = 0; filled < pages; filled = ret) {
+               ret = alloc_pages_bulk_array(GFP_KERNEL, pages,
+                                            rqstp->rq_pages);
+               if (ret > filled)
+                       /* Made progress, don't sleep yet */
+                       continue;
 
                set_current_state(TASK_INTERRUPTIBLE);
                if (signalled() || kthread_should_stop()) {