NFSv4: keep state manager thread active if swap is enabled
authorNeilBrown <neilb@suse.de>
Sun, 6 Mar 2022 23:41:44 +0000 (10:41 +1100)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Sun, 13 Mar 2022 16:59:35 +0000 (12:59 -0400)
If we are swapping over NFSv4, we may not be able to allocate memory to
start the state-manager thread at the time when we need it.
So keep it always running when swap is enabled, and just signal it to
start.

This requires updating and testing the cl_swapper count on the root
rpc_clnt after following all ->cl_parent links.

Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/file.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
include/linux/nfs_xdr.h
net/sunrpc/clnt.c

index 93c01aaa0a8dc4664dc39aa97f182d428ca400bf..d31bc430dce3aae8aafcf2e7681fc8301ce22b47 100644 (file)
@@ -483,8 +483,9 @@ static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
 {
        unsigned long blocks;
        long long isize;
-       struct rpc_clnt *clnt = NFS_CLIENT(file->f_mapping->host);
-       struct inode *inode = file->f_mapping->host;
+       struct inode *inode = file_inode(file);
+       struct rpc_clnt *clnt = NFS_CLIENT(inode);
+       struct nfs_client *cl = NFS_SERVER(inode)->nfs_client;
 
        spin_lock(&inode->i_lock);
        blocks = inode->i_blocks;
@@ -497,14 +498,22 @@ static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
 
        *span = sis->pages;
 
+
+       if (cl->rpc_ops->enable_swap)
+               cl->rpc_ops->enable_swap(inode);
+
        return rpc_clnt_swap_activate(clnt);
 }
 
 static void nfs_swap_deactivate(struct file *file)
 {
-       struct rpc_clnt *clnt = NFS_CLIENT(file->f_mapping->host);
+       struct inode *inode = file_inode(file);
+       struct rpc_clnt *clnt = NFS_CLIENT(inode);
+       struct nfs_client *cl = NFS_SERVER(inode)->nfs_client;
 
        rpc_clnt_swap_deactivate(clnt);
+       if (cl->rpc_ops->disable_swap)
+               cl->rpc_ops->disable_swap(file_inode(file));
 }
 
 const struct address_space_operations nfs_file_aops = {
index 84f39b6f1b1e9415d06a87bcdbf560bd62739548..79df6e83881b2154ce30bdc1ff3687fcbc8a9b5f 100644 (file)
@@ -42,6 +42,7 @@ enum nfs4_client_state {
        NFS4CLNT_LEASE_MOVED,
        NFS4CLNT_DELEGATION_EXPIRED,
        NFS4CLNT_RUN_MANAGER,
+       NFS4CLNT_MANAGER_AVAILABLE,
        NFS4CLNT_RECALL_RUNNING,
        NFS4CLNT_RECALL_ANY_LAYOUT_READ,
        NFS4CLNT_RECALL_ANY_LAYOUT_RW,
index fd8eece12e9470eefefaefd4f7493c2bbfc1153a..dd7a4c2a3f05a44521f83f4dab122b19a149d21f 100644 (file)
@@ -10468,6 +10468,24 @@ static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
        return error + error2 + error3;
 }
 
+static void nfs4_enable_swap(struct inode *inode)
+{
+       /* The state manager thread must always be running.
+        * It will notice the client is a swapper, and stay put.
+        */
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+
+       nfs4_schedule_state_manager(clp);
+}
+
+static void nfs4_disable_swap(struct inode *inode)
+{
+       /* The state manager thread will now exit once it is
+        * woken.
+        */
+       wake_up_var(&NFS_SERVER(inode)->nfs_client->cl_state);
+}
+
 static const struct inode_operations nfs4_dir_inode_operations = {
        .create         = nfs_create,
        .lookup         = nfs_lookup,
@@ -10545,6 +10563,8 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .create_server  = nfs4_create_server,
        .clone_server   = nfs_clone_server,
        .discover_trunking = nfs4_discover_trunking,
+       .enable_swap    = nfs4_enable_swap,
+       .disable_swap   = nfs4_disable_swap,
 };
 
 static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
index 58054dfdf2b03b156362118ef93a7d68e13140b9..4b2ea239a5377c1afd7ee8b18ed48e38233a8f91 100644 (file)
@@ -1207,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);
 
@@ -1226,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);
        }
@@ -2680,12 +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;
-               memflags = memalloc_nofs_save();
+               return;
+
        } while (refcount_read(&clp->cl_count) > 1 && !signalled());
        goto out_drain;
 
@@ -2706,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);
        module_put_and_kthread_exit(0);
        return 0;
index 82f7c2730b9aba310fdd5c3cb5893ba22d09c054..49ba486aea5fd831f707ed3bc5153814bbe394c0 100644 (file)
@@ -1797,6 +1797,8 @@ struct nfs_rpc_ops {
        struct nfs_server *(*clone_server)(struct nfs_server *, struct nfs_fh *,
                                           struct nfs_fattr *, rpc_authflavor_t);
        int     (*discover_trunking)(struct nfs_server *, struct nfs_fh *);
+       void    (*enable_swap)(struct inode *inode);
+       void    (*disable_swap)(struct inode *inode);
 };
 
 /*
index 4117ea4caa2e6fe5d06efb8c83010d211c146ccd..0f54a56d19d27c8f639b6da8a2d323032f34e204 100644 (file)
@@ -3069,6 +3069,8 @@ rpc_clnt_swap_activate_callback(struct rpc_clnt *clnt,
 int
 rpc_clnt_swap_activate(struct rpc_clnt *clnt)
 {
+       while (clnt != clnt->cl_parent)
+               clnt = clnt->cl_parent;
        if (atomic_inc_return(&clnt->cl_swapper) == 1)
                return rpc_clnt_iterate_for_each_xprt(clnt,
                                rpc_clnt_swap_activate_callback, NULL);