Commit d8a5ad75 authored by Trond Myklebust's avatar Trond Myklebust

NFS: Cleanup the coalescing code

Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 91e59c36
...@@ -223,48 +223,101 @@ nfs_wait_on_request(struct nfs_page *req) ...@@ -223,48 +223,101 @@ nfs_wait_on_request(struct nfs_page *req)
} }
/** /**
* nfs_coalesce_requests - Split coalesced requests out from a list. * nfs_pageio_init - initialise a page io descriptor
* @desc: pointer to descriptor
* @iosize: io block size
*/
void nfs_pageio_init(struct nfs_pageio_descriptor *desc, unsigned int bsize)
{
INIT_LIST_HEAD(&desc->pg_list);
desc->pg_count = 0;
desc->pg_bsize = bsize;
desc->pg_base = 0;
}
/**
* nfs_can_coalesce_requests - test two requests for compatibility
* @prev: pointer to nfs_page
* @req: pointer to nfs_page
*
* The nfs_page structures 'prev' and 'req' are compared to ensure that the
* page data area they describe is contiguous, and that their RPC
* credentials, NFSv4 open state, and lockowners are the same.
*
* Return 'true' if this is the case, else return 'false'.
*/
static int nfs_can_coalesce_requests(struct nfs_page *prev,
struct nfs_page *req)
{
if (req->wb_context->cred != prev->wb_context->cred)
return 0;
if (req->wb_context->lockowner != prev->wb_context->lockowner)
return 0;
if (req->wb_context->state != prev->wb_context->state)
return 0;
if (req->wb_index != (prev->wb_index + 1))
return 0;
if (req->wb_pgbase != 0)
return 0;
if (prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE)
return 0;
return 1;
}
/**
* nfs_pageio_add_request - Attempt to coalesce a request into a page list.
* @desc: destination io descriptor
* @req: request
*
* Returns true if the request 'req' was successfully coalesced into the
* existing list of pages 'desc'.
*/
static int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
struct nfs_page *req)
{
size_t newlen = req->wb_bytes;
if (desc->pg_count != 0) {
struct nfs_page *prev;
/*
* FIXME: ideally we should be able to coalesce all requests
* that are not block boundary aligned, but currently this
* is problematic for the case of bsize < PAGE_CACHE_SIZE,
* since nfs_flush_multi and nfs_pagein_multi assume you
* can have only one struct nfs_page.
*/
newlen += desc->pg_count;
if (desc->pg_base + newlen > desc->pg_bsize)
return 0;
prev = nfs_list_entry(desc->pg_list.prev);
if (!nfs_can_coalesce_requests(prev, req))
return 0;
} else
desc->pg_base = req->wb_pgbase;
nfs_list_remove_request(req);
nfs_list_add_request(req, &desc->pg_list);
desc->pg_count = newlen;
return 1;
}
/**
* nfs_pageio_add_list - Split coalesced requests out from a list.
* @desc: destination io descriptor
* @head: source list * @head: source list
* @dst: destination list
* @nmax: maximum number of requests to coalesce
* *
* Moves a maximum of 'nmax' elements from one list to another. * Moves a maximum of 'nmax' elements from one list to another.
* The elements are checked to ensure that they form a contiguous set * The elements are checked to ensure that they form a contiguous set
* of pages, and that the RPC credentials are the same. * of pages, and that the RPC credentials are the same.
*/ */
int void nfs_pageio_add_list(struct nfs_pageio_descriptor *desc,
nfs_coalesce_requests(struct list_head *head, struct list_head *dst, struct list_head *head)
unsigned int nmax)
{ {
struct nfs_page *req = NULL;
unsigned int npages = 0;
while (!list_empty(head)) { while (!list_empty(head)) {
struct nfs_page *prev = req; struct nfs_page *req = nfs_list_entry(head->next);
if (!nfs_pageio_add_request(desc, req))
req = nfs_list_entry(head->next);
if (prev) {
if (req->wb_context->cred != prev->wb_context->cred)
break;
if (req->wb_context->lockowner != prev->wb_context->lockowner)
break;
if (req->wb_context->state != prev->wb_context->state)
break;
if (req->wb_index != (prev->wb_index + 1))
break;
if (req->wb_pgbase != 0)
break;
}
nfs_list_remove_request(req);
nfs_list_add_request(req, dst);
npages++;
if (req->wb_pgbase + req->wb_bytes != PAGE_CACHE_SIZE)
break;
if (npages >= nmax)
break; break;
} }
return npages;
} }
#define NFS_SCAN_MAXENTRIES 16 #define NFS_SCAN_MAXENTRIES 16
......
...@@ -328,24 +328,26 @@ static int nfs_pagein_one(struct list_head *head, struct inode *inode) ...@@ -328,24 +328,26 @@ static int nfs_pagein_one(struct list_head *head, struct inode *inode)
} }
static int static int
nfs_pagein_list(struct list_head *head, int rpages) nfs_pagein_list(struct list_head *head, unsigned int rsize)
{ {
LIST_HEAD(one_request); struct nfs_pageio_descriptor desc;
struct nfs_page *req; struct nfs_page *req;
int error = 0; unsigned int pages = 0;
unsigned int pages = 0; int error = 0;
while (!list_empty(head)) { while (!list_empty(head)) {
pages += nfs_coalesce_requests(head, &one_request, rpages); nfs_pageio_init(&desc, rsize);
req = nfs_list_entry(one_request.next); nfs_pageio_add_list(&desc, head);
error = nfs_pagein_one(&one_request, req->wb_context->dentry->d_inode); req = nfs_list_entry(desc.pg_list.next);
error = nfs_pagein_one(&desc.pg_list, req->wb_context->dentry->d_inode);
if (error < 0) if (error < 0)
break; break;
pages += (desc.pg_count + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
} }
if (error >= 0)
return pages;
nfs_async_read_error(head); nfs_async_read_error(head);
if (error >= 0)
return pages;
return error; return error;
} }
...@@ -595,7 +597,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping, ...@@ -595,7 +597,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
filp->private_data); filp->private_data);
ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
if (!list_empty(&head)) { if (!list_empty(&head)) {
int err = nfs_pagein_list(&head, server->rpages); int err = nfs_pagein_list(&head, server->rsize);
if (!ret) if (!ret)
nfs_add_stats(inode, NFSIOS_READPAGES, err); nfs_add_stats(inode, NFSIOS_READPAGES, err);
ret = err; ret = err;
......
...@@ -945,9 +945,8 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how) ...@@ -945,9 +945,8 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how)
static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how) static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how)
{ {
LIST_HEAD(one_request); struct nfs_pageio_descriptor desc;
int (*flush_one)(struct inode *, struct list_head *, int); int (*flush_one)(struct inode *, struct list_head *, int);
struct nfs_page *req;
int wpages = NFS_SERVER(inode)->wpages; int wpages = NFS_SERVER(inode)->wpages;
int wsize = NFS_SERVER(inode)->wsize; int wsize = NFS_SERVER(inode)->wsize;
int error; int error;
...@@ -961,16 +960,16 @@ static int nfs_flush_list(struct inode *inode, struct list_head *head, int npage ...@@ -961,16 +960,16 @@ static int nfs_flush_list(struct inode *inode, struct list_head *head, int npage
how |= FLUSH_STABLE; how |= FLUSH_STABLE;
do { do {
nfs_coalesce_requests(head, &one_request, wpages); nfs_pageio_init(&desc, wsize);
req = nfs_list_entry(one_request.next); nfs_pageio_add_list(&desc, head);
error = flush_one(inode, &one_request, how); error = flush_one(inode, &desc.pg_list, how);
if (error < 0) if (error < 0)
goto out_err; goto out_err;
} while (!list_empty(head)); } while (!list_empty(head));
return 0; return 0;
out_err: out_err:
while (!list_empty(head)) { while (!list_empty(head)) {
req = nfs_list_entry(head->next); struct nfs_page *req = nfs_list_entry(head->next);
nfs_list_remove_request(req); nfs_list_remove_request(req);
nfs_redirty_request(req); nfs_redirty_request(req);
nfs_end_page_writeback(req->wb_page); nfs_end_page_writeback(req->wb_page);
......
...@@ -48,6 +48,13 @@ struct nfs_page { ...@@ -48,6 +48,13 @@ struct nfs_page {
struct nfs_writeverf wb_verf; /* Commit cookie */ struct nfs_writeverf wb_verf; /* Commit cookie */
}; };
struct nfs_pageio_descriptor {
struct list_head pg_list;
size_t pg_count;
size_t pg_bsize;
unsigned int pg_base;
};
#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) #define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags))
extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx, extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx,
...@@ -64,8 +71,10 @@ extern long nfs_scan_dirty(struct address_space *mapping, ...@@ -64,8 +71,10 @@ extern long nfs_scan_dirty(struct address_space *mapping,
struct list_head *dst); struct list_head *dst);
extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst, extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst,
unsigned long idx_start, unsigned int npages); unsigned long idx_start, unsigned int npages);
extern int nfs_coalesce_requests(struct list_head *, struct list_head *, extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
unsigned int); size_t iosize);
extern void nfs_pageio_add_list(struct nfs_pageio_descriptor *,
struct list_head *);
extern int nfs_wait_on_request(struct nfs_page *); extern int nfs_wait_on_request(struct nfs_page *);
extern void nfs_unlock_request(struct nfs_page *req); extern void nfs_unlock_request(struct nfs_page *req);
extern int nfs_set_page_writeback_locked(struct nfs_page *req); extern int nfs_set_page_writeback_locked(struct nfs_page *req);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment