]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/nfs/delegation.c
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / fs / nfs / delegation.c
index a777b3d0e7204903ffd431ce997976ea3bb3ec6e..1865322de142d0a656e13ac99106fa554bc3d95a 100644 (file)
 #include "internal.h"
 #include "nfs4trace.h"
 
+#define NFS_DEFAULT_DELEGATION_WATERMARK (5000U)
+
 static atomic_long_t nfs_active_delegations;
+static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
 
 static void __nfs_free_delegation(struct nfs_delegation *delegation)
 {
@@ -39,13 +42,27 @@ static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation)
        if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
                delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
                atomic_long_dec(&nfs_active_delegations);
+               if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
+                       nfs_clear_verifier_delegated(delegation->inode);
        }
 }
 
+static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation)
+{
+       refcount_inc(&delegation->refcount);
+       return delegation;
+}
+
+static void nfs_put_delegation(struct nfs_delegation *delegation)
+{
+       if (refcount_dec_and_test(&delegation->refcount))
+               __nfs_free_delegation(delegation);
+}
+
 static void nfs_free_delegation(struct nfs_delegation *delegation)
 {
        nfs_mark_delegation_revoked(delegation);
-       __nfs_free_delegation(delegation);
+       nfs_put_delegation(delegation);
 }
 
 /**
@@ -238,13 +255,18 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
 
 static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
 {
+       const struct cred *cred;
        int res = 0;
 
-       if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
-               res = nfs4_proc_delegreturn(inode,
-                               delegation->cred,
+       if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
+               spin_lock(&delegation->lock);
+               cred = get_cred(delegation->cred);
+               spin_unlock(&delegation->lock);
+               res = nfs4_proc_delegreturn(inode, cred,
                                &delegation->stateid,
                                issync);
+               put_cred(cred);
+       }
        return res;
 }
 
@@ -270,9 +292,13 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
        if (delegation == NULL)
                goto out;
        spin_lock(&delegation->lock);
-       if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
-               ret = delegation;
+       if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
+               /* Refcount matched in nfs_end_delegation_return() */
+               ret = nfs_get_delegation(delegation);
+       }
        spin_unlock(&delegation->lock);
+       if (ret)
+               nfs_clear_verifier_delegated(&nfsi->vfs_inode);
 out:
        return ret;
 }
@@ -390,6 +416,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
        if (delegation == NULL)
                return -ENOMEM;
        nfs4_stateid_copy(&delegation->stateid, stateid);
+       refcount_set(&delegation->refcount, 1);
        delegation->type = type;
        delegation->pagemod_limit = pagemod_limit;
        delegation->change_attr = inode_peek_iversion_raw(inode);
@@ -489,6 +516,8 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
 
        err = nfs_do_return_delegation(inode, delegation, issync);
 out:
+       /* Refcount matched in nfs_start_delegation_return_locked() */
+       nfs_put_delegation(delegation);
        return err;
 }
 
@@ -676,15 +705,19 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode)
        delegation = nfs4_get_valid_delegation(inode);
        if (!delegation)
                goto out;
-       if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) {
+       if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) ||
+           atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) {
                spin_lock(&delegation->lock);
                if (delegation->inode &&
                    list_empty(&NFS_I(inode)->open_files) &&
                    !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
                        clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
-                       ret = delegation;
+                       /* Refcount matched in nfs_end_delegation_return() */
+                       ret = nfs_get_delegation(delegation);
                }
                spin_unlock(&delegation->lock);
+               if (ret)
+                       nfs_clear_verifier_delegated(inode);
        }
 out:
        rcu_read_unlock();
@@ -1084,10 +1117,11 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
                        delegation = nfs_start_delegation_return_locked(NFS_I(inode));
                        rcu_read_unlock();
                        if (delegation != NULL) {
-                               delegation = nfs_detach_delegation(NFS_I(inode),
-                                       delegation, server);
-                               if (delegation != NULL)
+                               if (nfs_detach_delegation(NFS_I(inode), delegation,
+                                                       server) != NULL)
                                        nfs_free_delegation(delegation);
+                               /* Match nfs_start_delegation_return_locked */
+                               nfs_put_delegation(delegation);
                        }
                        iput(inode);
                        nfs_sb_deactive(server->super);
@@ -1365,3 +1399,5 @@ bool nfs4_delegation_flush_on_close(const struct inode *inode)
        rcu_read_unlock();
        return ret;
 }
+
+module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);