diff options
author | Fred Isaman <iisaman@netapp.com> | 2012-04-20 14:47:44 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-04-27 14:10:37 -0400 |
commit | cd841605f7a721878d8a2d1362484723d8abf569 (patch) | |
tree | b5c37db575cd545a183577249909e042fe38d646 /fs/nfs/write.c | |
parent | b5542849764aa56fd3f05c0041195b637b9d2ac2 (diff) | |
download | lwn-cd841605f7a721878d8a2d1362484723d8abf569.tar.gz lwn-cd841605f7a721878d8a2d1362484723d8abf569.zip |
NFS: create common nfs_pgio_header for both read and write
In order to avoid duplicating all the data in nfs_read_data whenever we
split it up into multiple RPC calls (either due to a short read result
or due to rsize < PAGE_SIZE), we split out the bits that are the same
per RPC call into a separate "header" structure.
The goal this patch moves towards is to have a single header
refcounted by several rpc_data structures. Thus, want to always refer
from rpc_data to the header, and not the other way. This patch comes
close to that ideal, but the directio code currently needs some
special casing, isolated in the nfs_direct_[read_write]hdr_release()
functions. This will be dealt with in a future patch.
Signed-off-by: Fred Isaman <iisaman@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r-- | fs/nfs/write.c | 104 |
1 files changed, 58 insertions, 46 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 76735dd8c9a7..dbb5c0a613b8 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -69,19 +69,24 @@ void nfs_commit_free(struct nfs_commit_data *p) } EXPORT_SYMBOL_GPL(nfs_commit_free); -struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) +struct nfs_write_header *nfs_writehdr_alloc(unsigned int pagecount) { - struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); + struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); if (p) { + struct nfs_pgio_header *hdr = &p->header; + struct nfs_write_data *data = &p->rpc_data; + memset(p, 0, sizeof(*p)); - INIT_LIST_HEAD(&p->pages); - p->npages = pagecount; - if (pagecount <= ARRAY_SIZE(p->page_array)) - p->pagevec = p->page_array; + INIT_LIST_HEAD(&hdr->pages); + INIT_LIST_HEAD(&data->list); + data->npages = pagecount; + data->header = hdr; + if (pagecount <= ARRAY_SIZE(data->page_array)) + data->pagevec = data->page_array; else { - p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); - if (!p->pagevec) { + data->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); + if (!data->pagevec) { mempool_free(p, nfs_wdata_mempool); p = NULL; } @@ -90,17 +95,18 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) return p; } -void nfs_writedata_free(struct nfs_write_data *p) +void nfs_writehdr_free(struct nfs_pgio_header *hdr) { - if (p && (p->pagevec != &p->page_array[0])) - kfree(p->pagevec); - mempool_free(p, nfs_wdata_mempool); + struct nfs_write_header *whdr = container_of(hdr, struct nfs_write_header, header); + mempool_free(whdr, nfs_wdata_mempool); } void nfs_writedata_release(struct nfs_write_data *wdata) { put_nfs_open_context(wdata->args.context); - nfs_writedata_free(wdata); + if (wdata->pagevec != wdata->page_array) + kfree(wdata->pagevec); + nfs_writehdr_free(wdata->header); } static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error) @@ -507,9 +513,8 @@ static inline int nfs_write_need_commit(struct nfs_write_data *data) { if (data->verf.committed == NFS_DATA_SYNC) - return data->lseg == NULL; - else - return data->verf.committed != NFS_FILE_SYNC; + return data->header->lseg == NULL; + return data->verf.committed != NFS_FILE_SYNC; } static inline @@ -517,7 +522,7 @@ int nfs_reschedule_unstable_write(struct nfs_page *req, struct nfs_write_data *data) { if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) { - nfs_mark_request_commit(req, data->lseg); + nfs_mark_request_commit(req, data->header->lseg); return 1; } if (test_and_clear_bit(PG_NEED_RESCHED, &req->wb_flags)) { @@ -841,13 +846,13 @@ int nfs_initiate_write(struct rpc_clnt *clnt, const struct rpc_call_ops *call_ops, int how) { - struct inode *inode = data->inode; + struct inode *inode = data->header->inode; int priority = flush_task_priority(how); struct rpc_task *task; struct rpc_message msg = { .rpc_argp = &data->args, .rpc_resp = &data->res, - .rpc_cred = data->cred, + .rpc_cred = data->header->cred, }; struct rpc_task_setup task_setup_data = { .rpc_client = clnt, @@ -896,14 +901,15 @@ static void nfs_write_rpcsetup(struct nfs_page *req, unsigned int count, unsigned int offset, int how) { + struct nfs_pgio_header *hdr = data->header; struct inode *inode = req->wb_context->dentry->d_inode; /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ - data->req = req; - data->inode = inode = req->wb_context->dentry->d_inode; - data->cred = req->wb_context->cred; + hdr->req = req; + hdr->inode = inode = req->wb_context->dentry->d_inode; + hdr->cred = req->wb_context->cred; data->args.fh = NFS_FH(inode); data->args.offset = req_offset(req) + offset; @@ -935,7 +941,7 @@ static int nfs_do_write(struct nfs_write_data *data, const struct rpc_call_ops *call_ops, int how) { - struct inode *inode = data->args.context->dentry->d_inode; + struct inode *inode = data->header->inode; return nfs_initiate_write(NFS_CLIENT(inode), data, call_ops, how); } @@ -981,6 +987,7 @@ static int nfs_flush_multi(struct nfs_pageio_descriptor *desc, struct list_head { struct nfs_page *req = nfs_list_entry(desc->pg_list.next); struct page *page = req->wb_page; + struct nfs_write_header *whdr; struct nfs_write_data *data; size_t wsize = desc->pg_bsize, nbytes; unsigned int offset; @@ -1000,9 +1007,10 @@ static int nfs_flush_multi(struct nfs_pageio_descriptor *desc, struct list_head do { size_t len = min(nbytes, wsize); - data = nfs_writedata_alloc(1); - if (!data) + whdr = nfs_writehdr_alloc(1); + if (!whdr) goto out_bad; + data = &whdr->rpc_data; data->pagevec[0] = page; nfs_write_rpcsetup(req, data, len, offset, desc->pg_ioflags); list_add(&data->list, res); @@ -1036,13 +1044,14 @@ static int nfs_flush_one(struct nfs_pageio_descriptor *desc, struct list_head *r { struct nfs_page *req; struct page **pages; + struct nfs_write_header *whdr; struct nfs_write_data *data; struct list_head *head = &desc->pg_list; int ret = 0; - data = nfs_writedata_alloc(nfs_page_array_len(desc->pg_base, - desc->pg_count)); - if (!data) { + whdr = nfs_writehdr_alloc(nfs_page_array_len(desc->pg_base, + desc->pg_count)); + if (!whdr) { while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); @@ -1051,14 +1060,15 @@ static int nfs_flush_one(struct nfs_pageio_descriptor *desc, struct list_head *r ret = -ENOMEM; goto out; } + data = &whdr->rpc_data; pages = data->pagevec; while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); - nfs_list_add_request(req, &data->pages); + nfs_list_add_request(req, &whdr->header.pages); *pages++ = req->wb_page; } - req = nfs_list_entry(data->pages.next); + req = nfs_list_entry(whdr->header.pages.next); if ((desc->pg_ioflags & FLUSH_COND_STABLE) && (desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit)) @@ -1126,10 +1136,11 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) dprintk("NFS: %5u write(%s/%lld %d@%lld)", task->tk_pid, - data->req->wb_context->dentry->d_inode->i_sb->s_id, + data->header->inode->i_sb->s_id, (long long) - NFS_FILEID(data->req->wb_context->dentry->d_inode), - data->req->wb_bytes, (long long)req_offset(data->req)); + NFS_FILEID(data->header->inode), + data->header->req->wb_bytes, + (long long)req_offset(data->header->req)); nfs_writeback_done(task, data); } @@ -1137,7 +1148,7 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) static void nfs_writeback_release_partial(void *calldata) { struct nfs_write_data *data = calldata; - struct nfs_page *req = data->req; + struct nfs_page *req = data->header->req; struct page *page = req->wb_page; int status = data->task.tk_status; @@ -1169,13 +1180,13 @@ static void nfs_writeback_release_partial(void *calldata) out: if (atomic_dec_and_test(&req->wb_complete)) nfs_writepage_release(req, data); - nfs_writedata_release(calldata); + nfs_writedata_release(data); } void nfs_write_prepare(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; - NFS_PROTO(data->inode)->write_rpc_prepare(task, data); + NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data); } void nfs_commit_prepare(struct rpc_task *task, void *calldata) @@ -1208,11 +1219,12 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) static void nfs_writeback_release_full(void *calldata) { struct nfs_write_data *data = calldata; + struct nfs_pgio_header *hdr = data->header; int status = data->task.tk_status; /* Update attributes as result of writeback. */ - while (!list_empty(&data->pages)) { - struct nfs_page *req = nfs_list_entry(data->pages.next); + while (!list_empty(&hdr->pages)) { + struct nfs_page *req = nfs_list_entry(hdr->pages.next); struct page *page = req->wb_page; nfs_list_remove_request(req); @@ -1233,7 +1245,7 @@ static void nfs_writeback_release_full(void *calldata) if (nfs_write_need_commit(data)) { memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); - nfs_mark_request_commit(req, data->lseg); + nfs_mark_request_commit(req, hdr->lseg); dprintk(" marked for commit\n"); goto next; } @@ -1244,7 +1256,7 @@ remove_request: nfs_unlock_request(req); nfs_end_page_writeback(page); } - nfs_writedata_release(calldata); + nfs_writedata_release(data); } static const struct rpc_call_ops nfs_write_full_ops = { @@ -1261,6 +1273,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) { struct nfs_writeargs *argp = &data->args; struct nfs_writeres *resp = &data->res; + struct inode *inode = data->header->inode; int status; dprintk("NFS: %5u nfs_writeback_done (status %d)\n", @@ -1273,10 +1286,10 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) * another writer had changed the file, but some applications * depend on tighter cache coherency when writing. */ - status = NFS_PROTO(data->inode)->write_done(task, data); + status = NFS_PROTO(inode)->write_done(task, data); if (status != 0) return; - nfs_add_stats(data->inode, NFSIOS_SERVERWRITTENBYTES, resp->count); + nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, resp->count); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) if (resp->verf->committed < argp->stable && task->tk_status >= 0) { @@ -1294,7 +1307,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) if (time_before(complain, jiffies)) { dprintk("NFS: faulty NFS server %s:" " (committed = %d) != (stable = %d)\n", - NFS_SERVER(data->inode)->nfs_client->cl_hostname, + NFS_SERVER(inode)->nfs_client->cl_hostname, resp->verf->committed, argp->stable); complain = jiffies + 300 * HZ; } @@ -1304,7 +1317,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) if (task->tk_status >= 0 && resp->count < argp->count) { static unsigned long complain; - nfs_inc_stats(data->inode, NFSIOS_SHORTWRITE); + nfs_inc_stats(inode, NFSIOS_SHORTWRITE); /* Has the server at least made some progress? */ if (resp->count != 0) { @@ -1333,7 +1346,6 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) /* Can't do anything about it except throw an error. */ task->tk_status = -EIO; } - return; } @@ -1745,7 +1757,7 @@ int nfs_migrate_page(struct address_space *mapping, struct page *newpage, int __init nfs_init_writepagecache(void) { nfs_wdata_cachep = kmem_cache_create("nfs_write_data", - sizeof(struct nfs_write_data), + sizeof(struct nfs_write_header), 0, SLAB_HWCACHE_ALIGN, NULL); if (nfs_wdata_cachep == NULL) |