[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>
This commit is contained in:
committed by
Linus Torvalds
parent
725d704eca
commit
d08b3851da
29
mm/memory.c
29
mm/memory.c
@ -1458,14 +1458,19 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
||||
{
|
||||
struct page *old_page, *new_page;
|
||||
pte_t entry;
|
||||
int reuse, ret = VM_FAULT_MINOR;
|
||||
int reuse = 0, ret = VM_FAULT_MINOR;
|
||||
struct page *dirty_page = NULL;
|
||||
|
||||
old_page = vm_normal_page(vma, address, orig_pte);
|
||||
if (!old_page)
|
||||
goto gotten;
|
||||
|
||||
if (unlikely((vma->vm_flags & (VM_SHARED|VM_WRITE)) ==
|
||||
(VM_SHARED|VM_WRITE))) {
|
||||
/*
|
||||
* Only catch write-faults on shared writable pages, read-only
|
||||
* shared pages can get COWed by get_user_pages(.write=1, .force=1).
|
||||
*/
|
||||
if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED)) ==
|
||||
(VM_WRITE|VM_SHARED))) {
|
||||
if (vma->vm_ops && vma->vm_ops->page_mkwrite) {
|
||||
/*
|
||||
* Notify the address space that the page is about to
|
||||
@ -1494,13 +1499,12 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
||||
if (!pte_same(*page_table, orig_pte))
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
dirty_page = old_page;
|
||||
get_page(dirty_page);
|
||||
reuse = 1;
|
||||
} else if (PageAnon(old_page) && !TestSetPageLocked(old_page)) {
|
||||
reuse = can_share_swap_page(old_page);
|
||||
unlock_page(old_page);
|
||||
} else {
|
||||
reuse = 0;
|
||||
}
|
||||
|
||||
if (reuse) {
|
||||
@ -1566,6 +1570,10 @@ gotten:
|
||||
page_cache_release(old_page);
|
||||
unlock:
|
||||
pte_unmap_unlock(page_table, ptl);
|
||||
if (dirty_page) {
|
||||
set_page_dirty(dirty_page);
|
||||
put_page(dirty_page);
|
||||
}
|
||||
return ret;
|
||||
oom:
|
||||
if (old_page)
|
||||
@ -2098,6 +2106,7 @@ static int do_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
||||
unsigned int sequence = 0;
|
||||
int ret = VM_FAULT_MINOR;
|
||||
int anon = 0;
|
||||
struct page *dirty_page = NULL;
|
||||
|
||||
pte_unmap(page_table);
|
||||
BUG_ON(vma->vm_flags & VM_PFNMAP);
|
||||
@ -2192,6 +2201,10 @@ retry:
|
||||
} else {
|
||||
inc_mm_counter(mm, file_rss);
|
||||
page_add_file_rmap(new_page);
|
||||
if (write_access) {
|
||||
dirty_page = new_page;
|
||||
get_page(dirty_page);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* One of our sibling threads was faster, back out. */
|
||||
@ -2204,6 +2217,10 @@ retry:
|
||||
lazy_mmu_prot_update(entry);
|
||||
unlock:
|
||||
pte_unmap_unlock(page_table, ptl);
|
||||
if (dirty_page) {
|
||||
set_page_dirty(dirty_page);
|
||||
put_page(dirty_page);
|
||||
}
|
||||
return ret;
|
||||
oom:
|
||||
page_cache_release(new_page);
|
||||
|
Reference in New Issue
Block a user