diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-03-15 17:16:40 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-03-17 11:09:33 -0400 |
commit | 8dd3775889345850ecddd689b5c200cdd91bd8c9 (patch) | |
tree | ea697cfcac3f3a927e90d0048e9ed76b5a3ea8e5 /fs/nfs/write.c | |
parent | 95a13f7b33be87d85d8e6652126a3f4d64d164db (diff) |
NFSv4.1: Clean ups and bugfixes for the pNFS read/writeback/commit code
Move more pnfs-isms out of the generic commit code.
Bugfixes:
- filelayout_scan_commit_lists doesn't need to get/put the lseg.
In fact since it is run under the inode->i_lock, the lseg_put()
can deadlock.
- Ensure that we distinguish between what needs to be done for
commit-to-data server and what needs to be done for commit-to-MDS
using the new flag PG_COMMIT_TO_DS. Otherwise we may end up calling
put_lseg() on a bucket for a struct nfs_page that got written
through the MDS.
- Fix a case where we were using list_del() on an nfs_page->wb_list
instead of list_del_init().
- filelayout_initiate_commit needs to call filelayout_commit_release
on error instead of the mds_ops->rpc_release(). Otherwise it won't
clear the commit lock.
Cleanups:
- Let the files layout manage the commit lists for the pNFS case.
Don't expose stuff like pnfs_choose_commit_list, and the fact
that the commit buckets hold references to the layout segment
in common code.
- Cast out the put_lseg() calls for the struct nfs_read/write_data->lseg
into the pNFS layer from whence they came.
- Let the pNFS layer manage the NFS_INO_PNFS_COMMIT bit.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: Fred Isaman <iisaman@netapp.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r-- | fs/nfs/write.c | 124 |
1 files changed, 77 insertions, 47 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index a630ad65d64c..0de19f413f92 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -100,7 +100,6 @@ void nfs_writedata_free(struct nfs_write_data *p) | |||
100 | 100 | ||
101 | void nfs_writedata_release(struct nfs_write_data *wdata) | 101 | void nfs_writedata_release(struct nfs_write_data *wdata) |
102 | { | 102 | { |
103 | put_lseg(wdata->lseg); | ||
104 | put_nfs_open_context(wdata->args.context); | 103 | put_nfs_open_context(wdata->args.context); |
105 | nfs_writedata_free(wdata); | 104 | nfs_writedata_free(wdata); |
106 | } | 105 | } |
@@ -393,8 +392,6 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) | |||
393 | spin_unlock(&inode->i_lock); | 392 | spin_unlock(&inode->i_lock); |
394 | } | 393 | } |
395 | 394 | ||
396 | static struct pnfs_layout_segment *nfs_clear_request_commit(struct nfs_page *req); | ||
397 | |||
398 | /* | 395 | /* |
399 | * Remove a write request from an inode | 396 | * Remove a write request from an inode |
400 | */ | 397 | */ |
@@ -402,18 +399,15 @@ static void nfs_inode_remove_request(struct nfs_page *req) | |||
402 | { | 399 | { |
403 | struct inode *inode = req->wb_context->dentry->d_inode; | 400 | struct inode *inode = req->wb_context->dentry->d_inode; |
404 | struct nfs_inode *nfsi = NFS_I(inode); | 401 | struct nfs_inode *nfsi = NFS_I(inode); |
405 | struct pnfs_layout_segment *lseg; | ||
406 | 402 | ||
407 | BUG_ON (!NFS_WBACK_BUSY(req)); | 403 | BUG_ON (!NFS_WBACK_BUSY(req)); |
408 | 404 | ||
409 | spin_lock(&inode->i_lock); | 405 | spin_lock(&inode->i_lock); |
410 | lseg = nfs_clear_request_commit(req); | ||
411 | set_page_private(req->wb_page, 0); | 406 | set_page_private(req->wb_page, 0); |
412 | ClearPagePrivate(req->wb_page); | 407 | ClearPagePrivate(req->wb_page); |
413 | clear_bit(PG_MAPPED, &req->wb_flags); | 408 | clear_bit(PG_MAPPED, &req->wb_flags); |
414 | nfsi->npages--; | 409 | nfsi->npages--; |
415 | spin_unlock(&inode->i_lock); | 410 | spin_unlock(&inode->i_lock); |
416 | put_lseg(lseg); | ||
417 | nfs_release_request(req); | 411 | nfs_release_request(req); |
418 | } | 412 | } |
419 | 413 | ||
@@ -424,26 +418,69 @@ nfs_mark_request_dirty(struct nfs_page *req) | |||
424 | } | 418 | } |
425 | 419 | ||
426 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 420 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
427 | /* | 421 | /** |
428 | * Add a request to the inode's commit list. | 422 | * nfs_request_add_commit_list - add request to a commit list |
423 | * @req: pointer to a struct nfs_page | ||
424 | * @head: commit list head | ||
425 | * | ||
426 | * This sets the PG_CLEAN bit, updates the inode global count of | ||
427 | * number of outstanding requests requiring a commit as well as | ||
428 | * the MM page stats. | ||
429 | * | ||
430 | * The caller must _not_ hold the inode->i_lock, but must be | ||
431 | * holding the nfs_page lock. | ||
429 | */ | 432 | */ |
430 | static void | 433 | void |
431 | nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) | 434 | nfs_request_add_commit_list(struct nfs_page *req, struct list_head *head) |
432 | { | 435 | { |
433 | struct inode *inode = req->wb_context->dentry->d_inode; | 436 | struct inode *inode = req->wb_context->dentry->d_inode; |
434 | struct nfs_inode *nfsi = NFS_I(inode); | ||
435 | struct list_head *clist; | ||
436 | 437 | ||
437 | clist = pnfs_choose_commit_list(req, lseg); | ||
438 | spin_lock(&inode->i_lock); | ||
439 | set_bit(PG_CLEAN, &(req)->wb_flags); | 438 | set_bit(PG_CLEAN, &(req)->wb_flags); |
440 | nfs_list_add_request(req, clist); | 439 | spin_lock(&inode->i_lock); |
441 | nfsi->ncommit++; | 440 | nfs_list_add_request(req, head); |
441 | NFS_I(inode)->ncommit++; | ||
442 | spin_unlock(&inode->i_lock); | 442 | spin_unlock(&inode->i_lock); |
443 | inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); | 443 | inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); |
444 | inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); | 444 | inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); |
445 | __mark_inode_dirty(inode, I_DIRTY_DATASYNC); | 445 | __mark_inode_dirty(inode, I_DIRTY_DATASYNC); |
446 | } | 446 | } |
447 | EXPORT_SYMBOL_GPL(nfs_request_add_commit_list); | ||
448 | |||
449 | /** | ||
450 | * nfs_request_remove_commit_list - Remove request from a commit list | ||
451 | * @req: pointer to a nfs_page | ||
452 | * | ||
453 | * This clears the PG_CLEAN bit, and updates the inode global count of | ||
454 | * number of outstanding requests requiring a commit | ||
455 | * It does not update the MM page stats. | ||
456 | * | ||
457 | * The caller _must_ hold the inode->i_lock and the nfs_page lock. | ||
458 | */ | ||
459 | void | ||
460 | nfs_request_remove_commit_list(struct nfs_page *req) | ||
461 | { | ||
462 | struct inode *inode = req->wb_context->dentry->d_inode; | ||
463 | |||
464 | if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) | ||
465 | return; | ||
466 | nfs_list_remove_request(req); | ||
467 | NFS_I(inode)->ncommit--; | ||
468 | } | ||
469 | EXPORT_SYMBOL_GPL(nfs_request_remove_commit_list); | ||
470 | |||
471 | |||
472 | /* | ||
473 | * Add a request to the inode's commit list. | ||
474 | */ | ||
475 | static void | ||
476 | nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) | ||
477 | { | ||
478 | struct inode *inode = req->wb_context->dentry->d_inode; | ||
479 | |||
480 | if (pnfs_mark_request_commit(req, lseg)) | ||
481 | return; | ||
482 | nfs_request_add_commit_list(req, &NFS_I(inode)->commit_list); | ||
483 | } | ||
447 | 484 | ||
448 | static void | 485 | static void |
449 | nfs_clear_page_commit(struct page *page) | 486 | nfs_clear_page_commit(struct page *page) |
@@ -452,18 +489,19 @@ nfs_clear_page_commit(struct page *page) | |||
452 | dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); | 489 | dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); |
453 | } | 490 | } |
454 | 491 | ||
455 | static struct pnfs_layout_segment * | 492 | static void |
456 | nfs_clear_request_commit(struct nfs_page *req) | 493 | nfs_clear_request_commit(struct nfs_page *req) |
457 | { | 494 | { |
458 | struct pnfs_layout_segment *lseg = NULL; | 495 | if (test_bit(PG_CLEAN, &req->wb_flags)) { |
496 | struct inode *inode = req->wb_context->dentry->d_inode; | ||
459 | 497 | ||
460 | if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) { | 498 | if (!pnfs_clear_request_commit(req)) { |
499 | spin_lock(&inode->i_lock); | ||
500 | nfs_request_remove_commit_list(req); | ||
501 | spin_unlock(&inode->i_lock); | ||
502 | } | ||
461 | nfs_clear_page_commit(req->wb_page); | 503 | nfs_clear_page_commit(req->wb_page); |
462 | lseg = pnfs_clear_request_commit(req); | ||
463 | NFS_I(req->wb_context->dentry->d_inode)->ncommit--; | ||
464 | list_del(&req->wb_list); | ||
465 | } | 504 | } |
466 | return lseg; | ||
467 | } | 505 | } |
468 | 506 | ||
469 | static inline | 507 | static inline |
@@ -490,15 +528,14 @@ int nfs_reschedule_unstable_write(struct nfs_page *req, | |||
490 | return 0; | 528 | return 0; |
491 | } | 529 | } |
492 | #else | 530 | #else |
493 | static inline void | 531 | static void |
494 | nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) | 532 | nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) |
495 | { | 533 | { |
496 | } | 534 | } |
497 | 535 | ||
498 | static inline struct pnfs_layout_segment * | 536 | static void |
499 | nfs_clear_request_commit(struct nfs_page *req) | 537 | nfs_clear_request_commit(struct nfs_page *req) |
500 | { | 538 | { |
501 | return NULL; | ||
502 | } | 539 | } |
503 | 540 | ||
504 | static inline | 541 | static inline |
@@ -523,25 +560,23 @@ nfs_need_commit(struct nfs_inode *nfsi) | |||
523 | } | 560 | } |
524 | 561 | ||
525 | /* i_lock held by caller */ | 562 | /* i_lock held by caller */ |
526 | int | 563 | static int |
527 | nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max) | 564 | nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max) |
528 | { | 565 | { |
529 | struct nfs_page *req, *tmp; | 566 | struct nfs_page *req, *tmp; |
530 | int ret = 0; | 567 | int ret = 0; |
531 | 568 | ||
532 | list_for_each_entry_safe(req, tmp, src, wb_list) { | 569 | list_for_each_entry_safe(req, tmp, src, wb_list) { |
533 | if (nfs_lock_request_dontget(req)) { | 570 | if (!nfs_lock_request(req)) |
534 | kref_get(&req->wb_kref); | 571 | continue; |
535 | list_move_tail(&req->wb_list, dst); | 572 | nfs_request_remove_commit_list(req); |
536 | clear_bit(PG_CLEAN, &(req)->wb_flags); | 573 | nfs_list_add_request(req, dst); |
537 | ret++; | 574 | ret++; |
538 | if (ret == max) | 575 | if (ret == max) |
539 | break; | 576 | break; |
540 | } | ||
541 | } | 577 | } |
542 | return ret; | 578 | return ret; |
543 | } | 579 | } |
544 | EXPORT_SYMBOL_GPL(nfs_scan_commit_list); | ||
545 | 580 | ||
546 | /* | 581 | /* |
547 | * nfs_scan_commit - Scan an inode for commit requests | 582 | * nfs_scan_commit - Scan an inode for commit requests |
@@ -559,14 +594,12 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst) | |||
559 | 594 | ||
560 | spin_lock(&inode->i_lock); | 595 | spin_lock(&inode->i_lock); |
561 | if (nfsi->ncommit > 0) { | 596 | if (nfsi->ncommit > 0) { |
597 | const int max = INT_MAX; | ||
562 | int pnfs_ret; | 598 | int pnfs_ret; |
563 | 599 | ||
564 | ret = nfs_scan_commit_list(&nfsi->commit_list, dst, INT_MAX); | 600 | ret = nfs_scan_commit_list(&nfsi->commit_list, dst, max); |
565 | pnfs_ret = pnfs_scan_commit_lists(inode, INT_MAX - ret); | 601 | pnfs_ret = pnfs_scan_commit_lists(inode, max - ret); |
566 | if (pnfs_ret) { | 602 | ret += pnfs_ret; |
567 | ret += pnfs_ret; | ||
568 | set_bit(NFS_INO_PNFS_COMMIT, &nfsi->flags); | ||
569 | } | ||
570 | nfsi->ncommit -= ret; | 603 | nfsi->ncommit -= ret; |
571 | } | 604 | } |
572 | spin_unlock(&inode->i_lock); | 605 | spin_unlock(&inode->i_lock); |
@@ -601,7 +634,6 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, | |||
601 | unsigned int rqend; | 634 | unsigned int rqend; |
602 | unsigned int end; | 635 | unsigned int end; |
603 | int error; | 636 | int error; |
604 | struct pnfs_layout_segment *lseg = NULL; | ||
605 | 637 | ||
606 | if (!PagePrivate(page)) | 638 | if (!PagePrivate(page)) |
607 | return NULL; | 639 | return NULL; |
@@ -637,8 +669,6 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, | |||
637 | spin_lock(&inode->i_lock); | 669 | spin_lock(&inode->i_lock); |
638 | } | 670 | } |
639 | 671 | ||
640 | lseg = nfs_clear_request_commit(req); | ||
641 | |||
642 | /* Okay, the request matches. Update the region */ | 672 | /* Okay, the request matches. Update the region */ |
643 | if (offset < req->wb_offset) { | 673 | if (offset < req->wb_offset) { |
644 | req->wb_offset = offset; | 674 | req->wb_offset = offset; |
@@ -650,7 +680,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, | |||
650 | req->wb_bytes = rqend - req->wb_offset; | 680 | req->wb_bytes = rqend - req->wb_offset; |
651 | out_unlock: | 681 | out_unlock: |
652 | spin_unlock(&inode->i_lock); | 682 | spin_unlock(&inode->i_lock); |
653 | put_lseg(lseg); | 683 | nfs_clear_request_commit(req); |
654 | return req; | 684 | return req; |
655 | out_flushme: | 685 | out_flushme: |
656 | spin_unlock(&inode->i_lock); | 686 | spin_unlock(&inode->i_lock); |
@@ -1337,7 +1367,6 @@ void nfs_commitdata_release(void *data) | |||
1337 | { | 1367 | { |
1338 | struct nfs_write_data *wdata = data; | 1368 | struct nfs_write_data *wdata = data; |
1339 | 1369 | ||
1340 | put_lseg(wdata->lseg); | ||
1341 | put_nfs_open_context(wdata->args.context); | 1370 | put_nfs_open_context(wdata->args.context); |
1342 | nfs_commit_free(wdata); | 1371 | nfs_commit_free(wdata); |
1343 | } | 1372 | } |
@@ -1647,6 +1676,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page) | |||
1647 | if (req == NULL) | 1676 | if (req == NULL) |
1648 | break; | 1677 | break; |
1649 | if (nfs_lock_request_dontget(req)) { | 1678 | if (nfs_lock_request_dontget(req)) { |
1679 | nfs_clear_request_commit(req); | ||
1650 | nfs_inode_remove_request(req); | 1680 | nfs_inode_remove_request(req); |
1651 | /* | 1681 | /* |
1652 | * In case nfs_inode_remove_request has marked the | 1682 | * In case nfs_inode_remove_request has marked the |