diff options
Diffstat (limited to 'mm/page-writeback.c')
| -rw-r--r-- | mm/page-writeback.c | 77 | 
1 files changed, 37 insertions, 40 deletions
diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 237107c1b084..1d2fc89ca56d 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c  | |||
| @@ -845,38 +845,6 @@ int set_page_dirty_lock(struct page *page) | |||
| 845 | EXPORT_SYMBOL(set_page_dirty_lock); | 845 | EXPORT_SYMBOL(set_page_dirty_lock); | 
| 846 | 846 | ||
| 847 | /* | 847 | /* | 
| 848 | * Clear a page's dirty flag, while caring for dirty memory accounting. | ||
| 849 | * Returns true if the page was previously dirty. | ||
| 850 | */ | ||
| 851 | int test_clear_page_dirty(struct page *page) | ||
| 852 | { | ||
| 853 | struct address_space *mapping = page_mapping(page); | ||
| 854 | unsigned long flags; | ||
| 855 | |||
| 856 | if (!mapping) | ||
| 857 | return TestClearPageDirty(page); | ||
| 858 | |||
| 859 | write_lock_irqsave(&mapping->tree_lock, flags); | ||
| 860 | if (TestClearPageDirty(page)) { | ||
| 861 | radix_tree_tag_clear(&mapping->page_tree, | ||
| 862 | page_index(page), PAGECACHE_TAG_DIRTY); | ||
| 863 | write_unlock_irqrestore(&mapping->tree_lock, flags); | ||
| 864 | /* | ||
| 865 | * We can continue to use `mapping' here because the | ||
| 866 | * page is locked, which pins the address_space | ||
| 867 | */ | ||
| 868 | if (mapping_cap_account_dirty(mapping)) { | ||
| 869 | page_mkclean(page); | ||
| 870 | dec_zone_page_state(page, NR_FILE_DIRTY); | ||
| 871 | } | ||
| 872 | return 1; | ||
| 873 | } | ||
| 874 | write_unlock_irqrestore(&mapping->tree_lock, flags); | ||
| 875 | return 0; | ||
| 876 | } | ||
| 877 | EXPORT_SYMBOL(test_clear_page_dirty); | ||
| 878 | |||
| 879 | /* | ||
| 880 | * Clear a page's dirty flag, while caring for dirty memory accounting. | 848 | * Clear a page's dirty flag, while caring for dirty memory accounting. | 
| 881 | * Returns true if the page was previously dirty. | 849 | * Returns true if the page was previously dirty. | 
| 882 | * | 850 | * | 
| @@ -894,17 +862,46 @@ int clear_page_dirty_for_io(struct page *page) | |||
| 894 | { | 862 | { | 
| 895 | struct address_space *mapping = page_mapping(page); | 863 | struct address_space *mapping = page_mapping(page); | 
| 896 | 864 | ||
| 897 | if (!mapping) | 865 | if (mapping && mapping_cap_account_dirty(mapping)) { | 
| 898 | return TestClearPageDirty(page); | 866 | /* | 
| 899 | 867 | * Yes, Virginia, this is indeed insane. | |
| 900 | if (TestClearPageDirty(page)) { | 868 | * | 
| 901 | if (mapping_cap_account_dirty(mapping)) { | 869 | * We use this sequence to make sure that | 
| 902 | page_mkclean(page); | 870 | * (a) we account for dirty stats properly | 
| 871 | * (b) we tell the low-level filesystem to | ||
| 872 | * mark the whole page dirty if it was | ||
| 873 | * dirty in a pagetable. Only to then | ||
| 874 | * (c) clean the page again and return 1 to | ||
| 875 | * cause the writeback. | ||
| 876 | * | ||
| 877 | * This way we avoid all nasty races with the | ||
| 878 | * dirty bit in multiple places and clearing | ||
| 879 | * them concurrently from different threads. | ||
| 880 | * | ||
| 881 | * Note! Normally the "set_page_dirty(page)" | ||
| 882 | * has no effect on the actual dirty bit - since | ||
| 883 | * that will already usually be set. But we | ||
| 884 | * need the side effects, and it can help us | ||
| 885 | * avoid races. | ||
| 886 | * | ||
| 887 | * We basically use the page "master dirty bit" | ||
| 888 | * as a serialization point for all the different | ||
| 889 | * threads doing their things. | ||
| 890 | * | ||
| 891 | * FIXME! We still have a race here: if somebody | ||
| 892 | * adds the page back to the page tables in | ||
| 893 | * between the "page_mkclean()" and the "TestClearPageDirty()", | ||
| 894 | * we might have it mapped without the dirty bit set. | ||
| 895 | */ | ||
| 896 | if (page_mkclean(page)) | ||
| 897 | set_page_dirty(page); | ||
| 898 | if (TestClearPageDirty(page)) { | ||
| 903 | dec_zone_page_state(page, NR_FILE_DIRTY); | 899 | dec_zone_page_state(page, NR_FILE_DIRTY); | 
| 900 | return 1; | ||
| 904 | } | 901 | } | 
| 905 | return 1; | 902 | return 0; | 
| 906 | } | 903 | } | 
| 907 | return 0; | 904 | return TestClearPageDirty(page); | 
| 908 | } | 905 | } | 
| 909 | EXPORT_SYMBOL(clear_page_dirty_for_io); | 906 | EXPORT_SYMBOL(clear_page_dirty_for_io); | 
| 910 | 907 | ||
