aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2007-04-02 18:48:28 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2007-05-01 01:17:04 -0400
commitbcb71bba7e64f0442d0ca339d7d3117a7060589f (patch)
tree072e9a28395eee87e539a48553b20fa9cb768e94
parentd8a5ad75cc4d577987964e37a4c43b1c648c201e (diff)
NFS: Another cleanup of the read/write request coalescing code
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/pagelist.c71
-rw-r--r--fs/nfs/read.c62
-rw-r--r--fs/nfs/write.c53
-rw-r--r--include/linux/nfs_page.h14
4 files changed, 125 insertions, 75 deletions
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 7017eb0e0bc8..528128545d66 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -225,14 +225,26 @@ out:
225/** 225/**
226 * nfs_pageio_init - initialise a page io descriptor 226 * nfs_pageio_init - initialise a page io descriptor
227 * @desc: pointer to descriptor 227 * @desc: pointer to descriptor
228 * @iosize: io block size 228 * @inode: pointer to inode
229 * @doio: pointer to io function
230 * @bsize: io block size
231 * @io_flags: extra parameters for the io function
229 */ 232 */
230void nfs_pageio_init(struct nfs_pageio_descriptor *desc, unsigned int bsize) 233void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
234 struct inode *inode,
235 int (*doio)(struct inode *, struct list_head *, size_t, int),
236 unsigned int bsize,
237 int io_flags)
231{ 238{
232 INIT_LIST_HEAD(&desc->pg_list); 239 INIT_LIST_HEAD(&desc->pg_list);
240 desc->pg_bytes_written = 0;
233 desc->pg_count = 0; 241 desc->pg_count = 0;
234 desc->pg_bsize = bsize; 242 desc->pg_bsize = bsize;
235 desc->pg_base = 0; 243 desc->pg_base = 0;
244 desc->pg_inode = inode;
245 desc->pg_doio = doio;
246 desc->pg_ioflags = io_flags;
247 desc->pg_error = 0;
236} 248}
237 249
238/** 250/**
@@ -265,15 +277,15 @@ static int nfs_can_coalesce_requests(struct nfs_page *prev,
265} 277}
266 278
267/** 279/**
268 * nfs_pageio_add_request - Attempt to coalesce a request into a page list. 280 * nfs_pageio_do_add_request - Attempt to coalesce a request into a page list.
269 * @desc: destination io descriptor 281 * @desc: destination io descriptor
270 * @req: request 282 * @req: request
271 * 283 *
272 * Returns true if the request 'req' was successfully coalesced into the 284 * Returns true if the request 'req' was successfully coalesced into the
273 * existing list of pages 'desc'. 285 * existing list of pages 'desc'.
274 */ 286 */
275static int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, 287static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
276 struct nfs_page *req) 288 struct nfs_page *req)
277{ 289{
278 size_t newlen = req->wb_bytes; 290 size_t newlen = req->wb_bytes;
279 291
@@ -301,6 +313,46 @@ static int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
301 return 1; 313 return 1;
302} 314}
303 315
316/*
317 * Helper for nfs_pageio_add_request and nfs_pageio_complete
318 */
319static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
320{
321 if (!list_empty(&desc->pg_list)) {
322 int error = desc->pg_doio(desc->pg_inode,
323 &desc->pg_list,
324 desc->pg_count,
325 desc->pg_ioflags);
326 if (error < 0)
327 desc->pg_error = error;
328 else
329 desc->pg_bytes_written += desc->pg_count;
330 }
331 if (list_empty(&desc->pg_list)) {
332 desc->pg_count = 0;
333 desc->pg_base = 0;
334 }
335}
336
337/**
338 * nfs_pageio_add_request - Attempt to coalesce a request into a page list.
339 * @desc: destination io descriptor
340 * @req: request
341 *
342 * Returns true if the request 'req' was successfully coalesced into the
343 * existing list of pages 'desc'.
344 */
345static int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
346 struct nfs_page *req)
347{
348 while (!nfs_pageio_do_add_request(desc, req)) {
349 nfs_pageio_doio(desc);
350 if (desc->pg_error < 0)
351 return 0;
352 }
353 return 1;
354}
355
304/** 356/**
305 * nfs_pageio_add_list - Split coalesced requests out from a list. 357 * nfs_pageio_add_list - Split coalesced requests out from a list.
306 * @desc: destination io descriptor 358 * @desc: destination io descriptor
@@ -320,6 +372,15 @@ void nfs_pageio_add_list(struct nfs_pageio_descriptor *desc,
320 } 372 }
321} 373}
322 374
375/**
376 * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor
377 * @desc: pointer to io descriptor
378 */
379void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
380{
381 nfs_pageio_doio(desc);
382}
383
323#define NFS_SCAN_MAXENTRIES 16 384#define NFS_SCAN_MAXENTRIES 16
324/** 385/**
325 * nfs_scan_dirty - Scan the radix tree for dirty requests 386 * nfs_scan_dirty - Scan the radix tree for dirty requests
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 97f0f42e136d..0effa74992df 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -27,7 +27,8 @@
27 27
28#define NFSDBG_FACILITY NFSDBG_PAGECACHE 28#define NFSDBG_FACILITY NFSDBG_PAGECACHE
29 29
30static int nfs_pagein_one(struct list_head *, struct inode *); 30static int nfs_pagein_multi(struct inode *, struct list_head *, size_t, int);
31static int nfs_pagein_one(struct inode *, struct list_head *, size_t, int);
31static const struct rpc_call_ops nfs_read_partial_ops; 32static const struct rpc_call_ops nfs_read_partial_ops;
32static const struct rpc_call_ops nfs_read_full_ops; 33static const struct rpc_call_ops nfs_read_full_ops;
33 34
@@ -133,7 +134,10 @@ static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
133 memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len); 134 memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len);
134 135
135 nfs_list_add_request(new, &one_request); 136 nfs_list_add_request(new, &one_request);
136 nfs_pagein_one(&one_request, inode); 137 if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
138 nfs_pagein_multi(inode, &one_request, len, 0);
139 else
140 nfs_pagein_one(inode, &one_request, len, 0);
137 return 0; 141 return 0;
138} 142}
139 143
@@ -230,7 +234,7 @@ static void nfs_execute_read(struct nfs_read_data *data)
230 * won't see the new data until our attribute cache is updated. This is more 234 * won't see the new data until our attribute cache is updated. This is more
231 * or less conventional NFS client behavior. 235 * or less conventional NFS client behavior.
232 */ 236 */
233static int nfs_pagein_multi(struct list_head *head, struct inode *inode) 237static int nfs_pagein_multi(struct inode *inode, struct list_head *head, size_t count, int flags)
234{ 238{
235 struct nfs_page *req = nfs_list_entry(head->next); 239 struct nfs_page *req = nfs_list_entry(head->next);
236 struct page *page = req->wb_page; 240 struct page *page = req->wb_page;
@@ -242,7 +246,7 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
242 246
243 nfs_list_remove_request(req); 247 nfs_list_remove_request(req);
244 248
245 nbytes = req->wb_bytes; 249 nbytes = count;
246 do { 250 do {
247 size_t len = min(nbytes,rsize); 251 size_t len = min(nbytes,rsize);
248 252
@@ -258,23 +262,19 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
258 262
259 ClearPageError(page); 263 ClearPageError(page);
260 offset = 0; 264 offset = 0;
261 nbytes = req->wb_bytes; 265 nbytes = count;
262 do { 266 do {
263 data = list_entry(list.next, struct nfs_read_data, pages); 267 data = list_entry(list.next, struct nfs_read_data, pages);
264 list_del_init(&data->pages); 268 list_del_init(&data->pages);
265 269
266 data->pagevec[0] = page; 270 data->pagevec[0] = page;
267 271
268 if (nbytes > rsize) { 272 if (nbytes < rsize)
269 nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, 273 rsize = nbytes;
270 rsize, offset); 274 nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
271 offset += rsize; 275 rsize, offset);
272 nbytes -= rsize; 276 offset += rsize;
273 } else { 277 nbytes -= rsize;
274 nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
275 nbytes, offset);
276 nbytes = 0;
277 }
278 nfs_execute_read(data); 278 nfs_execute_read(data);
279 } while (nbytes != 0); 279 } while (nbytes != 0);
280 280
@@ -291,30 +291,24 @@ out_bad:
291 return -ENOMEM; 291 return -ENOMEM;
292} 292}
293 293
294static int nfs_pagein_one(struct list_head *head, struct inode *inode) 294static int nfs_pagein_one(struct inode *inode, struct list_head *head, size_t count, int flags)
295{ 295{
296 struct nfs_page *req; 296 struct nfs_page *req;
297 struct page **pages; 297 struct page **pages;
298 struct nfs_read_data *data; 298 struct nfs_read_data *data;
299 unsigned int count;
300
301 if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
302 return nfs_pagein_multi(head, inode);
303 299
304 data = nfs_readdata_alloc(NFS_SERVER(inode)->rsize); 300 data = nfs_readdata_alloc(count);
305 if (!data) 301 if (!data)
306 goto out_bad; 302 goto out_bad;
307 303
308 INIT_LIST_HEAD(&data->pages); 304 INIT_LIST_HEAD(&data->pages);
309 pages = data->pagevec; 305 pages = data->pagevec;
310 count = 0;
311 while (!list_empty(head)) { 306 while (!list_empty(head)) {
312 req = nfs_list_entry(head->next); 307 req = nfs_list_entry(head->next);
313 nfs_list_remove_request(req); 308 nfs_list_remove_request(req);
314 nfs_list_add_request(req, &data->pages); 309 nfs_list_add_request(req, &data->pages);
315 ClearPageError(req->wb_page); 310 ClearPageError(req->wb_page);
316 *pages++ = req->wb_page; 311 *pages++ = req->wb_page;
317 count += req->wb_bytes;
318 } 312 }
319 req = nfs_list_entry(data->pages.next); 313 req = nfs_list_entry(data->pages.next);
320 314
@@ -328,22 +322,20 @@ out_bad:
328} 322}
329 323
330static int 324static int
331nfs_pagein_list(struct list_head *head, unsigned int rsize) 325nfs_pagein_list(struct inode *inode, struct list_head *head, unsigned int rsize)
332{ 326{
333 struct nfs_pageio_descriptor desc; 327 struct nfs_pageio_descriptor desc;
334 struct nfs_page *req;
335 unsigned int pages = 0; 328 unsigned int pages = 0;
336 int error = 0; 329 int error = 0;
337 330
338 while (!list_empty(head)) { 331 if (rsize < PAGE_CACHE_SIZE)
339 nfs_pageio_init(&desc, rsize); 332 nfs_pageio_init(&desc, inode, nfs_pagein_multi, rsize, 0);
340 nfs_pageio_add_list(&desc, head); 333 else
341 req = nfs_list_entry(desc.pg_list.next); 334 nfs_pageio_init(&desc, inode, nfs_pagein_one, rsize, 0);
342 error = nfs_pagein_one(&desc.pg_list, req->wb_context->dentry->d_inode); 335
343 if (error < 0) 336 nfs_pageio_add_list(&desc, head);
344 break; 337 nfs_pageio_complete(&desc);
345 pages += (desc.pg_count + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 338 pages += (desc.pg_bytes_written + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
346 }
347 339
348 nfs_async_read_error(head); 340 nfs_async_read_error(head);
349 if (error >= 0) 341 if (error >= 0)
@@ -597,7 +589,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
597 filp->private_data); 589 filp->private_data);
598 ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); 590 ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
599 if (!list_empty(&head)) { 591 if (!list_empty(&head)) {
600 int err = nfs_pagein_list(&head, server->rsize); 592 int err = nfs_pagein_list(inode, &head, server->rsize);
601 if (!ret) 593 if (!ret)
602 nfs_add_stats(inode, NFSIOS_READPAGES, err); 594 nfs_add_stats(inode, NFSIOS_READPAGES, err);
603 ret = err; 595 ret = err;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index b03ec1ba4d75..b6749967eead 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -835,7 +835,7 @@ static void nfs_execute_write(struct nfs_write_data *data)
835 * Generate multiple small requests to write out a single 835 * Generate multiple small requests to write out a single
836 * contiguous dirty area on one page. 836 * contiguous dirty area on one page.
837 */ 837 */
838static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how) 838static int nfs_flush_multi(struct inode *inode, struct list_head *head, size_t count, int how)
839{ 839{
840 struct nfs_page *req = nfs_list_entry(head->next); 840 struct nfs_page *req = nfs_list_entry(head->next);
841 struct page *page = req->wb_page; 841 struct page *page = req->wb_page;
@@ -847,7 +847,7 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
847 847
848 nfs_list_remove_request(req); 848 nfs_list_remove_request(req);
849 849
850 nbytes = req->wb_bytes; 850 nbytes = count;
851 do { 851 do {
852 size_t len = min(nbytes, wsize); 852 size_t len = min(nbytes, wsize);
853 853
@@ -862,23 +862,19 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
862 862
863 ClearPageError(page); 863 ClearPageError(page);
864 offset = 0; 864 offset = 0;
865 nbytes = req->wb_bytes; 865 nbytes = count;
866 do { 866 do {
867 data = list_entry(list.next, struct nfs_write_data, pages); 867 data = list_entry(list.next, struct nfs_write_data, pages);
868 list_del_init(&data->pages); 868 list_del_init(&data->pages);
869 869
870 data->pagevec[0] = page; 870 data->pagevec[0] = page;
871 871
872 if (nbytes > wsize) { 872 if (nbytes < wsize)
873 nfs_write_rpcsetup(req, data, &nfs_write_partial_ops, 873 wsize = nbytes;
874 wsize, offset, how); 874 nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
875 offset += wsize; 875 wsize, offset, how);
876 nbytes -= wsize; 876 offset += wsize;
877 } else { 877 nbytes -= wsize;
878 nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
879 nbytes, offset, how);
880 nbytes = 0;
881 }
882 nfs_execute_write(data); 878 nfs_execute_write(data);
883 } while (nbytes != 0); 879 } while (nbytes != 0);
884 880
@@ -904,26 +900,23 @@ out_bad:
904 * This is the case if nfs_updatepage detects a conflicting request 900 * This is the case if nfs_updatepage detects a conflicting request
905 * that has been written but not committed. 901 * that has been written but not committed.
906 */ 902 */
907static int nfs_flush_one(struct inode *inode, struct list_head *head, int how) 903static int nfs_flush_one(struct inode *inode, struct list_head *head, size_t count, int how)
908{ 904{
909 struct nfs_page *req; 905 struct nfs_page *req;
910 struct page **pages; 906 struct page **pages;
911 struct nfs_write_data *data; 907 struct nfs_write_data *data;
912 unsigned int count;
913 908
914 data = nfs_writedata_alloc(NFS_SERVER(inode)->wsize); 909 data = nfs_writedata_alloc(count);
915 if (!data) 910 if (!data)
916 goto out_bad; 911 goto out_bad;
917 912
918 pages = data->pagevec; 913 pages = data->pagevec;
919 count = 0;
920 while (!list_empty(head)) { 914 while (!list_empty(head)) {
921 req = nfs_list_entry(head->next); 915 req = nfs_list_entry(head->next);
922 nfs_list_remove_request(req); 916 nfs_list_remove_request(req);
923 nfs_list_add_request(req, &data->pages); 917 nfs_list_add_request(req, &data->pages);
924 ClearPageError(req->wb_page); 918 ClearPageError(req->wb_page);
925 *pages++ = req->wb_page; 919 *pages++ = req->wb_page;
926 count += req->wb_bytes;
927 } 920 }
928 req = nfs_list_entry(data->pages.next); 921 req = nfs_list_entry(data->pages.next);
929 922
@@ -946,28 +939,22 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how)
946static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how) 939static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how)
947{ 940{
948 struct nfs_pageio_descriptor desc; 941 struct nfs_pageio_descriptor desc;
949 int (*flush_one)(struct inode *, struct list_head *, int);
950 int wpages = NFS_SERVER(inode)->wpages; 942 int wpages = NFS_SERVER(inode)->wpages;
951 int wsize = NFS_SERVER(inode)->wsize; 943 int wsize = NFS_SERVER(inode)->wsize;
952 int error;
953 944
954 flush_one = nfs_flush_one;
955 if (wsize < PAGE_CACHE_SIZE)
956 flush_one = nfs_flush_multi;
957 /* For single writes, FLUSH_STABLE is more efficient */ 945 /* For single writes, FLUSH_STABLE is more efficient */
958 if (npages <= wpages && npages == NFS_I(inode)->npages 946 if (npages <= wpages && npages == NFS_I(inode)->npages
959 && nfs_list_entry(head->next)->wb_bytes <= wsize) 947 && nfs_list_entry(head->next)->wb_bytes <= wsize)
960 how |= FLUSH_STABLE; 948 how |= FLUSH_STABLE;
961 949
962 do { 950 if (wsize < PAGE_CACHE_SIZE)
963 nfs_pageio_init(&desc, wsize); 951 nfs_pageio_init(&desc, inode, nfs_flush_multi, wsize, how);
964 nfs_pageio_add_list(&desc, head); 952 else
965 error = flush_one(inode, &desc.pg_list, how); 953 nfs_pageio_init(&desc, inode, nfs_flush_one, wsize, how);
966 if (error < 0) 954 nfs_pageio_add_list(&desc, head);
967 goto out_err; 955 nfs_pageio_complete(&desc);
968 } while (!list_empty(head)); 956 if (desc.pg_error == 0)
969 return 0; 957 return 0;
970out_err:
971 while (!list_empty(head)) { 958 while (!list_empty(head)) {
972 struct nfs_page *req = nfs_list_entry(head->next); 959 struct nfs_page *req = nfs_list_entry(head->next);
973 nfs_list_remove_request(req); 960 nfs_list_remove_request(req);
@@ -975,7 +962,7 @@ out_err:
975 nfs_end_page_writeback(req->wb_page); 962 nfs_end_page_writeback(req->wb_page);
976 nfs_clear_page_writeback(req); 963 nfs_clear_page_writeback(req);
977 } 964 }
978 return error; 965 return desc.pg_error;
979} 966}
980 967
981/* 968/*
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index 3ef8e0441473..91c7b18c47d8 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -50,9 +50,15 @@ struct nfs_page {
50 50
51struct nfs_pageio_descriptor { 51struct nfs_pageio_descriptor {
52 struct list_head pg_list; 52 struct list_head pg_list;
53 unsigned long pg_bytes_written;
53 size_t pg_count; 54 size_t pg_count;
54 size_t pg_bsize; 55 size_t pg_bsize;
55 unsigned int pg_base; 56 unsigned int pg_base;
57
58 struct inode *pg_inode;
59 int (*pg_doio)(struct inode *, struct list_head *, size_t, int);
60 int pg_ioflags;
61 int pg_error;
56}; 62};
57 63
58#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) 64#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags))
@@ -71,10 +77,14 @@ extern long nfs_scan_dirty(struct address_space *mapping,
71 struct list_head *dst); 77 struct list_head *dst);
72extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst, 78extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst,
73 unsigned long idx_start, unsigned int npages); 79 unsigned long idx_start, unsigned int npages);
74extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc, 80extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
75 size_t iosize); 81 struct inode *inode,
82 int (*doio)(struct inode *, struct list_head *, size_t, int),
83 size_t bsize,
84 int how);
76extern void nfs_pageio_add_list(struct nfs_pageio_descriptor *, 85extern void nfs_pageio_add_list(struct nfs_pageio_descriptor *,
77 struct list_head *); 86 struct list_head *);
87extern void nfs_pageio_complete(struct nfs_pageio_descriptor *desc);
78extern int nfs_wait_on_request(struct nfs_page *); 88extern int nfs_wait_on_request(struct nfs_page *);
79extern void nfs_unlock_request(struct nfs_page *req); 89extern void nfs_unlock_request(struct nfs_page *req);
80extern int nfs_set_page_writeback_locked(struct nfs_page *req); 90extern int nfs_set_page_writeback_locked(struct nfs_page *req);