diff options
author | Oleg Nesterov <oleg@redhat.com> | 2013-07-31 16:53:28 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-31 17:41:02 -0400 |
commit | 3964acd0dbec123aa0a621973a2a0580034b4788 (patch) | |
tree | eb5bcfb186354c2ebfa6b0b07c37156b34d21e9e /mm | |
parent | 36f571e9ed0419e73d127e18aa8992ced867268c (diff) |
mm: mempolicy: fix mbind_range() && vma_adjust() interaction
vma_adjust() does vma_set_policy(vma, vma_policy(next)) and this
is doubly wrong:
1. This leaks vma->vm_policy if it is not NULL and not equal to
next->vm_policy.
This can happen if vma_merge() expands "area", not prev (case 8).
2. This sets the wrong policy if vma_merge() joins prev and area,
area is the vma the caller needs to update and it still has the
old policy.
Revert commit 1444f92c8498 ("mm: merging memory blocks resets
mempolicy") which introduced these problems.
Change mbind_range() to recheck mpol_equal() after vma_merge() to fix
the problem that commit tried to address.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Steven T Hampson <steven.t.hampson@intel.com>
Cc: Mel Gorman <mgorman@suse.de>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: <stable@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/mempolicy.c | 6 | ||||
-rw-r--r-- | mm/mmap.c | 2 |
2 files changed, 6 insertions, 2 deletions
diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 74310017296e..4baf12e534d1 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c | |||
@@ -732,7 +732,10 @@ static int mbind_range(struct mm_struct *mm, unsigned long start, | |||
732 | if (prev) { | 732 | if (prev) { |
733 | vma = prev; | 733 | vma = prev; |
734 | next = vma->vm_next; | 734 | next = vma->vm_next; |
735 | continue; | 735 | if (mpol_equal(vma_policy(vma), new_pol)) |
736 | continue; | ||
737 | /* vma_merge() joined vma && vma->next, case 8 */ | ||
738 | goto replace; | ||
736 | } | 739 | } |
737 | if (vma->vm_start != vmstart) { | 740 | if (vma->vm_start != vmstart) { |
738 | err = split_vma(vma->vm_mm, vma, vmstart, 1); | 741 | err = split_vma(vma->vm_mm, vma, vmstart, 1); |
@@ -744,6 +747,7 @@ static int mbind_range(struct mm_struct *mm, unsigned long start, | |||
744 | if (err) | 747 | if (err) |
745 | goto out; | 748 | goto out; |
746 | } | 749 | } |
750 | replace: | ||
747 | err = vma_replace_policy(vma, new_pol); | 751 | err = vma_replace_policy(vma, new_pol); |
748 | if (err) | 752 | if (err) |
749 | goto out; | 753 | goto out; |
@@ -865,7 +865,7 @@ again: remove_next = 1 + (end > next->vm_end); | |||
865 | if (next->anon_vma) | 865 | if (next->anon_vma) |
866 | anon_vma_merge(vma, next); | 866 | anon_vma_merge(vma, next); |
867 | mm->map_count--; | 867 | mm->map_count--; |
868 | vma_set_policy(vma, vma_policy(next)); | 868 | mpol_put(vma_policy(next)); |
869 | kmem_cache_free(vm_area_cachep, next); | 869 | kmem_cache_free(vm_area_cachep, next); |
870 | /* | 870 | /* |
871 | * In mprotect's case 6 (see comments on vma_merge), | 871 | * In mprotect's case 6 (see comments on vma_merge), |