diff options
author | Mel Gorman <mel@csn.ul.ie> | 2009-12-14 20:59:53 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-15 11:53:23 -0500 |
commit | 4eb2b1dcd598f8489130405c81c60c289896d92a (patch) | |
tree | 87bf0dec35f55688595447de65eb95952eb3dabc /mm/hugetlb.c | |
parent | 70da2340fbc68e91e701762f785479ab495a0869 (diff) |
hugetlb: acquire the i_mmap_lock before walking the prio_tree to unmap a page
When the owner of a mapping fails COW because a child process is holding a
reference, the children VMAs are walked and the page is unmapped. The
i_mmap_lock is taken for the unmapping of the page but not the walking of
the prio_tree. In theory, that tree could be changing if the lock is not
held. This patch takes the i_mmap_lock properly for the duration of the
prio_tree walk.
[hugh.dickins@tiscali.co.uk: Spotted the problem in the first place]
Signed-off-by: Mel Gorman <mel@csn.ul.ie>
Acked-by: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/hugetlb.c')
-rw-r--r-- | mm/hugetlb.c | 9 |
1 files changed, 8 insertions, 1 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 2ef66a2a148d..6df8065039eb 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c | |||
@@ -2237,6 +2237,12 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2237 | + (vma->vm_pgoff >> PAGE_SHIFT); | 2237 | + (vma->vm_pgoff >> PAGE_SHIFT); |
2238 | mapping = (struct address_space *)page_private(page); | 2238 | mapping = (struct address_space *)page_private(page); |
2239 | 2239 | ||
2240 | /* | ||
2241 | * Take the mapping lock for the duration of the table walk. As | ||
2242 | * this mapping should be shared between all the VMAs, | ||
2243 | * __unmap_hugepage_range() is called as the lock is already held | ||
2244 | */ | ||
2245 | spin_lock(&mapping->i_mmap_lock); | ||
2240 | vma_prio_tree_foreach(iter_vma, &iter, &mapping->i_mmap, pgoff, pgoff) { | 2246 | vma_prio_tree_foreach(iter_vma, &iter, &mapping->i_mmap, pgoff, pgoff) { |
2241 | /* Do not unmap the current VMA */ | 2247 | /* Do not unmap the current VMA */ |
2242 | if (iter_vma == vma) | 2248 | if (iter_vma == vma) |
@@ -2250,10 +2256,11 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2250 | * from the time of fork. This would look like data corruption | 2256 | * from the time of fork. This would look like data corruption |
2251 | */ | 2257 | */ |
2252 | if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER)) | 2258 | if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER)) |
2253 | unmap_hugepage_range(iter_vma, | 2259 | __unmap_hugepage_range(iter_vma, |
2254 | address, address + huge_page_size(h), | 2260 | address, address + huge_page_size(h), |
2255 | page); | 2261 | page); |
2256 | } | 2262 | } |
2263 | spin_unlock(&mapping->i_mmap_lock); | ||
2257 | 2264 | ||
2258 | return 1; | 2265 | return 1; |
2259 | } | 2266 | } |