diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 28e18777ec513c9cc575ad7d1e5bc039c8f9b43d..667df4c478c8c44af39876a2b44e798d43e4837f 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1611,7 +1611,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) @@ -1630,6 +1630,18 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff) * reverse order. Trylock is a way to avoid deadlock. */ if (mmap_write_trylock(mm)) { + /* + * 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 lock in read mode. + */ + if (vma->anon_vma) { + result = SCAN_PAGE_ANON; + goto unlock_next; + } if (!khugepaged_test_exit(mm)) { struct mmu_notifier_range range;