diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2006-09-26 02:30:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-26 11:48:44 -0400 |
commit | d08b3851da41d0ee60851f2c75b118e1f7a5fc89 (patch) | |
tree | a01f6930a1387e8f66607e2fe16c62bb7044353b /mm/page-writeback.c | |
parent | 725d704ecaca4a43f067092c140d4f3271cf2856 (diff) |
[PATCH] mm: tracking shared dirty pages
Tracking of dirty pages in shared writeable mmap()s.
The idea is simple: write protect clean shared writeable pages, catch the
write-fault, make writeable and set dirty. On page write-back clean all the
PTE dirty bits and write protect them once again.
The implementation is a tad harder, mainly because the default
backing_dev_info capabilities were too loosely maintained. Hence it is not
enough to test the backing_dev_info for cap_account_dirty.
The current heuristic is as follows, a VMA is eligible when:
- its shared writeable
(vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)
- it is not a 'special' mapping
(vm_flags & (VM_PFNMAP|VM_INSERTPAGE)) == 0
- the backing_dev_info is cap_account_dirty
mapping_cap_account_dirty(vma->vm_file->f_mapping)
- f_op->mmap() didn't change the default page protection
Page from remap_pfn_range() are explicitly excluded because their COW
semantics are already horrid enough (see vm_normal_page() in do_wp_page()) and
because they don't have a backing store anyway.
mprotect() is taught about the new behaviour as well. However it overrides
the last condition.
Cleaning the pages on write-back is done with page_mkclean() a new rmap call.
It can be called on any page, but is currently only implemented for mapped
pages, if the page is found the be of a VMA that accounts dirty pages it will
also wrprotect the PTE.
Finally, in fs/buffers.c:try_to_free_buffers(); remove clear_page_dirty() from
under ->private_lock. This seems to be safe, since ->private_lock is used to
serialize access to the buffers, not the page itself. This is needed because
clear_page_dirty() will call into page_mkclean() and would thereby violate
locking order.
[dhowells@redhat.com: Provide a page_mkclean() implementation for NOMMU]
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Hugh Dickins <hugh@veritas.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm/page-writeback.c')
-rw-r--r-- | mm/page-writeback.c | 17 |
1 files changed, 13 insertions, 4 deletions
diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 77a0bc4e261a..1c87430b7a25 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/backing-dev.h> | 23 | #include <linux/backing-dev.h> |
24 | #include <linux/blkdev.h> | 24 | #include <linux/blkdev.h> |
25 | #include <linux/mpage.h> | 25 | #include <linux/mpage.h> |
26 | #include <linux/rmap.h> | ||
26 | #include <linux/percpu.h> | 27 | #include <linux/percpu.h> |
27 | #include <linux/notifier.h> | 28 | #include <linux/notifier.h> |
28 | #include <linux/smp.h> | 29 | #include <linux/smp.h> |
@@ -550,7 +551,7 @@ int do_writepages(struct address_space *mapping, struct writeback_control *wbc) | |||
550 | return 0; | 551 | return 0; |
551 | wbc->for_writepages = 1; | 552 | wbc->for_writepages = 1; |
552 | if (mapping->a_ops->writepages) | 553 | if (mapping->a_ops->writepages) |
553 | ret = mapping->a_ops->writepages(mapping, wbc); | 554 | ret = mapping->a_ops->writepages(mapping, wbc); |
554 | else | 555 | else |
555 | ret = generic_writepages(mapping, wbc); | 556 | ret = generic_writepages(mapping, wbc); |
556 | wbc->for_writepages = 0; | 557 | wbc->for_writepages = 0; |
@@ -712,9 +713,15 @@ int test_clear_page_dirty(struct page *page) | |||
712 | radix_tree_tag_clear(&mapping->page_tree, | 713 | radix_tree_tag_clear(&mapping->page_tree, |
713 | page_index(page), | 714 | page_index(page), |
714 | PAGECACHE_TAG_DIRTY); | 715 | PAGECACHE_TAG_DIRTY); |
715 | if (mapping_cap_account_dirty(mapping)) | ||
716 | __dec_zone_page_state(page, NR_FILE_DIRTY); | ||
717 | write_unlock_irqrestore(&mapping->tree_lock, flags); | 716 | write_unlock_irqrestore(&mapping->tree_lock, flags); |
717 | /* | ||
718 | * We can continue to use `mapping' here because the | ||
719 | * page is locked, which pins the address_space | ||
720 | */ | ||
721 | if (mapping_cap_account_dirty(mapping)) { | ||
722 | page_mkclean(page); | ||
723 | dec_zone_page_state(page, NR_FILE_DIRTY); | ||
724 | } | ||
718 | return 1; | 725 | return 1; |
719 | } | 726 | } |
720 | write_unlock_irqrestore(&mapping->tree_lock, flags); | 727 | write_unlock_irqrestore(&mapping->tree_lock, flags); |
@@ -744,8 +751,10 @@ int clear_page_dirty_for_io(struct page *page) | |||
744 | 751 | ||
745 | if (mapping) { | 752 | if (mapping) { |
746 | if (TestClearPageDirty(page)) { | 753 | if (TestClearPageDirty(page)) { |
747 | if (mapping_cap_account_dirty(mapping)) | 754 | if (mapping_cap_account_dirty(mapping)) { |
755 | page_mkclean(page); | ||
748 | dec_zone_page_state(page, NR_FILE_DIRTY); | 756 | dec_zone_page_state(page, NR_FILE_DIRTY); |
757 | } | ||
749 | return 1; | 758 | return 1; |
750 | } | 759 | } |
751 | return 0; | 760 | return 0; |