diff options
Diffstat (limited to 'mm/hugetlb.c')
-rw-r--r-- | mm/hugetlb.c | 13 |
1 files changed, 11 insertions, 2 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 450493d25572..2ef66a2a148d 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c | |||
@@ -2293,6 +2293,9 @@ retry_avoidcopy: | |||
2293 | outside_reserve = 1; | 2293 | outside_reserve = 1; |
2294 | 2294 | ||
2295 | page_cache_get(old_page); | 2295 | page_cache_get(old_page); |
2296 | |||
2297 | /* Drop page_table_lock as buddy allocator may be called */ | ||
2298 | spin_unlock(&mm->page_table_lock); | ||
2296 | new_page = alloc_huge_page(vma, address, outside_reserve); | 2299 | new_page = alloc_huge_page(vma, address, outside_reserve); |
2297 | 2300 | ||
2298 | if (IS_ERR(new_page)) { | 2301 | if (IS_ERR(new_page)) { |
@@ -2310,19 +2313,25 @@ retry_avoidcopy: | |||
2310 | if (unmap_ref_private(mm, vma, old_page, address)) { | 2313 | if (unmap_ref_private(mm, vma, old_page, address)) { |
2311 | BUG_ON(page_count(old_page) != 1); | 2314 | BUG_ON(page_count(old_page) != 1); |
2312 | BUG_ON(huge_pte_none(pte)); | 2315 | BUG_ON(huge_pte_none(pte)); |
2316 | spin_lock(&mm->page_table_lock); | ||
2313 | goto retry_avoidcopy; | 2317 | goto retry_avoidcopy; |
2314 | } | 2318 | } |
2315 | WARN_ON_ONCE(1); | 2319 | WARN_ON_ONCE(1); |
2316 | } | 2320 | } |
2317 | 2321 | ||
2322 | /* Caller expects lock to be held */ | ||
2323 | spin_lock(&mm->page_table_lock); | ||
2318 | return -PTR_ERR(new_page); | 2324 | return -PTR_ERR(new_page); |
2319 | } | 2325 | } |
2320 | 2326 | ||
2321 | spin_unlock(&mm->page_table_lock); | ||
2322 | copy_huge_page(new_page, old_page, address, vma); | 2327 | copy_huge_page(new_page, old_page, address, vma); |
2323 | __SetPageUptodate(new_page); | 2328 | __SetPageUptodate(new_page); |
2324 | spin_lock(&mm->page_table_lock); | ||
2325 | 2329 | ||
2330 | /* | ||
2331 | * Retake the page_table_lock to check for racing updates | ||
2332 | * before the page tables are altered | ||
2333 | */ | ||
2334 | spin_lock(&mm->page_table_lock); | ||
2326 | ptep = huge_pte_offset(mm, address & huge_page_mask(h)); | 2335 | ptep = huge_pte_offset(mm, address & huge_page_mask(h)); |
2327 | if (likely(pte_same(huge_ptep_get(ptep), pte))) { | 2336 | if (likely(pte_same(huge_ptep_get(ptep), pte))) { |
2328 | /* Break COW */ | 2337 | /* Break COW */ |