NFSv4: Open state recovery must account for file permission changes
[pandora-kernel.git] / fs / nfs / nfs4state.c
index 6a7107a..c8b51f4 100644 (file)
@@ -935,7 +935,7 @@ static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
                case -NFS4ERR_BAD_SEQID:
                        if (seqid->sequence->flags & NFS_SEQID_CONFIRMED)
                                return;
-                       printk(KERN_WARNING "NFS: v4 server returned a bad"
+                       pr_warn_ratelimited("NFS: v4 server returned a bad"
                                        " sequence-id error on an"
                                        " unconfirmed sequence %p!\n",
                                        seqid->sequence);
@@ -1075,6 +1075,33 @@ void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4
        nfs4_schedule_state_manager(clp);
 }
 
+void nfs_inode_find_state_and_recover(struct inode *inode,
+               const nfs4_stateid *stateid)
+{
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_open_context *ctx;
+       struct nfs4_state *state;
+       bool found = false;
+
+       spin_lock(&inode->i_lock);
+       list_for_each_entry(ctx, &nfsi->open_files, list) {
+               state = ctx->state;
+               if (state == NULL)
+                       continue;
+               if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
+                       continue;
+               if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
+                       continue;
+               nfs4_state_mark_reclaim_nograce(clp, state);
+               found = true;
+       }
+       spin_unlock(&inode->i_lock);
+       if (found)
+               nfs4_schedule_state_manager(clp);
+}
+
+
 static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
 {
        struct inode *inode = state->inode;
@@ -1159,11 +1186,14 @@ restart:
                                spin_lock(&state->state_lock);
                                list_for_each_entry(lock, &state->lock_states, ls_locks) {
                                        if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
-                                               printk("%s: Lock reclaim failed!\n",
-                                                       __func__);
+                                               pr_warn_ratelimited("NFS: "
+                                                       "%s: Lock reclaim "
+                                                       "failed!\n", __func__);
                                }
                                spin_unlock(&state->state_lock);
                                nfs4_put_open_state(state);
+                               clear_bit(NFS_STATE_RECLAIM_NOGRACE,
+                                       &state->flags);
                                goto restart;
                        }
                }
@@ -1173,6 +1203,9 @@ restart:
                                                __func__, status);
                        case -ENOENT:
                        case -ENOMEM:
+                       case -EACCES:
+                       case -EROFS:
+                       case -EIO:
                        case -ESTALE:
                                /*
                                 * Open state on this file cannot be recovered
@@ -1419,7 +1452,8 @@ restart:
                        if (status < 0) {
                                set_bit(ops->owner_flag_bit, &sp->so_flags);
                                nfs4_put_state_owner(sp);
-                               return nfs4_recovery_handle_error(clp, status);
+                               status = nfs4_recovery_handle_error(clp, status);
+                               return (status != 0) ? status : -EAGAIN;
                        }
 
                        nfs4_put_state_owner(sp);
@@ -1428,7 +1462,7 @@ restart:
                spin_unlock(&clp->cl_lock);
        }
        rcu_read_unlock();
-       return status;
+       return 0;
 }
 
 static int nfs4_check_lease(struct nfs_client *clp)
@@ -1555,8 +1589,18 @@ static int nfs4_reset_session(struct nfs_client *clp)
 
        nfs4_begin_drain_session(clp);
        status = nfs4_proc_destroy_session(clp->cl_session);
-       if (status && status != -NFS4ERR_BADSESSION &&
-           status != -NFS4ERR_DEADSESSION) {
+       switch (status) {
+       case 0:
+       case -NFS4ERR_BADSESSION:
+       case -NFS4ERR_DEADSESSION:
+               break;
+       case -NFS4ERR_BACK_CHAN_BUSY:
+       case -NFS4ERR_DELAY:
+               set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+               status = 0;
+               ssleep(1);
+               goto out;
+       default:
                status = nfs4_recovery_handle_error(clp, status);
                goto out;
        }
@@ -1691,23 +1735,18 @@ static void nfs4_state_manager(struct nfs_client *clp)
                if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
                        status = nfs4_do_reclaim(clp,
                                clp->cl_mvops->reboot_recovery_ops);
-                       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
-                           test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state))
-                               continue;
-                       nfs4_state_end_reclaim_reboot(clp);
-                       if (test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
+                       if (status == -EAGAIN)
                                continue;
                        if (status < 0)
                                goto out_error;
+                       nfs4_state_end_reclaim_reboot(clp);
                }
 
                /* Now recover expired state... */
                if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
                        status = nfs4_do_reclaim(clp,
                                clp->cl_mvops->nograce_recovery_ops);
-                       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
-                           test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) ||
-                           test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
+                       if (status == -EAGAIN)
                                continue;
                        if (status < 0)
                                goto out_error;
@@ -1737,7 +1776,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
        } while (atomic_read(&clp->cl_count) > 1);
        return;
 out_error:
-       printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s"
+       pr_warn_ratelimited("NFS: state manager failed on NFSv4 server %s"
                        " with error %d\n", clp->cl_hostname, -status);
        nfs4_end_drain_session(clp);
        nfs4_clear_state_manager_bit(clp);