NFS: client needs to maintain list of inodes with active layouts
[linux-2.6-block.git] / fs / nfs / nfs4proc.c
index 70015dd60a9881f16318c78914996102d926f061..a5f1edb45b47068a60bb0ce08a243a5ed8a694c1 100644 (file)
@@ -129,7 +129,8 @@ const u32 nfs4_fsinfo_bitmap[2] = { FATTR4_WORD0_MAXFILESIZE
                        | FATTR4_WORD0_MAXREAD
                        | FATTR4_WORD0_MAXWRITE
                        | FATTR4_WORD0_LEASE_TIME,
-                       0
+                       FATTR4_WORD1_TIME_DELTA
+                       | FATTR4_WORD1_FS_LAYOUT_TYPES
 };
 
 const u32 nfs4_fs_locations_bitmap[2] = {
@@ -255,9 +256,6 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode,
                        nfs4_state_mark_reclaim_nograce(clp, state);
                        goto do_state_recovery;
                case -NFS4ERR_STALE_STATEID:
-                       if (state == NULL)
-                               break;
-                       nfs4_state_mark_reclaim_reboot(clp, state);
                case -NFS4ERR_STALE_CLIENTID:
                case -NFS4ERR_EXPIRED:
                        goto do_state_recovery;
@@ -303,15 +301,19 @@ do_state_recovery:
 }
 
 
-static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
+static void do_renew_lease(struct nfs_client *clp, unsigned long timestamp)
 {
-       struct nfs_client *clp = server->nfs_client;
        spin_lock(&clp->cl_lock);
        if (time_before(clp->cl_last_renewal,timestamp))
                clp->cl_last_renewal = timestamp;
        spin_unlock(&clp->cl_lock);
 }
 
+static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
+{
+       do_renew_lease(server->nfs_client, timestamp);
+}
+
 #if defined(CONFIG_NFS_V4_1)
 
 /*
@@ -330,10 +332,12 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp
  * Must be called while holding tbl->slot_tbl_lock
  */
 static void
-nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid)
+nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *free_slot)
 {
+       int free_slotid = free_slot - tbl->slots;
        int slotid = free_slotid;
 
+       BUG_ON(slotid < 0 || slotid >= NFS4_MAX_SLOT_TABLE);
        /* clear used bit in bitmap */
        __clear_bit(slotid, tbl->used_slots);
 
@@ -356,7 +360,7 @@ static void nfs41_check_drain_session_complete(struct nfs4_session *ses)
 {
        struct rpc_task *task;
 
-       if (!test_bit(NFS4CLNT_SESSION_DRAINING, &ses->clp->cl_state)) {
+       if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state)) {
                task = rpc_wake_up_next(&ses->fc_slot_table.slot_tbl_waitq);
                if (task)
                        rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
@@ -370,13 +374,12 @@ static void nfs41_check_drain_session_complete(struct nfs4_session *ses)
        complete(&ses->complete);
 }
 
-static void nfs41_sequence_free_slot(const struct nfs_client *clp,
-                             struct nfs4_sequence_res *res)
+static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
 {
        struct nfs4_slot_table *tbl;
 
-       tbl = &clp->cl_session->fc_slot_table;
-       if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) {
+       tbl = &res->sr_session->fc_slot_table;
+       if (!res->sr_slot) {
                /* just wake up the next guy waiting since
                 * we may have not consumed a slot after all */
                dprintk("%s: No slot\n", __func__);
@@ -384,19 +387,16 @@ static void nfs41_sequence_free_slot(const struct nfs_client *clp,
        }
 
        spin_lock(&tbl->slot_tbl_lock);
-       nfs4_free_slot(tbl, res->sr_slotid);
-       nfs41_check_drain_session_complete(clp->cl_session);
+       nfs4_free_slot(tbl, res->sr_slot);
+       nfs41_check_drain_session_complete(res->sr_session);
        spin_unlock(&tbl->slot_tbl_lock);
-       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+       res->sr_slot = NULL;
 }
 
-static void nfs41_sequence_done(struct nfs_client *clp,
-                               struct nfs4_sequence_res *res,
-                               int rpc_status)
+static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
 {
        unsigned long timestamp;
-       struct nfs4_slot_table *tbl;
-       struct nfs4_slot *slot;
+       struct nfs_client *clp;
 
        /*
         * sr_status remains 1 if an RPC level error occurred. The server
@@ -408,28 +408,53 @@ static void nfs41_sequence_done(struct nfs_client *clp,
                res->sr_status = NFS_OK;
 
        /* -ERESTARTSYS can result in skipping nfs41_sequence_setup */
-       if (res->sr_slotid == NFS4_MAX_SLOT_TABLE)
+       if (!res->sr_slot)
                goto out;
 
        /* Check the SEQUENCE operation status */
-       if (res->sr_status == 0) {
-               tbl = &clp->cl_session->fc_slot_table;
-               slot = tbl->slots + res->sr_slotid;
+       switch (res->sr_status) {
+       case 0:
                /* Update the slot's sequence and clientid lease timer */
-               ++slot->seq_nr;
+               ++res->sr_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);
+               clp = res->sr_session->clp;
+               do_renew_lease(clp, timestamp);
                /* Check sequence flags */
                if (atomic_read(&clp->cl_count) > 1)
                        nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
+               break;
+       case -NFS4ERR_DELAY:
+               /* The server detected a resend of the RPC call and
+                * returned NFS4ERR_DELAY as per Section 2.10.6.2
+                * of RFC5661.
+                */
+               dprintk("%s: slot=%ld seq=%d: Operation in progress\n",
+                       __func__,
+                       res->sr_slot - res->sr_session->fc_slot_table.slots,
+                       res->sr_slot->seq_nr);
+               goto out_retry;
+       default:
+               /* Just update the slot sequence no. */
+               ++res->sr_slot->seq_nr;
        }
 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);
+       nfs41_sequence_free_slot(res);
+       return 1;
+out_retry:
+       if (!rpc_restart_call(task))
+               goto out;
+       rpc_delay(task, NFS4_POLL_RETRY_MAX);
+       return 0;
+}
+
+static int nfs4_sequence_done(struct rpc_task *task,
+                              struct nfs4_sequence_res *res)
+{
+       if (res->sr_session == NULL)
+               return 1;
+       return nfs41_sequence_done(task, res);
 }
 
 /*
@@ -477,15 +502,13 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
 
        dprintk("--> %s\n", __func__);
        /* slot already allocated? */
-       if (res->sr_slotid != NFS4_MAX_SLOT_TABLE)
+       if (res->sr_slot != NULL)
                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_DRAINING, &session->clp->cl_state) &&
+       if (test_bit(NFS4_SESSION_DRAINING, &session->session_state) &&
            !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) {
                /*
                 * The state manager will wait until the slot table is empty.
@@ -523,8 +546,9 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
        dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr);
 
        res->sr_session = session;
-       res->sr_slotid = slotid;
+       res->sr_slot = slot;
        res->sr_renewal_time = jiffies;
+       res->sr_status_flags = 0;
        /*
         * sr_status is only set in decode_sequence, and so will remain
         * set to 1 if an rpc level failure occurs.
@@ -533,33 +557,34 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
        return 0;
 }
 
-int nfs4_setup_sequence(struct nfs_client *clp,
+int nfs4_setup_sequence(const struct nfs_server *server,
                        struct nfs4_sequence_args *args,
                        struct nfs4_sequence_res *res,
                        int cache_reply,
                        struct rpc_task *task)
 {
+       struct nfs4_session *session = nfs4_get_session(server);
        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))
+       if (session == NULL) {
+               args->sa_session = NULL;
+               res->sr_session = NULL;
                goto out;
-       ret = nfs41_setup_sequence(clp->cl_session, args, res, cache_reply,
-                                  task);
-       if (ret && ret != -EAGAIN) {
-               /* terminate rpc task */
-               task->tk_status = ret;
-               task->tk_action = NULL;
        }
+
+       dprintk("--> %s clp %p session %p sr_slot %ld\n",
+               __func__, session->clp, session, res->sr_slot ?
+                       res->sr_slot - session->fc_slot_table.slots : -1);
+
+       ret = nfs41_setup_sequence(session, args, res, cache_reply,
+                                  task);
 out:
        dprintk("<-- %s status=%d\n", __func__, ret);
        return ret;
 }
 
 struct nfs41_call_sync_data {
-       struct nfs_client *clp;
+       const struct nfs_server *seq_server;
        struct nfs4_sequence_args *seq_args;
        struct nfs4_sequence_res *seq_res;
        int cache_reply;
@@ -569,9 +594,9 @@ 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,
+       dprintk("--> %s data->seq_server %p\n", __func__, data->seq_server);
+
+       if (nfs4_setup_sequence(data->seq_server, data->seq_args,
                                data->seq_res, data->cache_reply, task))
                return;
        rpc_call_start(task);
@@ -587,7 +612,7 @@ 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_done(task, data->seq_res);
 }
 
 struct rpc_call_ops nfs41_call_sync_ops = {
@@ -600,8 +625,7 @@ struct rpc_call_ops nfs41_call_priv_sync_ops = {
        .rpc_call_done = nfs41_call_sync_done,
 };
 
-static int nfs4_call_sync_sequence(struct nfs_client *clp,
-                                  struct rpc_clnt *clnt,
+static int nfs4_call_sync_sequence(struct nfs_server *server,
                                   struct rpc_message *msg,
                                   struct nfs4_sequence_args *args,
                                   struct nfs4_sequence_res *res,
@@ -611,19 +635,19 @@ static int nfs4_call_sync_sequence(struct nfs_client *clp,
        int ret;
        struct rpc_task *task;
        struct nfs41_call_sync_data data = {
-               .clp = clp,
+               .seq_server = server,
                .seq_args = args,
                .seq_res = res,
                .cache_reply = cache_reply,
        };
        struct rpc_task_setup task_setup = {
-               .rpc_client = clnt,
+               .rpc_client = server->client,
                .rpc_message = msg,
                .callback_ops = &nfs41_call_sync_ops,
                .callback_data = &data
        };
 
-       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+       res->sr_slot = NULL;
        if (privileged)
                task_setup.callback_ops = &nfs41_call_priv_sync_ops;
        task = rpc_run_task(&task_setup);
@@ -642,10 +666,15 @@ int _nfs4_call_sync_session(struct nfs_server *server,
                            struct nfs4_sequence_res *res,
                            int cache_reply)
 {
-       return nfs4_call_sync_sequence(server->nfs_client, server->client,
-                                      msg, args, res, cache_reply, 0);
+       return nfs4_call_sync_sequence(server, msg, args, res, cache_reply, 0);
 }
 
+#else
+static int nfs4_sequence_done(struct rpc_task *task,
+                              struct nfs4_sequence_res *res)
+{
+       return 1;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 int _nfs4_call_sync(struct nfs_server *server,
@@ -659,18 +688,9 @@ int _nfs4_call_sync(struct nfs_server *server,
 }
 
 #define nfs4_call_sync(server, msg, args, res, cache_reply) \
-       (server)->nfs_client->cl_call_sync((server), (msg), &(args)->seq_args, \
+       (server)->nfs_client->cl_mvops->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 */
-}
-
 static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
 {
        struct nfs_inode *nfsi = NFS_I(dir);
@@ -712,7 +732,6 @@ 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,
@@ -745,19 +764,14 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
        p->o_arg.server = server;
        p->o_arg.bitmask = server->attr_bitmask;
        p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
-       if (flags & O_EXCL) {
-               if (nfs4_has_persistent_session(server->nfs_client)) {
-                       /* GUARDED */
-                       p->o_arg.u.attrs = &p->attrs;
-                       memcpy(&p->attrs, attrs, sizeof(p->attrs));
-               } else { /* EXCLUSIVE4_1 */
-                       u32 *s = (u32 *) p->o_arg.u.verifier.data;
-                       s[0] = jiffies;
-                       s[1] = current->pid;
-               }
-       } else if (flags & O_CREAT) {
+       if (flags & O_CREAT) {
+               u32 *s;
+
                p->o_arg.u.attrs = &p->attrs;
                memcpy(&p->attrs, attrs, sizeof(p->attrs));
+               s = (u32 *) p->o_arg.u.verifier.data;
+               s[0] = jiffies;
+               s[1] = current->pid;
        }
        p->c_arg.fh = &p->o_res.fh;
        p->c_arg.stateid = &p->o_res.stateid;
@@ -1102,6 +1116,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
        clear_bit(NFS_DELEGATED_STATE, &state->flags);
        smp_rmb();
        if (state->n_rdwr != 0) {
+               clear_bit(NFS_O_RDWR_STATE, &state->flags);
                ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate);
                if (ret != 0)
                        return ret;
@@ -1109,6 +1124,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
                        return -ESTALE;
        }
        if (state->n_wronly != 0) {
+               clear_bit(NFS_O_WRONLY_STATE, &state->flags);
                ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate);
                if (ret != 0)
                        return ret;
@@ -1116,6 +1132,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
                        return -ESTALE;
        }
        if (state->n_rdonly != 0) {
+               clear_bit(NFS_O_RDONLY_STATE, &state->flags);
                ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate);
                if (ret != 0)
                        return ret;
@@ -1170,7 +1187,7 @@ static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
        int err;
        do {
                err = _nfs4_do_open_reclaim(ctx, state);
-               if (err != -NFS4ERR_DELAY && err != -EKEYEXPIRED)
+               if (err != -NFS4ERR_DELAY)
                        break;
                nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
@@ -1240,6 +1257,13 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
                        case -NFS4ERR_ADMIN_REVOKED:
                        case -NFS4ERR_BAD_STATEID:
                                nfs4_state_mark_reclaim_nograce(server->nfs_client, state);
+                       case -EKEYEXPIRED:
+                               /*
+                                * User RPCSEC_GSS context has expired.
+                                * We cannot recover this stateid now, so
+                                * skip it and allow recovery thread to
+                                * proceed.
+                                */
                        case -ENOMEM:
                                err = 0;
                                goto out;
@@ -1255,8 +1279,6 @@ static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
        struct nfs4_opendata *data = calldata;
 
        data->rpc_status = task->tk_status;
-       if (RPC_ASSASSINATED(task))
-               return;
        if (data->rpc_status == 0) {
                memcpy(data->o_res.stateid.data, data->c_res.stateid.data,
                                sizeof(data->o_res.stateid.data));
@@ -1356,13 +1378,13 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
        }
        /* Update sequence id. */
        data->o_arg.id = sp->so_owner_id.id;
-       data->o_arg.clientid = sp->so_client->cl_clientid;
+       data->o_arg.clientid = sp->so_server->nfs_client->cl_clientid;
        if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) {
                task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
                nfs_copy_fh(&data->o_res.fh, data->o_arg.fh);
        }
        data->timestamp = jiffies;
-       if (nfs4_setup_sequence(data->o_arg.server->nfs_client,
+       if (nfs4_setup_sequence(data->o_arg.server,
                                &data->o_arg.seq_args,
                                &data->o_res.seq_res, 1, task))
                return;
@@ -1385,11 +1407,9 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata)
 
        data->rpc_status = task->tk_status;
 
-       nfs4_sequence_done(data->o_arg.server, &data->o_res.seq_res,
-                       task->tk_status);
-
-       if (RPC_ASSASSINATED(task))
+       if (!nfs4_sequence_done(task, &data->o_res.seq_res))
                return;
+
        if (task->tk_status == 0) {
                switch (data->o_res.f_attr->mode & S_IFMT) {
                        case S_IFREG:
@@ -1591,7 +1611,6 @@ static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state
                        goto out;
                case -NFS4ERR_GRACE:
                case -NFS4ERR_DELAY:
-               case -EKEYEXPIRED:
                        nfs4_handle_exception(server, err, &exception);
                        err = 0;
                }
@@ -1773,7 +1792,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        if (nfs4_copy_delegation_stateid(&arg.stateid, inode)) {
                /* Use that stateid */
        } else if (state != NULL) {
-               nfs4_copy_stateid(&arg.stateid, state, current->files);
+               nfs4_copy_stateid(&arg.stateid, state, current->files, current->tgid);
        } else
                memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
 
@@ -1838,8 +1857,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))
+       if (!nfs4_sequence_done(task, &calldata->res.seq_res))
                return;
         /* hmm. we are done with the inode, and in the process of freeing
         * the state_owner. we keep this around to process errors
@@ -1903,7 +1921,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
 
        nfs_fattr_init(calldata->res.fattr);
        calldata->timestamp = jiffies;
-       if (nfs4_setup_sequence((NFS_SERVER(calldata->inode))->nfs_client,
+       if (nfs4_setup_sequence(NFS_SERVER(calldata->inode),
                                &calldata->arg.seq_args, &calldata->res.seq_res,
                                1, task))
                return;
@@ -1962,7 +1980,6 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, i
        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;
        path_get(path);
        calldata->path = *path;
 
@@ -1985,118 +2002,17 @@ out:
        return status;
 }
 
-static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state, fmode_t fmode)
+static struct inode *
+nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr)
 {
-       struct file *filp;
-       int ret;
-
-       /* If the open_intent is for execute, we have an extra check to make */
-       if (fmode & FMODE_EXEC) {
-               ret = nfs_may_open(state->inode,
-                               state->owner->so_cred,
-                               nd->intent.open.flags);
-               if (ret < 0)
-                       goto out_close;
-       }
-       filp = lookup_instantiate_filp(nd, path->dentry, NULL);
-       if (!IS_ERR(filp)) {
-               struct nfs_open_context *ctx;
-               ctx = nfs_file_open_context(filp);
-               ctx->state = state;
-               return 0;
-       }
-       ret = PTR_ERR(filp);
-out_close:
-       nfs4_close_sync(path, state, fmode & (FMODE_READ|FMODE_WRITE));
-       return ret;
-}
-
-struct dentry *
-nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
-{
-       struct path path = {
-               .mnt = nd->path.mnt,
-               .dentry = dentry,
-       };
-       struct dentry *parent;
-       struct iattr attr;
-       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_umask();
-       } else {
-               attr.ia_valid = 0;
-               BUG_ON(nd->intent.open.flags & O_CREAT);
-       }
 
-       cred = rpc_lookup_cred();
-       if (IS_ERR(cred))
-               return (struct dentry *)cred;
-       parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
-       nfs_block_sillyrename(parent);
-       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) {
-                       d_add(dentry, NULL);
-                       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-               }
-               nfs_unblock_sillyrename(parent);
-               return (struct dentry *)state;
-       }
-       res = d_add_unique(dentry, igrab(state->inode));
-       if (res != NULL)
-               path.dentry = res;
-       nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir));
-       nfs_unblock_sillyrename(parent);
-       nfs4_intent_set_file(nd, &path, state, fmode);
-       return res;
-}
-
-int
-nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, struct nameidata *nd)
-{
-       struct path path = {
-               .mnt = nd->path.mnt,
-               .dentry = dentry,
-       };
-       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, fmode, openflags, NULL, cred);
-       put_rpccred(cred);
-       if (IS_ERR(state)) {
-               switch (PTR_ERR(state)) {
-                       case -EPERM:
-                       case -EACCES:
-                       case -EDQUOT:
-                       case -ENOSPC:
-                       case -EROFS:
-                               return PTR_ERR(state);
-                       default:
-                               goto out_drop;
-               }
-       }
-       if (state->inode == dentry->d_inode) {
-               nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-               nfs4_intent_set_file(nd, &path, state, fmode);
-               return 1;
-       }
-       nfs4_close_sync(&path, state, fmode);
-out_drop:
-       d_drop(dentry);
-       return 0;
+       state = nfs4_do_open(dir, &ctx->path, ctx->mode, open_flags, attr, ctx->cred);
+       if (IS_ERR(state))
+               return ERR_CAST(state);
+       ctx->state = state;
+       return igrab(state->inode);
 }
 
 static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
@@ -2260,8 +2176,7 @@ static int nfs4_get_referral(struct inode *dir, const struct qstr *name, struct
 out:
        if (page)
                __free_page(page);
-       if (locations)
-               kfree(locations);
+       kfree(locations);
        return status;
 }
 
@@ -2554,36 +2469,34 @@ static int nfs4_proc_readlink(struct inode *inode, struct page *page,
 
 static int
 nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
-                 int flags, struct nameidata *nd)
+                 int flags, struct nfs_open_context *ctx)
 {
-       struct path path = {
-               .mnt = nd->path.mnt,
+       struct path my_path = {
                .dentry = dentry,
        };
+       struct path *path = &my_path;
        struct nfs4_state *state;
-       struct rpc_cred *cred;
-       fmode_t fmode = flags & (FMODE_READ | FMODE_WRITE);
+       struct rpc_cred *cred = NULL;
+       fmode_t fmode = 0;
        int status = 0;
 
-       cred = rpc_lookup_cred();
-       if (IS_ERR(cred)) {
-               status = PTR_ERR(cred);
-               goto out;
+       if (ctx != NULL) {
+               cred = ctx->cred;
+               path = &ctx->path;
+               fmode = ctx->mode;
        }
-       state = nfs4_do_open(dir, &path, fmode, flags, sattr, cred);
+       state = nfs4_do_open(dir, path, fmode, flags, sattr, cred);
        d_drop(dentry);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
-               goto out_putcred;
+               goto out;
        }
        d_add(dentry, igrab(state->inode));
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
-               status = nfs4_intent_set_file(nd, &path, state, fmode);
+       if (ctx != NULL)
+               ctx->state = state;
        else
-               nfs4_close_sync(&path, state, fmode);
-out_putcred:
-       put_rpccred(cred);
+               nfs4_close_sync(path, state, fmode);
 out:
        return status;
 }
@@ -2641,6 +2554,7 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
 
        args->bitmask = server->cache_consistency_bitmask;
        res->server = server;
+       res->seq_res.sr_slot = NULL;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
 }
 
@@ -2648,7 +2562,8 @@ 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_sequence_done(task, &res->seq_res))
+               return 0;
        if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
                return 0;
        update_changeattr(dir, &res->cinfo);
@@ -2656,18 +2571,46 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
        return 1;
 }
 
+static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir)
+{
+       struct nfs_server *server = NFS_SERVER(dir);
+       struct nfs_renameargs *arg = msg->rpc_argp;
+       struct nfs_renameres *res = msg->rpc_resp;
+
+       msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
+       arg->bitmask = server->attr_bitmask;
+       res->server = server;
+}
+
+static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
+                                struct inode *new_dir)
+{
+       struct nfs_renameres *res = task->tk_msg.rpc_resp;
+
+       if (!nfs4_sequence_done(task, &res->seq_res))
+               return 0;
+       if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
+               return 0;
+
+       update_changeattr(old_dir, &res->old_cinfo);
+       nfs_post_op_update_inode(old_dir, res->old_fattr);
+       update_changeattr(new_dir, &res->new_cinfo);
+       nfs_post_op_update_inode(new_dir, res->new_fattr);
+       return 1;
+}
+
 static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
                struct inode *new_dir, struct qstr *new_name)
 {
        struct nfs_server *server = NFS_SERVER(old_dir);
-       struct nfs4_rename_arg arg = {
+       struct nfs_renameargs arg = {
                .old_dir = NFS_FH(old_dir),
                .new_dir = NFS_FH(new_dir),
                .old_name = old_name,
                .new_name = new_name,
                .bitmask = server->attr_bitmask,
        };
-       struct nfs4_rename_res res = {
+       struct nfs_renameres res = {
                .server = server,
        };
        struct rpc_message msg = {
@@ -2881,15 +2824,16 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
 }
 
 static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
-                  u64 cookie, struct page *page, unsigned int count, int plus)
+               u64 cookie, struct page **pages, unsigned int count, int plus)
 {
        struct inode            *dir = dentry->d_inode;
        struct nfs4_readdir_arg args = {
                .fh = NFS_FH(dir),
-               .pages = &page,
+               .pages = pages,
                .pgbase = 0,
                .count = count,
                .bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask,
+               .plus = plus,
        };
        struct nfs4_readdir_res res;
        struct rpc_message msg = {
@@ -2917,14 +2861,14 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
 }
 
 static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
-                  u64 cookie, struct page *page, unsigned int count, int plus)
+               u64 cookie, struct page **pages, unsigned int count, int plus)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
                err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode),
                                _nfs4_proc_readdir(dentry, cred, cookie,
-                                       page, count, plus),
+                                       pages, count, plus),
                                &exception);
        } while (exception.retry);
        return err;
@@ -3093,7 +3037,8 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
 
        dprintk("--> %s\n", __func__);
 
-       nfs4_sequence_done(server, &data->res.seq_res, task->tk_status);
+       if (!nfs4_sequence_done(task, &data->res.seq_res))
+               return -EAGAIN;
 
        if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
                nfs_restart_rpc(task, server->nfs_client);
@@ -3116,8 +3061,8 @@ static int nfs4_write_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_sequence_done(task, &data->res.seq_res))
+               return -EAGAIN;
 
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
                nfs_restart_rpc(task, NFS_SERVER(inode)->nfs_client);
@@ -3145,8 +3090,9 @@ 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_sequence_done(task, &data->res.seq_res))
+               return -EAGAIN;
+
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
                nfs_restart_rpc(task, NFS_SERVER(inode)->nfs_client);
                return -EAGAIN;
@@ -3196,10 +3142,7 @@ static void nfs4_renew_done(struct rpc_task *task, void *calldata)
                        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);
+       do_renew_lease(clp, timestamp);
 }
 
 static const struct rpc_call_ops nfs4_renew_ops = {
@@ -3240,10 +3183,7 @@ int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
        status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
        if (status < 0)
                return status;
-       spin_lock(&clp->cl_lock);
-       if (time_before(clp->cl_last_renewal,now))
-               clp->cl_last_renewal = now;
-       spin_unlock(&clp->cl_lock);
+       do_renew_lease(clp, now);
        return 0;
 }
 
@@ -3464,9 +3404,11 @@ 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 nfs_client *clp, struct nfs4_state *state)
+nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
 {
-       if (!clp || task->tk_status >= 0)
+       struct nfs_client *clp = server->nfs_client;
+
+       if (task->tk_status >= 0)
                return 0;
        switch(task->tk_status) {
                case -NFS4ERR_ADMIN_REVOKED:
@@ -3477,9 +3419,6 @@ _nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                        nfs4_state_mark_reclaim_nograce(clp, state);
                        goto do_state_recovery;
                case -NFS4ERR_STALE_STATEID:
-                       if (state == NULL)
-                               break;
-                       nfs4_state_mark_reclaim_reboot(clp, state);
                case -NFS4ERR_STALE_CLIENTID:
                case -NFS4ERR_EXPIRED:
                        goto do_state_recovery;
@@ -3498,8 +3437,7 @@ _nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                        return -EAGAIN;
 #endif /* CONFIG_NFS_V4_1 */
                case -NFS4ERR_DELAY:
-                       if (server)
-                               nfs_inc_server_stats(server, NFSIOS_DELAY);
+                       nfs_inc_server_stats(server, NFSIOS_DELAY);
                case -NFS4ERR_GRACE:
                case -EKEYEXPIRED:
                        rpc_delay(task, NFS4_POLL_RETRY_MAX);
@@ -3520,12 +3458,6 @@ do_state_recovery:
        return -EAGAIN;
 }
 
-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,
                struct nfs4_setclientid_res *res)
@@ -3620,7 +3552,6 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
                        case -NFS4ERR_RESOURCE:
                                /* The IBM lawyers misread another document! */
                        case -NFS4ERR_DELAY:
-                       case -EKEYEXPIRED:
                                err = nfs4_delay(clp->cl_rpcclient, &timeout);
                }
        } while (err == 0);
@@ -3641,8 +3572,8 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
 {
        struct nfs4_delegreturndata *data = calldata;
 
-       nfs4_sequence_done(data->res.server, &data->res.seq_res,
-                       task->tk_status);
+       if (!nfs4_sequence_done(task, &data->res.seq_res))
+               return;
 
        switch (task->tk_status) {
        case -NFS4ERR_STALE_STATEID:
@@ -3672,7 +3603,7 @@ static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data)
 
        d_data = (struct nfs4_delegreturndata *)data;
 
-       if (nfs4_setup_sequence(d_data->res.server->nfs_client,
+       if (nfs4_setup_sequence(d_data->res.server,
                                &d_data->args.seq_args,
                                &d_data->res.seq_res, 1, task))
                return;
@@ -3715,7 +3646,6 @@ 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;
@@ -3868,7 +3798,6 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
        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);
@@ -3892,9 +3821,7 @@ 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))
+       if (!nfs4_sequence_done(task, &calldata->res.seq_res))
                return;
        switch (task->tk_status) {
                case 0:
@@ -3927,7 +3854,7 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
                return;
        }
        calldata->timestamp = jiffies;
-       if (nfs4_setup_sequence(calldata->server->nfs_client,
+       if (nfs4_setup_sequence(calldata->server,
                                &calldata->arg.seq_args,
                                &calldata->res.seq_res, 1, task))
                return;
@@ -4050,7 +3977,6 @@ 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);
@@ -4082,7 +4008,8 @@ 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,
+       if (nfs4_setup_sequence(data->server,
+                               &data->arg.seq_args,
                                &data->res.seq_res, 1, task))
                return;
        rpc_call_start(task);
@@ -4101,12 +4028,10 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
 
        dprintk("%s: begin!\n", __func__);
 
-       nfs4_sequence_done(data->server, &data->res.seq_res,
-                       task->tk_status);
+       if (!nfs4_sequence_done(task, &data->res.seq_res))
+               return;
 
        data->rpc_status = task->tk_status;
-       if (RPC_ASSASSINATED(task))
-               goto out;
        if (data->arg.new_lock_owner != 0) {
                if (data->rpc_status == 0)
                        nfs_confirm_seqid(&data->lsp->ls_seqid, 0);
@@ -4238,7 +4163,7 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request
                if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
                        return 0;
                err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM);
-               if (err != -NFS4ERR_DELAY && err != -EKEYEXPIRED)
+               if (err != -NFS4ERR_DELAY)
                        break;
                nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
@@ -4263,7 +4188,6 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
                        goto out;
                case -NFS4ERR_GRACE:
                case -NFS4ERR_DELAY:
-               case -EKEYEXPIRED:
                        nfs4_handle_exception(server, err, &exception);
                        err = 0;
                }
@@ -4409,13 +4333,21 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
                                nfs4_state_mark_reclaim_nograce(server->nfs_client, state);
                                err = 0;
                                goto out;
+                       case -EKEYEXPIRED:
+                               /*
+                                * User RPCSEC_GSS context has expired.
+                                * We cannot recover this stateid now, so
+                                * skip it and allow recovery thread to
+                                * proceed.
+                                */
+                               err = 0;
+                               goto out;
                        case -ENOMEM:
                        case -NFS4ERR_DENIED:
                                /* kill_proc(fl->fl_pid, SIGLOST, 1); */
                                err = 0;
                                goto out;
                        case -NFS4ERR_DELAY:
-                       case -EKEYEXPIRED:
                                break;
                }
                err = nfs4_handle_exception(server, err, &exception);
@@ -4424,6 +4356,34 @@ out:
        return err;
 }
 
+static void nfs4_release_lockowner_release(void *calldata)
+{
+       kfree(calldata);
+}
+
+const struct rpc_call_ops nfs4_release_lockowner_ops = {
+       .rpc_release = nfs4_release_lockowner_release,
+};
+
+void nfs4_release_lockowner(const struct nfs4_lock_state *lsp)
+{
+       struct nfs_server *server = lsp->ls_state->owner->so_server;
+       struct nfs_release_lockowner_args *args;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER],
+       };
+
+       if (server->nfs_client->cl_mvops->minor_version != 0)
+               return;
+       args = kmalloc(sizeof(*args), GFP_NOFS);
+       if (!args)
+               return;
+       args->lock_owner.clientid = server->nfs_client->cl_clientid;
+       args->lock_owner.id = lsp->ls_id.id;
+       msg.rpc_argp = args;
+       rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, args);
+}
+
 #define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
 
 int nfs4_setxattr(struct dentry *dentry, const char *key, const void *buf,
@@ -4611,11 +4571,11 @@ static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
                        (struct nfs4_get_lease_time_data *)calldata;
 
        dprintk("--> %s\n", __func__);
-       nfs41_sequence_done(data->clp, &data->res->lr_seq_res, task->tk_status);
+       if (!nfs41_sequence_done(task, &data->res->lr_seq_res))
+               return;
        switch (task->tk_status) {
        case -NFS4ERR_DELAY:
        case -NFS4ERR_GRACE:
-       case -EKEYEXPIRED:
                dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status);
                rpc_delay(task, NFS4_POLL_RETRY_MIN);
                task->tk_status = 0;
@@ -4655,7 +4615,6 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
        };
        int status;
 
-       res.lr_seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        dprintk("--> %s\n", __func__);
        task = rpc_run_task(&task_setup);
 
@@ -4805,13 +4764,6 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
        if (!session)
                return NULL;
 
-       /*
-        * 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;
        init_completion(&session->complete);
 
        tbl = &session->fc_slot_table;
@@ -4824,6 +4776,8 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
        spin_lock_init(&tbl->slot_tbl_lock);
        rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table");
 
+       session->session_state = 1<<NFS4_SESSION_INITING;
+
        session->clp = clp;
        return session;
 }
@@ -5040,6 +4994,10 @@ int nfs4_init_session(struct nfs_server *server)
        if (!nfs4_has_session(clp))
                return 0;
 
+       session = clp->cl_session;
+       if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
+               return 0;
+
        rsize = server->rsize;
        if (rsize == 0)
                rsize = NFS_MAX_FILE_IO_SIZE;
@@ -5047,7 +5005,6 @@ int nfs4_init_session(struct nfs_server *server)
        if (wsize == 0)
                wsize = NFS_MAX_FILE_IO_SIZE;
 
-       session = clp->cl_session;
        session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
        session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
 
@@ -5060,69 +5017,69 @@ int nfs4_init_session(struct nfs_server *server)
 /*
  * Renew the cl_session lease.
  */
-static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
-{
+struct nfs4_sequence_data {
+       struct nfs_client *clp;
        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, args.sa_cache_this, 1);
-}
+};
 
 static void nfs41_sequence_release(void *data)
 {
-       struct nfs_client *clp = (struct nfs_client *)data;
+       struct nfs4_sequence_data *calldata = data;
+       struct nfs_client *clp = calldata->clp;
 
        if (atomic_read(&clp->cl_count) > 1)
                nfs4_schedule_state_renewal(clp);
        nfs_put_client(clp);
+       kfree(calldata);
+}
+
+static int nfs41_sequence_handle_errors(struct rpc_task *task, struct nfs_client *clp)
+{
+       switch(task->tk_status) {
+       case -NFS4ERR_DELAY:
+               rpc_delay(task, NFS4_POLL_RETRY_MAX);
+               return -EAGAIN;
+       default:
+               nfs4_schedule_state_recovery(clp);
+       }
+       return 0;
 }
 
 static void nfs41_sequence_call_done(struct rpc_task *task, void *data)
 {
-       struct nfs_client *clp = (struct nfs_client *)data;
+       struct nfs4_sequence_data *calldata = data;
+       struct nfs_client *clp = calldata->clp;
 
-       nfs41_sequence_done(clp, task->tk_msg.rpc_resp, task->tk_status);
+       if (!nfs41_sequence_done(task, task->tk_msg.rpc_resp))
+               return;
 
        if (task->tk_status < 0) {
                dprintk("%s ERROR %d\n", __func__, task->tk_status);
                if (atomic_read(&clp->cl_count) == 1)
                        goto out;
 
-               if (_nfs4_async_handle_error(task, NULL, clp, NULL)
-                                                               == -EAGAIN) {
-                       nfs_restart_rpc(task, clp);
+               if (nfs41_sequence_handle_errors(task, clp) == -EAGAIN) {
+                       rpc_restart_call_prepare(task);
                        return;
                }
        }
        dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred);
 out:
-       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_data *calldata = data;
+       struct nfs_client *clp = calldata->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))
+       if (nfs41_setup_sequence(clp->cl_session, args, res, 0, task))
                return;
        rpc_call_start(task);
 }
@@ -5133,32 +5090,66 @@ static const struct rpc_call_ops nfs41_sequence_ops = {
        .rpc_release = nfs41_sequence_release,
 };
 
-static int nfs41_proc_async_sequence(struct nfs_client *clp,
-                                    struct rpc_cred *cred)
+static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
 {
-       struct nfs4_sequence_args *args;
-       struct nfs4_sequence_res *res;
+       struct nfs4_sequence_data *calldata;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE],
                .rpc_cred = cred,
        };
+       struct rpc_task_setup task_setup_data = {
+               .rpc_client = clp->cl_rpcclient,
+               .rpc_message = &msg,
+               .callback_ops = &nfs41_sequence_ops,
+               .flags = RPC_TASK_ASYNC | RPC_TASK_SOFT,
+       };
 
        if (!atomic_inc_not_zero(&clp->cl_count))
-               return -EIO;
-       args = kzalloc(sizeof(*args), GFP_NOFS);
-       res = kzalloc(sizeof(*res), GFP_NOFS);
-       if (!args || !res) {
-               kfree(args);
-               kfree(res);
+               return ERR_PTR(-EIO);
+       calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
+       if (calldata == NULL) {
                nfs_put_client(clp);
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
        }
-       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
-       msg.rpc_argp = args;
-       msg.rpc_resp = res;
+       msg.rpc_argp = &calldata->args;
+       msg.rpc_resp = &calldata->res;
+       calldata->clp = clp;
+       task_setup_data.callback_data = calldata;
 
-       return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
-                             &nfs41_sequence_ops, (void *)clp);
+       return rpc_run_task(&task_setup_data);
+}
+
+static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cred)
+{
+       struct rpc_task *task;
+       int ret = 0;
+
+       task = _nfs41_proc_sequence(clp, cred);
+       if (IS_ERR(task))
+               ret = PTR_ERR(task);
+       else
+               rpc_put_task(task);
+       dprintk("<-- %s status=%d\n", __func__, ret);
+       return ret;
+}
+
+static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
+{
+       struct rpc_task *task;
+       int ret;
+
+       task = _nfs41_proc_sequence(clp, cred);
+       if (IS_ERR(task)) {
+               ret = PTR_ERR(task);
+               goto out;
+       }
+       ret = rpc_wait_for_completion_task(task);
+       if (!ret)
+               ret = task->tk_status;
+       rpc_put_task(task);
+out:
+       dprintk("<-- %s status=%d\n", __func__, ret);
+       return ret;
 }
 
 struct nfs4_reclaim_complete_data {
@@ -5172,13 +5163,30 @@ static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data)
        struct nfs4_reclaim_complete_data *calldata = data;
 
        rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
-       if (nfs4_setup_sequence(calldata->clp, &calldata->arg.seq_args,
+       if (nfs41_setup_sequence(calldata->clp->cl_session,
+                               &calldata->arg.seq_args,
                                &calldata->res.seq_res, 0, task))
                return;
 
        rpc_call_start(task);
 }
 
+static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nfs_client *clp)
+{
+       switch(task->tk_status) {
+       case 0:
+       case -NFS4ERR_COMPLETE_ALREADY:
+       case -NFS4ERR_WRONG_CRED: /* What to do here? */
+               break;
+       case -NFS4ERR_DELAY:
+               rpc_delay(task, NFS4_POLL_RETRY_MAX);
+               return -EAGAIN;
+       default:
+               nfs4_schedule_state_recovery(clp);
+       }
+       return 0;
+}
+
 static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data)
 {
        struct nfs4_reclaim_complete_data *calldata = data;
@@ -5186,32 +5194,13 @@ static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data)
        struct nfs4_sequence_res *res = &calldata->res.seq_res;
 
        dprintk("--> %s\n", __func__);
-       nfs41_sequence_done(clp, res, task->tk_status);
-       switch (task->tk_status) {
-       case 0:
-       case -NFS4ERR_COMPLETE_ALREADY:
-               break;
-       case -NFS4ERR_BADSESSION:
-       case -NFS4ERR_DEADSESSION:
-               /*
-                * Handle the session error, but do not retry the operation, as
-                * we have no way of telling whether the clientid had to be
-                * reset before we got our reply.  If reset, a new wave of
-                * reclaim operations will follow, containing their own reclaim
-                * complete.  We don't want our retry to get on the way of
-                * recovery by incorrectly indicating to the server that we're
-                * done reclaiming state since the process had to be restarted.
-                */
-               _nfs4_async_handle_error(task, NULL, clp, NULL);
-               break;
-       default:
-               if (_nfs4_async_handle_error(
-                               task, NULL, clp, NULL) == -EAGAIN) {
-                       rpc_restart_call_prepare(task);
-                       return;
-               }
-       }
+       if (!nfs41_sequence_done(task, res))
+               return;
 
+       if (nfs41_reclaim_complete_handle_errors(task, clp) == -EAGAIN) {
+               rpc_restart_call_prepare(task);
+               return;
+       }
        dprintk("<-- %s\n", __func__);
 }
 
@@ -5252,7 +5241,6 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp)
                goto out;
        calldata->clp = clp;
        calldata->arg.one_fs = 0;
-       calldata->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
 
        msg.rpc_argp = &calldata->arg;
        msg.rpc_resp = &calldata->res;
@@ -5325,28 +5313,30 @@ struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
 };
 #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
+static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
+       .minor_version = 0,
+       .call_sync = _nfs4_call_sync,
+       .validate_stateid = nfs4_validate_delegation_stateid,
+       .reboot_recovery_ops = &nfs40_reboot_recovery_ops,
+       .nograce_recovery_ops = &nfs40_nograce_recovery_ops,
+       .state_renewal_ops = &nfs40_state_renewal_ops,
 };
 
-struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[] = {
-       &nfs40_nograce_recovery_ops,
 #if defined(CONFIG_NFS_V4_1)
-       &nfs41_nograce_recovery_ops,
-#endif
+static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
+       .minor_version = 1,
+       .call_sync = _nfs4_call_sync_session,
+       .validate_stateid = nfs41_validate_delegation_stateid,
+       .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
+       .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
+       .state_renewal_ops = &nfs41_state_renewal_ops,
 };
+#endif
 
-struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[] = {
-       &nfs40_state_renewal_ops,
+const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
+       [0] = &nfs_v4_0_minor_ops,
 #if defined(CONFIG_NFS_V4_1)
-       &nfs41_state_renewal_ops,
+       [1] = &nfs_v4_1_minor_ops,
 #endif
 };
 
@@ -5376,6 +5366,8 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .unlink_setup   = nfs4_proc_unlink_setup,
        .unlink_done    = nfs4_proc_unlink_done,
        .rename         = nfs4_proc_rename,
+       .rename_setup   = nfs4_proc_rename_setup,
+       .rename_done    = nfs4_proc_rename_done,
        .link           = nfs4_proc_link,
        .symlink        = nfs4_proc_symlink,
        .mkdir          = nfs4_proc_mkdir,
@@ -5396,6 +5388,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .lock           = nfs4_proc_lock,
        .clear_acl_cache = nfs4_zap_acl_attr,
        .close_context  = nfs4_close_context,
+       .open_context   = nfs4_atomic_open,
 };
 
 /*