NFSv4.1: pnfs: add LAYOUTGET and GETDEVICEINFO infrastructure
[linux-2.6-block.git] / fs / nfs / nfs4proc.c
index a5f1edb45b47068a60bb0ce08a243a5ed8a694c1..7e14e991ddfafff86da1381e9bbc4730651d1fd6 100644 (file)
@@ -55,6 +55,7 @@
 #include "internal.h"
 #include "iostat.h"
 #include "callback.h"
+#include "pnfs.h"
 
 #define NFSDBG_FACILITY                NFSDBG_PROC
 
@@ -5256,6 +5257,147 @@ out:
        dprintk("<-- %s status=%d\n", __func__, status);
        return status;
 }
+
+static void
+nfs4_layoutget_prepare(struct rpc_task *task, void *calldata)
+{
+       struct nfs4_layoutget *lgp = calldata;
+       struct inode *ino = lgp->args.inode;
+       struct nfs_server *server = NFS_SERVER(ino);
+
+       dprintk("--> %s\n", __func__);
+       if (nfs4_setup_sequence(server, &lgp->args.seq_args,
+                               &lgp->res.seq_res, 0, task))
+               return;
+       rpc_call_start(task);
+}
+
+static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs4_layoutget *lgp = calldata;
+       struct nfs_server *server = NFS_SERVER(lgp->args.inode);
+
+       dprintk("--> %s\n", __func__);
+
+       if (!nfs4_sequence_done(task, &lgp->res.seq_res))
+               return;
+
+       switch (task->tk_status) {
+       case 0:
+               break;
+       case -NFS4ERR_LAYOUTTRYLATER:
+       case -NFS4ERR_RECALLCONFLICT:
+               task->tk_status = -NFS4ERR_DELAY;
+               /* Fall through */
+       default:
+               if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
+                       rpc_restart_call_prepare(task);
+                       return;
+               }
+       }
+       lgp->status = task->tk_status;
+       dprintk("<-- %s\n", __func__);
+}
+
+static void nfs4_layoutget_release(void *calldata)
+{
+       struct nfs4_layoutget *lgp = calldata;
+
+       dprintk("--> %s\n", __func__);
+       put_layout_hdr(lgp->args.inode);
+       if (lgp->res.layout.buf != NULL)
+               free_page((unsigned long) lgp->res.layout.buf);
+       put_nfs_open_context(lgp->args.ctx);
+       kfree(calldata);
+       dprintk("<-- %s\n", __func__);
+}
+
+static const struct rpc_call_ops nfs4_layoutget_call_ops = {
+       .rpc_call_prepare = nfs4_layoutget_prepare,
+       .rpc_call_done = nfs4_layoutget_done,
+       .rpc_release = nfs4_layoutget_release,
+};
+
+int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
+{
+       struct nfs_server *server = NFS_SERVER(lgp->args.inode);
+       struct rpc_task *task;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
+               .rpc_argp = &lgp->args,
+               .rpc_resp = &lgp->res,
+       };
+       struct rpc_task_setup task_setup_data = {
+               .rpc_client = server->client,
+               .rpc_message = &msg,
+               .callback_ops = &nfs4_layoutget_call_ops,
+               .callback_data = lgp,
+               .flags = RPC_TASK_ASYNC,
+       };
+       int status = 0;
+
+       dprintk("--> %s\n", __func__);
+
+       lgp->res.layout.buf = (void *)__get_free_page(GFP_NOFS);
+       if (lgp->res.layout.buf == NULL) {
+               nfs4_layoutget_release(lgp);
+               return -ENOMEM;
+       }
+
+       lgp->res.seq_res.sr_slot = NULL;
+       task = rpc_run_task(&task_setup_data);
+       if (IS_ERR(task))
+               return PTR_ERR(task);
+       status = nfs4_wait_for_completion_rpc_task(task);
+       if (status != 0)
+               goto out;
+       status = lgp->status;
+       if (status != 0)
+               goto out;
+       status = pnfs_layout_process(lgp);
+out:
+       rpc_put_task(task);
+       dprintk("<-- %s status=%d\n", __func__, status);
+       return status;
+}
+
+static int
+_nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
+{
+       struct nfs4_getdeviceinfo_args args = {
+               .pdev = pdev,
+       };
+       struct nfs4_getdeviceinfo_res res = {
+               .pdev = pdev,
+       };
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+       };
+       int status;
+
+       dprintk("--> %s\n", __func__);
+       status = nfs4_call_sync(server, &msg, &args, &res, 0);
+       dprintk("<-- %s status=%d\n", __func__, status);
+
+       return status;
+}
+
+int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
+{
+       struct nfs4_exception exception = { };
+       int err;
+
+       do {
+               err = nfs4_handle_exception(server,
+                                       _nfs4_proc_getdeviceinfo(server, pdev),
+                                       &exception);
+       } while (exception.retry);
+       return err;
+}
+EXPORT_SYMBOL_GPL(nfs4_proc_getdeviceinfo);
+
 #endif /* CONFIG_NFS_V4_1 */
 
 struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {