nfs41: Don't clear DRAINING flag on NFS4ERR_STALE_CLIENTID
[linux-2.6.git] / fs / nfs / nfs4proc.c
index c1bc9ca..fbae2c9 100644 (file)
@@ -270,7 +270,7 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode,
                case -NFS4ERR_SEQ_MISORDERED:
                        dprintk("%s ERROR: %d Reset session\n", __func__,
                                errorcode);
-                       set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+                       nfs4_schedule_state_recovery(clp);
                        exception->retry = 1;
                        break;
 #endif /* !defined(CONFIG_NFS_V4_1) */
@@ -318,59 +318,55 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp
  * 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.
+ *
+ * Must be called while holding tbl->slot_tbl_lock
  */
 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)
+               if (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,
+static 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);
+               dprintk("%s: No slot\n", __func__);
                return;
        }
+
+       spin_lock(&tbl->slot_tbl_lock);
        nfs4_free_slot(tbl, res->sr_slotid);
-       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
 
        /* Signal state manager thread if session is drained */
        if (test_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state)) {
-               spin_lock(&tbl->slot_tbl_lock);
                if (tbl->highest_used_slotid == -1) {
                        dprintk("%s COMPLETE: Session Drained\n", __func__);
                        complete(&clp->cl_session->complete);
                }
-               spin_unlock(&tbl->slot_tbl_lock);
-       }
+       } else
+               rpc_wake_up_next(&tbl->slot_tbl_waitq);
+       spin_unlock(&tbl->slot_tbl_lock);
+       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
 }
 
 static void nfs41_sequence_done(struct nfs_client *clp,
@@ -394,10 +390,10 @@ static void nfs41_sequence_done(struct nfs_client *clp,
        if (res->sr_slotid == NFS4_MAX_SLOT_TABLE)
                goto out;
 
-       tbl = &clp->cl_session->fc_slot_table;
-       slot = tbl->slots + res->sr_slotid;
-
+       /* Check the SEQUENCE operation status */
        if (res->sr_status == 0) {
+               tbl = &clp->cl_session->fc_slot_table;
+               slot = tbl->slots + res->sr_slotid;
                /* Update the slot's sequence and clientid lease timer */
                ++slot->seq_nr;
                timestamp = res->sr_renewal_time;
@@ -405,7 +401,8 @@ static void nfs41_sequence_done(struct nfs_client *clp,
                if (time_before(clp->cl_last_renewal, timestamp))
                        clp->cl_last_renewal = timestamp;
                spin_unlock(&clp->cl_lock);
-               return;
+               /* Check sequence flags */
+               nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
        }
 out:
        /* The session may be reset by one of the error handlers. */
@@ -466,15 +463,14 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
        tbl = &session->fc_slot_table;
 
        spin_lock(&tbl->slot_tbl_lock);
-       if (test_bit(NFS4CLNT_SESSION_RESET, &session->clp->cl_state)) {
+       if (test_bit(NFS4CLNT_SESSION_DRAINING, &session->clp->cl_state)) {
                /*
                 * The state manager will wait until the slot table is empty.
                 * Schedule the reset thread
                 */
-               dprintk("%s Schedule Session Reset\n", __func__);
                rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
-               nfs4_schedule_state_manager(session->clp);
                spin_unlock(&tbl->slot_tbl_lock);
+               dprintk("%s Schedule Session Reset\n", __func__);
                return -EAGAIN;
        }
 
@@ -554,7 +550,6 @@ 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 = {
@@ -630,12 +625,10 @@ static void nfs4_sequence_done(const struct nfs_server *server,
 #endif /* CONFIG_NFS_V4_1 */
 }
 
-void nfs4_restart_rpc(struct rpc_task *task, const struct nfs_client *clp,
-                     struct nfs4_sequence_res *res)
+void nfs4_restart_rpc(struct rpc_task *task, const struct nfs_client *clp)
 {
 #ifdef CONFIG_NFS_V4_1
        if (nfs4_has_session(clp)) {
-               nfs41_sequence_free_slot(clp, res);
                rpc_restart_call_prepare(task);
                return;
        }
@@ -643,15 +636,6 @@ void nfs4_restart_rpc(struct rpc_task *task, const struct nfs_client *clp,
        rpc_restart_call(task);
 }
 
-/* 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);
@@ -726,9 +710,15 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
        p->o_arg.bitmask = server->attr_bitmask;
        p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
        if (flags & O_EXCL) {
-               u32 *s = (u32 *) p->o_arg.u.verifier.data;
-               s[0] = jiffies;
-               s[1] = current->pid;
+               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) {
                p->o_arg.u.attrs = &p->attrs;
                memcpy(&p->attrs, attrs, sizeof(p->attrs));
@@ -1342,8 +1332,8 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata)
 
        data->rpc_status = task->tk_status;
 
-       nfs4_sequence_done_free_slot(data->o_arg.server, &data->o_res.seq_res,
-                                    task->tk_status);
+       nfs4_sequence_done(data->o_arg.server, &data->o_res.seq_res,
+                       task->tk_status);
 
        if (RPC_ASSASSINATED(task))
                return;
@@ -1749,12 +1739,10 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                                break;
                default:
                        if (nfs4_async_handle_error(task, server, state) == -EAGAIN) {
-                               nfs4_restart_rpc(task, server->nfs_client,
-                                                &calldata->res.seq_res);
+                               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);
 }
 
@@ -2545,7 +2533,6 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
        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;
@@ -2984,20 +2971,16 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
 
        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) {
-               nfs4_restart_rpc(task, server->nfs_client, &data->res.seq_res);
+               nfs4_restart_rpc(task, server->nfs_client);
                return -EAGAIN;
        }
 
        nfs_invalidate_atime(data->inode);
        if (task->tk_status > 0)
                renew_lease(server, data->timestamp);
-       else if (task->tk_status < 0)
-               nfs4_sequence_free_slot(server->nfs_client, &data->res.seq_res);
-
        return 0;
 }
 
@@ -3011,13 +2994,11 @@ 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) {
-               nfs4_restart_rpc(task, NFS_SERVER(inode)->nfs_client,
-                                &data->res.seq_res);
+               nfs4_restart_rpc(task, NFS_SERVER(inode)->nfs_client);
                return -EAGAIN;
        }
        if (task->tk_status >= 0) {
@@ -3045,12 +3026,9 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
        nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res,
                           task->tk_status);
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
-               nfs4_restart_rpc(task, NFS_SERVER(inode)->nfs_client,
-                                &data->res.seq_res);
+               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;
 }
@@ -3368,7 +3346,7 @@ _nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                case -NFS4ERR_SEQ_MISORDERED:
                        dprintk("%s ERROR %d, Reset session\n", __func__,
                                task->tk_status);
-                       set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+                       nfs4_schedule_state_recovery(clp);
                        task->tk_status = 0;
                        return -EAGAIN;
 #endif /* CONFIG_NFS_V4_1 */
@@ -3501,8 +3479,8 @@ 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);
+       nfs4_sequence_done(data->res.server, &data->res.seq_res,
+                       task->tk_status);
 
        data->rpc_status = task->tk_status;
        if (data->rpc_status == 0)
@@ -3760,11 +3738,8 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
                default:
                        if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
                                nfs4_restart_rpc(task,
-                                                calldata->server->nfs_client,
-                                                &calldata->res.seq_res);
+                                                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)
@@ -3946,8 +3921,8 @@ 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);
+       nfs4_sequence_done(data->server, &data->res.seq_res,
+                       task->tk_status);
 
        data->rpc_status = task->tk_status;
        if (RPC_ASSASSINATED(task))
@@ -4343,6 +4318,9 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
        dprintk("--> %s\n", __func__);
        BUG_ON(clp == NULL);
 
+       /* Remove server-only flags */
+       args.flags &= ~EXCHGID4_FLAG_CONFIRMED_R;
+
        p = (u32 *)verifier.data;
        *p++ = htonl((u32)clp->cl_boot_time.tv_sec);
        *p = htonl((u32)clp->cl_boot_time.tv_nsec);
@@ -4414,10 +4392,9 @@ static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
                dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status);
                rpc_delay(task, NFS4_POLL_RETRY_MIN);
                task->tk_status = 0;
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, data->clp);
                return;
        }
-       nfs41_sequence_free_slot(data->clp, &data->res->lr_seq_res);
        dprintk("<-- %s\n", __func__);
 }
 
@@ -4490,7 +4467,6 @@ static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, int max_slots,
        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);
@@ -4512,7 +4488,6 @@ static int nfs4_reset_slot_tables(struct nfs4_session *session)
                        1);
        if (status)
                return status;
-       init_completion(&session->complete);
 
        status = nfs4_reset_slot_table(&session->bc_slot_table,
                        session->bc_attrs.max_reqs,
@@ -4541,7 +4516,6 @@ static void nfs4_destroy_slot_tables(struct nfs4_session *session)
 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;
 
@@ -4552,18 +4526,9 @@ static int nfs4_init_slot_table(struct nfs4_slot_table *tbl,
        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 */
@@ -4573,10 +4538,6 @@ static int nfs4_init_slot_table(struct nfs4_slot_table *tbl,
 out:
        dprintk("<-- %s: return %d\n", __func__, ret);
        return ret;
-
-out_free:
-       kfree(slot);
-       goto out;
 }
 
 /*
@@ -4584,17 +4545,24 @@ out_free:
  */
 static int nfs4_init_slot_tables(struct nfs4_session *session)
 {
-       int status;
+       struct nfs4_slot_table *tbl;
+       int status = 0;
 
-       status = nfs4_init_slot_table(&session->fc_slot_table,
-                       session->fc_attrs.max_reqs, 1);
-       if (status)
-               return status;
+       tbl = &session->fc_slot_table;
+       if (tbl->slots == NULL) {
+               status = nfs4_init_slot_table(tbl,
+                               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);
+       tbl = &session->bc_slot_table;
+       if (tbl->slots == NULL) {
+               status = nfs4_init_slot_table(tbl,
+                               session->bc_attrs.max_reqs, 0);
+               if (status)
+                       nfs4_destroy_slot_tables(session);
+       }
 
        return status;
 }
@@ -4618,10 +4586,12 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
        init_completion(&session->complete);
 
        tbl = &session->fc_slot_table;
+       tbl->highest_used_slotid = -1;
        spin_lock_init(&tbl->slot_tbl_lock);
        rpc_init_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table");
 
        tbl = &session->bc_slot_table;
+       tbl->highest_used_slotid = -1;
        spin_lock_init(&tbl->slot_tbl_lock);
        rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table");
 
@@ -4773,11 +4743,10 @@ static int _nfs4_proc_create_session(struct nfs_client *clp)
  * 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 nfs4_proc_create_session(struct nfs_client *clp)
 {
        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);
@@ -4786,35 +4755,19 @@ int nfs4_proc_create_session(struct nfs_client *clp, int reset)
        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);
+       /* Init and reset the fore channel */
+       status = nfs4_init_slot_tables(session);
+       dprintk("slot table initialization returned %d\n", status);
+       if (status)
+               goto out;
+       status = nfs4_reset_slot_tables(session);
+       dprintk("slot table reset 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;
@@ -4853,13 +4806,16 @@ int nfs4_proc_destroy_session(struct nfs4_session *session)
 int nfs4_init_session(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
+       struct nfs4_session *session;
        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;
+       session = clp->cl_session;
+       session->fc_attrs.max_rqst_sz = server->wsize + nfs41_maxwrite_overhead;
+       session->fc_attrs.max_resp_sz = server->rsize + nfs41_maxread_overhead;
+
        ret = nfs4_recover_expired_lease(server);
        if (!ret)
                ret = nfs4_check_client_ready(clp);
@@ -4898,11 +4854,10 @@ void nfs41_sequence_call_done(struct rpc_task *task, void *data)
 
                if (_nfs4_async_handle_error(task, NULL, clp, NULL)
                                                                == -EAGAIN) {
-                       nfs4_restart_rpc(task, clp, task->tk_msg.rpc_resp);
+                       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);
 
        kfree(task->tk_msg.rpc_argp);
@@ -4957,6 +4912,109 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp,
                              &nfs41_sequence_ops, (void *)clp);
 }
 
+struct nfs4_reclaim_complete_data {
+       struct nfs_client *clp;
+       struct nfs41_reclaim_complete_args arg;
+       struct nfs41_reclaim_complete_res res;
+};
+
+static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data)
+{
+       struct nfs4_reclaim_complete_data *calldata = data;
+
+       if (nfs4_setup_sequence(calldata->clp, &calldata->arg.seq_args,
+                               &calldata->res.seq_res, 0, task))
+               return;
+
+       rpc_call_start(task);
+}
+
+static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data)
+{
+       struct nfs4_reclaim_complete_data *calldata = data;
+       struct nfs_client *clp = calldata->clp;
+       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;
+               }
+       }
+
+       dprintk("<-- %s\n", __func__);
+}
+
+static void nfs4_free_reclaim_complete_data(void *data)
+{
+       struct nfs4_reclaim_complete_data *calldata = data;
+
+       kfree(calldata);
+}
+
+static const struct rpc_call_ops nfs4_reclaim_complete_call_ops = {
+       .rpc_call_prepare = nfs4_reclaim_complete_prepare,
+       .rpc_call_done = nfs4_reclaim_complete_done,
+       .rpc_release = nfs4_free_reclaim_complete_data,
+};
+
+/*
+ * Issue a global reclaim complete.
+ */
+static int nfs41_proc_reclaim_complete(struct nfs_client *clp)
+{
+       struct nfs4_reclaim_complete_data *calldata;
+       struct rpc_task *task;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RECLAIM_COMPLETE],
+       };
+       struct rpc_task_setup task_setup_data = {
+               .rpc_client = clp->cl_rpcclient,
+               .rpc_message = &msg,
+               .callback_ops = &nfs4_reclaim_complete_call_ops,
+               .flags = RPC_TASK_ASYNC,
+       };
+       int status = -ENOMEM;
+
+       dprintk("--> %s\n", __func__);
+       calldata = kzalloc(sizeof(*calldata), GFP_KERNEL);
+       if (calldata == NULL)
+               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;
+       task_setup_data.callback_data = calldata;
+       task = rpc_run_task(&task_setup_data);
+       if (IS_ERR(task))
+               status = PTR_ERR(task);
+       rpc_put_task(task);
+out:
+       dprintk("<-- %s status=%d\n", __func__, status);
+       return status;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
@@ -4976,6 +5034,7 @@ struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
        .recover_lock   = nfs4_lock_reclaim,
        .establish_clid = nfs41_init_clientid,
        .get_clid_cred  = nfs4_get_exchange_id_cred,
+       .reclaim_complete = nfs41_proc_reclaim_complete,
 };
 #endif /* CONFIG_NFS_V4_1 */