]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/nfs/delegation.c
Merge tag 'mac80211-for-net-2020-02-14' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / fs / nfs / delegation.c
index a7e42725c3b16736a602dc97493a62ebfa9460fa..4a841071d8a71dc3a04c89ee44606e6324787acd 100644 (file)
 #include "internal.h"
 #include "nfs4trace.h"
 
-static void nfs_free_delegation(struct nfs_delegation *delegation)
+#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)
 {
        put_cred(delegation->cred);
        delegation->cred = NULL;
        kfree_rcu(delegation, rcu);
 }
 
+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);
+       }
+}
+
+static void nfs_free_delegation(struct nfs_delegation *delegation)
+{
+       nfs_mark_delegation_revoked(delegation);
+       __nfs_free_delegation(delegation);
+}
+
 /**
  * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
  * @delegation: delegation to process
@@ -343,7 +362,8 @@ nfs_update_inplace_delegation(struct nfs_delegation *delegation,
                delegation->stateid.seqid = update->stateid.seqid;
                smp_wmb();
                delegation->type = update->type;
-               clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
+               if (test_and_clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+                       atomic_long_inc(&nfs_active_delegations);
        }
 }
 
@@ -423,6 +443,8 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
        rcu_assign_pointer(nfsi->delegation, delegation);
        delegation = NULL;
 
+       atomic_long_inc(&nfs_active_delegations);
+
        trace_nfs4_set_delegation(inode, type);
 
        spin_lock(&inode->i_lock);
@@ -432,7 +454,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
 out:
        spin_unlock(&clp->cl_lock);
        if (delegation != NULL)
-               nfs_free_delegation(delegation);
+               __nfs_free_delegation(delegation);
        if (freeme != NULL) {
                nfs_do_return_delegation(inode, freeme, 0);
                nfs_free_delegation(freeme);
@@ -479,7 +501,7 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
 
        if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
                ret = true;
-       if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
+       else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) {
                struct inode *inode;
 
                spin_lock(&delegation->lock);
@@ -488,6 +510,8 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
                        ret = true;
                spin_unlock(&delegation->lock);
        }
+       if (ret)
+               clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
        if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) ||
            test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
                ret = false;
@@ -637,6 +661,40 @@ int nfs4_inode_return_delegation(struct inode *inode)
        return err;
 }
 
+/**
+ * nfs_inode_return_delegation_on_close - asynchronously return a delegation
+ * @inode: inode to process
+ *
+ * This routine is called on file close in order to determine if the
+ * inode delegation needs to be returned immediately.
+ */
+void nfs4_inode_return_delegation_on_close(struct inode *inode)
+{
+       struct nfs_delegation *delegation;
+       struct nfs_delegation *ret = NULL;
+
+       if (!inode)
+               return;
+       rcu_read_lock();
+       delegation = nfs4_get_valid_delegation(inode);
+       if (!delegation)
+               goto out;
+       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;
+               }
+               spin_unlock(&delegation->lock);
+       }
+out:
+       rcu_read_unlock();
+       nfs_end_delegation_return(inode, ret, 0);
+}
+
 /**
  * nfs4_inode_make_writeable
  * @inode: pointer to inode
@@ -761,13 +819,6 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
        rcu_read_unlock();
 }
 
-static void nfs_mark_delegation_revoked(struct nfs_server *server,
-               struct nfs_delegation *delegation)
-{
-       set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
-       delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
-}
-
 static void nfs_revoke_delegation(struct inode *inode,
                const nfs4_stateid *stateid)
 {
@@ -795,7 +846,7 @@ static void nfs_revoke_delegation(struct inode *inode,
                }
                spin_unlock(&delegation->lock);
        }
-       nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
+       nfs_mark_delegation_revoked(delegation);
        ret = true;
 out:
        rcu_read_unlock();
@@ -834,7 +885,7 @@ void nfs_delegation_mark_returned(struct inode *inode,
                        delegation->stateid.seqid = stateid->seqid;
        }
 
-       nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
+       nfs_mark_delegation_revoked(delegation);
 
 out_clear_returning:
        clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
@@ -1318,3 +1369,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);