NeilBrown a4e4f7
From: Trond Myklebust <trond.myklebust@hammerspace.com>
NeilBrown a4e4f7
Date: Sat, 29 Jan 2022 13:32:45 -0500
NeilBrown a4e4f7
Subject: [PATCH] NFSv4: Protect the state recovery thread against direct
NeilBrown a4e4f7
 reclaim
NeilBrown a4e4f7
Git-commit: 3e17898aca293a24dae757a440a50aa63ca29671
NeilBrown a4e4f7
Patch-mainline: v5.18
NeilBrown a4e4f7
References: git-fixes
NeilBrown a4e4f7
NeilBrown a4e4f7
If memory allocation triggers a direct reclaim from the state recovery
NeilBrown a4e4f7
thread, then we can deadlock. Use memalloc_nofs_save/restore to ensure
NeilBrown a4e4f7
that doesn't happen.
NeilBrown a4e4f7
NeilBrown a4e4f7
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
NeilBrown a4e4f7
Acked-by: NeilBrown <neilb@suse.com>
NeilBrown a4e4f7
NeilBrown a4e4f7
---
NeilBrown a4e4f7
 fs/nfs/nfs4state.c |   12 ++++++++++++
NeilBrown a4e4f7
 1 file changed, 12 insertions(+)
NeilBrown a4e4f7
NeilBrown a4e4f7
--- a/fs/nfs/nfs4state.c
NeilBrown a4e4f7
+++ b/fs/nfs/nfs4state.c
NeilBrown a4e4f7
@@ -49,6 +49,7 @@
NeilBrown a4e4f7
 #include <linux/workqueue.h>
NeilBrown a4e4f7
 #include <linux/bitops.h>
NeilBrown a4e4f7
 #include <linux/jiffies.h>
NeilBrown a4e4f7
+#include <linux/sched/mm.h>
NeilBrown a4e4f7
 
NeilBrown a4e4f7
 #include <linux/sunrpc/clnt.h>
NeilBrown a4e4f7
 
NeilBrown a4e4f7
@@ -2576,9 +2577,17 @@ static void nfs4_layoutreturn_any_run(st
NeilBrown a4e4f7
 
NeilBrown a4e4f7
 static void nfs4_state_manager(struct nfs_client *clp)
NeilBrown a4e4f7
 {
NeilBrown a4e4f7
+	unsigned int memflags;
NeilBrown a4e4f7
 	int status = 0;
NeilBrown a4e4f7
 	const char *section = "", *section_sep = "";
NeilBrown a4e4f7
 
NeilBrown a4e4f7
+	/*
NeilBrown a4e4f7
+	 * State recovery can deadlock if the direct reclaim code tries
NeilBrown a4e4f7
+	 * start NFS writeback. So ensure memory allocations are all
NeilBrown a4e4f7
+	 * GFP_NOFS.
NeilBrown a4e4f7
+	 */
NeilBrown a4e4f7
+	memflags = memalloc_nofs_save();
NeilBrown a4e4f7
+
NeilBrown a4e4f7
 	/* Ensure exclusive access to NFSv4 state */
NeilBrown a4e4f7
 	do {
NeilBrown a4e4f7
 		trace_nfs4_state_mgr(clp);
NeilBrown a4e4f7
@@ -2673,6 +2682,7 @@ static void nfs4_state_manager(struct nf
NeilBrown a4e4f7
 			clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
NeilBrown a4e4f7
 		}
NeilBrown a4e4f7
 
NeilBrown a4e4f7
+		memalloc_nofs_restore(memflags);
NeilBrown a4e4f7
 		nfs4_end_drain_session(clp);
NeilBrown a4e4f7
 		nfs4_clear_state_manager_bit(clp);
NeilBrown a4e4f7
 
NeilBrown a4e4f7
@@ -2690,6 +2700,7 @@ static void nfs4_state_manager(struct nf
NeilBrown a4e4f7
 			return;
NeilBrown a4e4f7
 		if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
NeilBrown a4e4f7
 			return;
NeilBrown a4e4f7
+		memflags = memalloc_nofs_save();
NeilBrown a4e4f7
 	} while (refcount_read(&clp->cl_count) > 1 && !signalled());
NeilBrown a4e4f7
 	goto out_drain;
NeilBrown a4e4f7
 
NeilBrown a4e4f7
@@ -2702,6 +2713,7 @@ out_error:
NeilBrown a4e4f7
 			clp->cl_hostname, -status);
NeilBrown a4e4f7
 	ssleep(1);
NeilBrown a4e4f7
 out_drain:
NeilBrown a4e4f7
+	memalloc_nofs_restore(memflags);
NeilBrown a4e4f7
 	nfs4_end_drain_session(clp);
NeilBrown a4e4f7
 	nfs4_clear_state_manager_bit(clp);
NeilBrown a4e4f7
 }