upgrade to fedora-2.6.12-1.1398.FC4 + vserver 2.0.rc7
[linux-2.6.git] / fs / nfs / nfs4state.c
index 91e9bd7..231cebc 100644 (file)
@@ -51,7 +51,7 @@
 
 #define OPENOWNER_POOL_SIZE    8
 
-static spinlock_t              state_spinlock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(state_spinlock);
 
 nfs4_stateid zero_stateid;
 
@@ -116,6 +116,7 @@ nfs4_alloc_client(struct in_addr *addr)
        INIT_LIST_HEAD(&clp->cl_superblocks);
        init_waitqueue_head(&clp->cl_waitq);
        rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
+       clp->cl_boot_time = CURRENT_TIME;
        clp->cl_state = 1 << NFS4CLNT_OK;
        return clp;
 }
@@ -205,7 +206,7 @@ nfs4_put_client(struct nfs4_client *clp)
        nfs4_free_client(clp);
 }
 
-int nfs4_init_client(struct nfs4_client *clp)
+static int __nfs4_init_client(struct nfs4_client *clp)
 {
        int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, nfs_callback_tcpport);
        if (status == 0)
@@ -215,6 +216,11 @@ int nfs4_init_client(struct nfs4_client *clp)
        return status;
 }
 
+int nfs4_init_client(struct nfs4_client *clp)
+{
+       return nfs4_map_errors(__nfs4_init_client(clp));
+}
+
 u32
 nfs4_alloc_lockowner_id(struct nfs4_client *clp)
 {
@@ -274,8 +280,8 @@ nfs4_alloc_state_owner(void)
        return sp;
 }
 
-static void
-nfs4_unhash_state_owner(struct nfs4_state_owner *sp)
+void
+nfs4_drop_state_owner(struct nfs4_state_owner *sp)
 {
        struct nfs4_client *clp = sp->so_client;
        spin_lock(&clp->cl_lock);
@@ -441,11 +447,13 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
        if (state == NULL && new != NULL) {
                state = new;
                /* Caller *must* be holding owner->so_sem */
-               list_add(&state->open_states, &owner->so_states);
+               /* Note: The reclaim code dictates that we add stateless
+                * and read-only stateids to the end of the list */
+               list_add_tail(&state->open_states, &owner->so_states);
                state->owner = owner;
                atomic_inc(&owner->so_count);
                list_add(&state->inode_states, &nfsi->open_states);
-               state->inode = inode;
+               state->inode = igrab(inode);
                spin_unlock(&inode->i_lock);
        } else {
                spin_unlock(&inode->i_lock);
@@ -471,6 +479,7 @@ void nfs4_put_open_state(struct nfs4_state *state)
                list_del(&state->inode_states);
        spin_unlock(&inode->i_lock);
        list_del(&state->open_states);
+       iput(inode);
        BUG_ON (state->state != 0);
        nfs4_free_open_state(state);
        nfs4_put_state_owner(owner);
@@ -486,7 +495,6 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
        struct nfs4_state_owner *owner = state->owner;
        struct nfs4_client *clp = owner->so_client;
        int newstate;
-       int status = 0;
 
        atomic_inc(&owner->so_count);
        down_read(&clp->cl_sem);
@@ -497,8 +505,12 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
                state->nreaders--;
        if (mode & FMODE_WRITE)
                state->nwriters--;
-       if (state->nwriters == 0 && state->nreaders == 0)
-               list_del_init(&state->inode_states);
+       if (state->nwriters == 0) {
+               if (state->nreaders == 0)
+                       list_del_init(&state->inode_states);
+               /* See reclaim code */
+               list_move_tail(&state->open_states, &owner->so_states);
+       }
        spin_unlock(&inode->i_lock);
        newstate = 0;
        if (state->state != 0) {
@@ -508,10 +520,8 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
                        newstate |= FMODE_WRITE;
                if (state->state == newstate)
                        goto out;
-               if (newstate != 0)
-                       status = nfs4_do_downgrade(inode, state, newstate);
-               else
-                       status = nfs4_do_close(inode, state);
+               if (nfs4_do_close(inode, state, newstate) == -EINPROGRESS)
+                       return;
        }
 out:
        nfs4_put_open_state(state);
@@ -710,7 +720,7 @@ void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp)
                sp->so_seqid++;
        /* If the server returns BAD_SEQID, unhash state_owner here */
        if (status == -NFS4ERR_BAD_SEQID)
-               nfs4_unhash_state_owner(sp);
+               nfs4_drop_state_owner(sp);
 }
 
 static int reclaimer(void *);
@@ -755,7 +765,7 @@ nfs4_schedule_state_recovery(struct nfs4_client *clp)
                schedule_work(&clp->cl_recoverd);
 }
 
-static int nfs4_reclaim_locks(struct nfs4_state *state)
+static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state)
 {
        struct inode *inode = state->inode;
        struct file_lock *fl;
@@ -766,7 +776,7 @@ static int nfs4_reclaim_locks(struct nfs4_state *state)
                        continue;
                if (((struct nfs_open_context *)fl->fl_file->private_data)->state != state)
                        continue;
-               status = nfs4_lock_reclaim(state, fl);
+               status = ops->recover_lock(state, fl);
                if (status >= 0)
                        continue;
                switch (status) {
@@ -788,20 +798,28 @@ out_err:
        return status;
 }
 
-static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp)
+static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct nfs4_state_owner *sp)
 {
        struct nfs4_state *state;
        struct nfs4_lock_state *lock;
        int status = 0;
 
+       /* Note: we rely on the sp->so_states list being ordered 
+        * so that we always reclaim open(O_RDWR) and/or open(O_WRITE)
+        * states first.
+        * This is needed to ensure that the server won't give us any
+        * read delegations that we have to return if, say, we are
+        * recovering after a network partition or a reboot from a
+        * server that doesn't support a grace period.
+        */
        list_for_each_entry(state, &sp->so_states, open_states) {
                if (state->state == 0)
                        continue;
-               status = nfs4_open_reclaim(sp, state);
+               status = ops->recover_open(sp, state);
                list_for_each_entry(lock, &state->lock_states, ls_locks)
                        lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
                if (status >= 0) {
-                       status = nfs4_reclaim_locks(state);
+                       status = nfs4_reclaim_locks(ops, state);
                        if (status < 0)
                                goto out_err;
                        list_for_each_entry(lock, &state->lock_states, ls_locks) {
@@ -815,8 +833,7 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp)
                        default:
                                printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
                                                __FUNCTION__, status);
-                       case -NFS4ERR_EXPIRED:
-                       case -NFS4ERR_NO_GRACE:
+                       case -ENOENT:
                        case -NFS4ERR_RECLAIM_BAD:
                        case -NFS4ERR_RECLAIM_CONFLICT:
                                /*
@@ -828,6 +845,8 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp)
                                /* Mark the file as being 'closed' */
                                state->state = 0;
                                break;
+                       case -NFS4ERR_EXPIRED:
+                       case -NFS4ERR_NO_GRACE:
                        case -NFS4ERR_STALE_CLIENTID:
                                goto out_err;
                }
@@ -842,6 +861,7 @@ static int reclaimer(void *ptr)
        struct reclaimer_args *args = (struct reclaimer_args *)ptr;
        struct nfs4_client *clp = args->clp;
        struct nfs4_state_owner *sp;
+       struct nfs4_state_recovery_ops *ops;
        int status = 0;
 
        daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr));
@@ -858,20 +878,34 @@ static int reclaimer(void *ptr)
                goto out;
 restart_loop:
        status = nfs4_proc_renew(clp);
-       if (status == 0 || status == -NFS4ERR_CB_PATH_DOWN)
-               goto out;
-       status = nfs4_init_client(clp);
+       switch (status) {
+               case 0:
+               case -NFS4ERR_CB_PATH_DOWN:
+                       goto out;
+               case -NFS4ERR_STALE_CLIENTID:
+               case -NFS4ERR_LEASE_MOVED:
+                       ops = &nfs4_reboot_recovery_ops;
+                       break;
+               default:
+                       ops = &nfs4_network_partition_recovery_ops;
+       };
+       status = __nfs4_init_client(clp);
        if (status)
                goto out_error;
-       /* Mark all delagations for reclaim */
+       /* Mark all delegations for reclaim */
        nfs_delegation_mark_reclaim(clp);
        /* Note: list is protected by exclusive lock on cl->cl_sem */
        list_for_each_entry(sp, &clp->cl_state_owners, so_list) {
-               status = nfs4_reclaim_open_state(sp);
+               status = nfs4_reclaim_open_state(ops, sp);
                if (status < 0) {
+                       if (status == -NFS4ERR_NO_GRACE) {
+                               ops = &nfs4_network_partition_recovery_ops;
+                               status = nfs4_reclaim_open_state(ops, sp);
+                       }
                        if (status == -NFS4ERR_STALE_CLIENTID)
                                goto restart_loop;
-                       goto out_error;
+                       if (status == -NFS4ERR_EXPIRED)
+                               goto restart_loop;
                }
        }
        nfs_delegation_reap_unclaimed(clp);