@@ -1278,6 +1278,23 @@ static enum scan_result alloc_charge_folio(struct folio **foliop, struct mm_stru
return SCAN_SUCCEED;
}
+static pgtable_t alloc_deposit_pte_table(struct mm_struct *mm)
+{
+ /*
+ * khugepaged is run from a kernel thread, so need to manually set the
+ * correct memcg so the allocation gets charged correctly.
+ */
+ struct mem_cgroup *memcg = get_mem_cgroup_from_mm(mm);
+ struct mem_cgroup *old_memcg = set_active_memcg(memcg);
+ pgtable_t pgtable;
+
+ pgtable = pte_alloc_one(mm);
+
+ set_active_memcg(old_memcg);
+ mem_cgroup_put(memcg);
+ return pgtable;
+}
+
/*
* collapse_huge_page() expects the mmap_lock to be unlocked before entering and
* will always return with the lock unlocked, to avoid holding the mmap_lock
@@ -1293,7 +1310,7 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s
LIST_HEAD(compound_pagelist);
pmd_t *pmd, _pmd;
pte_t *pte = NULL;
- pgtable_t pgtable;
+ pgtable_t pgtable = NULL;
struct folio *folio;
spinlock_t *pmd_ptl, *pte_ptl;
enum scan_result result = SCAN_FAIL;
@@ -1310,6 +1327,14 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s
goto out_nolock;
}
+ if (is_pmd_order(order)) {
+ pgtable = alloc_deposit_pte_table(mm);
+ if (!pgtable) {
+ result = SCAN_ALLOC_HUGE_PAGE_FAIL;
+ goto out_nolock;
+ }
+ }
+
mmap_read_lock(mm);
result = hugepage_vma_revalidate(mm, pmd_addr, /*expect_anon=*/ true,
&vma, cc, order);
@@ -1433,8 +1458,8 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s
spin_lock(pmd_ptl);
VM_WARN_ON_ONCE(!pmd_none(*pmd));
if (is_pmd_order(order)) {
- pgtable = pmd_pgtable(_pmd);
pgtable_trans_huge_deposit(mm, pmd, pgtable);
+ pgtable = NULL;
map_anon_folio_pmd_nopf(folio, pmd, vma, pmd_addr);
} else {
/*
@@ -1453,6 +1478,9 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s
}
spin_unlock(pmd_ptl);
+ if (is_pmd_order(order))
+ pte_free_defer(mm, pmd_pgtable(_pmd));
+
folio = NULL;
result = SCAN_SUCCEED;
@@ -1463,6 +1491,8 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s
anon_vma_unlock_write(vma->anon_vma);
mmap_write_unlock(mm);
out_nolock:
+ if (pgtable)
+ pte_free(mm, pgtable);
if (folio)
folio_put(folio);
trace_mm_collapse_huge_page(mm, result == SCAN_SUCCEED, result, order);