]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
NFSv4: Fix delegation return in cases where we have to retry
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Sat, 8 May 2021 14:01:32 +0000 (10:01 -0400)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Sun, 13 Jun 2021 23:36:27 +0000 (19:36 -0400)
If we're unable to immediately recover all locks because the server is
unable to immediately service our reclaim calls, then we want to retry
after we've finished servicing all the other asynchronous delegation
returns on our queue.

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

index e6ec6f09ac6e41df1d6c76683bbf70f31ca06b70..7c45ac3c3b0b5a04392369cd2ccd917ab3f75f96 100644 (file)
@@ -75,6 +75,13 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
        set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
 }
 
+static void nfs_mark_return_delegation(struct nfs_server *server,
+                                      struct nfs_delegation *delegation)
+{
+       set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+       set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
+}
+
 static bool
 nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
                fmode_t flags)
@@ -293,6 +300,7 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
                goto out;
        spin_lock(&delegation->lock);
        if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
+               clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags);
                /* Refcount matched in nfs_end_delegation_return() */
                ret = nfs_get_delegation(delegation);
        }
@@ -314,16 +322,17 @@ nfs_start_delegation_return(struct nfs_inode *nfsi)
        return delegation;
 }
 
-static void
-nfs_abort_delegation_return(struct nfs_delegation *delegation,
-               struct nfs_client *clp)
+static void nfs_abort_delegation_return(struct nfs_delegation *delegation,
+                                       struct nfs_client *clp, int err)
 {
 
        spin_lock(&delegation->lock);
        clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
-       set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+       if (err == -EAGAIN) {
+               set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags);
+               set_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state);
+       }
        spin_unlock(&delegation->lock);
-       set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
 }
 
 static struct nfs_delegation *
@@ -539,7 +548,7 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
        } while (err == 0);
 
        if (err) {
-               nfs_abort_delegation_return(delegation, clp);
+               nfs_abort_delegation_return(delegation, clp, err);
                goto out;
        }
 
@@ -568,6 +577,7 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
        if (ret)
                clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
        if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) ||
+           test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) ||
            test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
                ret = false;
 
@@ -647,6 +657,38 @@ out:
        return err;
 }
 
+static bool nfs_server_clear_delayed_delegations(struct nfs_server *server)
+{
+       struct nfs_delegation *d;
+       bool ret = false;
+
+       list_for_each_entry_rcu (d, &server->delegations, super_list) {
+               if (!test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags))
+                       continue;
+               nfs_mark_return_delegation(server, d);
+               clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags);
+               ret = true;
+       }
+       return ret;
+}
+
+static bool nfs_client_clear_delayed_delegations(struct nfs_client *clp)
+{
+       struct nfs_server *server;
+       bool ret = false;
+
+       if (!test_and_clear_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state))
+               goto out;
+       rcu_read_lock();
+       list_for_each_entry_rcu (server, &clp->cl_superblocks, client_link) {
+               if (nfs_server_clear_delayed_delegations(server))
+                       ret = true;
+       }
+       rcu_read_unlock();
+out:
+       return ret;
+}
+
 /**
  * nfs_client_return_marked_delegations - return previously marked delegations
  * @clp: nfs_client to process
@@ -659,8 +701,14 @@ out:
  */
 int nfs_client_return_marked_delegations(struct nfs_client *clp)
 {
-       return nfs_client_for_each_server(clp,
-                       nfs_server_return_marked_delegations, NULL);
+       int err = nfs_client_for_each_server(
+               clp, nfs_server_return_marked_delegations, NULL);
+       if (err)
+               return err;
+       /* If a return was delayed, sleep to prevent hard looping */
+       if (nfs_client_clear_delayed_delegations(clp))
+               ssleep(1);
+       return 0;
 }
 
 /**
@@ -775,13 +823,6 @@ static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
        set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
 }
 
-static void nfs_mark_return_delegation(struct nfs_server *server,
-               struct nfs_delegation *delegation)
-{
-       set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
-       set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
-}
-
 static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
 {
        struct nfs_delegation *delegation;
index c19b4fd20781255cd473cbd6dcfe93db188bd055..1c378992b7c0fce3be940398f5ced5ac3e36c7c4 100644 (file)
@@ -36,6 +36,7 @@ enum {
        NFS_DELEGATION_REVOKED,
        NFS_DELEGATION_TEST_EXPIRED,
        NFS_DELEGATION_INODE_FREEING,
+       NFS_DELEGATION_RETURN_DELAYED,
 };
 
 int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
index 543d916f79abb6474eb5c05eb49f97803e634cd6..3e344bec3647b09889b113c6b568d6316b41140d 100644 (file)
@@ -45,6 +45,7 @@ enum nfs4_client_state {
        NFS4CLNT_RECALL_RUNNING,
        NFS4CLNT_RECALL_ANY_LAYOUT_READ,
        NFS4CLNT_RECALL_ANY_LAYOUT_RW,
+       NFS4CLNT_DELEGRETURN_DELAYED,
 };
 
 #define NFS4_RENEW_TIMEOUT             0x01