NFSv4: Be less aggressive about returning delegations for open files
[firefly-linux-kernel-4.4.55.git] / fs / nfs / delegation.c
index fc8a213497a1d60f9341983a4ee1a244de474114..a377ea36381e8dbbfe2f57b792bb9b4fad397aed 100644 (file)
@@ -389,6 +389,24 @@ out:
        return err;
 }
 
+static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
+{
+       bool ret = false;
+
+       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) {
+               struct inode *inode;
+
+               spin_lock(&delegation->lock);
+               inode = delegation->inode;
+               if (inode && list_empty(&NFS_I(inode)->open_files))
+                       ret = true;
+               spin_unlock(&delegation->lock);
+       }
+       return ret;
+}
+
 /**
  * nfs_client_return_marked_delegations - return previously marked delegations
  * @clp: nfs_client to process
@@ -411,8 +429,7 @@ restart:
        list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
                list_for_each_entry_rcu(delegation, &server->delegations,
                                                                super_list) {
-                       if (!test_and_clear_bit(NFS_DELEGATION_RETURN,
-                                                       &delegation->flags))
+                       if (!nfs_delegation_need_return(delegation))
                                continue;
                        inode = nfs_delegation_grab_inode(delegation);
                        if (inode == NULL)
@@ -471,6 +488,13 @@ int nfs4_inode_return_delegation(struct inode *inode)
        return err;
 }
 
+static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
+               struct nfs_delegation *delegation)
+{
+       set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
+       set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
+}
+
 static void nfs_mark_return_delegation(struct nfs_server *server,
                struct nfs_delegation *delegation)
 {
@@ -574,7 +598,7 @@ static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
        list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
                if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
                        continue;
-               nfs_mark_return_delegation(server, delegation);
+               nfs_mark_return_if_closed_delegation(server, delegation);
        }
 }