Commit 811d736f9e8013966e1a5a930c0db09508bdbb15
Committed by
Jens Axboe
1 parent
7b0de42d7c
Exists in
master
and in
20 other branches
[PATCH] BLOCK: Dissociate generic_writepages() from mpage stuff [try #6]
Dissociate the generic_writepages() function from the mpage stuff, moving its declaration to linux/mm.h and actually emitting a full implementation into mm/page-writeback.c. The implementation is a partial duplicate of mpage_writepages() with all BIO references removed. It is used by NFS to do writeback. Signed-Off-By: David Howells <dhowells@redhat.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
Showing 5 changed files with 139 additions and 6 deletions Side-by-side Diff
fs/block_dev.c
fs/mpage.c
... | ... | @@ -693,6 +693,8 @@ |
693 | 693 | * the call was made get new I/O started against them. If wbc->sync_mode is |
694 | 694 | * WB_SYNC_ALL then we were called for data integrity and we must wait for |
695 | 695 | * existing IO to complete. |
696 | + * | |
697 | + * If you fix this you should check generic_writepages() also! | |
696 | 698 | */ |
697 | 699 | int |
698 | 700 | mpage_writepages(struct address_space *mapping, |
include/linux/mpage.h
... | ... | @@ -20,10 +20,4 @@ |
20 | 20 | struct writeback_control *wbc, get_block_t get_block); |
21 | 21 | int mpage_writepage(struct page *page, get_block_t *get_block, |
22 | 22 | struct writeback_control *wbc); |
23 | - | |
24 | -static inline int | |
25 | -generic_writepages(struct address_space *mapping, struct writeback_control *wbc) | |
26 | -{ | |
27 | - return mpage_writepages(mapping, wbc, NULL); | |
28 | -} |
include/linux/writeback.h
... | ... | @@ -111,6 +111,8 @@ |
111 | 111 | } |
112 | 112 | |
113 | 113 | int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0); |
114 | +extern int generic_writepages(struct address_space *mapping, | |
115 | + struct writeback_control *wbc); | |
114 | 116 | int do_writepages(struct address_space *mapping, struct writeback_control *wbc); |
115 | 117 | int sync_page_range(struct inode *inode, struct address_space *mapping, |
116 | 118 | loff_t pos, loff_t count); |
mm/page-writeback.c
... | ... | @@ -31,6 +31,7 @@ |
31 | 31 | #include <linux/cpu.h> |
32 | 32 | #include <linux/syscalls.h> |
33 | 33 | #include <linux/buffer_head.h> |
34 | +#include <linux/pagevec.h> | |
34 | 35 | |
35 | 36 | /* |
36 | 37 | * The maximum number of pages to writeout in a single bdflush/kupdate |
... | ... | @@ -550,6 +551,139 @@ |
550 | 551 | writeback_set_ratelimit(); |
551 | 552 | register_cpu_notifier(&ratelimit_nb); |
552 | 553 | } |
554 | + | |
555 | +/** | |
556 | + * generic_writepages - walk the list of dirty pages of the given | |
557 | + * address space and writepage() all of them. | |
558 | + * | |
559 | + * @mapping: address space structure to write | |
560 | + * @wbc: subtract the number of written pages from *@wbc->nr_to_write | |
561 | + * | |
562 | + * This is a library function, which implements the writepages() | |
563 | + * address_space_operation. | |
564 | + * | |
565 | + * If a page is already under I/O, generic_writepages() skips it, even | |
566 | + * if it's dirty. This is desirable behaviour for memory-cleaning writeback, | |
567 | + * but it is INCORRECT for data-integrity system calls such as fsync(). fsync() | |
568 | + * and msync() need to guarantee that all the data which was dirty at the time | |
569 | + * the call was made get new I/O started against them. If wbc->sync_mode is | |
570 | + * WB_SYNC_ALL then we were called for data integrity and we must wait for | |
571 | + * existing IO to complete. | |
572 | + * | |
573 | + * Derived from mpage_writepages() - if you fix this you should check that | |
574 | + * also! | |
575 | + */ | |
576 | +int generic_writepages(struct address_space *mapping, | |
577 | + struct writeback_control *wbc) | |
578 | +{ | |
579 | + struct backing_dev_info *bdi = mapping->backing_dev_info; | |
580 | + int ret = 0; | |
581 | + int done = 0; | |
582 | + int (*writepage)(struct page *page, struct writeback_control *wbc); | |
583 | + struct pagevec pvec; | |
584 | + int nr_pages; | |
585 | + pgoff_t index; | |
586 | + pgoff_t end; /* Inclusive */ | |
587 | + int scanned = 0; | |
588 | + int range_whole = 0; | |
589 | + | |
590 | + if (wbc->nonblocking && bdi_write_congested(bdi)) { | |
591 | + wbc->encountered_congestion = 1; | |
592 | + return 0; | |
593 | + } | |
594 | + | |
595 | + writepage = mapping->a_ops->writepage; | |
596 | + | |
597 | + /* deal with chardevs and other special file */ | |
598 | + if (!writepage) | |
599 | + return 0; | |
600 | + | |
601 | + pagevec_init(&pvec, 0); | |
602 | + if (wbc->range_cyclic) { | |
603 | + index = mapping->writeback_index; /* Start from prev offset */ | |
604 | + end = -1; | |
605 | + } else { | |
606 | + index = wbc->range_start >> PAGE_CACHE_SHIFT; | |
607 | + end = wbc->range_end >> PAGE_CACHE_SHIFT; | |
608 | + if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) | |
609 | + range_whole = 1; | |
610 | + scanned = 1; | |
611 | + } | |
612 | +retry: | |
613 | + while (!done && (index <= end) && | |
614 | + (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, | |
615 | + PAGECACHE_TAG_DIRTY, | |
616 | + min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1))) { | |
617 | + unsigned i; | |
618 | + | |
619 | + scanned = 1; | |
620 | + for (i = 0; i < nr_pages; i++) { | |
621 | + struct page *page = pvec.pages[i]; | |
622 | + | |
623 | + /* | |
624 | + * At this point we hold neither mapping->tree_lock nor | |
625 | + * lock on the page itself: the page may be truncated or | |
626 | + * invalidated (changing page->mapping to NULL), or even | |
627 | + * swizzled back from swapper_space to tmpfs file | |
628 | + * mapping | |
629 | + */ | |
630 | + lock_page(page); | |
631 | + | |
632 | + if (unlikely(page->mapping != mapping)) { | |
633 | + unlock_page(page); | |
634 | + continue; | |
635 | + } | |
636 | + | |
637 | + if (!wbc->range_cyclic && page->index > end) { | |
638 | + done = 1; | |
639 | + unlock_page(page); | |
640 | + continue; | |
641 | + } | |
642 | + | |
643 | + if (wbc->sync_mode != WB_SYNC_NONE) | |
644 | + wait_on_page_writeback(page); | |
645 | + | |
646 | + if (PageWriteback(page) || | |
647 | + !clear_page_dirty_for_io(page)) { | |
648 | + unlock_page(page); | |
649 | + continue; | |
650 | + } | |
651 | + | |
652 | + ret = (*writepage)(page, wbc); | |
653 | + if (ret) { | |
654 | + if (ret == -ENOSPC) | |
655 | + set_bit(AS_ENOSPC, &mapping->flags); | |
656 | + else | |
657 | + set_bit(AS_EIO, &mapping->flags); | |
658 | + } | |
659 | + | |
660 | + if (unlikely(ret == AOP_WRITEPAGE_ACTIVATE)) | |
661 | + unlock_page(page); | |
662 | + if (ret || (--(wbc->nr_to_write) <= 0)) | |
663 | + done = 1; | |
664 | + if (wbc->nonblocking && bdi_write_congested(bdi)) { | |
665 | + wbc->encountered_congestion = 1; | |
666 | + done = 1; | |
667 | + } | |
668 | + } | |
669 | + pagevec_release(&pvec); | |
670 | + cond_resched(); | |
671 | + } | |
672 | + if (!scanned && !done) { | |
673 | + /* | |
674 | + * We hit the last page and there is more work to be done: wrap | |
675 | + * back to the start of the file | |
676 | + */ | |
677 | + scanned = 1; | |
678 | + index = 0; | |
679 | + goto retry; | |
680 | + } | |
681 | + if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) | |
682 | + mapping->writeback_index = index; | |
683 | + return ret; | |
684 | +} | |
685 | + | |
686 | +EXPORT_SYMBOL(generic_writepages); | |
553 | 687 | |
554 | 688 | int do_writepages(struct address_space *mapping, struct writeback_control *wbc) |
555 | 689 | { |