nfs41: RECLAIM_COMPLETE functionality
authorRicardo Labiaga <Ricardo.Labiaga@netapp.com>
Sat, 5 Dec 2009 21:08:41 +0000 (16:08 -0500)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Sat, 5 Dec 2009 21:08:41 +0000 (16:08 -0500)
Implements RECLAIM_COMPLETE as an asynchronous RPC.
NFS4ERR_DELAY is retried, NFS4ERR_DEADSESSION invokes the error handling
but does not result in a retry, since we don't want to have a lingering
RECLAIM_COMPLETE call sent in the middle of a possible new state recovery
cycle.  If a session reset occurs, a new wave of reclaim operations will
follow, containing their own RECLAIM_COMPLETE call.  We don't want a
retry to get on the way of recovery by incorrectly indicating to the
server that we're done reclaiming state.

A subsequent patch invokes the functionality.

Signed-off-by: Ricardo Labiaga <Ricardo.Labiaga@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c

index 9a866368896a61571e1b46dddde468e56b7e2913..90923223a7c1ce01ce5192d3e78d5e4bbe9dd5ee 100644 (file)
@@ -181,6 +181,7 @@ struct nfs4_state_recovery_ops {
        int (*recover_lock)(struct nfs4_state *, struct file_lock *);
        int (*establish_clid)(struct nfs_client *, struct rpc_cred *);
        struct rpc_cred * (*get_clid_cred)(struct nfs_client *);
+       int (*reclaim_complete)(struct nfs_client *);
 };
 
 struct nfs4_state_maintenance_ops {
index 71993f122214c1de618b7221fda768de7d086646..206ce30a595d1b4c05e57853a595342ff3dac074 100644 (file)
@@ -4971,6 +4971,110 @@ 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;
+               }
+       }
+       nfs41_sequence_free_slot(clp, res);
+
+       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 = {
@@ -4990,6 +5094,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 */
 
index bc4ca6f4d8c6692c41488f1bf420fe6fb1bf27e5..f2d2dc497551dd1668d409a97790632a36f88e05 100644 (file)
@@ -1032,6 +1032,14 @@ static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
        nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot);
 }
 
+static void nfs4_reclaim_complete(struct nfs_client *clp,
+                                const struct nfs4_state_recovery_ops *ops)
+{
+       /* Notify the server we're done reclaiming our state */
+       if (ops->reclaim_complete)
+               (void)ops->reclaim_complete(clp);
+}
+
 static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
 {
        struct nfs4_state_owner *sp;