]> err.no Git - linux-2.6/blobdiff - fs/nfs/write.c
NFS: Cleanup of NFS write code in preparation for asynchronous o_direct
[linux-2.6] / fs / nfs / write.c
index e7c8361cf201b1277d465b76fd76d22450a96dda..5912274ff1a123ad1f96720ea668e3bde4e32840 100644 (file)
@@ -77,12 +77,14 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context*,
                                            struct inode *,
                                            struct page *,
                                            unsigned int, unsigned int);
-static void nfs_writeback_done_partial(struct nfs_write_data *, int);
-static void nfs_writeback_done_full(struct nfs_write_data *, int);
+static int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *);
 static int nfs_wait_on_write_congestion(struct address_space *, int);
 static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int);
 static int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
                           unsigned int npages, int how);
+static const struct rpc_call_ops nfs_write_partial_ops;
+static const struct rpc_call_ops nfs_write_full_ops;
+static const struct rpc_call_ops nfs_commit_ops;
 
 static kmem_cache_t *nfs_wdata_cachep;
 mempool_t *nfs_wdata_mempool;
@@ -872,10 +874,12 @@ static inline int flush_task_priority(int how)
  */
 static void nfs_write_rpcsetup(struct nfs_page *req,
                struct nfs_write_data *data,
+               const struct rpc_call_ops *call_ops,
                unsigned int count, unsigned int offset,
                int how)
 {
        struct inode            *inode;
+       int flags;
 
        /* Set up the RPC argument and reply structs
         * NB: take care not to mess about with data->commit et al. */
@@ -896,6 +900,9 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
        data->res.verf    = &data->verf;
        nfs_fattr_init(&data->fattr);
 
+       /* Set up the initial task struct.  */
+       flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
+       rpc_init_task(&data->task, NFS_CLIENT(inode), flags, call_ops, data);
        NFS_PROTO(inode)->write_setup(data, how);
 
        data->task.tk_priority = flush_task_priority(how);
@@ -959,14 +966,15 @@ static int nfs_flush_multi(struct list_head *head, struct inode *inode, int how)
                list_del_init(&data->pages);
 
                data->pagevec[0] = page;
-               data->complete = nfs_writeback_done_partial;
 
                if (nbytes > wsize) {
-                       nfs_write_rpcsetup(req, data, wsize, offset, how);
+                       nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
+                                       wsize, offset, how);
                        offset += wsize;
                        nbytes -= wsize;
                } else {
-                       nfs_write_rpcsetup(req, data, nbytes, offset, how);
+                       nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
+                                       nbytes, offset, how);
                        nbytes = 0;
                }
                nfs_execute_write(data);
@@ -1020,9 +1028,8 @@ static int nfs_flush_one(struct list_head *head, struct inode *inode, int how)
        }
        req = nfs_list_entry(data->pages.next);
 
-       data->complete = nfs_writeback_done_full;
        /* Set up the argument struct */
-       nfs_write_rpcsetup(req, data, count, 0, how);
+       nfs_write_rpcsetup(req, data, &nfs_write_full_ops, count, 0, how);
 
        nfs_execute_write(data);
        return 0;
@@ -1066,8 +1073,9 @@ nfs_flush_list(struct list_head *head, int wpages, int how)
 /*
  * Handle a write reply that flushed part of a page.
  */
-static void nfs_writeback_done_partial(struct nfs_write_data *data, int status)
+static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
 {
+       struct nfs_write_data   *data = calldata;
        struct nfs_page         *req = data->req;
        struct page             *page = req->wb_page;
 
@@ -1077,11 +1085,14 @@ static void nfs_writeback_done_partial(struct nfs_write_data *data, int status)
                req->wb_bytes,
                (long long)req_offset(req));
 
-       if (status < 0) {
+       if (nfs_writeback_done(task, data) != 0)
+               return;
+
+       if (task->tk_status < 0) {
                ClearPageUptodate(page);
                SetPageError(page);
-               req->wb_context->error = status;
-               dprintk(", error = %d\n", status);
+               req->wb_context->error = task->tk_status;
+               dprintk(", error = %d\n", task->tk_status);
        } else {
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
                if (data->verf.committed < NFS_FILE_SYNC) {
@@ -1102,6 +1113,11 @@ static void nfs_writeback_done_partial(struct nfs_write_data *data, int status)
                nfs_writepage_release(req);
 }
 
+static const struct rpc_call_ops nfs_write_partial_ops = {
+       .rpc_call_done = nfs_writeback_done_partial,
+       .rpc_release = nfs_writedata_release,
+};
+
 /*
  * Handle a write reply that flushes a whole page.
  *
@@ -1109,11 +1125,15 @@ static void nfs_writeback_done_partial(struct nfs_write_data *data, int status)
  *       writebacks since the page->count is kept > 1 for as long
  *       as the page has a write request pending.
  */
-static void nfs_writeback_done_full(struct nfs_write_data *data, int status)
+static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
 {
+       struct nfs_write_data   *data = calldata;
        struct nfs_page         *req;
        struct page             *page;
 
+       if (nfs_writeback_done(task, data) != 0)
+               return;
+
        /* Update attributes as result of writeback. */
        while (!list_empty(&data->pages)) {
                req = nfs_list_entry(data->pages.next);
@@ -1126,13 +1146,13 @@ static void nfs_writeback_done_full(struct nfs_write_data *data, int status)
                        req->wb_bytes,
                        (long long)req_offset(req));
 
-               if (status < 0) {
+               if (task->tk_status < 0) {
                        ClearPageUptodate(page);
                        SetPageError(page);
-                       req->wb_context->error = status;
+                       req->wb_context->error = task->tk_status;
                        end_page_writeback(page);
                        nfs_inode_remove_request(req);
-                       dprintk(", error = %d\n", status);
+                       dprintk(", error = %d\n", task->tk_status);
                        goto next;
                }
                end_page_writeback(page);
@@ -1154,18 +1174,28 @@ static void nfs_writeback_done_full(struct nfs_write_data *data, int status)
        }
 }
 
+static const struct rpc_call_ops nfs_write_full_ops = {
+       .rpc_call_done = nfs_writeback_done_full,
+       .rpc_release = nfs_writedata_release,
+};
+
+
 /*
  * This function is called when the WRITE call is complete.
  */
-void nfs_writeback_done(struct rpc_task *task, void *calldata)
+static int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
 {
-       struct nfs_write_data   *data = calldata;
        struct nfs_writeargs    *argp = &data->args;
        struct nfs_writeres     *resp = &data->res;
+       int status;
 
        dprintk("NFS: %4d nfs_writeback_done (status %d)\n",
                task->tk_pid, task->tk_status);
 
+       /* Call the NFS version-specific code */
+       status = NFS_PROTO(data->inode)->write_done(task, data);
+       if (status != 0)
+               return status;
        nfs_add_stats(data->inode, NFSIOS_SERVERWRITTENBYTES, resp->count);
 
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
@@ -1210,7 +1240,7 @@ void nfs_writeback_done(struct rpc_task *task, void *calldata)
                                argp->stable = NFS_FILE_SYNC;
                        }
                        rpc_restart_call(task);
-                       return;
+                       return -EAGAIN;
                }
                if (time_before(complain, jiffies)) {
                        printk(KERN_WARNING
@@ -1221,11 +1251,7 @@ void nfs_writeback_done(struct rpc_task *task, void *calldata)
                /* Can't do anything about it except throw an error. */
                task->tk_status = -EIO;
        }
-
-       /*
-        * Process the nfs_page list
-        */
-       data->complete(data, task->tk_status);
+       return 0;
 }
 
 
@@ -1239,10 +1265,12 @@ void nfs_commit_release(void *wdata)
  * Set up the argument/result storage required for the RPC call.
  */
 static void nfs_commit_rpcsetup(struct list_head *head,
-               struct nfs_write_data *data, int how)
+               struct nfs_write_data *data,
+               int how)
 {
        struct nfs_page         *first;
        struct inode            *inode;
+       int flags;
 
        /* Set up the RPC argument and reply structs
         * NB: take care not to mess about with data->commit et al. */
@@ -1262,7 +1290,10 @@ static void nfs_commit_rpcsetup(struct list_head *head,
        data->res.fattr   = &data->fattr;
        data->res.verf    = &data->verf;
        nfs_fattr_init(&data->fattr);
-       
+
+       /* Set up the initial task struct.  */
+       flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
+       rpc_init_task(&data->task, NFS_CLIENT(inode), flags, &nfs_commit_ops, data);
        NFS_PROTO(inode)->commit_setup(data, how);
 
        data->task.tk_priority = flush_task_priority(how);
@@ -1303,7 +1334,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
 /*
  * COMMIT call returned
  */
-void nfs_commit_done(struct rpc_task *task, void *calldata)
+static void nfs_commit_done(struct rpc_task *task, void *calldata)
 {
        struct nfs_write_data   *data = calldata;
        struct nfs_page         *req;
@@ -1312,6 +1343,10 @@ void nfs_commit_done(struct rpc_task *task, void *calldata)
         dprintk("NFS: %4d nfs_commit_done (status %d)\n",
                                 task->tk_pid, task->tk_status);
 
+       /* Call the NFS version-specific code */
+       if (NFS_PROTO(data->inode)->commit_done(task, data) != 0)
+               return;
+
        while (!list_empty(&data->pages)) {
                req = nfs_list_entry(data->pages.next);
                nfs_list_remove_request(req);
@@ -1345,6 +1380,11 @@ void nfs_commit_done(struct rpc_task *task, void *calldata)
        }
        sub_page_state(nr_unstable,res);
 }
+
+static const struct rpc_call_ops nfs_commit_ops = {
+       .rpc_call_done = nfs_commit_done,
+       .rpc_release = nfs_commit_release,
+};
 #endif
 
 static int nfs_flush_inode(struct inode *inode, unsigned long idx_start,