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>
This commit is contained in:
Fred Isaman
2012-04-20 14:47:44 -04:00
committed by Trond Myklebust
parent b554284976
commit cd841605f7
14 changed files with 376 additions and 278 deletions

View File

@@ -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)