diff options
Diffstat (limited to 'mm/page-writeback.c')
| -rw-r--r-- | mm/page-writeback.c | 143 |
1 files changed, 140 insertions, 3 deletions
diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 488b7088557c..c0d4ce144dec 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c | |||
| @@ -30,6 +30,8 @@ | |||
| 30 | #include <linux/sysctl.h> | 30 | #include <linux/sysctl.h> |
| 31 | #include <linux/cpu.h> | 31 | #include <linux/cpu.h> |
| 32 | #include <linux/syscalls.h> | 32 | #include <linux/syscalls.h> |
| 33 | #include <linux/buffer_head.h> | ||
| 34 | #include <linux/pagevec.h> | ||
| 33 | 35 | ||
| 34 | /* | 36 | /* |
| 35 | * The maximum number of pages to writeout in a single bdflush/kupdate | 37 | * The maximum number of pages to writeout in a single bdflush/kupdate |
| @@ -550,6 +552,139 @@ void __init page_writeback_init(void) | |||
| 550 | register_cpu_notifier(&ratelimit_nb); | 552 | register_cpu_notifier(&ratelimit_nb); |
| 551 | } | 553 | } |
| 552 | 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); | ||
| 687 | |||
| 553 | int do_writepages(struct address_space *mapping, struct writeback_control *wbc) | 688 | int do_writepages(struct address_space *mapping, struct writeback_control *wbc) |
| 554 | { | 689 | { |
| 555 | int ret; | 690 | int ret; |
| @@ -672,9 +807,11 @@ int fastcall set_page_dirty(struct page *page) | |||
| 672 | 807 | ||
| 673 | if (likely(mapping)) { | 808 | if (likely(mapping)) { |
| 674 | int (*spd)(struct page *) = mapping->a_ops->set_page_dirty; | 809 | int (*spd)(struct page *) = mapping->a_ops->set_page_dirty; |
| 675 | if (spd) | 810 | #ifdef CONFIG_BLOCK |
| 676 | return (*spd)(page); | 811 | if (!spd) |
| 677 | return __set_page_dirty_buffers(page); | 812 | spd = __set_page_dirty_buffers; |
| 813 | #endif | ||
| 814 | return (*spd)(page); | ||
| 678 | } | 815 | } |
| 679 | if (!PageDirty(page)) { | 816 | if (!PageDirty(page)) { |
| 680 | if (!TestSetPageDirty(page)) | 817 | if (!TestSetPageDirty(page)) |
