]> Pileus Git - ~andy/linux/blobdiff - fs/nfs/delegation.c
Merge branch 'queue' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target...
[~andy/linux] / fs / nfs / delegation.c
index 6390a4b5fee75823512f30de7f7452696a23093c..57db3244f4d967dd5479dbf3ab883c6d28cc2cbd 100644 (file)
@@ -64,17 +64,15 @@ int nfs4_have_delegation(struct inode *inode, fmode_t flags)
        return ret;
 }
 
-static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
+static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
 {
        struct inode *inode = state->inode;
        struct file_lock *fl;
        int status = 0;
 
-       if (inode->i_flock == NULL)
-               return 0;
-
        if (inode->i_flock == NULL)
                goto out;
+
        /* Protect inode->i_flock using the file locks lock */
        lock_flocks();
        for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
@@ -83,7 +81,7 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
                if (nfs_file_open_context(fl->fl_file) != ctx)
                        continue;
                unlock_flocks();
-               status = nfs4_lock_delegation_recall(state, fl);
+               status = nfs4_lock_delegation_recall(fl, state, stateid);
                if (status < 0)
                        goto out;
                lock_flocks();
@@ -120,7 +118,7 @@ again:
                seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
                err = nfs4_open_delegation_recall(ctx, state, stateid);
                if (!err)
-                       err = nfs_delegation_claim_locks(ctx, state);
+                       err = nfs_delegation_claim_locks(ctx, state, stateid);
                if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
                        err = -EAGAIN;
                mutex_unlock(&sp->so_delegreturn_mutex);
@@ -389,6 +387,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 +427,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 +486,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)
 {
@@ -478,6 +500,45 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
        set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
 }
 
+static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
+{
+       struct nfs_delegation *delegation;
+       bool ret = false;
+
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
+               nfs_mark_return_delegation(server, delegation);
+               ret = true;
+       }
+       return ret;
+}
+
+static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
+{
+       struct nfs_server *server;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               nfs_server_mark_return_all_delegations(server);
+       rcu_read_unlock();
+}
+
+static void nfs_delegation_run_state_manager(struct nfs_client *clp)
+{
+       if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
+               nfs4_schedule_state_manager(clp);
+}
+
+/**
+ * nfs_expire_all_delegations
+ * @clp: client to process
+ *
+ */
+void nfs_expire_all_delegations(struct nfs_client *clp)
+{
+       nfs_client_mark_return_all_delegations(clp);
+       nfs_delegation_run_state_manager(clp);
+}
+
 /**
  * nfs_super_return_all_delegations - return delegations for one superblock
  * @sb: sb to process
@@ -486,24 +547,22 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
 void nfs_server_return_all_delegations(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
-       struct nfs_delegation *delegation;
+       bool need_wait;
 
        if (clp == NULL)
                return;
 
        rcu_read_lock();
-       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
-               spin_lock(&delegation->lock);
-               set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
-               spin_unlock(&delegation->lock);
-       }
+       need_wait = nfs_server_mark_return_all_delegations(server);
        rcu_read_unlock();
 
-       if (nfs_client_return_marked_delegations(clp) != 0)
+       if (need_wait) {
                nfs4_schedule_state_manager(clp);
+               nfs4_wait_clnt_recover(clp);
+       }
 }
 
-static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
+static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
                                                 fmode_t flags)
 {
        struct nfs_delegation *delegation;
@@ -512,27 +571,21 @@ static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
                if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
                        continue;
                if (delegation->type & flags)
-                       nfs_mark_return_delegation(server, delegation);
+                       nfs_mark_return_if_closed_delegation(server, delegation);
        }
 }
 
-static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp,
+static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
                                                        fmode_t flags)
 {
        struct nfs_server *server;
 
        rcu_read_lock();
        list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
-               nfs_mark_return_all_delegation_types(server, flags);
+               nfs_mark_return_unused_delegation_types(server, flags);
        rcu_read_unlock();
 }
 
-static void nfs_delegation_run_state_manager(struct nfs_client *clp)
-{
-       if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
-               nfs4_schedule_state_manager(clp);
-}
-
 void nfs_remove_bad_delegation(struct inode *inode)
 {
        struct nfs_delegation *delegation;
@@ -546,27 +599,17 @@ void nfs_remove_bad_delegation(struct inode *inode)
 EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
 
 /**
- * nfs_expire_all_delegation_types
+ * nfs_expire_unused_delegation_types
  * @clp: client to process
  * @flags: delegation types to expire
  *
  */
-void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
+void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
 {
-       nfs_client_mark_return_all_delegation_types(clp, flags);
+       nfs_client_mark_return_unused_delegation_types(clp, flags);
        nfs_delegation_run_state_manager(clp);
 }
 
-/**
- * nfs_expire_all_delegations
- * @clp: client to process
- *
- */
-void nfs_expire_all_delegations(struct nfs_client *clp)
-{
-       nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
-}
-
 static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
 {
        struct nfs_delegation *delegation;
@@ -574,7 +617,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);
        }
 }