diff options
-rw-r--r-- | fs/nfs/pagelist.c | 117 | ||||
-rw-r--r-- | fs/nfs/read.c | 24 | ||||
-rw-r--r-- | fs/nfs/write.c | 11 | ||||
-rw-r--r-- | include/linux/nfs_page.h | 13 |
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 | */ | ||
230 | void 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 | */ | ||
249 | static 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 | */ | ||
275 | static 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 | */ |
235 | int | 313 | void nfs_pageio_add_list(struct nfs_pageio_descriptor *desc, |
236 | nfs_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 | ||
330 | static int | 330 | static int |
331 | nfs_pagein_list(struct list_head *head, int rpages) | 331 | nfs_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 | ||
946 | static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how) | 946 | static 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; |
971 | out_err: | 970 | out_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 | ||
51 | struct 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 | ||
53 | extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx, | 60 | 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 | struct list_head *dst); | 71 | struct list_head *dst); |
65 | extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst, | 72 | extern 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); |
67 | extern int nfs_coalesce_requests(struct list_head *, struct list_head *, | 74 | extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc, |
68 | unsigned int); | 75 | size_t iosize); |
76 | extern void nfs_pageio_add_list(struct nfs_pageio_descriptor *, | ||
77 | struct list_head *); | ||
69 | extern int nfs_wait_on_request(struct nfs_page *); | 78 | extern int nfs_wait_on_request(struct nfs_page *); |
70 | extern void nfs_unlock_request(struct nfs_page *req); | 79 | extern void nfs_unlock_request(struct nfs_page *req); |
71 | extern int nfs_set_page_writeback_locked(struct nfs_page *req); | 80 | extern int nfs_set_page_writeback_locked(struct nfs_page *req); |