diff options
author | Michel Lespinasse <walken@google.com> | 2010-10-26 17:21:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-26 19:52:09 -0400 |
commit | d065bd810b6deb67d4897a14bfe21f8eb526ba99 (patch) | |
tree | f58c59075732ec4ccba336278c9bdc7ff61bef94 /mm | |
parent | b522c94da5d9cbc73f708be5e530ebc3bbd4a031 (diff) |
mm: retry page fault when blocking on disk transfer
This change reduces mmap_sem hold times that are caused by waiting for
disk transfers when accessing file mapped VMAs.
It introduces the VM_FAULT_ALLOW_RETRY flag, which indicates that the call
site wants mmap_sem to be released if blocking on a pending disk transfer.
In that case, filemap_fault() returns the VM_FAULT_RETRY status bit and
do_page_fault() will then re-acquire mmap_sem and retry the page fault.
It is expected that the retry will hit the same page which will now be
cached, and thus it will complete with a low mmap_sem hold time.
Tests:
- microbenchmark: thread A mmaps a large file and does random read accesses
to the mmaped area - achieves about 55 iterations/s. Thread B does
mmap/munmap in a loop at a separate location - achieves 55 iterations/s
before, 15000 iterations/s after.
- We are seeing related effects in some applications in house, which show
significant performance regressions when running without this change.
[akpm@linux-foundation.org: fix warning & crash]
Signed-off-by: Michel Lespinasse <walken@google.com>
Acked-by: Rik van Riel <riel@redhat.com>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Reviewed-by: Wu Fengguang <fengguang.wu@intel.com>
Cc: Ying Han <yinghan@google.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Acked-by: "H. Peter Anvin" <hpa@zytor.com>
Cc: <linux-arch@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/filemap.c | 16 | ||||
-rw-r--r-- | mm/memory.c | 10 |
2 files changed, 23 insertions, 3 deletions
diff --git a/mm/filemap.c b/mm/filemap.c index 8ed709a83eb7..33f81252a744 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
@@ -612,6 +612,19 @@ void __lock_page_nosync(struct page *page) | |||
612 | TASK_UNINTERRUPTIBLE); | 612 | TASK_UNINTERRUPTIBLE); |
613 | } | 613 | } |
614 | 614 | ||
615 | int __lock_page_or_retry(struct page *page, struct mm_struct *mm, | ||
616 | unsigned int flags) | ||
617 | { | ||
618 | if (!(flags & FAULT_FLAG_ALLOW_RETRY)) { | ||
619 | __lock_page(page); | ||
620 | return 1; | ||
621 | } else { | ||
622 | up_read(&mm->mmap_sem); | ||
623 | wait_on_page_locked(page); | ||
624 | return 0; | ||
625 | } | ||
626 | } | ||
627 | |||
615 | /** | 628 | /** |
616 | * find_get_page - find and get a page reference | 629 | * find_get_page - find and get a page reference |
617 | * @mapping: the address_space to search | 630 | * @mapping: the address_space to search |
@@ -1550,7 +1563,8 @@ retry_find: | |||
1550 | goto no_cached_page; | 1563 | goto no_cached_page; |
1551 | } | 1564 | } |
1552 | 1565 | ||
1553 | lock_page(page); | 1566 | if (!lock_page_or_retry(page, vma->vm_mm, vmf->flags)) |
1567 | return ret | VM_FAULT_RETRY; | ||
1554 | 1568 | ||
1555 | /* Did it get truncated? */ | 1569 | /* Did it get truncated? */ |
1556 | if (unlikely(page->mapping != mapping)) { | 1570 | if (unlikely(page->mapping != mapping)) { |
diff --git a/mm/memory.c b/mm/memory.c index 92cc54e94137..714c4438d887 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -2627,6 +2627,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2627 | struct page *page, *swapcache = NULL; | 2627 | struct page *page, *swapcache = NULL; |
2628 | swp_entry_t entry; | 2628 | swp_entry_t entry; |
2629 | pte_t pte; | 2629 | pte_t pte; |
2630 | int locked; | ||
2630 | struct mem_cgroup *ptr = NULL; | 2631 | struct mem_cgroup *ptr = NULL; |
2631 | int exclusive = 0; | 2632 | int exclusive = 0; |
2632 | int ret = 0; | 2633 | int ret = 0; |
@@ -2677,8 +2678,12 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2677 | goto out_release; | 2678 | goto out_release; |
2678 | } | 2679 | } |
2679 | 2680 | ||
2680 | lock_page(page); | 2681 | locked = lock_page_or_retry(page, mm, flags); |
2681 | delayacct_clear_flag(DELAYACCT_PF_SWAPIN); | 2682 | delayacct_clear_flag(DELAYACCT_PF_SWAPIN); |
2683 | if (!locked) { | ||
2684 | ret |= VM_FAULT_RETRY; | ||
2685 | goto out_release; | ||
2686 | } | ||
2682 | 2687 | ||
2683 | /* | 2688 | /* |
2684 | * Make sure try_to_free_swap or reuse_swap_page or swapoff did not | 2689 | * Make sure try_to_free_swap or reuse_swap_page or swapoff did not |
@@ -2927,7 +2932,8 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2927 | vmf.page = NULL; | 2932 | vmf.page = NULL; |
2928 | 2933 | ||
2929 | ret = vma->vm_ops->fault(vma, &vmf); | 2934 | ret = vma->vm_ops->fault(vma, &vmf); |
2930 | if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) | 2935 | if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | |
2936 | VM_FAULT_RETRY))) | ||
2931 | return ret; | 2937 | return ret; |
2932 | 2938 | ||
2933 | if (unlikely(PageHWPoison(vmf.page))) { | 2939 | if (unlikely(PageHWPoison(vmf.page))) { |