]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFSv4: Fix races between nfs_remove_bad_delegation() and delegation return
authorTrond Myklebust <trond.myklebust@primarydata.com>
Mon, 10 Nov 2014 23:43:56 +0000 (18:43 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Nov 2014 17:23:14 +0000 (09:23 -0800)
commit 869f9dfa4d6d57b79e0afc3af14772c2a023eeb1 upstream.

Any attempt to call nfs_remove_bad_delegation() while a delegation is being
returned is currently a no-op. This means that we can end up looping
forever in nfs_end_delegation_return() if something causes the delegation
to be revoked.
This patch adds a mechanism whereby the state recovery code can communicate
to the delegation return code that the delegation is no longer valid and
that it should not be used when reclaiming state.
It also changes the return value for nfs4_handle_delegation_recall_error()
to ensure that nfs_end_delegation_return() does not reattempt the lock
reclaim before state recovery is done.

http://lkml.kernel.org/r/CAN-5tyHwG=Cn2Q9KsHWadewjpTTy_K26ee+UnSvHvG4192p-Xw@mail.gmail.com
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/nfs4proc.c

index 4904a1dba937f468ba9b4c3e10e0e9daad4a404d..7f3f60641344437ab4f493eebf6093e39957c3be 100644 (file)
@@ -195,7 +195,11 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
 {
        int res = 0;
 
-       res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
+       if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+               res = nfs4_proc_delegreturn(inode,
+                               delegation->cred,
+                               &delegation->stateid,
+                               issync);
        nfs_free_delegation(delegation);
        return res;
 }
@@ -382,11 +386,13 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
 {
        struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
        struct nfs_inode *nfsi = NFS_I(inode);
-       int err;
+       int err = 0;
 
        if (delegation == NULL)
                return 0;
        do {
+               if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+                       break;
                err = nfs_delegation_claim_opens(inode, &delegation->stateid);
                if (!issync || err != -EAGAIN)
                        break;
@@ -607,10 +613,23 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
        rcu_read_unlock();
 }
 
+static void nfs_revoke_delegation(struct inode *inode)
+{
+       struct nfs_delegation *delegation;
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation != NULL) {
+               set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
+               nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
+       }
+       rcu_read_unlock();
+}
+
 void nfs_remove_bad_delegation(struct inode *inode)
 {
        struct nfs_delegation *delegation;
 
+       nfs_revoke_delegation(inode);
        delegation = nfs_inode_detach_delegation(inode);
        if (delegation) {
                nfs_inode_find_state_and_recover(inode, &delegation->stateid);
index 5c1cce39297f68fb178b4a125a83001aeac4afde..e3c20a3ccc937453b678e9bb02d1a46827f11be0 100644 (file)
@@ -31,6 +31,7 @@ enum {
        NFS_DELEGATION_RETURN_IF_CLOSED,
        NFS_DELEGATION_REFERENCED,
        NFS_DELEGATION_RETURNING,
+       NFS_DELEGATION_REVOKED,
 };
 
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
index 877d022497cc2e65fcea24b94645c27b57132333..5dceb60f997bde3d4563f07581cd8af6deda737c 100644 (file)
@@ -1642,7 +1642,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
                        nfs_inode_find_state_and_recover(state->inode,
                                        stateid);
                        nfs4_schedule_stateid_recovery(server, state);
-                       return 0;
+                       return -EAGAIN;
                case -NFS4ERR_DELAY:
                case -NFS4ERR_GRACE:
                        set_bit(NFS_DELEGATED_STATE, &state->flags);