diff options
author | Shaohua Li <shaohua.li@intel.com> | 2011-05-24 20:11:20 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-25 11:39:04 -0400 |
commit | 965f55dea0e331152fa53941a51e4e16f9f06fae (patch) | |
tree | 3cf8beb31bac7b325b62e8ef75618e0086a3c760 | |
parent | 5f70b962ccc2f2e6259417cf3d1233dc9e16cf5e (diff) |
mmap: avoid merging cloned VMAs
Avoid merging a VMA with another VMA which is cloned from the parent process.
The cloned VMA shares the anon_vma lock with the parent process's VMA. If
we do the merge, more vmas (even the new range is only for current
process) use the perent process's anon_vma lock. This introduces
scalability issues. find_mergeable_anon_vma() already considers this
case.
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | mm/mmap.c | 18 |
1 files changed, 13 insertions, 5 deletions
@@ -703,9 +703,17 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma, | |||
703 | } | 703 | } |
704 | 704 | ||
705 | static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1, | 705 | static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1, |
706 | struct anon_vma *anon_vma2) | 706 | struct anon_vma *anon_vma2, |
707 | struct vm_area_struct *vma) | ||
707 | { | 708 | { |
708 | return !anon_vma1 || !anon_vma2 || (anon_vma1 == anon_vma2); | 709 | /* |
710 | * The list_is_singular() test is to avoid merging VMA cloned from | ||
711 | * parents. This can improve scalability caused by anon_vma lock. | ||
712 | */ | ||
713 | if ((!anon_vma1 || !anon_vma2) && (!vma || | ||
714 | list_is_singular(&vma->anon_vma_chain))) | ||
715 | return 1; | ||
716 | return anon_vma1 == anon_vma2; | ||
709 | } | 717 | } |
710 | 718 | ||
711 | /* | 719 | /* |
@@ -724,7 +732,7 @@ can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, | |||
724 | struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) | 732 | struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) |
725 | { | 733 | { |
726 | if (is_mergeable_vma(vma, file, vm_flags) && | 734 | if (is_mergeable_vma(vma, file, vm_flags) && |
727 | is_mergeable_anon_vma(anon_vma, vma->anon_vma)) { | 735 | is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { |
728 | if (vma->vm_pgoff == vm_pgoff) | 736 | if (vma->vm_pgoff == vm_pgoff) |
729 | return 1; | 737 | return 1; |
730 | } | 738 | } |
@@ -743,7 +751,7 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, | |||
743 | struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) | 751 | struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) |
744 | { | 752 | { |
745 | if (is_mergeable_vma(vma, file, vm_flags) && | 753 | if (is_mergeable_vma(vma, file, vm_flags) && |
746 | is_mergeable_anon_vma(anon_vma, vma->anon_vma)) { | 754 | is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { |
747 | pgoff_t vm_pglen; | 755 | pgoff_t vm_pglen; |
748 | vm_pglen = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | 756 | vm_pglen = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; |
749 | if (vma->vm_pgoff + vm_pglen == vm_pgoff) | 757 | if (vma->vm_pgoff + vm_pglen == vm_pgoff) |
@@ -821,7 +829,7 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, | |||
821 | can_vma_merge_before(next, vm_flags, | 829 | can_vma_merge_before(next, vm_flags, |
822 | anon_vma, file, pgoff+pglen) && | 830 | anon_vma, file, pgoff+pglen) && |
823 | is_mergeable_anon_vma(prev->anon_vma, | 831 | is_mergeable_anon_vma(prev->anon_vma, |
824 | next->anon_vma)) { | 832 | next->anon_vma, NULL)) { |
825 | /* cases 1, 6 */ | 833 | /* cases 1, 6 */ |
826 | err = vma_adjust(prev, prev->vm_start, | 834 | err = vma_adjust(prev, prev->vm_start, |
827 | next->vm_end, prev->vm_pgoff, NULL); | 835 | next->vm_end, prev->vm_pgoff, NULL); |