diff options
-rw-r--r-- | mm/page-writeback.c | 45 |
1 files changed, 37 insertions, 8 deletions
diff --git a/mm/page-writeback.c b/mm/page-writeback.c index b3a198c9248d..1d2fc89ca56d 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c | |||
@@ -862,17 +862,46 @@ int clear_page_dirty_for_io(struct page *page) | |||
862 | { | 862 | { |
863 | struct address_space *mapping = page_mapping(page); | 863 | struct address_space *mapping = page_mapping(page); |
864 | 864 | ||
865 | if (!mapping) | 865 | if (mapping && mapping_cap_account_dirty(mapping)) { |
866 | return TestClearPageDirty(page); | 866 | /* |
867 | 867 | * Yes, Virginia, this is indeed insane. | |
868 | if (TestClearPageDirty(page)) { | 868 | * |
869 | if (mapping_cap_account_dirty(mapping)) { | 869 | * We use this sequence to make sure that |
870 | 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)) { | ||
871 | dec_zone_page_state(page, NR_FILE_DIRTY); | 899 | dec_zone_page_state(page, NR_FILE_DIRTY); |
900 | return 1; | ||
872 | } | 901 | } |
873 | return 1; | 902 | return 0; |
874 | } | 903 | } |
875 | return 0; | 904 | return TestClearPageDirty(page); |
876 | } | 905 | } |
877 | EXPORT_SYMBOL(clear_page_dirty_for_io); | 906 | EXPORT_SYMBOL(clear_page_dirty_for_io); |
878 | 907 | ||