Resurrect 'try_to_free_buffers()' VM hackery
[linux-drm-fsl-dcu.git] / mm / page-writeback.c
index 237107c1b0847d79e6139daf5c811ea1c432c087..1d2fc89ca56d522c0811db6fe5022ef9c2fa17c7 100644 (file)
@@ -844,38 +844,6 @@ int set_page_dirty_lock(struct page *page)
 }
 EXPORT_SYMBOL(set_page_dirty_lock);
 
-/*
- * Clear a page's dirty flag, while caring for dirty memory accounting. 
- * Returns true if the page was previously dirty.
- */
-int test_clear_page_dirty(struct page *page)
-{
-       struct address_space *mapping = page_mapping(page);
-       unsigned long flags;
-
-       if (!mapping)
-               return TestClearPageDirty(page);
-
-       write_lock_irqsave(&mapping->tree_lock, flags);
-       if (TestClearPageDirty(page)) {
-               radix_tree_tag_clear(&mapping->page_tree,
-                               page_index(page), PAGECACHE_TAG_DIRTY);
-               write_unlock_irqrestore(&mapping->tree_lock, flags);
-               /*
-                * We can continue to use `mapping' here because the
-                * page is locked, which pins the address_space
-                */
-               if (mapping_cap_account_dirty(mapping)) {
-                       page_mkclean(page);
-                       dec_zone_page_state(page, NR_FILE_DIRTY);
-               }
-               return 1;
-       }
-       write_unlock_irqrestore(&mapping->tree_lock, flags);
-       return 0;
-}
-EXPORT_SYMBOL(test_clear_page_dirty);
-
 /*
  * Clear a page's dirty flag, while caring for dirty memory accounting.
  * Returns true if the page was previously dirty.
@@ -894,17 +862,46 @@ int clear_page_dirty_for_io(struct page *page)
 {
        struct address_space *mapping = page_mapping(page);
 
-       if (!mapping)
-               return TestClearPageDirty(page);
-
-       if (TestClearPageDirty(page)) {
-               if (mapping_cap_account_dirty(mapping)) {
-                       page_mkclean(page);
+       if (mapping && mapping_cap_account_dirty(mapping)) {
+               /*
+                * Yes, Virginia, this is indeed insane.
+                *
+                * We use this sequence to make sure that
+                *  (a) we account for dirty stats properly
+                *  (b) we tell the low-level filesystem to
+                *      mark the whole page dirty if it was
+                *      dirty in a pagetable. Only to then
+                *  (c) clean the page again and return 1 to
+                *      cause the writeback.
+                *
+                * This way we avoid all nasty races with the
+                * dirty bit in multiple places and clearing
+                * them concurrently from different threads.
+                *
+                * Note! Normally the "set_page_dirty(page)"
+                * has no effect on the actual dirty bit - since
+                * that will already usually be set. But we
+                * need the side effects, and it can help us
+                * avoid races.
+                *
+                * We basically use the page "master dirty bit"
+                * as a serialization point for all the different
+                * threads doing their things.
+                *
+                * FIXME! We still have a race here: if somebody
+                * adds the page back to the page tables in
+                * between the "page_mkclean()" and the "TestClearPageDirty()",
+                * we might have it mapped without the dirty bit set.
+                */
+               if (page_mkclean(page))
+                       set_page_dirty(page);
+               if (TestClearPageDirty(page)) {
                        dec_zone_page_state(page, NR_FILE_DIRTY);
+                       return 1;
                }
-               return 1;
+               return 0;
        }
-       return 0;
+       return TestClearPageDirty(page);
 }
 EXPORT_SYMBOL(clear_page_dirty_for_io);