diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-04-02 18:48:28 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-05-01 01:17:04 -0400 |
commit | bcb71bba7e64f0442d0ca339d7d3117a7060589f (patch) | |
tree | 072e9a28395eee87e539a48553b20fa9cb768e94 /fs/nfs | |
parent | d8a5ad75cc4d577987964e37a4c43b1c648c201e (diff) |
NFS: Another cleanup of the read/write request coalescing code
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/pagelist.c | 71 | ||||
-rw-r--r-- | fs/nfs/read.c | 62 | ||||
-rw-r--r-- | fs/nfs/write.c | 53 |
3 files changed, 113 insertions, 73 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 | */ |
230 | void nfs_pageio_init(struct nfs_pageio_descriptor *desc, unsigned int bsize) | 233 | void 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 | */ |
275 | static int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, | 287 | static 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 | */ | ||
319 | static 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 | */ | ||
345 | static 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 | */ | ||
379 | void 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 | ||
30 | static int nfs_pagein_one(struct list_head *, struct inode *); | 30 | static int nfs_pagein_multi(struct inode *, struct list_head *, size_t, int); |
31 | static int nfs_pagein_one(struct inode *, struct list_head *, size_t, int); | ||
31 | static const struct rpc_call_ops nfs_read_partial_ops; | 32 | static const struct rpc_call_ops nfs_read_partial_ops; |
32 | static const struct rpc_call_ops nfs_read_full_ops; | 33 | static 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 | */ |
233 | static int nfs_pagein_multi(struct list_head *head, struct inode *inode) | 237 | static 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 | ||
294 | static int nfs_pagein_one(struct list_head *head, struct inode *inode) | 294 | static 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 | ||
330 | static int | 324 | static int |
331 | nfs_pagein_list(struct list_head *head, unsigned int rsize) | 325 | nfs_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 | */ |
838 | static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how) | 838 | static 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 | */ |
907 | static int nfs_flush_one(struct inode *inode, struct list_head *head, int how) | 903 | static 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) | |||
946 | static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how) | 939 | static 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; |
970 | out_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 | /* |