Export a thin wrapper around collapse_huge_page() that allows external subsystems such as DAMON to trigger THP collapse on a target address range. Currently restricted to PMD order (HPAGE_PMD_ORDER), since collapse_huge_page() does not yet support arbitrary mTHP orders. The restriction can be relaxed when khugepaged gains mTHP support. The caller must hold a reference to @mm. Do not hold mmap lock: collapse_huge_page() acquires mmap_read_lock for validation, releases it, then acquires mmap_write_lock for the actual collapse. Holding an outer mmap_read_lock would cause a self-deadlock when the same thread attempts the inner mmap_write_lock. Co-developed-by: Kunwu Chan Signed-off-by: Kunwu Chan Signed-off-by: Wang Lian --- include/linux/khugepaged.h | 3 +++ mm/khugepaged.c | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/include/linux/khugepaged.h b/include/linux/khugepaged.h index d7a9053ff4fe..6fb8a6857790 100644 --- a/include/linux/khugepaged.h +++ b/include/linux/khugepaged.h @@ -20,6 +20,9 @@ extern bool current_is_khugepaged(void); void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr, bool install_pmd); +int damon_collapse_folio_range(struct mm_struct *mm, unsigned long start_addr, + unsigned int target_order); + static inline void khugepaged_fork(struct mm_struct *mm, struct mm_struct *oldmm) { if (mm_flags_test(MMF_VM_HUGEPAGE, oldmm)) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 617bca76db49..0387841ba2e7 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -3272,3 +3272,42 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start, return thps == ((hend - hstart) >> HPAGE_PMD_SHIFT) ? 0 : madvise_collapse_errno(last_fail); } + +/** + * damon_collapse_folio_range() - Collapse base pages in range into a THP + * @mm: mm_struct of the target process + * @start_addr: start address (must be order-aligned) + * @target_order: page order of the collapse result (currently only + * HPAGE_PMD_ORDER is supported) + * + * Thin wrapper around collapse_huge_page() for external callers such as + * DAMON. The caller must hold a reference to @mm. Do not hold mmap + * lock: collapse_huge_page() acquires mmap_read_lock for validation, + * releases it, then acquires mmap_write_lock for the collapse. Holding + * an outer mmap_read_lock would self-deadlock. + * + * Return: 0 on success, -EINVAL on bad arguments, negative error from + * madvise_collapse_errno() otherwise. + */ +int damon_collapse_folio_range(struct mm_struct *mm, unsigned long start_addr, + unsigned int target_order) +{ + struct collapse_control cc = { + .is_khugepaged = false, + }; + enum scan_result result; + + if (target_order != HPAGE_PMD_ORDER) { + pr_warn_once("%s: only PMD order (%u) is supported, got %u\n", + __func__, HPAGE_PMD_ORDER, target_order); + return -EINVAL; + } + if (start_addr & ((PAGE_SIZE << target_order) - 1)) + return -EINVAL; + + result = collapse_huge_page(mm, start_addr, 1, 0, &cc, target_order); + if (result == SCAN_SUCCEED) + return 0; + return madvise_collapse_errno(result); +} +EXPORT_SYMBOL_GPL(damon_collapse_folio_range); -- 2.50.1 (Apple Git-155)