diff options
author | Fred Isaman <iisaman@netapp.com> | 2012-04-20 14:47:44 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-04-27 14:10:37 -0400 |
commit | cd841605f7a721878d8a2d1362484723d8abf569 (patch) | |
tree | b5c37db575cd545a183577249909e042fe38d646 /fs/nfs/read.c | |
parent | b5542849764aa56fd3f05c0041195b637b9d2ac2 (diff) |
NFS: create common nfs_pgio_header for both read and write
In order to avoid duplicating all the data in nfs_read_data whenever we
split it up into multiple RPC calls (either due to a short read result
or due to rsize < PAGE_SIZE), we split out the bits that are the same
per RPC call into a separate "header" structure.
The goal this patch moves towards is to have a single header
refcounted by several rpc_data structures. Thus, want to always refer
from rpc_data to the header, and not the other way. This patch comes
close to that ideal, but the directio code currently needs some
special casing, isolated in the nfs_direct_[read_write]hdr_release()
functions. This will be dealt with in a future patch.
Signed-off-by: Fred Isaman <iisaman@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/read.c')
-rw-r--r-- | fs/nfs/read.c | 89 |
1 files changed, 51 insertions, 38 deletions
diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 4ddba670634..d6d46823d9e 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c | |||
@@ -35,19 +35,24 @@ static const struct rpc_call_ops nfs_read_full_ops; | |||
35 | 35 | ||
36 | static struct kmem_cache *nfs_rdata_cachep; | 36 | static struct kmem_cache *nfs_rdata_cachep; |
37 | 37 | ||
38 | struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) | 38 | struct nfs_read_header *nfs_readhdr_alloc(unsigned int pagecount) |
39 | { | 39 | { |
40 | struct nfs_read_data *p; | 40 | struct nfs_read_header *p; |
41 | 41 | ||
42 | p = kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL); | 42 | p = kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL); |
43 | if (p) { | 43 | if (p) { |
44 | INIT_LIST_HEAD(&p->pages); | 44 | struct nfs_pgio_header *hdr = &p->header; |
45 | p->npages = pagecount; | 45 | struct nfs_read_data *data = &p->rpc_data; |
46 | if (pagecount <= ARRAY_SIZE(p->page_array)) | 46 | |
47 | p->pagevec = p->page_array; | 47 | INIT_LIST_HEAD(&hdr->pages); |
48 | INIT_LIST_HEAD(&data->list); | ||
49 | data->npages = pagecount; | ||
50 | data->header = hdr; | ||
51 | if (pagecount <= ARRAY_SIZE(data->page_array)) | ||
52 | data->pagevec = data->page_array; | ||
48 | else { | 53 | else { |
49 | p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL); | 54 | data->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL); |
50 | if (!p->pagevec) { | 55 | if (!data->pagevec) { |
51 | kmem_cache_free(nfs_rdata_cachep, p); | 56 | kmem_cache_free(nfs_rdata_cachep, p); |
52 | p = NULL; | 57 | p = NULL; |
53 | } | 58 | } |
@@ -56,17 +61,19 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) | |||
56 | return p; | 61 | return p; |
57 | } | 62 | } |
58 | 63 | ||
59 | void nfs_readdata_free(struct nfs_read_data *p) | 64 | void nfs_readhdr_free(struct nfs_pgio_header *hdr) |
60 | { | 65 | { |
61 | if (p && (p->pagevec != &p->page_array[0])) | 66 | struct nfs_read_header *rhdr = container_of(hdr, struct nfs_read_header, header); |
62 | kfree(p->pagevec); | 67 | |
63 | kmem_cache_free(nfs_rdata_cachep, p); | 68 | kmem_cache_free(nfs_rdata_cachep, rhdr); |
64 | } | 69 | } |
65 | 70 | ||
66 | void nfs_readdata_release(struct nfs_read_data *rdata) | 71 | void nfs_readdata_release(struct nfs_read_data *rdata) |
67 | { | 72 | { |
68 | put_nfs_open_context(rdata->args.context); | 73 | put_nfs_open_context(rdata->args.context); |
69 | nfs_readdata_free(rdata); | 74 | if (rdata->pagevec != rdata->page_array) |
75 | kfree(rdata->pagevec); | ||
76 | nfs_readhdr_free(rdata->header); | ||
70 | } | 77 | } |
71 | 78 | ||
72 | static | 79 | static |
@@ -173,13 +180,13 @@ int nfs_initiate_read(struct rpc_clnt *clnt, | |||
173 | struct nfs_read_data *data, | 180 | struct nfs_read_data *data, |
174 | const struct rpc_call_ops *call_ops) | 181 | const struct rpc_call_ops *call_ops) |
175 | { | 182 | { |
176 | struct inode *inode = data->inode; | 183 | struct inode *inode = data->header->inode; |
177 | int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0; | 184 | int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0; |
178 | struct rpc_task *task; | 185 | struct rpc_task *task; |
179 | struct rpc_message msg = { | 186 | struct rpc_message msg = { |
180 | .rpc_argp = &data->args, | 187 | .rpc_argp = &data->args, |
181 | .rpc_resp = &data->res, | 188 | .rpc_resp = &data->res, |
182 | .rpc_cred = data->cred, | 189 | .rpc_cred = data->header->cred, |
183 | }; | 190 | }; |
184 | struct rpc_task_setup task_setup_data = { | 191 | struct rpc_task_setup task_setup_data = { |
185 | .task = &data->task, | 192 | .task = &data->task, |
@@ -216,11 +223,11 @@ EXPORT_SYMBOL_GPL(nfs_initiate_read); | |||
216 | static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, | 223 | static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, |
217 | unsigned int count, unsigned int offset) | 224 | unsigned int count, unsigned int offset) |
218 | { | 225 | { |
219 | struct inode *inode = req->wb_context->dentry->d_inode; | 226 | struct inode *inode = data->header->inode; |
220 | 227 | ||
221 | data->req = req; | 228 | data->header->req = req; |
222 | data->inode = inode; | 229 | data->header->inode = inode; |
223 | data->cred = req->wb_context->cred; | 230 | data->header->cred = req->wb_context->cred; |
224 | 231 | ||
225 | data->args.fh = NFS_FH(inode); | 232 | data->args.fh = NFS_FH(inode); |
226 | data->args.offset = req_offset(req) + offset; | 233 | data->args.offset = req_offset(req) + offset; |
@@ -239,7 +246,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, | |||
239 | static int nfs_do_read(struct nfs_read_data *data, | 246 | static int nfs_do_read(struct nfs_read_data *data, |
240 | const struct rpc_call_ops *call_ops) | 247 | const struct rpc_call_ops *call_ops) |
241 | { | 248 | { |
242 | struct inode *inode = data->args.context->dentry->d_inode; | 249 | struct inode *inode = data->header->inode; |
243 | 250 | ||
244 | return nfs_initiate_read(NFS_CLIENT(inode), data, call_ops); | 251 | return nfs_initiate_read(NFS_CLIENT(inode), data, call_ops); |
245 | } | 252 | } |
@@ -293,6 +300,7 @@ static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc, struct list_head | |||
293 | { | 300 | { |
294 | struct nfs_page *req = nfs_list_entry(desc->pg_list.next); | 301 | struct nfs_page *req = nfs_list_entry(desc->pg_list.next); |
295 | struct page *page = req->wb_page; | 302 | struct page *page = req->wb_page; |
303 | struct nfs_read_header *rhdr; | ||
296 | struct nfs_read_data *data; | 304 | struct nfs_read_data *data; |
297 | size_t rsize = desc->pg_bsize, nbytes; | 305 | size_t rsize = desc->pg_bsize, nbytes; |
298 | unsigned int offset; | 306 | unsigned int offset; |
@@ -306,9 +314,10 @@ static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc, struct list_head | |||
306 | do { | 314 | do { |
307 | size_t len = min(nbytes,rsize); | 315 | size_t len = min(nbytes,rsize); |
308 | 316 | ||
309 | data = nfs_readdata_alloc(1); | 317 | rhdr = nfs_readhdr_alloc(1); |
310 | if (!data) | 318 | if (!rhdr) |
311 | goto out_bad; | 319 | goto out_bad; |
320 | data = &rhdr->rpc_data; | ||
312 | data->pagevec[0] = page; | 321 | data->pagevec[0] = page; |
313 | nfs_read_rpcsetup(req, data, len, offset); | 322 | nfs_read_rpcsetup(req, data, len, offset); |
314 | list_add(&data->list, res); | 323 | list_add(&data->list, res); |
@@ -333,26 +342,28 @@ static int nfs_pagein_one(struct nfs_pageio_descriptor *desc, struct list_head * | |||
333 | { | 342 | { |
334 | struct nfs_page *req; | 343 | struct nfs_page *req; |
335 | struct page **pages; | 344 | struct page **pages; |
345 | struct nfs_read_header *rhdr; | ||
336 | struct nfs_read_data *data; | 346 | struct nfs_read_data *data; |
337 | struct list_head *head = &desc->pg_list; | 347 | struct list_head *head = &desc->pg_list; |
338 | int ret = 0; | 348 | int ret = 0; |
339 | 349 | ||
340 | data = nfs_readdata_alloc(nfs_page_array_len(desc->pg_base, | 350 | rhdr = nfs_readhdr_alloc(nfs_page_array_len(desc->pg_base, |
341 | desc->pg_count)); | 351 | desc->pg_count)); |
342 | if (!data) { | 352 | if (!rhdr) { |
343 | nfs_async_read_error(head); | 353 | nfs_async_read_error(head); |
344 | ret = -ENOMEM; | 354 | ret = -ENOMEM; |
345 | goto out; | 355 | goto out; |
346 | } | 356 | } |
347 | 357 | ||
358 | data = &rhdr->rpc_data; | ||
348 | pages = data->pagevec; | 359 | pages = data->pagevec; |
349 | while (!list_empty(head)) { | 360 | while (!list_empty(head)) { |
350 | req = nfs_list_entry(head->next); | 361 | req = nfs_list_entry(head->next); |
351 | nfs_list_remove_request(req); | 362 | nfs_list_remove_request(req); |
352 | nfs_list_add_request(req, &data->pages); | 363 | nfs_list_add_request(req, &rhdr->header.pages); |
353 | *pages++ = req->wb_page; | 364 | *pages++ = req->wb_page; |
354 | } | 365 | } |
355 | req = nfs_list_entry(data->pages.next); | 366 | req = nfs_list_entry(rhdr->header.pages.next); |
356 | 367 | ||
357 | nfs_read_rpcsetup(req, data, desc->pg_count, 0); | 368 | nfs_read_rpcsetup(req, data, desc->pg_count, 0); |
358 | list_add(&data->list, res); | 369 | list_add(&data->list, res); |
@@ -390,20 +401,21 @@ static const struct nfs_pageio_ops nfs_pageio_read_ops = { | |||
390 | */ | 401 | */ |
391 | int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) | 402 | int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) |
392 | { | 403 | { |
404 | struct inode *inode = data->header->inode; | ||
393 | int status; | 405 | int status; |
394 | 406 | ||
395 | dprintk("NFS: %s: %5u, (status %d)\n", __func__, task->tk_pid, | 407 | dprintk("NFS: %s: %5u, (status %d)\n", __func__, task->tk_pid, |
396 | task->tk_status); | 408 | task->tk_status); |
397 | 409 | ||
398 | status = NFS_PROTO(data->inode)->read_done(task, data); | 410 | status = NFS_PROTO(inode)->read_done(task, data); |
399 | if (status != 0) | 411 | if (status != 0) |
400 | return status; | 412 | return status; |
401 | 413 | ||
402 | nfs_add_stats(data->inode, NFSIOS_SERVERREADBYTES, data->res.count); | 414 | nfs_add_stats(inode, NFSIOS_SERVERREADBYTES, data->res.count); |
403 | 415 | ||
404 | if (task->tk_status == -ESTALE) { | 416 | if (task->tk_status == -ESTALE) { |
405 | set_bit(NFS_INO_STALE, &NFS_I(data->inode)->flags); | 417 | set_bit(NFS_INO_STALE, &NFS_I(inode)->flags); |
406 | nfs_mark_for_revalidate(data->inode); | 418 | nfs_mark_for_revalidate(inode); |
407 | } | 419 | } |
408 | return 0; | 420 | return 0; |
409 | } | 421 | } |
@@ -417,7 +429,7 @@ static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data | |||
417 | return; | 429 | return; |
418 | 430 | ||
419 | /* This is a short read! */ | 431 | /* This is a short read! */ |
420 | nfs_inc_stats(data->inode, NFSIOS_SHORTREAD); | 432 | nfs_inc_stats(data->header->inode, NFSIOS_SHORTREAD); |
421 | /* Has the server at least made some progress? */ | 433 | /* Has the server at least made some progress? */ |
422 | if (resp->count == 0) | 434 | if (resp->count == 0) |
423 | return; | 435 | return; |
@@ -449,7 +461,7 @@ static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata) | |||
449 | static void nfs_readpage_release_partial(void *calldata) | 461 | static void nfs_readpage_release_partial(void *calldata) |
450 | { | 462 | { |
451 | struct nfs_read_data *data = calldata; | 463 | struct nfs_read_data *data = calldata; |
452 | struct nfs_page *req = data->req; | 464 | struct nfs_page *req = data->header->req; |
453 | struct page *page = req->wb_page; | 465 | struct page *page = req->wb_page; |
454 | int status = data->task.tk_status; | 466 | int status = data->task.tk_status; |
455 | 467 | ||
@@ -461,13 +473,13 @@ static void nfs_readpage_release_partial(void *calldata) | |||
461 | SetPageUptodate(page); | 473 | SetPageUptodate(page); |
462 | nfs_readpage_release(req); | 474 | nfs_readpage_release(req); |
463 | } | 475 | } |
464 | nfs_readdata_release(calldata); | 476 | nfs_readdata_release(data); |
465 | } | 477 | } |
466 | 478 | ||
467 | void nfs_read_prepare(struct rpc_task *task, void *calldata) | 479 | void nfs_read_prepare(struct rpc_task *task, void *calldata) |
468 | { | 480 | { |
469 | struct nfs_read_data *data = calldata; | 481 | struct nfs_read_data *data = calldata; |
470 | NFS_PROTO(data->inode)->read_rpc_prepare(task, data); | 482 | NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data); |
471 | } | 483 | } |
472 | 484 | ||
473 | static const struct rpc_call_ops nfs_read_partial_ops = { | 485 | static const struct rpc_call_ops nfs_read_partial_ops = { |
@@ -524,9 +536,10 @@ static void nfs_readpage_result_full(struct rpc_task *task, void *calldata) | |||
524 | static void nfs_readpage_release_full(void *calldata) | 536 | static void nfs_readpage_release_full(void *calldata) |
525 | { | 537 | { |
526 | struct nfs_read_data *data = calldata; | 538 | struct nfs_read_data *data = calldata; |
539 | struct nfs_pgio_header *hdr = data->header; | ||
527 | 540 | ||
528 | while (!list_empty(&data->pages)) { | 541 | while (!list_empty(&hdr->pages)) { |
529 | struct nfs_page *req = nfs_list_entry(data->pages.next); | 542 | struct nfs_page *req = nfs_list_entry(hdr->pages.next); |
530 | 543 | ||
531 | nfs_list_remove_request(req); | 544 | nfs_list_remove_request(req); |
532 | nfs_readpage_release(req); | 545 | nfs_readpage_release(req); |
@@ -685,7 +698,7 @@ out: | |||
685 | int __init nfs_init_readpagecache(void) | 698 | int __init nfs_init_readpagecache(void) |
686 | { | 699 | { |
687 | nfs_rdata_cachep = kmem_cache_create("nfs_read_data", | 700 | nfs_rdata_cachep = kmem_cache_create("nfs_read_data", |
688 | sizeof(struct nfs_read_data), | 701 | sizeof(struct nfs_read_header), |
689 | 0, SLAB_HWCACHE_ALIGN, | 702 | 0, SLAB_HWCACHE_ALIGN, |
690 | NULL); | 703 | NULL); |
691 | if (nfs_rdata_cachep == NULL) | 704 | if (nfs_rdata_cachep == NULL) |