diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2007-05-11 01:22:51 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-11 11:29:35 -0400 |
commit | 0ea971801625184a91a6d80ea85e53875caa0bf5 (patch) | |
tree | 6f4144b7ba809fccfe6d75314df8c348596c9a01 | |
parent | e10cc1df1d2014f68a4bdcf73f6dd122c4561f94 (diff) |
consolidate generic_writepages and mpage_writepages
Clean up massive code duplication between mpage_writepages() and
generic_writepages().
The new generic function, write_cache_pages() takes a function pointer
argument, which will be called for each page to be written.
Maybe cifs_writepages() too can use this infrastructure, but I'm not
touching that with a ten-foot pole.
The upcoming page writeback support in fuse will also want this.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Acked-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/mpage.c | 174 | ||||
-rw-r--r-- | include/linux/mpage.h | 1 | ||||
-rw-r--r-- | include/linux/writeback.h | 10 | ||||
-rw-r--r-- | mm/page-writeback.c | 59 |
4 files changed, 93 insertions, 151 deletions
diff --git a/fs/mpage.c b/fs/mpage.c index 0fb914fc2ee0..c1698f2291aa 100644 --- a/fs/mpage.c +++ b/fs/mpage.c | |||
@@ -454,11 +454,18 @@ EXPORT_SYMBOL(mpage_readpage); | |||
454 | * written, so it can intelligently allocate a suitably-sized BIO. For now, | 454 | * written, so it can intelligently allocate a suitably-sized BIO. For now, |
455 | * just allocate full-size (16-page) BIOs. | 455 | * just allocate full-size (16-page) BIOs. |
456 | */ | 456 | */ |
457 | static struct bio * | 457 | struct mpage_data { |
458 | __mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, | 458 | struct bio *bio; |
459 | sector_t *last_block_in_bio, int *ret, struct writeback_control *wbc, | 459 | sector_t last_block_in_bio; |
460 | writepage_t writepage_fn) | 460 | get_block_t *get_block; |
461 | unsigned use_writepage; | ||
462 | }; | ||
463 | |||
464 | static int __mpage_writepage(struct page *page, struct writeback_control *wbc, | ||
465 | void *data) | ||
461 | { | 466 | { |
467 | struct mpage_data *mpd = data; | ||
468 | struct bio *bio = mpd->bio; | ||
462 | struct address_space *mapping = page->mapping; | 469 | struct address_space *mapping = page->mapping; |
463 | struct inode *inode = page->mapping->host; | 470 | struct inode *inode = page->mapping->host; |
464 | const unsigned blkbits = inode->i_blkbits; | 471 | const unsigned blkbits = inode->i_blkbits; |
@@ -476,6 +483,7 @@ __mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, | |||
476 | int length; | 483 | int length; |
477 | struct buffer_head map_bh; | 484 | struct buffer_head map_bh; |
478 | loff_t i_size = i_size_read(inode); | 485 | loff_t i_size = i_size_read(inode); |
486 | int ret = 0; | ||
479 | 487 | ||
480 | if (page_has_buffers(page)) { | 488 | if (page_has_buffers(page)) { |
481 | struct buffer_head *head = page_buffers(page); | 489 | struct buffer_head *head = page_buffers(page); |
@@ -538,7 +546,7 @@ __mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, | |||
538 | 546 | ||
539 | map_bh.b_state = 0; | 547 | map_bh.b_state = 0; |
540 | map_bh.b_size = 1 << blkbits; | 548 | map_bh.b_size = 1 << blkbits; |
541 | if (get_block(inode, block_in_file, &map_bh, 1)) | 549 | if (mpd->get_block(inode, block_in_file, &map_bh, 1)) |
542 | goto confused; | 550 | goto confused; |
543 | if (buffer_new(&map_bh)) | 551 | if (buffer_new(&map_bh)) |
544 | unmap_underlying_metadata(map_bh.b_bdev, | 552 | unmap_underlying_metadata(map_bh.b_bdev, |
@@ -584,7 +592,7 @@ page_is_mapped: | |||
584 | /* | 592 | /* |
585 | * This page will go to BIO. Do we need to send this BIO off first? | 593 | * This page will go to BIO. Do we need to send this BIO off first? |
586 | */ | 594 | */ |
587 | if (bio && *last_block_in_bio != blocks[0] - 1) | 595 | if (bio && mpd->last_block_in_bio != blocks[0] - 1) |
588 | bio = mpage_bio_submit(WRITE, bio); | 596 | bio = mpage_bio_submit(WRITE, bio); |
589 | 597 | ||
590 | alloc_new: | 598 | alloc_new: |
@@ -641,7 +649,7 @@ alloc_new: | |||
641 | boundary_block, 1 << blkbits); | 649 | boundary_block, 1 << blkbits); |
642 | } | 650 | } |
643 | } else { | 651 | } else { |
644 | *last_block_in_bio = blocks[blocks_per_page - 1]; | 652 | mpd->last_block_in_bio = blocks[blocks_per_page - 1]; |
645 | } | 653 | } |
646 | goto out; | 654 | goto out; |
647 | 655 | ||
@@ -649,18 +657,19 @@ confused: | |||
649 | if (bio) | 657 | if (bio) |
650 | bio = mpage_bio_submit(WRITE, bio); | 658 | bio = mpage_bio_submit(WRITE, bio); |
651 | 659 | ||
652 | if (writepage_fn) { | 660 | if (mpd->use_writepage) { |
653 | *ret = (*writepage_fn)(page, wbc); | 661 | ret = mapping->a_ops->writepage(page, wbc); |
654 | } else { | 662 | } else { |
655 | *ret = -EAGAIN; | 663 | ret = -EAGAIN; |
656 | goto out; | 664 | goto out; |
657 | } | 665 | } |
658 | /* | 666 | /* |
659 | * The caller has a ref on the inode, so *mapping is stable | 667 | * The caller has a ref on the inode, so *mapping is stable |
660 | */ | 668 | */ |
661 | mapping_set_error(mapping, *ret); | 669 | mapping_set_error(mapping, ret); |
662 | out: | 670 | out: |
663 | return bio; | 671 | mpd->bio = bio; |
672 | return ret; | ||
664 | } | 673 | } |
665 | 674 | ||
666 | /** | 675 | /** |
@@ -683,120 +692,27 @@ out: | |||
683 | * the call was made get new I/O started against them. If wbc->sync_mode is | 692 | * the call was made get new I/O started against them. If wbc->sync_mode is |
684 | * WB_SYNC_ALL then we were called for data integrity and we must wait for | 693 | * WB_SYNC_ALL then we were called for data integrity and we must wait for |
685 | * existing IO to complete. | 694 | * existing IO to complete. |
686 | * | ||
687 | * If you fix this you should check generic_writepages() also! | ||
688 | */ | 695 | */ |
689 | int | 696 | int |
690 | mpage_writepages(struct address_space *mapping, | 697 | mpage_writepages(struct address_space *mapping, |
691 | struct writeback_control *wbc, get_block_t get_block) | 698 | struct writeback_control *wbc, get_block_t get_block) |
692 | { | 699 | { |
693 | struct backing_dev_info *bdi = mapping->backing_dev_info; | 700 | int ret; |
694 | struct bio *bio = NULL; | 701 | |
695 | sector_t last_block_in_bio = 0; | 702 | if (!get_block) |
696 | int ret = 0; | 703 | ret = generic_writepages(mapping, wbc); |
697 | int done = 0; | 704 | else { |
698 | int (*writepage)(struct page *page, struct writeback_control *wbc); | 705 | struct mpage_data mpd = { |
699 | struct pagevec pvec; | 706 | .bio = NULL, |
700 | int nr_pages; | 707 | .last_block_in_bio = 0, |
701 | pgoff_t index; | 708 | .get_block = get_block, |
702 | pgoff_t end; /* Inclusive */ | 709 | .use_writepage = 1, |
703 | int scanned = 0; | 710 | }; |
704 | int range_whole = 0; | 711 | |
705 | 712 | ret = write_cache_pages(mapping, wbc, __mpage_writepage, &mpd); | |
706 | if (wbc->nonblocking && bdi_write_congested(bdi)) { | 713 | if (mpd.bio) |
707 | wbc->encountered_congestion = 1; | 714 | mpage_bio_submit(WRITE, mpd.bio); |
708 | return 0; | ||
709 | } | ||
710 | |||
711 | writepage = NULL; | ||
712 | if (get_block == NULL) | ||
713 | writepage = mapping->a_ops->writepage; | ||
714 | |||
715 | pagevec_init(&pvec, 0); | ||
716 | if (wbc->range_cyclic) { | ||
717 | index = mapping->writeback_index; /* Start from prev offset */ | ||
718 | end = -1; | ||
719 | } else { | ||
720 | index = wbc->range_start >> PAGE_CACHE_SHIFT; | ||
721 | end = wbc->range_end >> PAGE_CACHE_SHIFT; | ||
722 | if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) | ||
723 | range_whole = 1; | ||
724 | scanned = 1; | ||
725 | } | 715 | } |
726 | retry: | ||
727 | while (!done && (index <= end) && | ||
728 | (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, | ||
729 | PAGECACHE_TAG_DIRTY, | ||
730 | min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1))) { | ||
731 | unsigned i; | ||
732 | |||
733 | scanned = 1; | ||
734 | for (i = 0; i < nr_pages; i++) { | ||
735 | struct page *page = pvec.pages[i]; | ||
736 | |||
737 | /* | ||
738 | * At this point we hold neither mapping->tree_lock nor | ||
739 | * lock on the page itself: the page may be truncated or | ||
740 | * invalidated (changing page->mapping to NULL), or even | ||
741 | * swizzled back from swapper_space to tmpfs file | ||
742 | * mapping | ||
743 | */ | ||
744 | |||
745 | lock_page(page); | ||
746 | |||
747 | if (unlikely(page->mapping != mapping)) { | ||
748 | unlock_page(page); | ||
749 | continue; | ||
750 | } | ||
751 | |||
752 | if (!wbc->range_cyclic && page->index > end) { | ||
753 | done = 1; | ||
754 | unlock_page(page); | ||
755 | continue; | ||
756 | } | ||
757 | |||
758 | if (wbc->sync_mode != WB_SYNC_NONE) | ||
759 | wait_on_page_writeback(page); | ||
760 | |||
761 | if (PageWriteback(page) || | ||
762 | !clear_page_dirty_for_io(page)) { | ||
763 | unlock_page(page); | ||
764 | continue; | ||
765 | } | ||
766 | |||
767 | if (writepage) { | ||
768 | ret = (*writepage)(page, wbc); | ||
769 | mapping_set_error(mapping, ret); | ||
770 | } else { | ||
771 | bio = __mpage_writepage(bio, page, get_block, | ||
772 | &last_block_in_bio, &ret, wbc, | ||
773 | page->mapping->a_ops->writepage); | ||
774 | } | ||
775 | if (unlikely(ret == AOP_WRITEPAGE_ACTIVATE)) | ||
776 | unlock_page(page); | ||
777 | if (ret || (--(wbc->nr_to_write) <= 0)) | ||
778 | done = 1; | ||
779 | if (wbc->nonblocking && bdi_write_congested(bdi)) { | ||
780 | wbc->encountered_congestion = 1; | ||
781 | done = 1; | ||
782 | } | ||
783 | } | ||
784 | pagevec_release(&pvec); | ||
785 | cond_resched(); | ||
786 | } | ||
787 | if (!scanned && !done) { | ||
788 | /* | ||
789 | * We hit the last page and there is more work to be done: wrap | ||
790 | * back to the start of the file | ||
791 | */ | ||
792 | scanned = 1; | ||
793 | index = 0; | ||
794 | goto retry; | ||
795 | } | ||
796 | if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) | ||
797 | mapping->writeback_index = index; | ||
798 | if (bio) | ||
799 | mpage_bio_submit(WRITE, bio); | ||
800 | return ret; | 716 | return ret; |
801 | } | 717 | } |
802 | EXPORT_SYMBOL(mpage_writepages); | 718 | EXPORT_SYMBOL(mpage_writepages); |
@@ -804,15 +720,15 @@ EXPORT_SYMBOL(mpage_writepages); | |||
804 | int mpage_writepage(struct page *page, get_block_t get_block, | 720 | int mpage_writepage(struct page *page, get_block_t get_block, |
805 | struct writeback_control *wbc) | 721 | struct writeback_control *wbc) |
806 | { | 722 | { |
807 | int ret = 0; | 723 | struct mpage_data mpd = { |
808 | struct bio *bio; | 724 | .bio = NULL, |
809 | sector_t last_block_in_bio = 0; | 725 | .last_block_in_bio = 0, |
810 | 726 | .get_block = get_block, | |
811 | bio = __mpage_writepage(NULL, page, get_block, | 727 | .use_writepage = 0, |
812 | &last_block_in_bio, &ret, wbc, NULL); | 728 | }; |
813 | if (bio) | 729 | int ret = __mpage_writepage(page, wbc, &mpd); |
814 | mpage_bio_submit(WRITE, bio); | 730 | if (mpd.bio) |
815 | 731 | mpage_bio_submit(WRITE, mpd.bio); | |
816 | return ret; | 732 | return ret; |
817 | } | 733 | } |
818 | EXPORT_SYMBOL(mpage_writepage); | 734 | EXPORT_SYMBOL(mpage_writepage); |
diff --git a/include/linux/mpage.h b/include/linux/mpage.h index cc5fb75af78a..068a0c9946af 100644 --- a/include/linux/mpage.h +++ b/include/linux/mpage.h | |||
@@ -12,7 +12,6 @@ | |||
12 | #ifdef CONFIG_BLOCK | 12 | #ifdef CONFIG_BLOCK |
13 | 13 | ||
14 | struct writeback_control; | 14 | struct writeback_control; |
15 | typedef int (writepage_t)(struct page *page, struct writeback_control *wbc); | ||
16 | 15 | ||
17 | int mpage_readpages(struct address_space *mapping, struct list_head *pages, | 16 | int mpage_readpages(struct address_space *mapping, struct list_head *pages, |
18 | unsigned nr_pages, get_block_t get_block); | 17 | unsigned nr_pages, get_block_t get_block); |
diff --git a/include/linux/writeback.h b/include/linux/writeback.h index daa6c125f66e..050915b59576 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h | |||
@@ -111,9 +111,15 @@ balance_dirty_pages_ratelimited(struct address_space *mapping) | |||
111 | balance_dirty_pages_ratelimited_nr(mapping, 1); | 111 | balance_dirty_pages_ratelimited_nr(mapping, 1); |
112 | } | 112 | } |
113 | 113 | ||
114 | typedef int (*writepage_t)(struct page *page, struct writeback_control *wbc, | ||
115 | void *data); | ||
116 | |||
114 | int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0); | 117 | int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0); |
115 | extern int generic_writepages(struct address_space *mapping, | 118 | int generic_writepages(struct address_space *mapping, |
116 | struct writeback_control *wbc); | 119 | struct writeback_control *wbc); |
120 | int write_cache_pages(struct address_space *mapping, | ||
121 | struct writeback_control *wbc, writepage_t writepage, | ||
122 | void *data); | ||
117 | int do_writepages(struct address_space *mapping, struct writeback_control *wbc); | 123 | int do_writepages(struct address_space *mapping, struct writeback_control *wbc); |
118 | int sync_page_range(struct inode *inode, struct address_space *mapping, | 124 | int sync_page_range(struct inode *inode, struct address_space *mapping, |
119 | loff_t pos, loff_t count); | 125 | loff_t pos, loff_t count); |
diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 63cd88840eb2..eec1481ba44f 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c | |||
@@ -588,31 +588,27 @@ void __init page_writeback_init(void) | |||
588 | } | 588 | } |
589 | 589 | ||
590 | /** | 590 | /** |
591 | * generic_writepages - walk the list of dirty pages of the given address space and writepage() all of them. | 591 | * write_cache_pages - walk the list of dirty pages of the given address space and write all of them. |
592 | * @mapping: address space structure to write | 592 | * @mapping: address space structure to write |
593 | * @wbc: subtract the number of written pages from *@wbc->nr_to_write | 593 | * @wbc: subtract the number of written pages from *@wbc->nr_to_write |
594 | * @writepage: function called for each page | ||
595 | * @data: data passed to writepage function | ||
594 | * | 596 | * |
595 | * This is a library function, which implements the writepages() | 597 | * If a page is already under I/O, write_cache_pages() skips it, even |
596 | * address_space_operation. | ||
597 | * | ||
598 | * If a page is already under I/O, generic_writepages() skips it, even | ||
599 | * if it's dirty. This is desirable behaviour for memory-cleaning writeback, | 598 | * if it's dirty. This is desirable behaviour for memory-cleaning writeback, |
600 | * but it is INCORRECT for data-integrity system calls such as fsync(). fsync() | 599 | * but it is INCORRECT for data-integrity system calls such as fsync(). fsync() |
601 | * and msync() need to guarantee that all the data which was dirty at the time | 600 | * and msync() need to guarantee that all the data which was dirty at the time |
602 | * the call was made get new I/O started against them. If wbc->sync_mode is | 601 | * the call was made get new I/O started against them. If wbc->sync_mode is |
603 | * WB_SYNC_ALL then we were called for data integrity and we must wait for | 602 | * WB_SYNC_ALL then we were called for data integrity and we must wait for |
604 | * existing IO to complete. | 603 | * existing IO to complete. |
605 | * | ||
606 | * Derived from mpage_writepages() - if you fix this you should check that | ||
607 | * also! | ||
608 | */ | 604 | */ |
609 | int generic_writepages(struct address_space *mapping, | 605 | int write_cache_pages(struct address_space *mapping, |
610 | struct writeback_control *wbc) | 606 | struct writeback_control *wbc, writepage_t writepage, |
607 | void *data) | ||
611 | { | 608 | { |
612 | struct backing_dev_info *bdi = mapping->backing_dev_info; | 609 | struct backing_dev_info *bdi = mapping->backing_dev_info; |
613 | int ret = 0; | 610 | int ret = 0; |
614 | int done = 0; | 611 | int done = 0; |
615 | int (*writepage)(struct page *page, struct writeback_control *wbc); | ||
616 | struct pagevec pvec; | 612 | struct pagevec pvec; |
617 | int nr_pages; | 613 | int nr_pages; |
618 | pgoff_t index; | 614 | pgoff_t index; |
@@ -625,12 +621,6 @@ int generic_writepages(struct address_space *mapping, | |||
625 | return 0; | 621 | return 0; |
626 | } | 622 | } |
627 | 623 | ||
628 | writepage = mapping->a_ops->writepage; | ||
629 | |||
630 | /* deal with chardevs and other special file */ | ||
631 | if (!writepage) | ||
632 | return 0; | ||
633 | |||
634 | pagevec_init(&pvec, 0); | 624 | pagevec_init(&pvec, 0); |
635 | if (wbc->range_cyclic) { | 625 | if (wbc->range_cyclic) { |
636 | index = mapping->writeback_index; /* Start from prev offset */ | 626 | index = mapping->writeback_index; /* Start from prev offset */ |
@@ -682,8 +672,7 @@ retry: | |||
682 | continue; | 672 | continue; |
683 | } | 673 | } |
684 | 674 | ||
685 | ret = (*writepage)(page, wbc); | 675 | ret = (*writepage)(page, wbc, data); |
686 | mapping_set_error(mapping, ret); | ||
687 | 676 | ||
688 | if (unlikely(ret == AOP_WRITEPAGE_ACTIVATE)) | 677 | if (unlikely(ret == AOP_WRITEPAGE_ACTIVATE)) |
689 | unlock_page(page); | 678 | unlock_page(page); |
@@ -710,6 +699,38 @@ retry: | |||
710 | mapping->writeback_index = index; | 699 | mapping->writeback_index = index; |
711 | return ret; | 700 | return ret; |
712 | } | 701 | } |
702 | EXPORT_SYMBOL(write_cache_pages); | ||
703 | |||
704 | /* | ||
705 | * Function used by generic_writepages to call the real writepage | ||
706 | * function and set the mapping flags on error | ||
707 | */ | ||
708 | static int __writepage(struct page *page, struct writeback_control *wbc, | ||
709 | void *data) | ||
710 | { | ||
711 | struct address_space *mapping = data; | ||
712 | int ret = mapping->a_ops->writepage(page, wbc); | ||
713 | mapping_set_error(mapping, ret); | ||
714 | return ret; | ||
715 | } | ||
716 | |||
717 | /** | ||
718 | * generic_writepages - walk the list of dirty pages of the given address space and writepage() all of them. | ||
719 | * @mapping: address space structure to write | ||
720 | * @wbc: subtract the number of written pages from *@wbc->nr_to_write | ||
721 | * | ||
722 | * This is a library function, which implements the writepages() | ||
723 | * address_space_operation. | ||
724 | */ | ||
725 | int generic_writepages(struct address_space *mapping, | ||
726 | struct writeback_control *wbc) | ||
727 | { | ||
728 | /* deal with chardevs and other special file */ | ||
729 | if (!mapping->a_ops->writepage) | ||
730 | return 0; | ||
731 | |||
732 | return write_cache_pages(mapping, wbc, __writepage, mapping); | ||
733 | } | ||
713 | 734 | ||
714 | EXPORT_SYMBOL(generic_writepages); | 735 | EXPORT_SYMBOL(generic_writepages); |
715 | 736 | ||