NFSv4: Fix a bug when the server returns NFS4ERR_RESOURCE
[safe/jmp/linux-2.6] / fs / nfs / nfs4proc.c
index 8a2dee9..65c2527 100644 (file)
@@ -36,7 +36,6 @@
  */
 
 #include <linux/mm.h>
-#include <linux/utsname.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_page.h>
-#include <linux/smp_lock.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
+#include <linux/module.h>
+#include <linux/sunrpc/bc_xprt.h>
 
 #include "nfs4_fs.h"
 #include "delegation.h"
 #include "internal.h"
 #include "iostat.h"
+#include "callback.h"
 
 #define NFSDBG_FACILITY                NFSDBG_PROC
 
 #define NFS4_POLL_RETRY_MIN    (HZ/10)
 #define NFS4_POLL_RETRY_MAX    (15*HZ)
 
+#define NFS4_MAX_LOOP_ON_RECOVER (10)
+
 struct nfs4_opendata;
 static int _nfs4_proc_open(struct nfs4_opendata *data);
 static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
@@ -67,14 +70,19 @@ static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct
 static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
 
 /* Prevent leaks of NFSv4 errors into userland */
-int nfs4_map_errors(int err)
+static int nfs4_map_errors(int err)
 {
-       if (err < -1000) {
+       if (err >= -1000)
+               return err;
+       switch (err) {
+       case -NFS4ERR_RESOURCE:
+               return -EREMOTEIO;
+       default:
                dprintk("%s could not handle NFSv4 error %d\n",
                                __func__, -err);
-               return -EIO;
+               break;
        }
-       return err;
+       return -EIO;
 }
 
 /*
@@ -193,22 +201,14 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
        kunmap_atomic(start, KM_USER0);
 }
 
-static int nfs4_wait_bit_killable(void *word)
-{
-       if (fatal_signal_pending(current))
-               return -ERESTARTSYS;
-       schedule();
-       return 0;
-}
-
 static int nfs4_wait_clnt_recover(struct nfs_client *clp)
 {
        int res;
 
        might_sleep();
 
-       res = wait_on_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER,
-                       nfs4_wait_bit_killable, TASK_KILLABLE);
+       res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
+                       nfs_wait_bit_killable, TASK_KILLABLE);
        return res;
 }
 
@@ -255,7 +255,25 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode,
                        ret = nfs4_wait_clnt_recover(clp);
                        if (ret == 0)
                                exception->retry = 1;
+#if !defined(CONFIG_NFS_V4_1)
                        break;
+#else /* !defined(CONFIG_NFS_V4_1) */
+                       if (!nfs4_has_session(server->nfs_client))
+                               break;
+                       /* FALLTHROUGH */
+               case -NFS4ERR_BADSESSION:
+               case -NFS4ERR_BADSLOT:
+               case -NFS4ERR_BAD_HIGH_SLOT:
+               case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+               case -NFS4ERR_DEADSESSION:
+               case -NFS4ERR_SEQ_FALSE_RETRY:
+               case -NFS4ERR_SEQ_MISORDERED:
+                       dprintk("%s ERROR: %d Reset session\n", __func__,
+                               errorcode);
+                       set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
+                       exception->retry = 1;
+                       /* FALLTHROUGH */
+#endif /* !defined(CONFIG_NFS_V4_1) */
                case -NFS4ERR_FILE_OPEN:
                case -NFS4ERR_GRACE:
                case -NFS4ERR_DELAY:
@@ -279,6 +297,355 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp
        spin_unlock(&clp->cl_lock);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+
+/*
+ * nfs4_free_slot - free a slot and efficiently update slot table.
+ *
+ * freeing a slot is trivially done by clearing its respective bit
+ * in the bitmap.
+ * If the freed slotid equals highest_used_slotid we want to update it
+ * so that the server would be able to size down the slot table if needed,
+ * otherwise we know that the highest_used_slotid is still in use.
+ * When updating highest_used_slotid there may be "holes" in the bitmap
+ * so we need to scan down from highest_used_slotid to 0 looking for the now
+ * highest slotid in use.
+ * If none found, highest_used_slotid is set to -1.
+ */
+static void
+nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid)
+{
+       int slotid = free_slotid;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       /* clear used bit in bitmap */
+       __clear_bit(slotid, tbl->used_slots);
+
+       /* update highest_used_slotid when it is freed */
+       if (slotid == tbl->highest_used_slotid) {
+               slotid = find_last_bit(tbl->used_slots, tbl->max_slots);
+               if (slotid >= 0 && slotid < tbl->max_slots)
+                       tbl->highest_used_slotid = slotid;
+               else
+                       tbl->highest_used_slotid = -1;
+       }
+       rpc_wake_up_next(&tbl->slot_tbl_waitq);
+       spin_unlock(&tbl->slot_tbl_lock);
+       dprintk("%s: free_slotid %u highest_used_slotid %d\n", __func__,
+               free_slotid, tbl->highest_used_slotid);
+}
+
+void nfs41_sequence_free_slot(const struct nfs_client *clp,
+                             struct nfs4_sequence_res *res)
+{
+       struct nfs4_slot_table *tbl;
+
+       if (!nfs4_has_session(clp)) {
+               dprintk("%s: No session\n", __func__);
+               return;
+       }
+       tbl = &clp->cl_session->fc_slot_table;
+       if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) {
+               dprintk("%s: No slot\n", __func__);
+               /* just wake up the next guy waiting since
+                * we may have not consumed a slot after all */
+               rpc_wake_up_next(&tbl->slot_tbl_waitq);
+               return;
+       }
+       nfs4_free_slot(tbl, res->sr_slotid);
+       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+}
+
+static void nfs41_sequence_done(struct nfs_client *clp,
+                               struct nfs4_sequence_res *res,
+                               int rpc_status)
+{
+       unsigned long timestamp;
+       struct nfs4_slot_table *tbl;
+       struct nfs4_slot *slot;
+
+       /*
+        * sr_status remains 1 if an RPC level error occurred. The server
+        * may or may not have processed the sequence operation..
+        * Proceed as if the server received and processed the sequence
+        * operation.
+        */
+       if (res->sr_status == 1)
+               res->sr_status = NFS_OK;
+
+       /* -ERESTARTSYS can result in skipping nfs41_sequence_setup */
+       if (res->sr_slotid == NFS4_MAX_SLOT_TABLE)
+               goto out;
+
+       tbl = &clp->cl_session->fc_slot_table;
+       slot = tbl->slots + res->sr_slotid;
+
+       if (res->sr_status == 0) {
+               /* Update the slot's sequence and clientid lease timer */
+               ++slot->seq_nr;
+               timestamp = res->sr_renewal_time;
+               spin_lock(&clp->cl_lock);
+               if (time_before(clp->cl_last_renewal, timestamp))
+                       clp->cl_last_renewal = timestamp;
+               spin_unlock(&clp->cl_lock);
+               return;
+       }
+out:
+       /* The session may be reset by one of the error handlers. */
+       dprintk("%s: Error %d free the slot \n", __func__, res->sr_status);
+       nfs41_sequence_free_slot(clp, res);
+}
+
+/*
+ * nfs4_find_slot - efficiently look for a free slot
+ *
+ * nfs4_find_slot looks for an unset bit in the used_slots bitmap.
+ * If found, we mark the slot as used, update the highest_used_slotid,
+ * and respectively set up the sequence operation args.
+ * The slot number is returned if found, or NFS4_MAX_SLOT_TABLE otherwise.
+ *
+ * Note: must be called with under the slot_tbl_lock.
+ */
+static u8
+nfs4_find_slot(struct nfs4_slot_table *tbl, struct rpc_task *task)
+{
+       int slotid;
+       u8 ret_id = NFS4_MAX_SLOT_TABLE;
+       BUILD_BUG_ON((u8)NFS4_MAX_SLOT_TABLE != (int)NFS4_MAX_SLOT_TABLE);
+
+       dprintk("--> %s used_slots=%04lx highest_used=%d max_slots=%d\n",
+               __func__, tbl->used_slots[0], tbl->highest_used_slotid,
+               tbl->max_slots);
+       slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slots);
+       if (slotid >= tbl->max_slots)
+               goto out;
+       __set_bit(slotid, tbl->used_slots);
+       if (slotid > tbl->highest_used_slotid)
+               tbl->highest_used_slotid = slotid;
+       ret_id = slotid;
+out:
+       dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n",
+               __func__, tbl->used_slots[0], tbl->highest_used_slotid, ret_id);
+       return ret_id;
+}
+
+static int nfs4_recover_session(struct nfs4_session *session)
+{
+       struct nfs_client *clp = session->clp;
+       unsigned int loop;
+       int ret;
+
+       for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
+               ret = nfs4_wait_clnt_recover(clp);
+               if (ret != 0)
+                       break;
+               if (!test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state))
+                       break;
+               nfs4_schedule_state_manager(clp);
+               ret = -EIO;
+       }
+       return ret;
+}
+
+static int nfs41_setup_sequence(struct nfs4_session *session,
+                               struct nfs4_sequence_args *args,
+                               struct nfs4_sequence_res *res,
+                               int cache_reply,
+                               struct rpc_task *task)
+{
+       struct nfs4_slot *slot;
+       struct nfs4_slot_table *tbl;
+       int status = 0;
+       u8 slotid;
+
+       dprintk("--> %s\n", __func__);
+       /* slot already allocated? */
+       if (res->sr_slotid != NFS4_MAX_SLOT_TABLE)
+               return 0;
+
+       memset(res, 0, sizeof(*res));
+       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+       tbl = &session->fc_slot_table;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       if (test_bit(NFS4CLNT_SESSION_SETUP, &session->clp->cl_state)) {
+               if (tbl->highest_used_slotid != -1) {
+                       rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
+                       spin_unlock(&tbl->slot_tbl_lock);
+                       dprintk("<-- %s: Session reset: draining\n", __func__);
+                       return -EAGAIN;
+               }
+
+               /* The slot table is empty; start the reset thread */
+               dprintk("%s Session Reset\n", __func__);
+               spin_unlock(&tbl->slot_tbl_lock);
+               status = nfs4_recover_session(session);
+               if (status)
+                       return status;
+               spin_lock(&tbl->slot_tbl_lock);
+       }
+
+       slotid = nfs4_find_slot(tbl, task);
+       if (slotid == NFS4_MAX_SLOT_TABLE) {
+               rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
+               spin_unlock(&tbl->slot_tbl_lock);
+               dprintk("<-- %s: no free slots\n", __func__);
+               return -EAGAIN;
+       }
+       spin_unlock(&tbl->slot_tbl_lock);
+
+       slot = tbl->slots + slotid;
+       args->sa_session = session;
+       args->sa_slotid = slotid;
+       args->sa_cache_this = cache_reply;
+
+       dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr);
+
+       res->sr_session = session;
+       res->sr_slotid = slotid;
+       res->sr_renewal_time = jiffies;
+       /*
+        * sr_status is only set in decode_sequence, and so will remain
+        * set to 1 if an rpc level failure occurs.
+        */
+       res->sr_status = 1;
+       return 0;
+}
+
+int nfs4_setup_sequence(struct nfs_client *clp,
+                       struct nfs4_sequence_args *args,
+                       struct nfs4_sequence_res *res,
+                       int cache_reply,
+                       struct rpc_task *task)
+{
+       int ret = 0;
+
+       dprintk("--> %s clp %p session %p sr_slotid %d\n",
+               __func__, clp, clp->cl_session, res->sr_slotid);
+
+       if (!nfs4_has_session(clp))
+               goto out;
+       ret = nfs41_setup_sequence(clp->cl_session, args, res, cache_reply,
+                                  task);
+       if (ret != -EAGAIN) {
+               /* terminate rpc task */
+               task->tk_status = ret;
+               task->tk_action = NULL;
+       }
+out:
+       dprintk("<-- %s status=%d\n", __func__, ret);
+       return ret;
+}
+
+struct nfs41_call_sync_data {
+       struct nfs_client *clp;
+       struct nfs4_sequence_args *seq_args;
+       struct nfs4_sequence_res *seq_res;
+       int cache_reply;
+};
+
+static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata)
+{
+       struct nfs41_call_sync_data *data = calldata;
+
+       dprintk("--> %s data->clp->cl_session %p\n", __func__,
+               data->clp->cl_session);
+       if (nfs4_setup_sequence(data->clp, data->seq_args,
+                               data->seq_res, data->cache_reply, task))
+               return;
+       rpc_call_start(task);
+}
+
+static void nfs41_call_sync_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs41_call_sync_data *data = calldata;
+
+       nfs41_sequence_done(data->clp, data->seq_res, task->tk_status);
+       nfs41_sequence_free_slot(data->clp, data->seq_res);
+}
+
+struct rpc_call_ops nfs41_call_sync_ops = {
+       .rpc_call_prepare = nfs41_call_sync_prepare,
+       .rpc_call_done = nfs41_call_sync_done,
+};
+
+static int nfs4_call_sync_sequence(struct nfs_client *clp,
+                                  struct rpc_clnt *clnt,
+                                  struct rpc_message *msg,
+                                  struct nfs4_sequence_args *args,
+                                  struct nfs4_sequence_res *res,
+                                  int cache_reply)
+{
+       int ret;
+       struct rpc_task *task;
+       struct nfs41_call_sync_data data = {
+               .clp = clp,
+               .seq_args = args,
+               .seq_res = res,
+               .cache_reply = cache_reply,
+       };
+       struct rpc_task_setup task_setup = {
+               .rpc_client = clnt,
+               .rpc_message = msg,
+               .callback_ops = &nfs41_call_sync_ops,
+               .callback_data = &data
+       };
+
+       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+       task = rpc_run_task(&task_setup);
+       if (IS_ERR(task))
+               ret = PTR_ERR(task);
+       else {
+               ret = task->tk_status;
+               rpc_put_task(task);
+       }
+       return ret;
+}
+
+int _nfs4_call_sync_session(struct nfs_server *server,
+                           struct rpc_message *msg,
+                           struct nfs4_sequence_args *args,
+                           struct nfs4_sequence_res *res,
+                           int cache_reply)
+{
+       return nfs4_call_sync_sequence(server->nfs_client, server->client,
+                                      msg, args, res, cache_reply);
+}
+
+#endif /* CONFIG_NFS_V4_1 */
+
+int _nfs4_call_sync(struct nfs_server *server,
+                   struct rpc_message *msg,
+                   struct nfs4_sequence_args *args,
+                   struct nfs4_sequence_res *res,
+                   int cache_reply)
+{
+       args->sa_session = res->sr_session = NULL;
+       return rpc_call_sync(server->client, msg, 0);
+}
+
+#define nfs4_call_sync(server, msg, args, res, cache_reply) \
+       (server)->nfs_client->cl_call_sync((server), (msg), &(args)->seq_args, \
+                       &(res)->seq_res, (cache_reply))
+
+static void nfs4_sequence_done(const struct nfs_server *server,
+                              struct nfs4_sequence_res *res, int rpc_status)
+{
+#ifdef CONFIG_NFS_V4_1
+       if (nfs4_has_session(server->nfs_client))
+               nfs41_sequence_done(server->nfs_client, res, rpc_status);
+#endif /* CONFIG_NFS_V4_1 */
+}
+
+/* no restart, therefore free slot here */
+static void nfs4_sequence_done_free_slot(const struct nfs_server *server,
+                                        struct nfs4_sequence_res *res,
+                                        int rpc_status)
+{
+       nfs4_sequence_done(server, res, rpc_status);
+       nfs4_sequence_free_slot(server->nfs_client, res);
+}
+
 static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
 {
        struct nfs_inode *nfsi = NFS_I(dir);
@@ -320,10 +687,11 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
        p->o_res.server = p->o_arg.server;
        nfs_fattr_init(&p->f_attr);
        nfs_fattr_init(&p->dir_attr);
+       p->o_res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
 }
 
 static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
-               struct nfs4_state_owner *sp, int flags,
+               struct nfs4_state_owner *sp, fmode_t fmode, int flags,
                const struct iattr *attrs)
 {
        struct dentry *parent = dget_parent(path->dentry);
@@ -343,7 +711,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
        p->owner = sp;
        atomic_inc(&sp->so_count);
        p->o_arg.fh = NFS_FH(dir);
-       p->o_arg.open_flags = flags,
+       p->o_arg.open_flags = flags;
+       p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
        p->o_arg.clientid = server->nfs_client->cl_clientid;
        p->o_arg.id = sp->so_owner_id.id;
        p->o_arg.name = &p->path.dentry->d_name;
@@ -399,10 +768,13 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
        return ret;
 }
 
-static int can_open_cached(struct nfs4_state *state, int mode)
+static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)
 {
        int ret = 0;
-       switch (mode & (FMODE_READ|FMODE_WRITE|O_EXCL)) {
+
+       if (open_mode & O_EXCL)
+               goto out;
+       switch (mode & (FMODE_READ|FMODE_WRITE)) {
                case FMODE_READ:
                        ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0;
                        break;
@@ -412,21 +784,23 @@ static int can_open_cached(struct nfs4_state *state, int mode)
                case FMODE_READ|FMODE_WRITE:
                        ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
        }
+out:
        return ret;
 }
 
-static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags)
+static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
 {
-       if ((delegation->type & open_flags) != open_flags)
+       if ((delegation->type & fmode) != fmode)
                return 0;
        if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
                return 0;
+       nfs_mark_delegation_referenced(delegation);
        return 1;
 }
 
-static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
+static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
 {
-       switch (open_flags) {
+       switch (fmode) {
                case FMODE_WRITE:
                        state->n_wronly++;
                        break;
@@ -436,15 +810,15 @@ static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
                case FMODE_READ|FMODE_WRITE:
                        state->n_rdwr++;
        }
-       nfs4_state_set_mode_locked(state, state->state | open_flags);
+       nfs4_state_set_mode_locked(state, state->state | fmode);
 }
 
-static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
+static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
 {
        if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
                memcpy(state->stateid.data, stateid->data, sizeof(state->stateid.data));
        memcpy(state->open_stateid.data, stateid->data, sizeof(state->open_stateid.data));
-       switch (open_flags) {
+       switch (fmode) {
                case FMODE_READ:
                        set_bit(NFS_O_RDONLY_STATE, &state->flags);
                        break;
@@ -456,14 +830,14 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
        }
 }
 
-static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
+static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
 {
        write_seqlock(&state->seqlock);
-       nfs_set_open_stateid_locked(state, stateid, open_flags);
+       nfs_set_open_stateid_locked(state, stateid, fmode);
        write_sequnlock(&state->seqlock);
 }
 
-static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, int open_flags)
+static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
 {
        /*
         * Protect the call to nfs4_state_set_mode_locked and
@@ -475,20 +849,20 @@ static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_s
                set_bit(NFS_DELEGATED_STATE, &state->flags);
        }
        if (open_stateid != NULL)
-               nfs_set_open_stateid_locked(state, open_stateid, open_flags);
+               nfs_set_open_stateid_locked(state, open_stateid, fmode);
        write_sequnlock(&state->seqlock);
        spin_lock(&state->owner->so_lock);
-       update_open_stateflags(state, open_flags);
+       update_open_stateflags(state, fmode);
        spin_unlock(&state->owner->so_lock);
 }
 
-static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, int open_flags)
+static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
 {
        struct nfs_inode *nfsi = NFS_I(state->inode);
        struct nfs_delegation *deleg_cur;
        int ret = 0;
 
-       open_flags &= (FMODE_READ|FMODE_WRITE);
+       fmode &= (FMODE_READ|FMODE_WRITE);
 
        rcu_read_lock();
        deleg_cur = rcu_dereference(nfsi->delegation);
@@ -497,7 +871,7 @@ static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stat
 
        spin_lock(&deleg_cur->lock);
        if (nfsi->delegation != deleg_cur ||
-           (deleg_cur->type & open_flags) != open_flags)
+           (deleg_cur->type & fmode) != fmode)
                goto no_delegation_unlock;
 
        if (delegation == NULL)
@@ -505,7 +879,8 @@ static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stat
        else if (memcmp(deleg_cur->stateid.data, delegation->data, NFS4_STATEID_SIZE) != 0)
                goto no_delegation_unlock;
 
-       __update_open_stateid(state, open_stateid, &deleg_cur->stateid, open_flags);
+       nfs_mark_delegation_referenced(deleg_cur);
+       __update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
        ret = 1;
 no_delegation_unlock:
        spin_unlock(&deleg_cur->lock);
@@ -513,7 +888,7 @@ no_delegation:
        rcu_read_unlock();
 
        if (!ret && open_stateid != NULL) {
-               __update_open_stateid(state, open_stateid, NULL, open_flags);
+               __update_open_stateid(state, open_stateid, NULL, fmode);
                ret = 1;
        }
 
@@ -521,13 +896,13 @@ no_delegation:
 }
 
 
-static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open_flags)
+static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmode)
 {
        struct nfs_delegation *delegation;
 
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(inode)->delegation);
-       if (delegation == NULL || (delegation->type & open_flags) == open_flags) {
+       if (delegation == NULL || (delegation->type & fmode) == fmode) {
                rcu_read_unlock();
                return;
        }
@@ -540,15 +915,16 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
        struct nfs4_state *state = opendata->state;
        struct nfs_inode *nfsi = NFS_I(state->inode);
        struct nfs_delegation *delegation;
-       int open_mode = opendata->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL);
+       int open_mode = opendata->o_arg.open_flags & O_EXCL;
+       fmode_t fmode = opendata->o_arg.fmode;
        nfs4_stateid stateid;
        int ret = -EAGAIN;
 
        for (;;) {
-               if (can_open_cached(state, open_mode)) {
+               if (can_open_cached(state, fmode, open_mode)) {
                        spin_lock(&state->owner->so_lock);
-                       if (can_open_cached(state, open_mode)) {
-                               update_open_stateflags(state, open_mode);
+                       if (can_open_cached(state, fmode, open_mode)) {
+                               update_open_stateflags(state, fmode);
                                spin_unlock(&state->owner->so_lock);
                                goto out_return_state;
                        }
@@ -557,7 +933,7 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
                rcu_read_lock();
                delegation = rcu_dereference(nfsi->delegation);
                if (delegation == NULL ||
-                   !can_open_delegated(delegation, open_mode)) {
+                   !can_open_delegated(delegation, fmode)) {
                        rcu_read_unlock();
                        break;
                }
@@ -570,7 +946,7 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
                ret = -EAGAIN;
 
                /* Try to update the stateid using the delegation */
-               if (update_open_stateid(state, NULL, &stateid, open_mode))
+               if (update_open_stateid(state, NULL, &stateid, fmode))
                        goto out_return_state;
        }
 out:
@@ -622,7 +998,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
        }
 
        update_open_stateid(state, &data->o_res.stateid, NULL,
-                       data->o_arg.open_flags);
+                       data->o_arg.fmode);
        iput(inode);
 out:
        return state;
@@ -653,7 +1029,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
 {
        struct nfs4_opendata *opendata;
 
-       opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL);
+       opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, 0, NULL);
        if (opendata == NULL)
                return ERR_PTR(-ENOMEM);
        opendata->state = state;
@@ -661,12 +1037,13 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
        return opendata;
 }
 
-static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, struct nfs4_state **res)
+static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res)
 {
        struct nfs4_state *newstate;
        int ret;
 
-       opendata->o_arg.open_flags = openflags;
+       opendata->o_arg.open_flags = 0;
+       opendata->o_arg.fmode = fmode;
        memset(&opendata->o_res, 0, sizeof(opendata->o_res));
        memset(&opendata->c_res, 0, sizeof(opendata->c_res));
        nfs4_init_opendata_res(opendata);
@@ -676,7 +1053,7 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openf
        newstate = nfs4_opendata_to_nfs4_state(opendata);
        if (IS_ERR(newstate))
                return PTR_ERR(newstate);
-       nfs4_close_state(&opendata->path, newstate, openflags);
+       nfs4_close_state(&opendata->path, newstate, fmode);
        *res = newstate;
        return 0;
 }
@@ -732,7 +1109,7 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
 {
        struct nfs_delegation *delegation;
        struct nfs4_opendata *opendata;
-       int delegation_type = 0;
+       fmode_t delegation_type = 0;
        int status;
 
        opendata = nfs4_open_recoverdata_alloc(ctx, state);
@@ -803,16 +1180,30 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
                err = _nfs4_open_delegation_recall(ctx, state, stateid);
                switch (err) {
                        case 0:
-                               return err;
+                       case -ENOENT:
+                       case -ESTALE:
+                               goto out;
                        case -NFS4ERR_STALE_CLIENTID:
                        case -NFS4ERR_STALE_STATEID:
                        case -NFS4ERR_EXPIRED:
                                /* Don't recall a delegation if it was lost */
                                nfs4_schedule_state_recovery(server->nfs_client);
-                               return err;
+                               goto out;
+                       case -ERESTARTSYS:
+                               /*
+                                * The show must go on: exit, but mark the
+                                * stateid as needing recovery.
+                                */
+                       case -NFS4ERR_ADMIN_REVOKED:
+                       case -NFS4ERR_BAD_STATEID:
+                               nfs4_state_mark_reclaim_nograce(server->nfs_client, state);
+                       case -ENOMEM:
+                               err = 0;
+                               goto out;
                }
                err = nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
+out:
        return err;
 }
 
@@ -845,7 +1236,7 @@ static void nfs4_open_confirm_release(void *calldata)
                goto out_free;
        state = nfs4_opendata_to_nfs4_state(data);
        if (!IS_ERR(state))
-               nfs4_close_state(&data->path, state, data->o_arg.open_flags);
+               nfs4_close_state(&data->path, state, data->o_arg.fmode);
 out_free:
        nfs4_opendata_put(data);
 }
@@ -909,7 +1300,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
        if (data->state != NULL) {
                struct nfs_delegation *delegation;
 
-               if (can_open_cached(data->state, data->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL)))
+               if (can_open_cached(data->state, data->o_arg.fmode, data->o_arg.open_flags))
                        goto out_no_action;
                rcu_read_lock();
                delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
@@ -928,6 +1319,10 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
                nfs_copy_fh(&data->o_res.fh, data->o_arg.fh);
        }
        data->timestamp = jiffies;
+       if (nfs4_setup_sequence(data->o_arg.server->nfs_client,
+                               &data->o_arg.seq_args,
+                               &data->o_res.seq_res, 1, task))
+               return;
        rpc_call_start(task);
        return;
 out_no_action:
@@ -940,6 +1335,10 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata)
        struct nfs4_opendata *data = calldata;
 
        data->rpc_status = task->tk_status;
+
+       nfs4_sequence_done_free_slot(data->o_arg.server, &data->o_res.seq_res,
+                                    task->tk_status);
+
        if (RPC_ASSASSINATED(task))
                return;
        if (task->tk_status == 0) {
@@ -978,7 +1377,7 @@ static void nfs4_open_release(void *calldata)
                goto out_free;
        state = nfs4_opendata_to_nfs4_state(data);
        if (!IS_ERR(state))
-               nfs4_close_state(&data->path, state, data->o_arg.open_flags);
+               nfs4_close_state(&data->path, state, data->o_arg.fmode);
 out_free:
        nfs4_opendata_put(data);
 }
@@ -1053,18 +1452,20 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
 static int nfs4_recover_expired_lease(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
+       unsigned int loop;
        int ret;
 
-       for (;;) {
+       for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
                ret = nfs4_wait_clnt_recover(clp);
                if (ret != 0)
-                       return ret;
+                       break;
                if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
                    !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
                        break;
                nfs4_schedule_state_recovery(clp);
+               ret = -EIO;
        }
-       return 0;
+       return ret;
 }
 
 /*
@@ -1095,8 +1496,9 @@ static inline int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4
 
        do {
                err = _nfs4_open_expired(ctx, state);
-               if (err == -NFS4ERR_DELAY)
-                       nfs4_handle_exception(server, err, &exception);
+               if (err != -NFS4ERR_DELAY)
+                       break;
+               nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
        return err;
 }
@@ -1133,7 +1535,7 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
 /*
  * Returns a referenced nfs4_state
  */
-static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
+static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
 {
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
@@ -1151,9 +1553,9 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
        if (status != 0)
                goto err_put_state_owner;
        if (path->dentry->d_inode != NULL)
-               nfs4_return_incompatible_delegation(path->dentry->d_inode, flags & (FMODE_READ|FMODE_WRITE));
+               nfs4_return_incompatible_delegation(path->dentry->d_inode, fmode);
        status = -ENOMEM;
-       opendata = nfs4_opendata_alloc(path, sp, flags, sattr);
+       opendata = nfs4_opendata_alloc(path, sp, fmode, flags, sattr);
        if (opendata == NULL)
                goto err_put_state_owner;
 
@@ -1185,14 +1587,14 @@ out_err:
 }
 
 
-static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred)
+static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
 {
        struct nfs4_exception exception = { };
        struct nfs4_state *res;
        int status;
 
        do {
-               status = _nfs4_do_open(dir, path, flags, sattr, cred, &res);
+               status = _nfs4_do_open(dir, path, fmode, flags, sattr, cred, &res);
                if (status == 0)
                        break;
                /* NOTE: BAD_SEQID means the server and client disagree about the
@@ -1267,7 +1669,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        } else
                memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
 
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &arg, &res, 1);
        if (status == 0 && state != NULL)
                renew_lease(server, timestamp);
        return status;
@@ -1316,6 +1718,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
        struct nfs4_state *state = calldata->state;
        struct nfs_server *server = NFS_SERVER(calldata->inode);
 
+       nfs4_sequence_done(server, &calldata->res.seq_res, task->tk_status);
        if (RPC_ASSASSINATED(task))
                return;
         /* hmm. we are done with the inode, and in the process of freeing
@@ -1330,14 +1733,15 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                case -NFS4ERR_OLD_STATEID:
                case -NFS4ERR_BAD_STATEID:
                case -NFS4ERR_EXPIRED:
-                       if (calldata->arg.open_flags == 0)
+                       if (calldata->arg.fmode == 0)
                                break;
                default:
                        if (nfs4_async_handle_error(task, server, state) == -EAGAIN) {
-                               rpc_restart_call(task);
+                               nfs4_restart_rpc(task, server->nfs_client);
                                return;
                        }
        }
+       nfs4_sequence_free_slot(server->nfs_client, &calldata->res.seq_res);
        nfs_refresh_inode(calldata->inode, calldata->res.fattr);
 }
 
@@ -1372,12 +1776,16 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
        nfs_fattr_init(calldata->res.fattr);
        if (test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0) {
                task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
-               calldata->arg.open_flags = FMODE_READ;
+               calldata->arg.fmode = FMODE_READ;
        } else if (test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0) {
                task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
-               calldata->arg.open_flags = FMODE_WRITE;
+               calldata->arg.fmode = FMODE_WRITE;
        }
        calldata->timestamp = jiffies;
+       if (nfs4_setup_sequence((NFS_SERVER(calldata->inode))->nfs_client,
+                               &calldata->arg.seq_args, &calldata->res.seq_res,
+                               1, task))
+               return;
        rpc_call_start(task);
 }
 
@@ -1417,22 +1825,25 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
        };
        int status = -ENOMEM;
 
-       calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
+       calldata = kzalloc(sizeof(*calldata), GFP_KERNEL);
        if (calldata == NULL)
                goto out;
        calldata->inode = state->inode;
        calldata->state = state;
        calldata->arg.fh = NFS_FH(state->inode);
        calldata->arg.stateid = &state->open_stateid;
+       if (nfs4_has_session(server->nfs_client))
+               memset(calldata->arg.stateid->data, 0, 4);    /* clear seqid */
        /* Serialization for the sequence id */
        calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
        if (calldata->arg.seqid == NULL)
                goto out_free_calldata;
-       calldata->arg.open_flags = 0;
-       calldata->arg.bitmask = server->attr_bitmask;
+       calldata->arg.fmode = 0;
+       calldata->arg.bitmask = server->cache_consistency_bitmask;
        calldata->res.fattr = &calldata->fattr;
        calldata->res.seqid = calldata->arg.seqid;
        calldata->res.server = server;
+       calldata->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        calldata->path.mnt = mntget(path->mnt);
        calldata->path.dentry = dget(path->dentry);
 
@@ -1455,13 +1866,13 @@ out:
        return status;
 }
 
-static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state)
+static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state, fmode_t fmode)
 {
        struct file *filp;
        int ret;
 
        /* If the open_intent is for execute, we have an extra check to make */
-       if (nd->intent.open.flags & FMODE_EXEC) {
+       if (fmode & FMODE_EXEC) {
                ret = nfs_may_open(state->inode,
                                state->owner->so_cred,
                                nd->intent.open.flags);
@@ -1477,7 +1888,7 @@ static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct
        }
        ret = PTR_ERR(filp);
 out_close:
-       nfs4_close_sync(path, state, nd->intent.open.flags);
+       nfs4_close_sync(path, state, fmode & (FMODE_READ|FMODE_WRITE));
        return ret;
 }
 
@@ -1493,12 +1904,13 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
        struct rpc_cred *cred;
        struct nfs4_state *state;
        struct dentry *res;
+       fmode_t fmode = nd->intent.open.flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC);
 
        if (nd->flags & LOOKUP_CREATE) {
                attr.ia_mode = nd->intent.open.create_mode;
                attr.ia_valid = ATTR_MODE;
                if (!IS_POSIXACL(dir))
-                       attr.ia_mode &= ~current->fs->umask;
+                       attr.ia_mode &= ~current_umask();
        } else {
                attr.ia_valid = 0;
                BUG_ON(nd->intent.open.flags & O_CREAT);
@@ -1510,7 +1922,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
        parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
        nfs_block_sillyrename(parent);
-       state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred);
+       state = nfs4_do_open(dir, &path, fmode, nd->intent.open.flags, &attr, cred);
        put_rpccred(cred);
        if (IS_ERR(state)) {
                if (PTR_ERR(state) == -ENOENT) {
@@ -1525,7 +1937,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
                path.dentry = res;
        nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir));
        nfs_unblock_sillyrename(parent);
-       nfs4_intent_set_file(nd, &path, state);
+       nfs4_intent_set_file(nd, &path, state, fmode);
        return res;
 }
 
@@ -1538,11 +1950,12 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
        };
        struct rpc_cred *cred;
        struct nfs4_state *state;
+       fmode_t fmode = openflags & (FMODE_READ | FMODE_WRITE);
 
        cred = rpc_lookup_cred();
        if (IS_ERR(cred))
                return PTR_ERR(cred);
-       state = nfs4_do_open(dir, &path, openflags, NULL, cred);
+       state = nfs4_do_open(dir, &path, fmode, openflags, NULL, cred);
        put_rpccred(cred);
        if (IS_ERR(state)) {
                switch (PTR_ERR(state)) {
@@ -1559,37 +1972,75 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
        }
        if (state->inode == dentry->d_inode) {
                nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-               nfs4_intent_set_file(nd, &path, state);
+               nfs4_intent_set_file(nd, &path, state, fmode);
                return 1;
        }
-       nfs4_close_sync(&path, state, openflags);
+       nfs4_close_sync(&path, state, fmode);
 out_drop:
        d_drop(dentry);
        return 0;
 }
 
+void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
+{
+       if (ctx->state == NULL)
+               return;
+       if (is_sync)
+               nfs4_close_sync(&ctx->path, ctx->state, ctx->mode);
+       else
+               nfs4_close_state(&ctx->path, ctx->state, ctx->mode);
+}
 
 static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
 {
+       struct nfs4_server_caps_arg args = {
+               .fhandle = fhandle,
+       };
        struct nfs4_server_caps_res res = {};
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SERVER_CAPS],
-               .rpc_argp = fhandle,
+               .rpc_argp = &args,
                .rpc_resp = &res,
        };
        int status;
 
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 0);
        if (status == 0) {
                memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
+               server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS|
+                               NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
+                               NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|
+                               NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME|
+                               NFS_CAP_CTIME|NFS_CAP_MTIME);
                if (res.attr_bitmask[0] & FATTR4_WORD0_ACL)
                        server->caps |= NFS_CAP_ACLS;
                if (res.has_links != 0)
                        server->caps |= NFS_CAP_HARDLINKS;
                if (res.has_symlinks != 0)
                        server->caps |= NFS_CAP_SYMLINKS;
+               if (res.attr_bitmask[0] & FATTR4_WORD0_FILEID)
+                       server->caps |= NFS_CAP_FILEID;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_MODE)
+                       server->caps |= NFS_CAP_MODE;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_NUMLINKS)
+                       server->caps |= NFS_CAP_NLINK;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER)
+                       server->caps |= NFS_CAP_OWNER;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER_GROUP)
+                       server->caps |= NFS_CAP_OWNER_GROUP;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_ACCESS)
+                       server->caps |= NFS_CAP_ATIME;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_METADATA)
+                       server->caps |= NFS_CAP_CTIME;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY)
+                       server->caps |= NFS_CAP_MTIME;
+
+               memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
+               server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
+               server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
                server->acl_bitmask = res.acl_bitmask;
        }
+
        return status;
 }
 
@@ -1621,8 +2072,9 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
+
        nfs_fattr_init(info->fattr);
-       return rpc_call_sync(server->client, &msg, 0);
+       return nfs4_call_sync(server, &msg, &args, &res, 0);
 }
 
 static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -1712,7 +2164,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
        };
        
        nfs_fattr_init(fattr);
-       return rpc_call_sync(server->client, &msg, 0);
+       return nfs4_call_sync(server, &msg, &args, &res, 0);
 }
 
 static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
@@ -1796,7 +2248,7 @@ static int _nfs4_proc_lookupfh(struct nfs_server *server, const struct nfs_fh *d
        nfs_fattr_init(fattr);
 
        dprintk("NFS call  lookupfh %s\n", name->name);
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 0);
        dprintk("NFS reply lookupfh: %d\n", status);
        return status;
 }
@@ -1882,7 +2334,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
                        args.access |= NFS4_ACCESS_EXECUTE;
        }
        nfs_fattr_init(&fattr);
-       status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 0);
        if (!status) {
                entry->mask = 0;
                if (res.access & NFS4_ACCESS_READ)
@@ -1941,13 +2393,14 @@ static int _nfs4_proc_readlink(struct inode *inode, struct page *page,
                .pglen    = pglen,
                .pages    = &page,
        };
+       struct nfs4_readlink_res res;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READLINK],
                .rpc_argp = &args,
-               .rpc_resp = NULL,
+               .rpc_resp = &res,
        };
 
-       return rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       return nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0);
 }
 
 static int nfs4_proc_readlink(struct inode *inode, struct page *page,
@@ -1988,6 +2441,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
        };
        struct nfs4_state *state;
        struct rpc_cred *cred;
+       fmode_t fmode = flags & (FMODE_READ | FMODE_WRITE);
        int status = 0;
 
        cred = rpc_lookup_cred();
@@ -1995,7 +2449,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                status = PTR_ERR(cred);
                goto out;
        }
-       state = nfs4_do_open(dir, &path, flags, sattr, cred);
+       state = nfs4_do_open(dir, &path, fmode, flags, sattr, cred);
        d_drop(dentry);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
@@ -2011,9 +2465,9 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                nfs_post_op_update_inode(state->inode, &fattr);
        }
        if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
-               status = nfs4_intent_set_file(nd, &path, state);
+               status = nfs4_intent_set_file(nd, &path, state, fmode);
        else
-               nfs4_close_sync(&path, state, flags);
+               nfs4_close_sync(&path, state, fmode);
 out_putcred:
        put_rpccred(cred);
 out:
@@ -2040,7 +2494,7 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
        int                     status;
 
        nfs_fattr_init(&res.dir_attr);
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 1);
        if (status == 0) {
                update_changeattr(dir, &res.cinfo);
                nfs_post_op_update_inode(dir, &res.dir_attr);
@@ -2066,7 +2520,7 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
        struct nfs_removeargs *args = msg->rpc_argp;
        struct nfs_removeres *res = msg->rpc_resp;
 
-       args->bitmask = server->attr_bitmask;
+       args->bitmask = server->cache_consistency_bitmask;
        res->server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
 }
@@ -2075,8 +2529,10 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
 {
        struct nfs_removeres *res = task->tk_msg.rpc_resp;
 
+       nfs4_sequence_done(res->server, &res->seq_res, task->tk_status);
        if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
                return 0;
+       nfs4_sequence_free_slot(res->server->nfs_client, &res->seq_res);
        update_changeattr(dir, &res->cinfo);
        nfs_post_op_update_inode(dir, &res->dir_attr);
        return 1;
@@ -2108,7 +2564,7 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
        
        nfs_fattr_init(res.old_fattr);
        nfs_fattr_init(res.new_fattr);
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &arg, &res, 1);
 
        if (!status) {
                update_changeattr(old_dir, &res.old_cinfo);
@@ -2157,7 +2613,7 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
 
        nfs_fattr_init(res.fattr);
        nfs_fattr_init(res.dir_attr);
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &arg, &res, 1);
        if (!status) {
                update_changeattr(dir, &res.cinfo);
                nfs_post_op_update_inode(dir, res.dir_attr);
@@ -2218,7 +2674,8 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
 
 static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
 {
-       int status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0);
+       int status = nfs4_call_sync(NFS_SERVER(dir), &data->msg,
+                                   &data->arg, &data->res, 1);
        if (status == 0) {
                update_changeattr(dir, &data->res.dir_cinfo);
                nfs_post_op_update_inode(dir, data->res.dir_fattr);
@@ -2310,7 +2767,7 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
                .pages = &page,
                .pgbase = 0,
                .count = count,
-               .bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask,
+               .bitmask = NFS_SERVER(dentry->d_inode)->cache_consistency_bitmask,
        };
        struct nfs4_readdir_res res;
        struct rpc_message msg = {
@@ -2327,7 +2784,7 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
                        (unsigned long long)cookie);
        nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
        res.pgbase = args.pgbase;
-       status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+       status = nfs4_call_sync(NFS_SERVER(dir), &msg, &args, &res, 0);
        if (status == 0)
                memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
 
@@ -2405,14 +2862,17 @@ static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
                .fh = fhandle,
                .bitmask = server->attr_bitmask,
        };
+       struct nfs4_statfs_res res = {
+               .fsstat = fsstat,
+       };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_STATFS],
                .rpc_argp = &args,
-               .rpc_resp = fsstat,
+               .rpc_resp = &res,
        };
 
        nfs_fattr_init(fsstat->fattr);
-       return rpc_call_sync(server->client, &msg, 0);
+       return  nfs4_call_sync(server, &msg, &args, &res, 0);
 }
 
 static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat)
@@ -2434,13 +2894,16 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
                .fh = fhandle,
                .bitmask = server->attr_bitmask,
        };
+       struct nfs4_fsinfo_res res = {
+               .fsinfo = fsinfo,
+       };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO],
                .rpc_argp = &args,
-               .rpc_resp = fsinfo,
+               .rpc_resp = &res,
        };
 
-       return rpc_call_sync(server->client, &msg, 0);
+       return nfs4_call_sync(server, &msg, &args, &res, 0);
 }
 
 static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
@@ -2469,10 +2932,13 @@ static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle
                .fh = fhandle,
                .bitmask = server->attr_bitmask,
        };
+       struct nfs4_pathconf_res res = {
+               .pathconf = pathconf,
+       };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF],
                .rpc_argp = &args,
-               .rpc_resp = pathconf,
+               .rpc_resp = &res,
        };
 
        /* None of the pathconf attributes are mandatory to implement */
@@ -2482,7 +2948,7 @@ static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle
        }
 
        nfs_fattr_init(pathconf->fattr);
-       return rpc_call_sync(server->client, &msg, 0);
+       return nfs4_call_sync(server, &msg, &args, &res, 0);
 }
 
 static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -2503,8 +2969,13 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
 {
        struct nfs_server *server = NFS_SERVER(data->inode);
 
+       dprintk("--> %s\n", __func__);
+
+       /* nfs4_sequence_free_slot called in the read rpc_call_done */
+       nfs4_sequence_done(server, &data->res.seq_res, task->tk_status);
+
        if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, server->nfs_client);
                return -EAGAIN;
        }
 
@@ -2524,8 +2995,12 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
 {
        struct inode *inode = data->inode;
        
+       /* slot is freed in nfs_writeback_done */
+       nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res,
+                          task->tk_status);
+
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, NFS_SERVER(inode)->nfs_client);
                return -EAGAIN;
        }
        if (task->tk_status >= 0) {
@@ -2539,7 +3014,7 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag
 {
        struct nfs_server *server = NFS_SERVER(data->inode);
 
-       data->args.bitmask = server->attr_bitmask;
+       data->args.bitmask = server->cache_consistency_bitmask;
        data->res.server = server;
        data->timestamp   = jiffies;
 
@@ -2550,10 +3025,14 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
 {
        struct inode *inode = data->inode;
        
+       nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res,
+                          task->tk_status);
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, NFS_SERVER(inode)->nfs_client);
                return -EAGAIN;
        }
+       nfs4_sequence_free_slot(NFS_SERVER(inode)->nfs_client,
+                               &data->res.seq_res);
        nfs_refresh_inode(inode, data->res.fattr);
        return 0;
 }
@@ -2562,7 +3041,7 @@ static void nfs4_proc_commit_setup(struct nfs_write_data *data, struct rpc_messa
 {
        struct nfs_server *server = NFS_SERVER(data->inode);
        
-       data->args.bitmask = server->attr_bitmask;
+       data->args.bitmask = server->cache_consistency_bitmask;
        data->res.server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT];
 }
@@ -2577,18 +3056,18 @@ static void nfs4_renew_done(struct rpc_task *task, void *data)
        unsigned long timestamp = (unsigned long)data;
 
        if (task->tk_status < 0) {
-               switch (task->tk_status) {
-                       case -NFS4ERR_STALE_CLIENTID:
-                       case -NFS4ERR_EXPIRED:
-                       case -NFS4ERR_CB_PATH_DOWN:
-                               nfs4_schedule_state_recovery(clp);
-               }
+               /* Unless we're shutting down, schedule state recovery! */
+               if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) != 0)
+                       nfs4_schedule_state_recovery(clp);
                return;
        }
        spin_lock(&clp->cl_lock);
        if (time_before(clp->cl_last_renewal,timestamp))
                clp->cl_last_renewal = timestamp;
        spin_unlock(&clp->cl_lock);
+       dprintk("%s calling put_rpccred on rpc_cred %p\n", __func__,
+                               task->tk_msg.rpc_cred);
+       put_rpccred(task->tk_msg.rpc_cred);
 }
 
 static const struct rpc_call_ops nfs4_renew_ops = {
@@ -2728,12 +3207,14 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
                .acl_pages = pages,
                .acl_len = buflen,
        };
-       size_t resp_len = buflen;
+       struct nfs_getaclres res = {
+               .acl_len = buflen,
+       };
        void *resp_buf;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
                .rpc_argp = &args,
-               .rpc_resp = &resp_len,
+               .rpc_resp = &res,
        };
        struct page *localpage = NULL;
        int ret;
@@ -2747,26 +3228,26 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
                        return -ENOMEM;
                args.acl_pages[0] = localpage;
                args.acl_pgbase = 0;
-               resp_len = args.acl_len = PAGE_SIZE;
+               args.acl_len = PAGE_SIZE;
        } else {
                resp_buf = buf;
                buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase);
        }
-       ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       ret = nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0);
        if (ret)
                goto out_free;
-       if (resp_len > args.acl_len)
-               nfs4_write_cached_acl(inode, NULL, resp_len);
+       if (res.acl_len > args.acl_len)
+               nfs4_write_cached_acl(inode, NULL, res.acl_len);
        else
-               nfs4_write_cached_acl(inode, resp_buf, resp_len);
+               nfs4_write_cached_acl(inode, resp_buf, res.acl_len);
        if (buf) {
                ret = -ERANGE;
-               if (resp_len > buflen)
+               if (res.acl_len > buflen)
                        goto out_free;
                if (localpage)
-                       memcpy(buf, resp_buf, resp_len);
+                       memcpy(buf, resp_buf, res.acl_len);
        }
-       ret = resp_len;
+       ret = res.acl_len;
 out_free:
        if (localpage)
                __free_page(localpage);
@@ -2796,8 +3277,6 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
        ret = nfs_revalidate_inode(server, inode);
        if (ret < 0)
                return ret;
-       if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
-               nfs_zap_acl_cache(inode);
        ret = nfs4_read_cached_acl(inode, buf, buflen);
        if (ret != -ENOENT)
                return ret;
@@ -2813,10 +3292,11 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
                .acl_pages      = pages,
                .acl_len        = buflen,
        };
+       struct nfs_setaclres res;
        struct rpc_message msg = {
                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETACL],
                .rpc_argp       = &arg,
-               .rpc_resp       = NULL,
+               .rpc_resp       = &res,
        };
        int ret;
 
@@ -2824,7 +3304,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
                return -EOPNOTSUPP;
        nfs_inode_return_delegation(inode);
        buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
-       ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       ret = nfs4_call_sync(server, &msg, &arg, &res, 1);
        nfs_access_zap_cache(inode);
        nfs_zap_acl_cache(inode);
        return ret;
@@ -2843,10 +3323,8 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
 }
 
 static int
-nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
+_nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs_client *clp, struct nfs4_state *state)
 {
-       struct nfs_client *clp = server->nfs_client;
-
        if (!clp || task->tk_status >= 0)
                return 0;
        switch(task->tk_status) {
@@ -2861,12 +3339,27 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                case -NFS4ERR_EXPIRED:
                        rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
                        nfs4_schedule_state_recovery(clp);
-                       if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0)
+                       if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
                                rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
                        task->tk_status = 0;
                        return -EAGAIN;
+#if defined(CONFIG_NFS_V4_1)
+               case -NFS4ERR_BADSESSION:
+               case -NFS4ERR_BADSLOT:
+               case -NFS4ERR_BAD_HIGH_SLOT:
+               case -NFS4ERR_DEADSESSION:
+               case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+               case -NFS4ERR_SEQ_FALSE_RETRY:
+               case -NFS4ERR_SEQ_MISORDERED:
+                       dprintk("%s ERROR %d, Reset session\n", __func__,
+                               task->tk_status);
+                       set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
+                       task->tk_status = 0;
+                       return -EAGAIN;
+#endif /* CONFIG_NFS_V4_1 */
                case -NFS4ERR_DELAY:
-                       nfs_inc_server_stats(server, NFSIOS_DELAY);
+                       if (server)
+                               nfs_inc_server_stats(server, NFSIOS_DELAY);
                case -NFS4ERR_GRACE:
                        rpc_delay(task, NFS4_POLL_RETRY_MAX);
                        task->tk_status = 0;
@@ -2879,6 +3372,12 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
        return 0;
 }
 
+static int
+nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
+{
+       return _nfs4_async_handle_error(task, server, server->nfs_client, state);
+}
+
 int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
 {
        nfs4_verifier sc_verifier;
@@ -2986,6 +3485,10 @@ struct nfs4_delegreturndata {
 static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
 {
        struct nfs4_delegreturndata *data = calldata;
+
+       nfs4_sequence_done_free_slot(data->res.server, &data->res.seq_res,
+                                    task->tk_status);
+
        data->rpc_status = task->tk_status;
        if (data->rpc_status == 0)
                renew_lease(data->res.server, data->timestamp);
@@ -2996,7 +3499,25 @@ static void nfs4_delegreturn_release(void *calldata)
        kfree(calldata);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data)
+{
+       struct nfs4_delegreturndata *d_data;
+
+       d_data = (struct nfs4_delegreturndata *)data;
+
+       if (nfs4_setup_sequence(d_data->res.server->nfs_client,
+                               &d_data->args.seq_args,
+                               &d_data->res.seq_res, 1, task))
+               return;
+       rpc_call_start(task);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static const struct rpc_call_ops nfs4_delegreturn_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs4_delegreturn_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs4_delegreturn_done,
        .rpc_release = nfs4_delegreturn_release,
 };
@@ -3018,7 +3539,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
        };
        int status = 0;
 
-       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (data == NULL)
                return -ENOMEM;
        data->args.fhandle = &data->fh;
@@ -3028,6 +3549,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
        memcpy(&data->stateid, stateid, sizeof(data->stateid));
        data->res.fattr = &data->fattr;
        data->res.server = server;
+       data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        nfs_fattr_init(data->res.fattr);
        data->timestamp = jiffies;
        data->rpc_status = 0;
@@ -3113,7 +3635,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
                goto out;
        lsp = request->fl_u.nfs4_fl.owner;
        arg.lock_owner.id = lsp->ls_id.id;
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &arg, &res, 1);
        switch (status) {
                case 0:
                        request->fl_type = F_UNLCK;
@@ -3173,13 +3695,14 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
        struct nfs4_unlockdata *p;
        struct inode *inode = lsp->ls_state->inode;
 
-       p = kmalloc(sizeof(*p), GFP_KERNEL);
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
        if (p == NULL)
                return NULL;
        p->arg.fh = NFS_FH(inode);
        p->arg.fl = &p->fl;
        p->arg.seqid = seqid;
        p->res.seqid = seqid;
+       p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        p->arg.stateid = &lsp->ls_stateid;
        p->lsp = lsp;
        atomic_inc(&lsp->ls_count);
@@ -3203,6 +3726,8 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
 {
        struct nfs4_unlockdata *calldata = data;
 
+       nfs4_sequence_done(calldata->server, &calldata->res.seq_res,
+                          task->tk_status);
        if (RPC_ASSASSINATED(task))
                return;
        switch (task->tk_status) {
@@ -3219,8 +3744,11 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
                        break;
                default:
                        if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
-                               rpc_restart_call(task);
+                               nfs4_restart_rpc(task,
+                                               calldata->server->nfs_client);
        }
+       nfs4_sequence_free_slot(calldata->server->nfs_client,
+                               &calldata->res.seq_res);
 }
 
 static void nfs4_locku_prepare(struct rpc_task *task, void *data)
@@ -3235,6 +3763,10 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
                return;
        }
        calldata->timestamp = jiffies;
+       if (nfs4_setup_sequence(calldata->server->nfs_client,
+                               &calldata->arg.seq_args,
+                               &calldata->res.seq_res, 1, task))
+               return;
        rpc_call_start(task);
 }
 
@@ -3327,6 +3859,7 @@ struct nfs4_lockdata {
        unsigned long timestamp;
        int rpc_status;
        int cancelled;
+       struct nfs_server *server;
 };
 
 static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
@@ -3352,7 +3885,9 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
        p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
        p->arg.lock_owner.id = lsp->ls_id.id;
        p->res.lock_seqid = p->arg.lock_seqid;
+       p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        p->lsp = lsp;
+       p->server = server;
        atomic_inc(&lsp->ls_count);
        p->ctx = get_nfs_open_context(ctx);
        memcpy(&p->fl, fl, sizeof(p->fl));
@@ -3382,6 +3917,9 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
        } else
                data->arg.new_lock_owner = 0;
        data->timestamp = jiffies;
+       if (nfs4_setup_sequence(data->server->nfs_client, &data->arg.seq_args,
+                               &data->res.seq_res, 1, task))
+               return;
        rpc_call_start(task);
        dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
 }
@@ -3392,6 +3930,9 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
 
        dprintk("%s: begin!\n", __func__);
 
+       nfs4_sequence_done_free_slot(data->server, &data->res.seq_res,
+                                    task->tk_status);
+
        data->rpc_status = task->tk_status;
        if (RPC_ASSASSINATED(task))
                goto out;
@@ -3473,8 +4014,6 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
        ret = nfs4_wait_for_completion_rpc_task(task);
        if (ret == 0) {
                ret = data->rpc_status;
-               if (ret == -NFS4ERR_DENIED)
-                       ret = -EAGAIN;
        } else
                data->cancelled = 1;
        rpc_put_task(task);
@@ -3562,9 +4101,11 @@ static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *
        int err;
 
        do {
+               err = _nfs4_proc_setlk(state, cmd, request);
+               if (err == -NFS4ERR_DENIED)
+                       err = -EAGAIN;
                err = nfs4_handle_exception(NFS_SERVER(state->inode),
-                               _nfs4_proc_setlk(state, cmd, request),
-                               &exception);
+                               err, &exception);
        } while (exception.retry);
        return err;
 }
@@ -3584,15 +4125,23 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
        if (request->fl_start < 0 || request->fl_end < 0)
                return -EINVAL;
 
-       if (IS_GETLK(cmd))
-               return nfs4_proc_getlk(state, F_GETLK, request);
+       if (IS_GETLK(cmd)) {
+               if (state != NULL)
+                       return nfs4_proc_getlk(state, F_GETLK, request);
+               return 0;
+       }
 
        if (!(IS_SETLK(cmd) || IS_SETLKW(cmd)))
                return -EINVAL;
 
-       if (request->fl_type == F_UNLCK)
-               return nfs4_proc_unlck(state, cmd, request);
+       if (request->fl_type == F_UNLCK) {
+               if (state != NULL)
+                       return nfs4_proc_unlck(state, cmd, request);
+               return 0;
+       }
 
+       if (state == NULL)
+               return -ENOLCK;
        do {
                status = nfs4_proc_setlk(state, cmd, request);
                if ((status != -EAGAIN) || IS_SETLK(cmd))
@@ -3616,8 +4165,37 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
                goto out;
        do {
                err = _nfs4_do_setlk(state, F_SETLK, fl, 0);
-               if (err != -NFS4ERR_DELAY)
-                       break;
+               switch (err) {
+                       default:
+                               printk(KERN_ERR "%s: unhandled error %d.\n",
+                                               __func__, err);
+                       case 0:
+                       case -ESTALE:
+                               goto out;
+                       case -NFS4ERR_EXPIRED:
+                       case -NFS4ERR_STALE_CLIENTID:
+                       case -NFS4ERR_STALE_STATEID:
+                               nfs4_schedule_state_recovery(server->nfs_client);
+                               goto out;
+                       case -ERESTARTSYS:
+                               /*
+                                * The show must go on: exit, but mark the
+                                * stateid as needing recovery.
+                                */
+                       case -NFS4ERR_ADMIN_REVOKED:
+                       case -NFS4ERR_BAD_STATEID:
+                       case -NFS4ERR_OPENMODE:
+                               nfs4_state_mark_reclaim_nograce(server->nfs_client, state);
+                               err = 0;
+                               goto out;
+                       case -ENOMEM:
+                       case -NFS4ERR_DENIED:
+                               /* kill_proc(fl->fl_pid, SIGLOST, 1); */
+                               err = 0;
+                               goto out;
+                       case -NFS4ERR_DELAY:
+                               break;
+               }
                err = nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
 out:
@@ -3665,6 +4243,19 @@ ssize_t nfs4_listxattr(struct dentry *dentry, char *buf, size_t buflen)
        return len;
 }
 
+static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr)
+{
+       if (!((fattr->valid & NFS_ATTR_FATTR_FILEID) &&
+               (fattr->valid & NFS_ATTR_FATTR_FSID) &&
+               (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)))
+               return;
+
+       fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
+               NFS_ATTR_FATTR_NLINK;
+       fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+       fattr->nlink = 2;
+}
+
 int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
                struct nfs4_fs_locations *fs_locations, struct page *page)
 {
@@ -3679,10 +4270,13 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
                .page = page,
                .bitmask = bitmask,
        };
+       struct nfs4_fs_locations_res res = {
+               .fs_locations = fs_locations,
+       };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
                .rpc_argp = &args,
-               .rpc_resp = fs_locations,
+               .rpc_resp = &res,
        };
        int status;
 
@@ -3690,23 +4284,736 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
        nfs_fattr_init(&fs_locations->fattr);
        fs_locations->server = server;
        fs_locations->nlocations = 0;
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 0);
+       nfs_fixup_referral_attributes(&fs_locations->fattr);
        dprintk("%s: returned status = %d\n", __func__, status);
        return status;
 }
 
-struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = {
-       .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
-       .state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
-       .recover_open   = nfs4_open_reclaim,
-       .recover_lock   = nfs4_lock_reclaim,
-};
+#ifdef CONFIG_NFS_V4_1
+/*
+ * nfs4_proc_exchange_id()
+ *
+ * Since the clientid has expired, all compounds using sessions
+ * associated with the stale clientid will be returning
+ * NFS4ERR_BADSESSION in the sequence operation, and will therefore
+ * be in some phase of session reset.
+ */
+static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
+{
+       nfs4_verifier verifier;
+       struct nfs41_exchange_id_args args = {
+               .client = clp,
+               .flags = clp->cl_exchange_flags,
+       };
+       struct nfs41_exchange_id_res res = {
+               .client = clp,
+       };
+       int status;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+               .rpc_cred = cred,
+       };
+       __be32 *p;
 
-struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = {
-       .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
-       .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
-       .recover_open   = nfs4_open_expired,
-       .recover_lock   = nfs4_lock_expired,
+       dprintk("--> %s\n", __func__);
+       BUG_ON(clp == NULL);
+
+       p = (u32 *)verifier.data;
+       *p++ = htonl((u32)clp->cl_boot_time.tv_sec);
+       *p = htonl((u32)clp->cl_boot_time.tv_nsec);
+       args.verifier = &verifier;
+
+       while (1) {
+               args.id_len = scnprintf(args.id, sizeof(args.id),
+                                       "%s/%s %u",
+                                       clp->cl_ipaddr,
+                                       rpc_peeraddr2str(clp->cl_rpcclient,
+                                                        RPC_DISPLAY_ADDR),
+                                       clp->cl_id_uniquifier);
+
+               status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+
+               if (status != NFS4ERR_CLID_INUSE)
+                       break;
+
+               if (signalled())
+                       break;
+
+               if (++clp->cl_id_uniquifier == 0)
+                       break;
+       }
+
+       dprintk("<-- %s status= %d\n", __func__, status);
+       return status;
+}
+
+struct nfs4_get_lease_time_data {
+       struct nfs4_get_lease_time_args *args;
+       struct nfs4_get_lease_time_res *res;
+       struct nfs_client *clp;
+};
+
+static void nfs4_get_lease_time_prepare(struct rpc_task *task,
+                                       void *calldata)
+{
+       int ret;
+       struct nfs4_get_lease_time_data *data =
+                       (struct nfs4_get_lease_time_data *)calldata;
+
+       dprintk("--> %s\n", __func__);
+       /* just setup sequence, do not trigger session recovery
+          since we're invoked within one */
+       ret = nfs41_setup_sequence(data->clp->cl_session,
+                                       &data->args->la_seq_args,
+                                       &data->res->lr_seq_res, 0, task);
+
+       BUG_ON(ret == -EAGAIN);
+       rpc_call_start(task);
+       dprintk("<-- %s\n", __func__);
+}
+
+/*
+ * Called from nfs4_state_manager thread for session setup, so don't recover
+ * from sequence operation or clientid errors.
+ */
+static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs4_get_lease_time_data *data =
+                       (struct nfs4_get_lease_time_data *)calldata;
+
+       dprintk("--> %s\n", __func__);
+       nfs41_sequence_done(data->clp, &data->res->lr_seq_res, task->tk_status);
+       switch (task->tk_status) {
+       case -NFS4ERR_DELAY:
+       case -NFS4ERR_GRACE:
+               dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status);
+               rpc_delay(task, NFS4_POLL_RETRY_MIN);
+               task->tk_status = 0;
+               nfs4_restart_rpc(task, data->clp);
+               return;
+       }
+       nfs41_sequence_free_slot(data->clp, &data->res->lr_seq_res);
+       dprintk("<-- %s\n", __func__);
+}
+
+struct rpc_call_ops nfs4_get_lease_time_ops = {
+       .rpc_call_prepare = nfs4_get_lease_time_prepare,
+       .rpc_call_done = nfs4_get_lease_time_done,
+};
+
+int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
+{
+       struct rpc_task *task;
+       struct nfs4_get_lease_time_args args;
+       struct nfs4_get_lease_time_res res = {
+               .lr_fsinfo = fsinfo,
+       };
+       struct nfs4_get_lease_time_data data = {
+               .args = &args,
+               .res = &res,
+               .clp = clp,
+       };
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GET_LEASE_TIME],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+       };
+       struct rpc_task_setup task_setup = {
+               .rpc_client = clp->cl_rpcclient,
+               .rpc_message = &msg,
+               .callback_ops = &nfs4_get_lease_time_ops,
+               .callback_data = &data
+       };
+       int status;
+
+       res.lr_seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
+       dprintk("--> %s\n", __func__);
+       task = rpc_run_task(&task_setup);
+
+       if (IS_ERR(task))
+               status = PTR_ERR(task);
+       else {
+               status = task->tk_status;
+               rpc_put_task(task);
+       }
+       dprintk("<-- %s return %d\n", __func__, status);
+
+       return status;
+}
+
+/*
+ * Reset a slot table
+ */
+static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, int max_slots,
+               int old_max_slots, int ivalue)
+{
+       int i;
+       int ret = 0;
+
+       dprintk("--> %s: max_reqs=%u, tbl %p\n", __func__, max_slots, tbl);
+
+       /*
+        * Until we have dynamic slot table adjustment, insist
+        * upon the same slot table size
+        */
+       if (max_slots != old_max_slots) {
+               dprintk("%s reset slot table does't match old\n",
+                       __func__);
+               ret = -EINVAL; /*XXX NFS4ERR_REQ_TOO_BIG ? */
+               goto out;
+       }
+       spin_lock(&tbl->slot_tbl_lock);
+       for (i = 0; i < max_slots; ++i)
+               tbl->slots[i].seq_nr = ivalue;
+       tbl->highest_used_slotid = -1;
+       spin_unlock(&tbl->slot_tbl_lock);
+       dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
+               tbl, tbl->slots, tbl->max_slots);
+out:
+       dprintk("<-- %s: return %d\n", __func__, ret);
+       return ret;
+}
+
+/*
+ * Reset the forechannel and backchannel slot tables
+ */
+static int nfs4_reset_slot_tables(struct nfs4_session *session)
+{
+       int status;
+
+       status = nfs4_reset_slot_table(&session->fc_slot_table,
+                       session->fc_attrs.max_reqs,
+                       session->fc_slot_table.max_slots,
+                       1);
+       if (status)
+               return status;
+
+       status = nfs4_reset_slot_table(&session->bc_slot_table,
+                       session->bc_attrs.max_reqs,
+                       session->bc_slot_table.max_slots,
+                       0);
+       return status;
+}
+
+/* Destroy the slot table */
+static void nfs4_destroy_slot_tables(struct nfs4_session *session)
+{
+       if (session->fc_slot_table.slots != NULL) {
+               kfree(session->fc_slot_table.slots);
+               session->fc_slot_table.slots = NULL;
+       }
+       if (session->bc_slot_table.slots != NULL) {
+               kfree(session->bc_slot_table.slots);
+               session->bc_slot_table.slots = NULL;
+       }
+       return;
+}
+
+/*
+ * Initialize slot table
+ */
+static int nfs4_init_slot_table(struct nfs4_slot_table *tbl,
+               int max_slots, int ivalue)
+{
+       int i;
+       struct nfs4_slot *slot;
+       int ret = -ENOMEM;
+
+       BUG_ON(max_slots > NFS4_MAX_SLOT_TABLE);
+
+       dprintk("--> %s: max_reqs=%u\n", __func__, max_slots);
+
+       slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_KERNEL);
+       if (!slot)
+               goto out;
+       for (i = 0; i < max_slots; ++i)
+               slot[i].seq_nr = ivalue;
+       ret = 0;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       if (tbl->slots != NULL) {
+               spin_unlock(&tbl->slot_tbl_lock);
+               dprintk("%s: slot table already initialized. tbl=%p slots=%p\n",
+                       __func__, tbl, tbl->slots);
+               WARN_ON(1);
+               goto out_free;
+       }
+       tbl->max_slots = max_slots;
+       tbl->slots = slot;
+       tbl->highest_used_slotid = -1;  /* no slot is currently used */
+       spin_unlock(&tbl->slot_tbl_lock);
+       dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
+               tbl, tbl->slots, tbl->max_slots);
+out:
+       dprintk("<-- %s: return %d\n", __func__, ret);
+       return ret;
+
+out_free:
+       kfree(slot);
+       goto out;
+}
+
+/*
+ * Initialize the forechannel and backchannel tables
+ */
+static int nfs4_init_slot_tables(struct nfs4_session *session)
+{
+       int status;
+
+       status = nfs4_init_slot_table(&session->fc_slot_table,
+                       session->fc_attrs.max_reqs, 1);
+       if (status)
+               return status;
+
+       status = nfs4_init_slot_table(&session->bc_slot_table,
+                       session->bc_attrs.max_reqs, 0);
+       if (status)
+               nfs4_destroy_slot_tables(session);
+
+       return status;
+}
+
+struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
+{
+       struct nfs4_session *session;
+       struct nfs4_slot_table *tbl;
+
+       session = kzalloc(sizeof(struct nfs4_session), GFP_KERNEL);
+       if (!session)
+               return NULL;
+
+       set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
+       /*
+        * The create session reply races with the server back
+        * channel probe. Mark the client NFS_CS_SESSION_INITING
+        * so that the client back channel can find the
+        * nfs_client struct
+        */
+       clp->cl_cons_state = NFS_CS_SESSION_INITING;
+
+       tbl = &session->fc_slot_table;
+       spin_lock_init(&tbl->slot_tbl_lock);
+       rpc_init_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table");
+
+       tbl = &session->bc_slot_table;
+       spin_lock_init(&tbl->slot_tbl_lock);
+       rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table");
+
+       session->clp = clp;
+       return session;
+}
+
+void nfs4_destroy_session(struct nfs4_session *session)
+{
+       nfs4_proc_destroy_session(session);
+       dprintk("%s Destroy backchannel for xprt %p\n",
+               __func__, session->clp->cl_rpcclient->cl_xprt);
+       xprt_destroy_backchannel(session->clp->cl_rpcclient->cl_xprt,
+                               NFS41_BC_MIN_CALLBACKS);
+       nfs4_destroy_slot_tables(session);
+       kfree(session);
+}
+
+/*
+ * Initialize the values to be used by the client in CREATE_SESSION
+ * If nfs4_init_session set the fore channel request and response sizes,
+ * use them.
+ *
+ * Set the back channel max_resp_sz_cached to zero to force the client to
+ * always set csa_cachethis to FALSE because the current implementation
+ * of the back channel DRC only supports caching the CB_SEQUENCE operation.
+ */
+static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
+{
+       struct nfs4_session *session = args->client->cl_session;
+       unsigned int mxrqst_sz = session->fc_attrs.max_rqst_sz,
+                    mxresp_sz = session->fc_attrs.max_resp_sz;
+
+       if (mxrqst_sz == 0)
+               mxrqst_sz = NFS_MAX_FILE_IO_SIZE;
+       if (mxresp_sz == 0)
+               mxresp_sz = NFS_MAX_FILE_IO_SIZE;
+       /* Fore channel attributes */
+       args->fc_attrs.headerpadsz = 0;
+       args->fc_attrs.max_rqst_sz = mxrqst_sz;
+       args->fc_attrs.max_resp_sz = mxresp_sz;
+       args->fc_attrs.max_resp_sz_cached = mxresp_sz;
+       args->fc_attrs.max_ops = NFS4_MAX_OPS;
+       args->fc_attrs.max_reqs = session->clp->cl_rpcclient->cl_xprt->max_reqs;
+
+       dprintk("%s: Fore Channel : max_rqst_sz=%u max_resp_sz=%u "
+               "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n",
+               __func__,
+               args->fc_attrs.max_rqst_sz, args->fc_attrs.max_resp_sz,
+               args->fc_attrs.max_resp_sz_cached, args->fc_attrs.max_ops,
+               args->fc_attrs.max_reqs);
+
+       /* Back channel attributes */
+       args->bc_attrs.headerpadsz = 0;
+       args->bc_attrs.max_rqst_sz = PAGE_SIZE;
+       args->bc_attrs.max_resp_sz = PAGE_SIZE;
+       args->bc_attrs.max_resp_sz_cached = 0;
+       args->bc_attrs.max_ops = NFS4_MAX_BACK_CHANNEL_OPS;
+       args->bc_attrs.max_reqs = 1;
+
+       dprintk("%s: Back Channel : max_rqst_sz=%u max_resp_sz=%u "
+               "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n",
+               __func__,
+               args->bc_attrs.max_rqst_sz, args->bc_attrs.max_resp_sz,
+               args->bc_attrs.max_resp_sz_cached, args->bc_attrs.max_ops,
+               args->bc_attrs.max_reqs);
+}
+
+static int _verify_channel_attr(char *chan, char *attr_name, u32 sent, u32 rcvd)
+{
+       if (rcvd <= sent)
+               return 0;
+       printk(KERN_WARNING "%s: Session INVALID: %s channel %s increased. "
+               "sent=%u rcvd=%u\n", __func__, chan, attr_name, sent, rcvd);
+       return -EINVAL;
+}
+
+#define _verify_fore_channel_attr(_name_) \
+       _verify_channel_attr("fore", #_name_, \
+                            args->fc_attrs._name_, \
+                            session->fc_attrs._name_)
+
+#define _verify_back_channel_attr(_name_) \
+       _verify_channel_attr("back", #_name_, \
+                            args->bc_attrs._name_, \
+                            session->bc_attrs._name_)
+
+/*
+ * The server is not allowed to increase the fore channel header pad size,
+ * maximum response size, or maximum number of operations.
+ *
+ * The back channel attributes are only negotiatied down: We send what the
+ * (back channel) server insists upon.
+ */
+static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args,
+                                    struct nfs4_session *session)
+{
+       int ret = 0;
+
+       ret |= _verify_fore_channel_attr(headerpadsz);
+       ret |= _verify_fore_channel_attr(max_resp_sz);
+       ret |= _verify_fore_channel_attr(max_ops);
+
+       ret |= _verify_back_channel_attr(headerpadsz);
+       ret |= _verify_back_channel_attr(max_rqst_sz);
+       ret |= _verify_back_channel_attr(max_resp_sz);
+       ret |= _verify_back_channel_attr(max_resp_sz_cached);
+       ret |= _verify_back_channel_attr(max_ops);
+       ret |= _verify_back_channel_attr(max_reqs);
+
+       return ret;
+}
+
+static int _nfs4_proc_create_session(struct nfs_client *clp)
+{
+       struct nfs4_session *session = clp->cl_session;
+       struct nfs41_create_session_args args = {
+               .client = clp,
+               .cb_program = NFS4_CALLBACK,
+       };
+       struct nfs41_create_session_res res = {
+               .client = clp,
+       };
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+       };
+       int status;
+
+       nfs4_init_channel_attrs(&args);
+       args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN);
+
+       status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0);
+
+       if (!status)
+               /* Verify the session's negotiated channel_attrs values */
+               status = nfs4_verify_channel_attrs(&args, session);
+       if (!status) {
+               /* Increment the clientid slot sequence id */
+               clp->cl_seqid++;
+       }
+
+       return status;
+}
+
+/*
+ * Issues a CREATE_SESSION operation to the server.
+ * It is the responsibility of the caller to verify the session is
+ * expired before calling this routine.
+ */
+int nfs4_proc_create_session(struct nfs_client *clp, int reset)
+{
+       int status;
+       unsigned *ptr;
+       struct nfs_fsinfo fsinfo;
+       struct nfs4_session *session = clp->cl_session;
+
+       dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);
+
+       status = _nfs4_proc_create_session(clp);
+       if (status)
+               goto out;
+
+       /* Init or reset the fore channel */
+       if (reset)
+               status = nfs4_reset_slot_tables(session);
+       else
+               status = nfs4_init_slot_tables(session);
+       dprintk("fore channel slot table initialization returned %d\n", status);
+       if (status)
+               goto out;
+
+       ptr = (unsigned *)&session->sess_id.data[0];
+       dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__,
+               clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]);
+
+       if (reset)
+               /* Lease time is aleady set */
+               goto out;
+
+       /* Get the lease time */
+       status = nfs4_proc_get_lease_time(clp, &fsinfo);
+       if (status == 0) {
+               /* Update lease time and schedule renewal */
+               spin_lock(&clp->cl_lock);
+               clp->cl_lease_time = fsinfo.lease_time * HZ;
+               clp->cl_last_renewal = jiffies;
+               clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+               spin_unlock(&clp->cl_lock);
+
+               nfs4_schedule_state_renewal(clp);
+       }
+out:
+       dprintk("<-- %s\n", __func__);
+       return status;
+}
+
+/*
+ * Issue the over-the-wire RPC DESTROY_SESSION.
+ * The caller must serialize access to this routine.
+ */
+int nfs4_proc_destroy_session(struct nfs4_session *session)
+{
+       int status = 0;
+       struct rpc_message msg;
+
+       dprintk("--> nfs4_proc_destroy_session\n");
+
+       /* session is still being setup */
+       if (session->clp->cl_cons_state != NFS_CS_READY)
+               return status;
+
+       msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION];
+       msg.rpc_argp = session;
+       msg.rpc_resp = NULL;
+       msg.rpc_cred = NULL;
+       status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0);
+
+       if (status)
+               printk(KERN_WARNING
+                       "Got error %d from the server on DESTROY_SESSION. "
+                       "Session has been destroyed regardless...\n", status);
+
+       dprintk("<-- nfs4_proc_destroy_session\n");
+       return status;
+}
+
+int nfs4_init_session(struct nfs_server *server)
+{
+       struct nfs_client *clp = server->nfs_client;
+       int ret;
+
+       if (!nfs4_has_session(clp))
+               return 0;
+
+       clp->cl_session->fc_attrs.max_rqst_sz = server->wsize;
+       clp->cl_session->fc_attrs.max_resp_sz = server->rsize;
+       ret = nfs4_recover_expired_lease(server);
+       if (!ret)
+               ret = nfs4_check_client_ready(clp);
+       return ret;
+}
+
+/*
+ * Renew the cl_session lease.
+ */
+static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
+{
+       struct nfs4_sequence_args args;
+       struct nfs4_sequence_res res;
+
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+               .rpc_cred = cred,
+       };
+
+       args.sa_cache_this = 0;
+
+       return nfs4_call_sync_sequence(clp, clp->cl_rpcclient, &msg, &args,
+                                      &res, 0);
+}
+
+void nfs41_sequence_call_done(struct rpc_task *task, void *data)
+{
+       struct nfs_client *clp = (struct nfs_client *)data;
+
+       nfs41_sequence_done(clp, task->tk_msg.rpc_resp, task->tk_status);
+
+       if (task->tk_status < 0) {
+               dprintk("%s ERROR %d\n", __func__, task->tk_status);
+
+               if (_nfs4_async_handle_error(task, NULL, clp, NULL)
+                                                               == -EAGAIN) {
+                       nfs4_restart_rpc(task, clp);
+                       return;
+               }
+       }
+       nfs41_sequence_free_slot(clp, task->tk_msg.rpc_resp);
+       dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred);
+
+       put_rpccred(task->tk_msg.rpc_cred);
+       kfree(task->tk_msg.rpc_argp);
+       kfree(task->tk_msg.rpc_resp);
+
+       dprintk("<-- %s\n", __func__);
+}
+
+static void nfs41_sequence_prepare(struct rpc_task *task, void *data)
+{
+       struct nfs_client *clp;
+       struct nfs4_sequence_args *args;
+       struct nfs4_sequence_res *res;
+
+       clp = (struct nfs_client *)data;
+       args = task->tk_msg.rpc_argp;
+       res = task->tk_msg.rpc_resp;
+
+       if (nfs4_setup_sequence(clp, args, res, 0, task))
+               return;
+       rpc_call_start(task);
+}
+
+static const struct rpc_call_ops nfs41_sequence_ops = {
+       .rpc_call_done = nfs41_sequence_call_done,
+       .rpc_call_prepare = nfs41_sequence_prepare,
+};
+
+static int nfs41_proc_async_sequence(struct nfs_client *clp,
+                                    struct rpc_cred *cred)
+{
+       struct nfs4_sequence_args *args;
+       struct nfs4_sequence_res *res;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE],
+               .rpc_cred = cred,
+       };
+
+       args = kzalloc(sizeof(*args), GFP_KERNEL);
+       if (!args)
+               return -ENOMEM;
+       res = kzalloc(sizeof(*res), GFP_KERNEL);
+       if (!res) {
+               kfree(args);
+               return -ENOMEM;
+       }
+       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+       msg.rpc_argp = args;
+       msg.rpc_resp = res;
+
+       return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
+                             &nfs41_sequence_ops, (void *)clp);
+}
+
+#endif /* CONFIG_NFS_V4_1 */
+
+struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
+       .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
+       .state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
+       .recover_open   = nfs4_open_reclaim,
+       .recover_lock   = nfs4_lock_reclaim,
+       .establish_clid = nfs4_init_clientid,
+       .get_clid_cred  = nfs4_get_setclientid_cred,
+};
+
+#if defined(CONFIG_NFS_V4_1)
+struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
+       .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
+       .state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
+       .recover_open   = nfs4_open_reclaim,
+       .recover_lock   = nfs4_lock_reclaim,
+       .establish_clid = nfs4_proc_exchange_id,
+       .get_clid_cred  = nfs4_get_exchange_id_cred,
+};
+#endif /* CONFIG_NFS_V4_1 */
+
+struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
+       .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
+       .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
+       .recover_open   = nfs4_open_expired,
+       .recover_lock   = nfs4_lock_expired,
+       .establish_clid = nfs4_init_clientid,
+       .get_clid_cred  = nfs4_get_setclientid_cred,
+};
+
+#if defined(CONFIG_NFS_V4_1)
+struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = {
+       .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
+       .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
+       .recover_open   = nfs4_open_expired,
+       .recover_lock   = nfs4_lock_expired,
+       .establish_clid = nfs4_proc_exchange_id,
+       .get_clid_cred  = nfs4_get_exchange_id_cred,
+};
+#endif /* CONFIG_NFS_V4_1 */
+
+struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = {
+       .sched_state_renewal = nfs4_proc_async_renew,
+       .get_state_renewal_cred_locked = nfs4_get_renew_cred_locked,
+       .renew_lease = nfs4_proc_renew,
+};
+
+#if defined(CONFIG_NFS_V4_1)
+struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
+       .sched_state_renewal = nfs41_proc_async_sequence,
+       .get_state_renewal_cred_locked = nfs4_get_machine_cred_locked,
+       .renew_lease = nfs4_proc_sequence,
+};
+#endif
+
+/*
+ * Per minor version reboot and network partition recovery ops
+ */
+
+struct nfs4_state_recovery_ops *nfs4_reboot_recovery_ops[] = {
+       &nfs40_reboot_recovery_ops,
+#if defined(CONFIG_NFS_V4_1)
+       &nfs41_reboot_recovery_ops,
+#endif
+};
+
+struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[] = {
+       &nfs40_nograce_recovery_ops,
+#if defined(CONFIG_NFS_V4_1)
+       &nfs41_nograce_recovery_ops,
+#endif
+};
+
+struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[] = {
+       &nfs40_state_renewal_ops,
+#if defined(CONFIG_NFS_V4_1)
+       &nfs41_state_renewal_ops,
+#endif
 };
 
 static const struct inode_operations nfs4_file_inode_operations = {
@@ -3754,6 +5061,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .commit_done    = nfs4_commit_done,
        .lock           = nfs4_proc_lock,
        .clear_acl_cache = nfs4_zap_acl_attr,
+       .close_context  = nfs4_close_context,
 };
 
 /*