NFSv4: Support recalling delegations by stateid
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Thu, 5 Jul 2007 18:55:18 +0000 (14:55 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 11 Jul 2007 03:40:40 +0000 (23:40 -0400)
There appear to be some rogue servers out there that issue multiple
delegations with different stateids for the same file. Ensure that when we
return delegations, we do so on a per-stateid basis rather than a per-file
basis.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/delegation.c
fs/nfs/delegation.h

index 9f17b91205cf51499e42324e97a8fd5f3b7cf171..cee2ba42b68ddc719bd56ddf5cf77c9fbd387b92 100644 (file)
@@ -57,7 +57,7 @@ out_err:
        return status;
 }
 
-static void nfs_delegation_claim_opens(struct inode *inode)
+static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_open_context *ctx;
@@ -72,6 +72,8 @@ again:
                        continue;
                if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
                        continue;
+               if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
+                       continue;
                get_nfs_open_context(ctx);
                spin_unlock(&inode->i_lock);
                err = nfs4_open_delegation_recall(ctx, state);
@@ -170,33 +172,55 @@ static void nfs_msync_inode(struct inode *inode)
 /*
  * Basic procedure for returning a delegation to the server
  */
-int __nfs_inode_return_delegation(struct inode *inode)
+static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
 {
        struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
        struct nfs_inode *nfsi = NFS_I(inode);
-       struct nfs_delegation *delegation;
-       int res = 0;
 
        nfs_msync_inode(inode);
        down_read(&clp->cl_sem);
        /* Guard against new delegated open calls */
        down_write(&nfsi->rwsem);
-       spin_lock(&clp->cl_lock);
-       delegation = nfsi->delegation;
-       if (delegation != NULL) {
-               list_del_init(&delegation->super_list);
-               nfsi->delegation = NULL;
-               nfsi->delegation_state = 0;
-       }
-       spin_unlock(&clp->cl_lock);
-       nfs_delegation_claim_opens(inode);
+       nfs_delegation_claim_opens(inode, &delegation->stateid);
        up_write(&nfsi->rwsem);
        up_read(&clp->cl_sem);
        nfs_msync_inode(inode);
 
-       if (delegation != NULL)
-               res = nfs_do_return_delegation(inode, delegation);
-       return res;
+       return nfs_do_return_delegation(inode, delegation);
+}
+
+static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
+{
+       struct nfs_delegation *delegation = nfsi->delegation;
+
+       if (delegation == NULL)
+               goto nomatch;
+       if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
+                               sizeof(delegation->stateid.data)) != 0)
+               goto nomatch;
+       list_del_init(&delegation->super_list);
+       nfsi->delegation = NULL;
+       nfsi->delegation_state = 0;
+       return delegation;
+nomatch:
+       return NULL;
+}
+
+int nfs_inode_return_delegation(struct inode *inode)
+{
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_delegation *delegation;
+       int err = 0;
+
+       if (nfsi->delegation_state != 0) {
+               spin_lock(&clp->cl_lock);
+               delegation = nfs_detach_delegation_locked(nfsi, NULL);
+               spin_unlock(&clp->cl_lock);
+               if (delegation != NULL)
+                       err = __nfs_inode_return_delegation(inode, delegation);
+       }
+       return err;
 }
 
 /*
@@ -218,8 +242,9 @@ restart:
                inode = igrab(delegation->inode);
                if (inode == NULL)
                        continue;
+               nfs_detach_delegation_locked(NFS_I(inode), NULL);
                spin_unlock(&clp->cl_lock);
-               nfs_inode_return_delegation(inode);
+               __nfs_inode_return_delegation(inode, delegation);
                iput(inode);
                goto restart;
        }
@@ -243,8 +268,9 @@ restart:
                inode = igrab(delegation->inode);
                if (inode == NULL)
                        continue;
+               nfs_detach_delegation_locked(NFS_I(inode), NULL);
                spin_unlock(&clp->cl_lock);
-               nfs_inode_return_delegation(inode);
+               __nfs_inode_return_delegation(inode, delegation);
                iput(inode);
                goto restart;
        }
@@ -285,8 +311,9 @@ restart:
                inode = igrab(delegation->inode);
                if (inode == NULL)
                        continue;
+               nfs_detach_delegation_locked(NFS_I(inode), NULL);
                spin_unlock(&clp->cl_lock);
-               nfs_inode_return_delegation(inode);
+               __nfs_inode_return_delegation(inode, delegation);
                iput(inode);
                goto restart;
        }
@@ -316,21 +343,14 @@ static int recall_thread(void *data)
        down_read(&clp->cl_sem);
        down_write(&nfsi->rwsem);
        spin_lock(&clp->cl_lock);
-       delegation = nfsi->delegation;
-       if (delegation != NULL && memcmp(delegation->stateid.data,
-                               args->stateid->data,
-                               sizeof(delegation->stateid.data)) == 0) {
-               list_del_init(&delegation->super_list);
-               nfsi->delegation = NULL;
-               nfsi->delegation_state = 0;
+       delegation = nfs_detach_delegation_locked(nfsi, args->stateid);
+       if (delegation != NULL)
                args->result = 0;
-       } else {
-               delegation = NULL;
+       else
                args->result = -ENOENT;
-       }
        spin_unlock(&clp->cl_lock);
        complete(&args->started);
-       nfs_delegation_claim_opens(inode);
+       nfs_delegation_claim_opens(inode, args->stateid);
        up_write(&nfsi->rwsem);
        up_read(&clp->cl_sem);
        nfs_msync_inode(inode);
index f6e42fb21afbb0f988d545a2b12eba98a678d161..7b22f1742445744f8cddf755d58da81eccb854cb 100644 (file)
@@ -26,7 +26,7 @@ struct nfs_delegation {
 
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
 void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
-int __nfs_inode_return_delegation(struct inode *inode);
+int nfs_inode_return_delegation(struct inode *inode);
 int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
 
 struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
@@ -52,14 +52,6 @@ static inline int nfs_have_delegation(struct inode *inode, int flags)
        return 0;
 }
 
-static inline int nfs_inode_return_delegation(struct inode *inode)
-{
-       int err = 0;
-
-       if (NFS_I(inode)->delegation != NULL)
-               err = __nfs_inode_return_delegation(inode);
-       return err;
-}
 #else
 static inline int nfs_have_delegation(struct inode *inode, int flags)
 {