aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/pagelist.c117
-rw-r--r--fs/nfs/read.c24
-rw-r--r--fs/nfs/write.c11
-rw-r--r--include/linux/nfs_page.h13
4 files changed, 114 insertions, 51 deletions
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index ca4b1d4ff42b..7017eb0e0bc8 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -223,48 +223,101 @@ out:
223} 223}
224 224
225/** 225/**
226 * nfs_coalesce_requests - Split coalesced requests out from a list. 226 * nfs_pageio_init - initialise a page io descriptor
227 * @desc: pointer to descriptor
228 * @iosize: io block size
229 */
230void nfs_pageio_init(struct nfs_pageio_descriptor *desc, unsigned int bsize)
231{
232 INIT_LIST_HEAD(&desc->pg_list);
233 desc->pg_count = 0;
234 desc->pg_bsize = bsize;
235 desc->pg_base = 0;
236}
237
238/**
239 * nfs_can_coalesce_requests - test two requests for compatibility
240 * @prev: pointer to nfs_page
241 * @req: pointer to nfs_page
242 *
243 * The nfs_page structures 'prev' and 'req' are compared to ensure that the
244 * page data area they describe is contiguous, and that their RPC
245 * credentials, NFSv4 open state, and lockowners are the same.
246 *
247 * Return 'true' if this is the case, else return 'false'.
248 */
249static int nfs_can_coalesce_requests(struct nfs_page *prev,
250 struct nfs_page *req)
251{
252 if (req->wb_context->cred != prev->wb_context->cred)
253 return 0;
254 if (req->wb_context->lockowner != prev->wb_context->lockowner)
255 return 0;
256 if (req->wb_context->state != prev->wb_context->state)
257 return 0;
258 if (req->wb_index != (prev->wb_index + 1))
259 return 0;
260 if (req->wb_pgbase != 0)
261 return 0;
262 if (prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE)
263 return 0;
264 return 1;
265}
266
267/**
268 * nfs_pageio_add_request - Attempt to coalesce a request into a page list.
269 * @desc: destination io descriptor
270 * @req: request
271 *
272 * Returns true if the request 'req' was successfully coalesced into the
273 * existing list of pages 'desc'.
274 */
275static int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
276 struct nfs_page *req)
277{
278 size_t newlen = req->wb_bytes;
279
280 if (desc->pg_count != 0) {
281 struct nfs_page *prev;
282
283 /*
284 * FIXME: ideally we should be able to coalesce all requests
285 * that are not block boundary aligned, but currently this
286 * is problematic for the case of bsize < PAGE_CACHE_SIZE,
287 * since nfs_flush_multi and nfs_pagein_multi assume you
288 * can have only one struct nfs_page.
289 */
290 newlen += desc->pg_count;
291 if (desc->pg_base + newlen > desc->pg_bsize)
292 return 0;
293 prev = nfs_list_entry(desc->pg_list.prev);
294 if (!nfs_can_coalesce_requests(prev, req))
295 return 0;
296 } else
297 desc->pg_base = req->wb_pgbase;
298 nfs_list_remove_request(req);
299 nfs_list_add_request(req, &desc->pg_list);
300 desc->pg_count = newlen;
301 return 1;
302}
303
304/**
305 * nfs_pageio_add_list - Split coalesced requests out from a list.
306 * @desc: destination io descriptor
227 * @head: source list 307 * @head: source list
228 * @dst: destination list
229 * @nmax: maximum number of requests to coalesce
230 * 308 *
231 * Moves a maximum of 'nmax' elements from one list to another. 309 * Moves a maximum of 'nmax' elements from one list to another.
232 * The elements are checked to ensure that they form a contiguous set 310 * The elements are checked to ensure that they form a contiguous set
233 * of pages, and that the RPC credentials are the same. 311 * of pages, and that the RPC credentials are the same.
234 */ 312 */
235int 313void nfs_pageio_add_list(struct nfs_pageio_descriptor *desc,
236nfs_coalesce_requests(struct list_head *head, struct list_head *dst, 314 struct list_head *head)
237 unsigned int nmax)
238{ 315{
239 struct nfs_page *req = NULL;
240 unsigned int npages = 0;
241
242 while (!list_empty(head)) { 316 while (!list_empty(head)) {
243 struct nfs_page *prev = req; 317 struct nfs_page *req = nfs_list_entry(head->next);
244 318 if (!nfs_pageio_add_request(desc, req))
245 req = nfs_list_entry(head->next);
246 if (prev) {
247 if (req->wb_context->cred != prev->wb_context->cred)
248 break;
249 if (req->wb_context->lockowner != prev->wb_context->lockowner)
250 break;
251 if (req->wb_context->state != prev->wb_context->state)
252 break;
253 if (req->wb_index != (prev->wb_index + 1))
254 break;
255
256 if (req->wb_pgbase != 0)
257 break;
258 }
259 nfs_list_remove_request(req);
260 nfs_list_add_request(req, dst);
261 npages++;
262 if (req->wb_pgbase + req->wb_bytes != PAGE_CACHE_SIZE)
263 break;
264 if (npages >= nmax)
265 break; 319 break;
266 } 320 }
267 return npages;
268} 321}
269 322
270#define NFS_SCAN_MAXENTRIES 16 323#define NFS_SCAN_MAXENTRIES 16
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 6ab4d5a9edf2..97f0f42e136d 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -328,24 +328,26 @@ out_bad:
328} 328}
329 329
330static int 330static int
331nfs_pagein_list(struct list_head *head, int rpages) 331nfs_pagein_list(struct list_head *head, unsigned int rsize)
332{ 332{
333 LIST_HEAD(one_request); 333 struct nfs_pageio_descriptor desc;
334 struct nfs_page *req; 334 struct nfs_page *req;
335 int error = 0; 335 unsigned int pages = 0;
336 unsigned int pages = 0; 336 int error = 0;
337 337
338 while (!list_empty(head)) { 338 while (!list_empty(head)) {
339 pages += nfs_coalesce_requests(head, &one_request, rpages); 339 nfs_pageio_init(&desc, rsize);
340 req = nfs_list_entry(one_request.next); 340 nfs_pageio_add_list(&desc, head);
341 error = nfs_pagein_one(&one_request, req->wb_context->dentry->d_inode); 341 req = nfs_list_entry(desc.pg_list.next);
342 error = nfs_pagein_one(&desc.pg_list, req->wb_context->dentry->d_inode);
342 if (error < 0) 343 if (error < 0)
343 break; 344 break;
345 pages += (desc.pg_count + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
344 } 346 }
345 if (error >= 0)
346 return pages;
347 347
348 nfs_async_read_error(head); 348 nfs_async_read_error(head);
349 if (error >= 0)
350 return pages;
349 return error; 351 return error;
350} 352}
351 353
@@ -595,7 +597,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
595 filp->private_data); 597 filp->private_data);
596 ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); 598 ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
597 if (!list_empty(&head)) { 599 if (!list_empty(&head)) {
598 int err = nfs_pagein_list(&head, server->rpages); 600 int err = nfs_pagein_list(&head, server->rsize);
599 if (!ret) 601 if (!ret)
600 nfs_add_stats(inode, NFSIOS_READPAGES, err); 602 nfs_add_stats(inode, NFSIOS_READPAGES, err);
601 ret = err; 603 ret = err;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index dbad89c8e427..b03ec1ba4d75 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -945,9 +945,8 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how)
945 945
946static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how) 946static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how)
947{ 947{
948 LIST_HEAD(one_request); 948 struct nfs_pageio_descriptor desc;
949 int (*flush_one)(struct inode *, struct list_head *, int); 949 int (*flush_one)(struct inode *, struct list_head *, int);
950 struct nfs_page *req;
951 int wpages = NFS_SERVER(inode)->wpages; 950 int wpages = NFS_SERVER(inode)->wpages;
952 int wsize = NFS_SERVER(inode)->wsize; 951 int wsize = NFS_SERVER(inode)->wsize;
953 int error; 952 int error;
@@ -961,16 +960,16 @@ static int nfs_flush_list(struct inode *inode, struct list_head *head, int npage
961 how |= FLUSH_STABLE; 960 how |= FLUSH_STABLE;
962 961
963 do { 962 do {
964 nfs_coalesce_requests(head, &one_request, wpages); 963 nfs_pageio_init(&desc, wsize);
965 req = nfs_list_entry(one_request.next); 964 nfs_pageio_add_list(&desc, head);
966 error = flush_one(inode, &one_request, how); 965 error = flush_one(inode, &desc.pg_list, how);
967 if (error < 0) 966 if (error < 0)
968 goto out_err; 967 goto out_err;
969 } while (!list_empty(head)); 968 } while (!list_empty(head));
970 return 0; 969 return 0;
971out_err: 970out_err:
972 while (!list_empty(head)) { 971 while (!list_empty(head)) {
973 req = nfs_list_entry(head->next); 972 struct nfs_page *req = nfs_list_entry(head->next);
974 nfs_list_remove_request(req); 973 nfs_list_remove_request(req);
975 nfs_redirty_request(req); 974 nfs_redirty_request(req);
976 nfs_end_page_writeback(req->wb_page); 975 nfs_end_page_writeback(req->wb_page);
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index 16b0266b14fd..3ef8e0441473 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -48,6 +48,13 @@ struct nfs_page {
48 struct nfs_writeverf wb_verf; /* Commit cookie */ 48 struct nfs_writeverf wb_verf; /* Commit cookie */
49}; 49};
50 50
51struct nfs_pageio_descriptor {
52 struct list_head pg_list;
53 size_t pg_count;
54 size_t pg_bsize;
55 unsigned int pg_base;
56};
57
51#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) 58#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags))
52 59
53extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx, 60extern 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 struct list_head *dst); 71 struct list_head *dst);
65extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst, 72extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst,
66 unsigned long idx_start, unsigned int npages); 73 unsigned long idx_start, unsigned int npages);
67extern int nfs_coalesce_requests(struct list_head *, struct list_head *, 74extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
68 unsigned int); 75 size_t iosize);
76extern void nfs_pageio_add_list(struct nfs_pageio_descriptor *,
77 struct list_head *);
69extern int nfs_wait_on_request(struct nfs_page *); 78extern int nfs_wait_on_request(struct nfs_page *);
70extern void nfs_unlock_request(struct nfs_page *req); 79extern void nfs_unlock_request(struct nfs_page *req);
71extern int nfs_set_page_writeback_locked(struct nfs_page *req); 80extern int nfs_set_page_writeback_locked(struct nfs_page *req);