Merge tag 'nfs-for-5.18-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[linux-2.6-microblaze.git] / fs / nfs / nfs4state.c
index 02a899e..9e1c987 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>
 
@@ -666,7 +667,7 @@ nfs4_alloc_open_state(void)
 {
        struct nfs4_state *state;
 
-       state = kzalloc(sizeof(*state), GFP_NOFS);
+       state = kzalloc(sizeof(*state), GFP_KERNEL_ACCOUNT);
        if (!state)
                return NULL;
        refcount_set(&state->count, 1);
@@ -820,7 +821,7 @@ static void __nfs4_close(struct nfs4_state *state,
 
 void nfs4_close_state(struct nfs4_state *state, fmode_t fmode)
 {
-       __nfs4_close(state, fmode, GFP_NOFS, 0);
+       __nfs4_close(state, fmode, GFP_KERNEL, 0);
 }
 
 void nfs4_close_sync(struct nfs4_state *state, fmode_t fmode)
@@ -869,14 +870,15 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f
        struct nfs4_lock_state *lsp;
        struct nfs_server *server = state->owner->so_server;
 
-       lsp = kzalloc(sizeof(*lsp), GFP_NOFS);
+       lsp = kzalloc(sizeof(*lsp), GFP_KERNEL_ACCOUNT);
        if (lsp == NULL)
                return NULL;
        nfs4_init_seqid_counter(&lsp->ls_seqid);
        refcount_set(&lsp->ls_count, 1);
        lsp->ls_state = state;
        lsp->ls_owner = fl_owner;
-       lsp->ls_seqid.owner_id = ida_simple_get(&server->lockowner_id, 0, 0, GFP_NOFS);
+       lsp->ls_seqid.owner_id = ida_simple_get(&server->lockowner_id,
+                                               0, 0, GFP_KERNEL_ACCOUNT);
        if (lsp->ls_seqid.owner_id < 0)
                goto out_free;
        INIT_LIST_HEAD(&lsp->ls_locks);
@@ -1205,10 +1207,17 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
 {
        struct task_struct *task;
        char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
+       struct rpc_clnt *cl = clp->cl_rpcclient;
+
+       while (cl != cl->cl_parent)
+               cl = cl->cl_parent;
 
        set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
-       if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
+       if (test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) != 0) {
+               wake_up_var(&clp->cl_state);
                return;
+       }
+       set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
        __module_get(THIS_MODULE);
        refcount_inc(&clp->cl_count);
 
@@ -1224,6 +1233,7 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
                printk(KERN_ERR "%s: kthread_run: %ld\n",
                        __func__, PTR_ERR(task));
                nfs4_clear_state_manager_bit(clp);
+               clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
                nfs_put_client(clp);
                module_put(THIS_MODULE);
        }
@@ -2560,9 +2570,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 +2675,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);
 
@@ -2669,11 +2688,8 @@ static void nfs4_state_manager(struct nfs_client *clp)
                        clear_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state);
                }
 
-               /* Did we race with an attempt to give us more work? */
-               if (!test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state))
-                       return;
-               if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
-                       return;
+               return;
+
        } while (refcount_read(&clp->cl_count) > 1 && !signalled());
        goto out_drain;
 
@@ -2686,6 +2702,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);
 }
@@ -2693,9 +2710,31 @@ out_drain:
 static int nfs4_run_state_manager(void *ptr)
 {
        struct nfs_client *clp = ptr;
+       struct rpc_clnt *cl = clp->cl_rpcclient;
+
+       while (cl != cl->cl_parent)
+               cl = cl->cl_parent;
 
        allow_signal(SIGKILL);
+again:
+       set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
        nfs4_state_manager(clp);
+       if (atomic_read(&cl->cl_swapper)) {
+               wait_var_event_interruptible(&clp->cl_state,
+                                            test_bit(NFS4CLNT_RUN_MANAGER,
+                                                     &clp->cl_state));
+               if (atomic_read(&cl->cl_swapper) &&
+                   test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state))
+                       goto again;
+               /* Either no longer a swapper, or were signalled */
+       }
+       clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
+
+       if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
+           test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
+           !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state))
+               goto again;
+
        nfs_put_client(clp);
        return 0;
 }