summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mm/khugepaged.c15
1 files changed, 14 insertions, 1 deletions
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 28e18777ec51..511499e8e29a 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)
@@ -1633,6 +1633,19 @@ 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 lock in read mode.
+ */
+ if (vma->anon_vma) {
+ mmap_write_unlock(mm);
+ continue;
+ }
+
mmu_notifier_range_init(&range,
MMU_NOTIFY_CLEAR, 0,
NULL, mm, addr,