NFSv4: Protect the state recovery thread against direct reclaim
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Sat, 29 Jan 2022 18:32:45 +0000 (13:32 -0500)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 25 Feb 2022 23:50:12 +0000 (18:50 -0500)
If memory allocation triggers a direct reclaim from the state recovery
thread, then we can deadlock. Use memalloc_nofs_save/restore to ensure
that doesn't happen.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/nfs4state.c

index f5a62c0..0f48186 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/workqueue.h>
 #include <linux/bitops.h>
 #include <linux/jiffies.h>
+#include <linux/sched/mm.h>
 
 #include <linux/sunrpc/clnt.h>
 
@@ -2560,9 +2561,17 @@ static void nfs4_layoutreturn_any_run(struct nfs_client *clp)
 
 static void nfs4_state_manager(struct nfs_client *clp)
 {
+       unsigned int memflags;
        int status = 0;
        const char *section = "", *section_sep = "";
 
+       /*
+        * State recovery can deadlock if the direct reclaim code tries
+        * start NFS writeback. So ensure memory allocations are all
+        * GFP_NOFS.
+        */
+       memflags = memalloc_nofs_save();
+
        /* Ensure exclusive access to NFSv4 state */
        do {
                trace_nfs4_state_mgr(clp);
@@ -2657,6 +2666,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
                        clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
                }
 
+               memalloc_nofs_restore(memflags);
                nfs4_end_drain_session(clp);
                nfs4_clear_state_manager_bit(clp);
 
@@ -2674,6 +2684,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
                        return;
                if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
                        return;
+               memflags = memalloc_nofs_save();
        } while (refcount_read(&clp->cl_count) > 1 && !signalled());
        goto out_drain;
 
@@ -2686,6 +2697,7 @@ out_error:
                        clp->cl_hostname, -status);
        ssleep(1);
 out_drain:
+       memalloc_nofs_restore(memflags);
        nfs4_end_drain_session(clp);
        nfs4_clear_state_manager_bit(clp);
 }