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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Mar 2023 08:39:30 +0000 (09:39 +0100)
[ Upstream commit 4dc73c679114a2f408567e2e44770ed934190db2 ]

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>
Stable-dep-of: b46d80bd2d6e ("nfs4trace: fix state manager flag printing")
Signed-off-by: Sasha Levin <sashal@kernel.org>
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 ad5114e480097a78c560789cddbc9348278ccd0c..dd53d0f97c57d24bb19d8b13f799ce47cb25717f 100644 (file)
@@ -484,8 +484,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;
@@ -498,14 +499,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 f8672a34fd635c130c291c8f5f7ebf37ebc27234..0a1e1c64b131ae4ceccf55e88c1a20658c396a4d 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 b6b1fad031c78256ec665edd07efc860182d1dff..5b671ca429d2b8f4641b7fc8ad9e1fe7eeb044c7 100644 (file)
@@ -10530,6 +10530,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,
@@ -10607,6 +10625,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 0cd803b4d90ce31eeb953b57bcb7875c34aa8039..7223816bc5d53db3f840135ccdc9d013338f859e 100644 (file)
@@ -1209,10 +1209,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);
 
@@ -1230,6 +1237,7 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
                if (!nfs_client_init_is_complete(clp))
                        nfs_mark_client_ready(clp, 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);
        }
@@ -2689,12 +2697,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;
 
@@ -2715,9 +2719,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_exit(0);
        return 0;
index 783f871b4e12d620b39fc82abffb5a58cf93ac74..7fcd56c6ded65222987c06db531f452d5ca31a67 100644 (file)
@@ -1806,6 +1806,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 bbeb80e1133df7413a5ae7cc1a8dd3ab244d30da..6622dc1fa8f2f1e37c40dc226dd28a08f8dd0701 100644 (file)
@@ -3117,6 +3117,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);