| /* |
| * Copied some writeback library functions to teach about PageForked(). |
| * |
| * We should check the update of original functions, and sync with it. |
| */ |
| |
| #include <linux/rmap.h> /* for page_mkclean() */ |
| |
| /* |
| * Clear a page's dirty flag, while caring for dirty memory accounting. |
| * Returns true if the page was previously dirty. |
| * |
| * This is for preparing to put the page under writeout. We leave the page |
| * tagged as dirty in the radix tree so that a concurrent write-for-sync |
| * can discover it via a PAGECACHE_TAG_DIRTY walk. The ->writepage |
| * implementation will run either set_page_writeback() or set_page_dirty(), |
| * at which stage we bring the page's dirty flag and radix-tree dirty tag |
| * back into sync. |
| * |
| * This incoherency between the page's dirty flag and radix-tree tag is |
| * unfortunate, but it only exists while the page is locked. |
| */ |
| static int tux3_clear_page_dirty_for_io(struct page *page) |
| { |
| struct address_space *mapping = page->mapping; |
| |
| BUG_ON(!PageLocked(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. |
| */ |
| /* If PageForked(), don't touch PTE and don't dirty */ |
| if (!PageForked(page) && page_mkclean(page)) |
| set_page_dirty(page); |
| /* |
| * We carefully synchronise fault handlers against |
| * installing a dirty pte and marking the page dirty |
| * at this point. We do this by having them hold the |
| * page lock at some point after installing their |
| * pte, but before marking the page dirty. |
| * Pages are always locked coming in here, so we get |
| * the desired exclusion. See mm/memory.c:do_wp_page() |
| * for more comments. |
| */ |
| if (TestClearPageDirty(page)) { |
| dec_zone_page_state(page, NR_FILE_DIRTY); |
| dec_bdi_stat(mapping->backing_dev_info, |
| BDI_RECLAIMABLE); |
| return 1; |
| } |
| return 0; |
| } |
| return TestClearPageDirty(page); |
| } |
| |
| static void __tux3_test_set_page_writeback(struct page *page, int old_writeback) |
| { |
| struct address_space *mapping = page->mapping; |
| #if 0 /* FIXME */ |
| bool locked; |
| unsigned long memcg_flags; |
| |
| mem_cgroup_begin_update_page_stat(page, &locked, &memcg_flags); |
| #endif |
| if (mapping) { |
| struct backing_dev_info *bdi = mapping->backing_dev_info; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&mapping->tree_lock, flags); |
| if (!old_writeback) { |
| /* If PageForked(), don't touch tag */ |
| if (!PageForked(page)) |
| radix_tree_tag_set(&mapping->page_tree, |
| page_index(page), |
| PAGECACHE_TAG_WRITEBACK); |
| if (bdi_cap_account_writeback(bdi)) |
| __inc_bdi_stat(bdi, BDI_WRITEBACK); |
| } |
| /* If PageForked(), don't touch tag */ |
| if (!PageDirty(page) && !PageForked(page)) |
| radix_tree_tag_clear(&mapping->page_tree, |
| page_index(page), |
| PAGECACHE_TAG_DIRTY); |
| radix_tree_tag_clear(&mapping->page_tree, |
| page_index(page), |
| PAGECACHE_TAG_TOWRITE); |
| spin_unlock_irqrestore(&mapping->tree_lock, flags); |
| } |
| if (!old_writeback) |
| account_page_writeback(page); |
| #if 0 /* FIXME */ |
| mem_cgroup_end_update_page_stat(page, &locked, &memcg_flags); |
| #endif |
| } |