[PATCH 1/5] mm: add basic madvise helpers and branch for THP setup

Luka Bai
2026-05-01T05:56:06Z

From: Luka Bai <lukabai@tencent.com>

Transparent huge page is now properly working with most of the mm
framework, and well fused with the folio concept that can be
reclaimed or allocated with a large order. However, its deed is not
very "estimable". For example, a THP is easily split in many path like
partially mapped, swap out or fork + COW(for child processes).

In some cases, we may want it to have some concluded result. Since
some workloads expect a relatively "stable" THP, while others may want
to save memory more rather than the performance benifits.

This patch adds some basic helpers and branch in madvise path so that
we can add madvise choices on THP to conduct what we do on different
types of operations like COW or swap that may split THP, on the level
of vma.

We transfer the type of configuration using parameters of madvise,
analyze it and save the result in vma->vm_flags for later use.

Currently the only operation in the list is COW. It decides whether
we want to use hugepages for the child process when it writes a spot
on the shared anonymous pmd so that we can make sure the THP not
being split after writing. This patch only adds the basic setup
helpers, the real usage will be added in the later patches.

Signed-off-by: Luka Bai <lukabai@tencent.com>
---
include/linux/huge_mm.h | 6 ++++++
include/linux/mm.h | 19 +++++++++++++++++++
include/uapi/asm-generic/mman-common.h | 9 +++++++++
mm/madvise.c | 25 +++++++++++++++++++++++++
4 files changed, 59 insertions(+)

diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index 48496f09909b..a0ce8c0b81f5 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -6,6 +6,7 @@

#include <linux/fs.h> /* only for vma_is_dax() */
#include <linux/kobject.h>
+#include <uapi/asm-generic/mman-common.h>

vm_fault_t do_huge_pmd_anonymous_page(struct vm_fault *vmf);
int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
@@ -363,6 +364,11 @@ static inline bool thp_disabled_by_hw(void)
return transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_UNSUPPORTED);
}

+static inline bool madv_thp_cow(int behavior)
+{
+ return behavior & MADV_THP_COW;
+}
+
unsigned long thp_get_unmapped_area(struct file *filp, unsigned long addr,
unsigned long len, unsigned long pgoff, unsigned long flags);
unsigned long thp_get_unmapped_area_vmflags(struct file *filp, unsigned long addr,
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 1d76da6e0791..8a800819cfa2 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -391,6 +391,10 @@ enum {
#else
DECLARE_VMA_BIT_ALIAS(STACK, GROWSDOWN),
#endif
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ DECLARE_VMA_BIT(THP_SETUP_1, 43),
+ DECLARE_VMA_BIT_ALIAS(THP_COW, THP_SETUP_1),
+#endif
};
#undef DECLARE_VMA_BIT
#undef DECLARE_VMA_BIT_ALIAS
@@ -510,6 +514,9 @@ enum {
#define VM_DROPPABLE VM_NONE
#define VMA_DROPPABLE EMPTY_VMA_FLAGS
#endif
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#define VM_THP_COW INIT_VM_FLAG(THP_COW)
+#endif

/* Bits set in the VMA until the stack is in its final location */
#define VM_STACK_INCOMPLETE_SETUP (VM_RAND_READ | VM_SEQ_READ | VM_STACK_EARLY)
@@ -4128,6 +4135,18 @@ extern int do_munmap(struct mm_struct *, unsigned long, size_t,
struct list_head *uf);
extern int do_madvise(struct mm_struct *mm, unsigned long start, size_t len_in, int behavior);

+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+static inline bool madv_thp_behavior(int behavior)
+{
+ return behavior >= MADV_THP_SETUP_BASE && behavior < MADV_THP_SETUP_END;
+}
+#else
+static inline bool madv_thp_behavior(int behavior)
+{
+ return false;
+}
+#endif
+
#ifdef CONFIG_MMU
extern int __mm_populate(unsigned long addr, unsigned long len,
int ignore_errors);
diff --git a/include/uapi/asm-generic/mman-common.h b/include/uapi/asm-generic/mman-common.h
index ef1c27fa3c57..1617ed374503 100644
--- a/include/uapi/asm-generic/mman-common.h
+++ b/include/uapi/asm-generic/mman-common.h
@@ -82,6 +82,15 @@
#define MADV_GUARD_INSTALL 102 /* fatal signal on access to range */
#define MADV_GUARD_REMOVE 103 /* unguard range */

+/* for THP setup */
+#define MADV_THP_SETUP_BASE 256
+enum {
+ MADV_THP_COW_BIT,
+ MADV_THP_SETUP_MAX_BIT,
+};
+#define MADV_THP_COW (MADV_THP_SETUP_BASE + (1 << MADV_THP_COW_BIT))
+#define MADV_THP_SETUP_END (MADV_THP_SETUP_BASE + (1 << MADV_THP_SETUP_MAX_BIT))
+
/* compatibility flags */
#define MAP_FILE 0

diff --git a/mm/madvise.c b/mm/madvise.c
index 69708e953cf5..5dbfc89682d7 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -1331,6 +1331,25 @@ static bool can_madvise_modify(struct madvise_behavior *madv_behavior)
}
#endif

+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+static vm_flags_t madvise_thp_setup(struct madvise_behavior *madv_behavior)
+{
+ int thp_behavior = madv_behavior->behavior - MADV_THP_SETUP_BASE;
+ struct vm_area_struct *vma = madv_behavior->vma;
+ vm_flags_t new_flags = vma->vm_flags;
+
+ if (madv_thp_cow(thp_behavior))
+ new_flags |= VM_THP_COW;
+
+ return new_flags;
+}
+#else
+static vm_flags_t madvise_thp_setup(struct madvise_behavior *madv_behavior)
+{
+ return madv_behavior->vma->vm_flags;
+}
+#endif
+
/*
* Apply an madvise behavior to a region of a vma. madvise_update_vma
* will handle splitting a vm area into separate areas, each area with its own
@@ -1427,6 +1446,10 @@ static int madvise_vma_behavior(struct madvise_behavior *madv_behavior)
break;
}

+ /* Handle THP behaviors */
+ if (madv_thp_behavior(behavior))
+ new_flags = madvise_thp_setup(madv_behavior);
+
/* This is a write operation.*/
VM_WARN_ON_ONCE(madv_behavior->lock_mode != MADVISE_MMAP_WRITE_LOCK);

@@ -1555,6 +1578,8 @@ madvise_behavior_valid(int behavior)
return true;

default:
+ if (madv_thp_behavior(behavior))
+ return true;
return false;
}
}

--
2.52.0