diff --git a/mm/khugepaged.c b/mm/khugepaged.c index f1f98305433e..d6da1fcbef6f 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1476,7 +1476,7 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff) * has higher cost too. It would also probably require locking * the anon_vma. */ - if (vma->anon_vma) + if (READ_ONCE(vma->anon_vma)) continue; addr = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT); if (addr & ~HPAGE_PMD_MASK) @@ -1498,6 +1498,18 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff) if (!khugepaged_test_exit(mm)) { struct mmu_notifier_range range; + /* + * Re-check whether we have an ->anon_vma, because + * collapse_and_free_pmd() requires that either no + * ->anon_vma exists or the anon_vma is locked. + * We already checked ->anon_vma above, but that check + * is racy because ->anon_vma can be populated under the + * mmap_sem in read mode. + */ + if (vma->anon_vma) { + up_write(&mm->mmap_sem); + continue; + } mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, NULL, mm, addr,