Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
@ 2026-07-20  3:03 Lian Wang
  2026-07-20  3:03 ` [RFC PATCH v3 1/3] " Lian Wang
                   ` (5 more replies)
  0 siblings, 6 replies; 14+ messages in thread
From: Lian Wang @ 2026-07-20  3:03 UTC (permalink / raw)
  To: damon, linux-mm
  Cc: sj, akpm, linux-kernel, david, ljs, liam, vbabka, rppt, surenb,
	mhocko, npache, ziy, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, gutierrez.asier, zengheng4, kasong, corbet,
	skhan, linux-doc, linux-kselftest, lianux.mm, lianux.wang,
	kunwu.chan

DAMOS_SPLIT splits large folios in a target region down to a
configured target order, using the existing split_folio_to_order().
No new core-mm code or exported symbols are introduced.

Based on mm-unstable at 61cccb8363fc ("mm/swap, PM: hibernate:
atomically replace hibernation pin").

Different addresses within a PMD-mapped folio resolve to the same
PMD Accessed bit.  Accesses to a small part of the folio can
therefore coarsen DAMON's observed hot set relative to the actual
working set.

DAMOS already provides promotion actions (HUGEPAGE, COLLAPSE) but
has no corresponding demotion action.  DAMOS_SPLIT fills this gap.
It is a mechanism, not a policy -- it does not decide which folios
to split.  Selection is left to DAMON's existing access patterns,
filters, and future probe/PMU signals.

target_order selects the split target: 0 for order-0 base pages,
or a supported smaller mTHP order.  Both anonymous and file-backed
folios are supported.  The locking follows split_huge_pages_in_pid()
in mm/huge_memory.c.

Tests
=====

damos_split.py (VM + Kunpeng 920):
  anon THP -> order-0 split: PASS

sangfor_exp.py (Kunpeng 920, tmpfs, 4096 MiB):

  - Created a 4 GiB PMD-mapped tmpfs workload.
  - Applied DAMOS_SPLIT with target_order=0.
  - ShmemPmdMapped dropped from 4194304 KiB to 0 in every round.
  - Repeated for five rounds without functional failures.

The functional selftest (damos_split.py) is included in this series.
Additional experiment scripts and raw results are available on
request.  Performance characterization using masim [1] and KMB [2]
is in progress.

[1] https://github.com/sjp38/masim
[2] https://gitee.com/OpenCloudOS/kernel-multi-bench

Open questions
==============

- Selection policy: this series keeps folio selection outside the
  action and relies on DAMOS access patterns, filters, and quotas.
  Is this the appropriate layering for future probe-based signals?

- Hysteresis: khugepaged may re-collapse a just-split folio.
  Should cooldown live in DAMON policy or khugepaged?

- File-backed folios: adjust target_order upward to filesystem
  minimum, or keep current "fail and skip"?

Beyond the action API itself, feedback on real workloads that need
proactive large-folio demotion is particularly welcome.  Follow-up
work will evaluate candidate selection signals, including DAMON
probes and hardware-assisted sampling, as well as target-order
selection and split/collapse hysteresis.  Those policies are
intentionally kept outside this series.

Changes since v2 [3]
====================

- Split-only series (collapse deferred).
- Dropped SPE feedback (mechanism/policy separation).
- DAMOS_MTHP_SPLIT -> DAMOS_SPLIT.
- order field in existing union (no struct size increase).
- Added functional selftest (damos_split.py).
- checkpatch: 0 errors, 0 warnings.

[3] https://lore.kernel.org/20260701123000.00000-1-lianux.mm@gmail.com/

Lian Wang (Processmission) (3):
  mm/damon: introduce DAMOS_SPLIT action
  mm/damon/vaddr: implement DAMOS_SPLIT handler
  selftests/damon: add functional test for DAMOS_SPLIT

 .../ABI/testing/sysfs-kernel-mm-damon         |  7 ++
 Documentation/mm/damon/design.rst             |  5 +
 include/linux/damon.h                         | 15 ++-
 mm/damon/core.c                               |  2 +
 mm/damon/sysfs-schemes.c                      | 48 +++++++
 mm/damon/vaddr.c                              | 90 +++++++++++++
 tools/testing/selftests/damon/Makefile        |  1 +
 tools/testing/selftests/damon/_damon_sysfs.py |  9 +-
 tools/testing/selftests/damon/damos_split.py  | 99 +++++++++++++++
 tools/testing/selftests/damon/sysfs.py        | 11 +-
 10 files changed, 279 insertions(+), 8 deletions(-)


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 1/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20  3:03 [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Lian Wang
@ 2026-07-20  3:03 ` Lian Wang
  2026-07-20  9:47   ` Gutierrez Asier
  2026-07-20  3:03 ` [RFC PATCH v3 2/3] mm/damon/vaddr: implement DAMOS_SPLIT handler Lian Wang
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 14+ messages in thread
From: Lian Wang @ 2026-07-20  3:03 UTC (permalink / raw)
  To: damon, linux-mm
  Cc: sj, akpm, linux-kernel, david, ljs, liam, vbabka, rppt, surenb,
	mhocko, npache, ziy, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, gutierrez.asier, zengheng4, kasong, corbet,
	skhan, linux-doc, linux-kselftest, lianux.mm, lianux.wang,
	kunwu.chan

Add a new DAMOS action, DAMOS_SPLIT, that splits large folios in the
target region down to a smaller order.  A new per-scheme field and sysfs
file, target_order, selects the split target: 0 for order-0 base pages,
or 2..HPAGE_PMD_ORDER-1 for a smaller mTHP order (order-1 is rejected at
store time since anonymous THPs cannot be split to order-1).

Unlike DAMOS_COLLAPSE which builds large folios, splitting only dismantles
them; it allocates nothing, copies nothing, and no THP admission policy
applies.  khugepaged deliberately refuses to collapse a folio to a
smaller order, leaving the shrink direction to callers like this one.

This patch adds the DAMOS_SPLIT action, the ``order`` field (placed in
the existing union alongside target_nid and migrate_dests), its sysfs
interface, and documentation.  The vaddr operations set handler is added
in the next patch.

Link: https://lore.kernel.org/20260605161422.213817-9-npache@redhat.com/
 ("mm/khugepaged: skip collapsing mTHP to smaller orders")



Co-developed-by: Kunwu Chan <kunwu.chan@linux.dev>
Signed-off-by: Kunwu Chan <kunwu.chan@linux.dev>
Signed-off-by: Lian Wang (Processmission) <lianux.mm@gmail.com>
---
 .../ABI/testing/sysfs-kernel-mm-damon         |  7 +++
 Documentation/mm/damon/design.rst             |  5 ++
 include/linux/damon.h                         | 15 ++++--
 mm/damon/core.c                               |  2 +
 mm/damon/sysfs-schemes.c                      | 48 +++++++++++++++++++
 tools/testing/selftests/damon/sysfs.py        | 11 +++--
 6 files changed, 79 insertions(+), 9 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon
index 907a504fb64c..ee8e260a631c 100644
--- a/Documentation/ABI/testing/sysfs-kernel-mm-damon
+++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon
@@ -265,6 +265,13 @@ Contact:	SJ Park <sj@kernel.org>
 Description:	Action's target NUMA node id.  Supported by only relevant
 		actions.
 
+What:		/sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/target_order
+Date:		Jul 2026
+Contact:	SJ Park <sj@kernel.org>
+Description:	Target folio order for the ``split`` action.  Large folios in
+		the target region are split down to this order.  Valid values
+		are 0 (order-0 base pages) and 2..HPAGE_PMD_ORDER-1.
+
 What:		/sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/apply_interval_us
 Date:		Sep 2023
 Contact:	SJ Park <sj@kernel.org>
diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst
index aed6cb1cf483..87a801954b1b 100644
--- a/Documentation/mm/damon/design.rst
+++ b/Documentation/mm/damon/design.rst
@@ -549,6 +549,11 @@ that supports each action are as below.
    Supported by ``vaddr`` and ``fvaddr`` operations set. When
    TRANSPARENT_HUGEPAGE is disabled, the application of the action will just
    fail.
+ - ``split``: Split each large folio in the region down to the order specified
+   by the scheme's ``target_order`` (``0`` for order-0 base pages, or
+   ``2..HPAGE_PMD_ORDER-1``).  Supported by ``vaddr`` and ``fvaddr`` operations
+   set. When TRANSPARENT_HUGEPAGE is disabled, the application of the action
+   will just fail.
  - ``lru_prio``: Prioritize the region on its LRU lists.
    Supported by ``paddr`` operations set.
  - ``lru_deprio``: Deprioritize the region on its LRU lists.
diff --git a/include/linux/damon.h b/include/linux/damon.h
index f69442a9d431..88b61b900a36 100644
--- a/include/linux/damon.h
+++ b/include/linux/damon.h
@@ -110,6 +110,7 @@ struct damon_target {
  * @DAMOS_HUGEPAGE:	Call ``madvise()`` for the region with MADV_HUGEPAGE.
  * @DAMOS_NOHUGEPAGE:	Call ``madvise()`` for the region with MADV_NOHUGEPAGE.
  * @DAMOS_COLLAPSE:	Call ``madvise()`` for the region with MADV_COLLAPSE.
+ * @DAMOS_SPLIT:	Split each large folio in the region to a smaller order.
  * @DAMOS_LRU_PRIO:	Prioritize the region on its LRU lists.
  * @DAMOS_LRU_DEPRIO:	Deprioritize the region on its LRU lists.
  * @DAMOS_MIGRATE_HOT:  Migrate the regions prioritizing warmer regions.
@@ -130,6 +131,7 @@ enum damos_action {
 	DAMOS_HUGEPAGE,
 	DAMOS_NOHUGEPAGE,
 	DAMOS_COLLAPSE,
+	DAMOS_SPLIT,
 	DAMOS_LRU_PRIO,
 	DAMOS_LRU_DEPRIO,
 	DAMOS_MIGRATE_HOT,
@@ -582,10 +584,15 @@ struct damos {
 	struct damos_quota quota;
 	struct damos_watermarks wmarks;
 	union {
-		struct {
-			int target_nid;
-			struct damos_migrate_dests migrate_dests;
-		};
+		int target_nid;
+		struct damos_migrate_dests migrate_dests;
+		/*
+		 * @order: target folio order for DAMOS_SPLIT.
+		 * Split large folios down to this order.  0 for
+		 * order-0 base pages, 2..HPAGE_PMD_ORDER-1 for
+		 * smaller mTHP.  Order-1 is rejected.
+		 */
+		unsigned int order;
 	};
 	struct list_head core_filters;
 	struct list_head ops_filters;
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 806a67d02a6e..394c62455ad5 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -693,6 +693,7 @@ struct damos *damon_new_scheme(struct damos_access_pattern *pattern,
 		return NULL;
 	scheme->pattern = *pattern;
 	scheme->action = action;
+	scheme->order = 0;
 	scheme->apply_interval_us = apply_interval_us;
 	/*
 	 * next_apply_sis will be set when kdamond starts.  While kdamond is
@@ -1446,6 +1447,7 @@ static int damos_commit(struct damos *dst, struct damos *src)
 
 	dst->pattern = src->pattern;
 	dst->action = src->action;
+	dst->order = src->order;
 	dst->apply_interval_us = src->apply_interval_us;
 
 	err = damos_commit_quota(&dst->quota, &src->quota);
diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c
index 32f495a96b17..725ed81ccf6c 100644
--- a/mm/damon/sysfs-schemes.c
+++ b/mm/damon/sysfs-schemes.c
@@ -4,7 +4,9 @@
  */
 
 #include <linux/slab.h>
+#include <linux/mm.h>
 #include <linux/numa.h>
+#include <linux/huge_mm.h>
 
 #include "sysfs-common.h"
 
@@ -2260,6 +2262,7 @@ struct damon_sysfs_scheme {
 	struct damon_sysfs_stats *stats;
 	struct damon_sysfs_scheme_regions *tried_regions;
 	int target_nid;
+	unsigned int target_order;
 	struct damos_sysfs_dests *dests;
 };
 
@@ -2293,6 +2296,10 @@ static struct damos_sysfs_action_name damos_sysfs_action_names[] = {
 		.action = DAMOS_COLLAPSE,
 		.name = "collapse",
 	},
+	{
+		.action = DAMOS_SPLIT,
+		.name = "split",
+	},
 	{
 		.action = DAMOS_LRU_PRIO,
 		.name = "lru_prio",
@@ -2326,6 +2333,7 @@ static struct damon_sysfs_scheme *damon_sysfs_scheme_alloc(
 	scheme->action = action;
 	scheme->apply_interval_us = apply_interval_us;
 	scheme->target_nid = NUMA_NO_NODE;
+	scheme->target_order = 0;
 	return scheme;
 }
 
@@ -2645,6 +2653,40 @@ static ssize_t target_nid_store(struct kobject *kobj,
 	return err ? err : count;
 }
 
+static ssize_t target_order_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_scheme *scheme = container_of(kobj,
+			struct damon_sysfs_scheme, kobj);
+
+	return sysfs_emit(buf, "%u\n", scheme->target_order);
+}
+
+static ssize_t target_order_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_scheme *scheme = container_of(kobj,
+			struct damon_sysfs_scheme, kobj);
+	unsigned int val;
+	int err;
+
+	err = kstrtouint(buf, 0, &val);
+	if (err)
+		return err;
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+	/* Valid split targets: 0 (order-0 base pages) or 2..HPAGE_PMD_ORDER-1. */
+	if (val != 0 && (val < 2 || val >= HPAGE_PMD_ORDER))
+		return -EINVAL;
+#else
+	if (val != 0)
+		return -EINVAL;
+#endif
+
+	scheme->target_order = val;
+	return count;
+}
+
 static void damon_sysfs_scheme_release(struct kobject *kobj)
 {
 	kfree(container_of(kobj, struct damon_sysfs_scheme, kobj));
@@ -2659,10 +2701,14 @@ static struct kobj_attribute damon_sysfs_scheme_apply_interval_us_attr =
 static struct kobj_attribute damon_sysfs_scheme_target_nid_attr =
 		__ATTR_RW_MODE(target_nid, 0600);
 
+static struct kobj_attribute damon_sysfs_scheme_target_order_attr =
+		__ATTR_RW_MODE(target_order, 0600);
+
 static struct attribute *damon_sysfs_scheme_attrs[] = {
 	&damon_sysfs_scheme_action_attr.attr,
 	&damon_sysfs_scheme_apply_interval_us_attr.attr,
 	&damon_sysfs_scheme_target_nid_attr.attr,
+	&damon_sysfs_scheme_target_order_attr.attr,
 	NULL,
 };
 ATTRIBUTE_GROUPS(damon_sysfs_scheme);
@@ -3011,6 +3057,8 @@ static struct damos *damon_sysfs_mk_scheme(
 	if (!scheme)
 		return NULL;
 
+	scheme->order = sysfs_scheme->target_order;
+
 	err = damos_sysfs_add_quota_score(sysfs_quotas->goals, &scheme->quota);
 	if (err) {
 		damon_destroy_scheme(scheme);
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py
index 3ffa054b6386..49d3c319dfd8 100755
--- a/tools/testing/selftests/damon/sysfs.py
+++ b/tools/testing/selftests/damon/sysfs.py
@@ -131,11 +131,12 @@ def assert_scheme_committed(scheme, dump):
             'hugepage': 3,
             'nohugepage': 4,
             'collapse': 5,
-            'lru_prio': 6,
-            'lru_deprio': 7,
-            'migrate_hot': 8,
-            'migrate_cold': 9,
-            'stat': 10,
+            'split': 6,
+            'lru_prio': 7,
+            'lru_deprio': 8,
+            'migrate_hot': 9,
+            'migrate_cold': 10,
+            'stat': 11,
             }
     assert_true(dump['action'] == action_val[scheme.action], 'action', dump)
     assert_true(dump['apply_interval_us'] == scheme. apply_interval_us,


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 2/3] mm/damon/vaddr: implement DAMOS_SPLIT handler
  2026-07-20  3:03 [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Lian Wang
  2026-07-20  3:03 ` [RFC PATCH v3 1/3] " Lian Wang
@ 2026-07-20  3:03 ` Lian Wang
  2026-07-20  3:03 ` [RFC PATCH v3 3/3] selftests/damon: add functional test for DAMOS_SPLIT Lian Wang
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Lian Wang @ 2026-07-20  3:03 UTC (permalink / raw)
  To: damon, linux-mm
  Cc: sj, akpm, linux-kernel, david, ljs, liam, vbabka, rppt, surenb,
	mhocko, npache, ziy, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, gutierrez.asier, zengheng4, kasong, corbet,
	skhan, linux-doc, linux-kselftest, lianux.mm, lianux.wang,
	kunwu.chan

Implement DAMOS_SPLIT for the vaddr and fvaddr operations sets.  For
each large folio in the target region whose order exceeds the scheme's
order, split it down using split_folio_to_order() -- the same helper
used by truncation, migration, ksm and shmem.  No new core-mm code or
exported symbols are introduced.

split_folio_to_order() must not run under the page table lock, so the
folio is pinned (folio_trylock + folio_get), the walk is ended to drop
the page table lock, and the split runs while holding only mmap_read_lock.

The scheme's operations-layer filters are honoured, and the amount of
memory that passed those filters is reported via sz_filter_passed.



Co-developed-by: Kunwu Chan <kunwu.chan@linux.dev>
Signed-off-by: Kunwu Chan <kunwu.chan@linux.dev>
Signed-off-by: Lian Wang (Processmission) <lianux.mm@gmail.com>
---
 mm/damon/vaddr.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)

diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c
index d10b8042adb5..a8607448584d 100644
--- a/mm/damon/vaddr.c
+++ b/mm/damon/vaddr.c
@@ -6,6 +6,7 @@
 #define pr_fmt(fmt) "damon-va: " fmt
 
 #include <linux/highmem.h>
+#include <linux/huge_mm.h>
 #include <linux/hugetlb.h>
 #include <linux/mman.h>
 #include <linux/mmu_notifier.h>
@@ -896,6 +897,111 @@ static unsigned long damos_va_stat(struct damon_target *target,
 	return 0;
 }
 
+/*
+ * damos_va_split() - Split large folios in a region down to @target_order
+ * using the existing split_folio_to_order().
+ *
+ * Locking: folio_walk_start() returns the folio with the page table lock
+ * held.  split_folio_to_order() must not run under the page table lock,
+ * so we pin the folio (reference + lock), end the walk to drop the page
+ * table lock, and split while holding only mmap_read_lock.
+ * split_folio_to_order() returns -EBUSY for a raced or pinned folio;
+ * we skip such folios.
+ */
+static unsigned long damos_va_split(struct damon_target *target,
+		struct damon_region *r, struct damos *s,
+		unsigned long *sz_filter_passed)
+{
+	unsigned int target_order = s->order;
+	unsigned long addr = ALIGN_DOWN(r->ar.start, PAGE_SIZE);
+	unsigned long end = r->ar.end;
+	unsigned long applied = 0;
+	struct mm_struct *mm;
+
+	mm = damon_get_mm(target);
+	if (!mm)
+		return 0;
+
+	while (addr < end) {
+		struct vm_area_struct *vma;
+		struct folio *folio;
+		struct folio_walk fw;
+		unsigned long folio_sz = 0;
+
+		if (mmap_read_lock_killable(mm))
+			break;
+
+		vma = vma_lookup(mm, addr);
+		if (!vma) {
+			/* Skip the gap to the next VMA, if any. */
+			vma = find_vma(mm, addr);
+			mmap_read_unlock(mm);
+			if (!vma || vma->vm_start >= end)
+				break;
+			addr = vma->vm_start;
+			continue;
+		}
+
+		/* Folios in these VMAs are not our business. */
+		if (vma->vm_flags & (VM_HUGETLB | VM_MIXEDMAP)) {
+			addr = vma->vm_end;
+			mmap_read_unlock(mm);
+			continue;
+		}
+
+		folio = folio_walk_start(&fw, vma, addr, 0);
+		if (!folio) {
+			mmap_read_unlock(mm);
+			addr += PAGE_SIZE;
+			continue;
+		}
+
+		folio_sz = folio_size(folio);
+
+		/*
+		 * For file-backed folios, @target_order may be below the
+		 * filesystem's minimum folio order (mapping_min_folio_order()).
+		 * split_folio_to_order() will simply fail in that case and
+		 * we skip the folio.  This is a safe no-op; future work can
+		 * adjust target_order upward like split_huge_pages_in_pid()
+		 * does when a specific filesystem needs it.
+		 */
+
+		/* Honour the scheme's operations-layer filters. */
+		if (damos_ops_has_filter(s)) {
+			if (damos_va_filter_out(s, folio, vma, addr,
+					fw.level == FW_LEVEL_PTE ? fw.ptep : NULL,
+					fw.level == FW_LEVEL_PMD ? fw.pmdp : NULL)) {
+				folio_walk_end(&fw, vma);
+				mmap_read_unlock(mm);
+				goto next;
+			}
+			*sz_filter_passed += folio_sz;
+		}
+
+		if (folio_order(folio) > target_order && folio_trylock(folio)) {
+			folio_get(folio);
+			/* Drop the page table lock before splitting. */
+			folio_walk_end(&fw, vma);
+
+			if (!split_folio_to_order(folio, target_order))
+				applied += folio_sz;
+
+			folio_unlock(folio);
+			folio_put(folio);
+		} else {
+			folio_walk_end(&fw, vma);
+		}
+		mmap_read_unlock(mm);
+next:
+		addr = ALIGN_DOWN(addr, folio_sz) + folio_sz;
+		cond_resched();
+	}
+
+	mmput(mm);
+	return applied;
+}
+
 static unsigned long damon_va_apply_scheme(struct damon_ctx *ctx,
 		struct damon_target *t, struct damon_region *r,
 		struct damos *scheme, unsigned long *sz_filter_passed)
@@ -926,6 +1032,8 @@ static unsigned long damon_va_apply_scheme(struct damon_ctx *ctx,
 		return damos_va_migrate(t, r, scheme, sz_filter_passed);
 	case DAMOS_STAT:
 		return damos_va_stat(t, r, scheme, sz_filter_passed);
+	case DAMOS_SPLIT:
+		return damos_va_split(t, r, scheme, sz_filter_passed);
 	default:
 		/*
 		 * DAMOS actions that are not yet supported by 'vaddr'.


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 3/3] selftests/damon: add functional test for DAMOS_SPLIT
  2026-07-20  3:03 [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Lian Wang
  2026-07-20  3:03 ` [RFC PATCH v3 1/3] " Lian Wang
  2026-07-20  3:03 ` [RFC PATCH v3 2/3] mm/damon/vaddr: implement DAMOS_SPLIT handler Lian Wang
@ 2026-07-20  3:03 ` Lian Wang
  2026-07-20  9:28 ` [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Gutierrez Asier
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Lian Wang @ 2026-07-20  3:03 UTC (permalink / raw)
  To: damon, linux-mm
  Cc: sj, akpm, linux-kernel, david, ljs, liam, vbabka, rppt, surenb,
	mhocko, npache, ziy, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, gutierrez.asier, zengheng4, kasong, corbet,
	skhan, linux-doc, linux-kselftest, lianux.mm, lianux.wang,
	kunwu.chan

Add damos_split.py, which allocates a MADV_HUGEPAGE-backed region in a
child process and runs a DAMON/DAMOS scheme with action 'split' and
target_order 0 against it, then verifies the huge pages are split into
base pages (the child's AnonHugePages drops).

Extend the _damon_sysfs.py Damos helper with the target_order parameter
so a scheme's split target can be expressed.



Co-developed-by: Kunwu Chan <kunwu.chan@linux.dev>
Signed-off-by: Kunwu Chan <kunwu.chan@linux.dev>
Signed-off-by: Lian Wang (Processmission) <lianux.mm@gmail.com>
---
 tools/testing/selftests/damon/Makefile        |   1 +
 tools/testing/selftests/damon/_damon_sysfs.py |   9 +-
 tools/testing/selftests/damon/damos_split.py  | 125 ++++++++++++++++++
 3 files changed, 134 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/damon/damos_split.py

diff --git a/tools/testing/selftests/damon/Makefile b/tools/testing/selftests/damon/Makefile
index ece244e5c5b9..a623c271355f 100644
--- a/tools/testing/selftests/damon/Makefile
+++ b/tools/testing/selftests/damon/Makefile
@@ -12,6 +12,7 @@ TEST_PROGS += sysfs.sh
 TEST_PROGS += sysfs.py
 TEST_PROGS += sysfs_update_schemes_tried_regions_wss_estimation.py
 TEST_PROGS += damos_quota.py damos_quota_goal.py damos_apply_interval.py
+TEST_PROGS += damos_split.py
 TEST_PROGS += damos_tried_regions.py damon_nr_regions.py
 TEST_PROGS += sysfs_refresh.py
 TEST_PROGS += reclaim.sh lru_sort.sh
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py
index e6a2265d721e..5a01f31ad9f2 100644
--- a/tools/testing/selftests/damon/_damon_sysfs.py
+++ b/tools/testing/selftests/damon/_damon_sysfs.py
@@ -419,6 +419,7 @@ class Damos:
     filters = None
     apply_interval_us = None
     target_nid = None
+    target_order = None
     dests = None
     idx = None
     context = None
@@ -429,7 +430,7 @@ class Damos:
     def __init__(self, action='stat', access_pattern=DamosAccessPattern(),
                  quota=DamosQuota(), watermarks=DamosWatermarks(),
                  core_filters=[], ops_filters=[], filters=[], target_nid=0,
-                 dests=DamosDests(), apply_interval_us=0):
+                 target_order=0, dests=DamosDests(), apply_interval_us=0):
         self.action = action
         self.access_pattern = access_pattern
         self.access_pattern.scheme = self
@@ -448,6 +449,7 @@ class Damos:
         self.filters.scheme = self
 
         self.target_nid = target_nid
+        self.target_order = target_order
         self.dests = dests
         self.dests.scheme = self
 
@@ -492,6 +494,11 @@ class Damos:
         if err is not None:
             return err
 
+        err = write_file(os.path.join(self.sysfs_dir(), 'target_order'), '%d' %
+                         self.target_order)
+        if err is not None:
+            return err
+
         err = self.dests.stage()
         if err is not None:
             return err
diff --git a/tools/testing/selftests/damon/damos_split.py b/tools/testing/selftests/damon/damos_split.py
new file mode 100644
index 000000000000..089fbe0f0d4d
--- /dev/null
+++ b/tools/testing/selftests/damon/damos_split.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Functional test for the DAMOS_SPLIT action.
+#
+# A child process allocates a MADV_HUGEPAGE-backed anonymous region and
+# faults it in so that it is backed by (m)THPs.  The parent then runs a
+# DAMON/DAMOS scheme with action 'split' and target_order 0 against the
+# child and checks that the huge pages are split into base pages, i.e. the
+# child's AnonHugePages (as reported by /proc/<pid>/smaps) drops.
+
+import ctypes
+import os
+import signal
+import sys
+import time
+
+import _damon_sysfs
+
+PMD_SIZE = 2 * 1024 * 1024
+MADV_HUGEPAGE = 14
+PROT_READ_WRITE = 0x1 | 0x2
+MAP_PRIVATE_ANON = 0x2 | 0x20
+REGION_SIZE = 32 * PMD_SIZE
+
+def child_workload():
+    '''Allocate a PMD-aligned, THP-backed region, fault it in, then idle.'''
+    libc = ctypes.CDLL('libc.so.6', use_errno=True)
+    libc.mmap.restype = ctypes.c_void_p
+    libc.mmap.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_int,
+                          ctypes.c_int, ctypes.c_int, ctypes.c_long]
+    libc.madvise.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_int]
+
+    # Over-allocate so that a PMD-aligned window is available.
+    raw = libc.mmap(None, REGION_SIZE + PMD_SIZE, PROT_READ_WRITE,
+                    MAP_PRIVATE_ANON, -1, 0)
+    if raw is None or raw == ctypes.c_void_p(-1).value:
+        os._exit(2)
+    base = (raw + PMD_SIZE - 1) & ~(PMD_SIZE - 1)
+    libc.madvise(ctypes.c_void_p(base), REGION_SIZE, MADV_HUGEPAGE)
+
+    buf = (ctypes.c_char * REGION_SIZE).from_address(base)
+    for off in range(0, REGION_SIZE, 4096):
+        buf[off] = 1
+
+    # Ready; idle until the parent tears us down.
+    signal.pause()
+
+def anon_huge_kb(pid):
+    total = 0
+    try:
+        with open('/proc/%d/smaps' % pid) as f:
+            for line in f:
+                if line.startswith('AnonHugePages:'):
+                    total += int(line.split()[1])
+    except FileNotFoundError:
+        return -1
+    return total
+
+def main():
+    if not os.path.exists('/sys/kernel/mm/transparent_hugepage/enabled'):
+        print('SKIP: transparent hugepage is not available')
+        exit(0)
+
+    pid = os.fork()
+    if pid == 0:
+        child_workload()
+        os._exit(0)
+
+    try:
+        # Give the child time to fault in its huge pages.
+        time.sleep(2)
+        before = anon_huge_kb(pid)
+        if before <= 0:
+            print('SKIP: workload did not get any THP (AnonHugePages=%d)'
+                  % before)
+            os.kill(pid, signal.SIGKILL)
+            exit(0)
+
+        # Split every large folio in the target down to order-0 base pages.
+        kdamonds = _damon_sysfs.Kdamonds([_damon_sysfs.Kdamond(
+            contexts=[_damon_sysfs.DamonCtx(
+                ops='vaddr',
+                targets=[_damon_sysfs.DamonTarget(pid=pid)],
+                schemes=[_damon_sysfs.Damos(
+                    action='split',
+                    target_order=0,
+                    # match every region regardless of access/age/size, so
+                    # the ARM64 stale-TLB blind spot cannot mask the target
+                    access_pattern=_damon_sysfs.DamosAccessPattern(
+                        size=[0, 2**64 - 1],
+                        nr_accesses=[0, 2**64 - 1],
+                        age=[0, 2**64 - 1]),
+                    apply_interval_us=0)])])])
+        err = kdamonds.start()
+        if err is not None:
+            print('kdamonds start failed: %s' % err)
+            os.kill(pid, signal.SIGKILL)
+            exit(1)
+
+        # Let the scheme find and split the regions.
+        after = before
+        for _ in range(50):
+            time.sleep(0.2)
+            after = anon_huge_kb(pid)
+            if after == 0:
+                break
+
+        kdamonds.stop()
+        os.kill(pid, signal.SIGKILL)
+
+        if after >= before:
+            print('FAIL: AnonHugePages did not shrink: before=%d KiB '
+                  'after=%d KiB' % (before, after))
+            exit(1)
+        print('PASS: AnonHugePages %d KiB -> %d KiB after DAMOS_SPLIT'
+              % (before, after))
+    finally:
+        try:
+            os.kill(pid, signal.SIGKILL)
+        except ProcessLookupError:
+            pass
+
+if __name__ == '__main__':
+    main()


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20  3:03 [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Lian Wang
                   ` (2 preceding siblings ...)
  2026-07-20  3:03 ` [RFC PATCH v3 3/3] selftests/damon: add functional test for DAMOS_SPLIT Lian Wang
@ 2026-07-20  9:28 ` Gutierrez Asier
  2026-07-20  9:43   ` Lian Wang
  2026-07-20  9:44 ` David Hildenbrand (Arm)
  2026-07-21  1:08 ` SJ Park
  5 siblings, 1 reply; 14+ messages in thread
From: Gutierrez Asier @ 2026-07-20  9:28 UTC (permalink / raw)
  To: Lian Wang, damon, linux-mm
  Cc: sj, akpm, linux-kernel, david, ljs, liam, vbabka, rppt, surenb,
	mhocko, npache, ziy, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, zengheng4, kasong, corbet, skhan, linux-doc,
	linux-kselftest, lianux.wang, kunwu.chan

Hi Lian,

On 7/20/2026 6:03 AM, Lian Wang wrote:
> DAMOS_SPLIT splits large folios in a target region down to a
> configured target order, using the existing split_folio_to_order().
> No new core-mm code or exported symbols are introduced.
> 
> Based on mm-unstable at 61cccb8363fc ("mm/swap, PM: hibernate:
> atomically replace hibernation pin").
> 
> Different addresses within a PMD-mapped folio resolve to the same
> PMD Accessed bit.  Accesses to a small part of the folio can
> therefore coarsen DAMON's observed hot set relative to the actual
> working set.
> 
> DAMOS already provides promotion actions (HUGEPAGE, COLLAPSE) but
> has no corresponding demotion action.  DAMOS_SPLIT fills this gap.
> It is a mechanism, not a policy -- it does not decide which folios
> to split.  Selection is left to DAMON's existing access patterns,
> filters, and future probe/PMU signals.
You should mention why page split is be needed. The fact that page
collapsing exist doesn't necessarily mean that split should exist.

I agree that it is a nice feature, but it should be backed in the
cover letter.
> target_order selects the split target: 0 for order-0 base pages,
> or a supported smaller mTHP order.  Both anonymous and file-backed
> folios are supported.  The locking follows split_huge_pages_in_pid()
> in mm/huge_memory.c.
> 
> Tests
> =====
> 
> damos_split.py (VM + Kunpeng 920):
>   anon THP -> order-0 split: PASS
> 
> sangfor_exp.py (Kunpeng 920, tmpfs, 4096 MiB):
> 
>   - Created a 4 GiB PMD-mapped tmpfs workload.
>   - Applied DAMOS_SPLIT with target_order=0.
>   - ShmemPmdMapped dropped from 4194304 KiB to 0 in every round.
>   - Repeated for five rounds without functional failures.
> 
> The functional selftest (damos_split.py) is included in this series.
> Additional experiment scripts and raw results are available on
> request.  Performance characterization using masim [1] and KMB [2]
> is in progress.
> 
> [1] https://github.com/sjp38/masim
> [2] https://gitee.com/OpenCloudOS/kernel-multi-bench
> 
> Open questions
> ==============
> 
> - Selection policy: this series keeps folio selection outside the
>   action and relies on DAMOS access patterns, filters, and quotas.
>   Is this the appropriate layering for future probe-based signals?
> 
> - Hysteresis: khugepaged may re-collapse a just-split folio.
>   Should cooldown live in DAMON policy or khugepaged?
> 
> - File-backed folios: adjust target_order upward to filesystem
>   minimum, or keep current "fail and skip"?
> 
> Beyond the action API itself, feedback on real workloads that need
> proactive large-folio demotion is particularly welcome.  Follow-up
> work will evaluate candidate selection signals, including DAMON
> probes and hardware-assisted sampling, as well as target-order
> selection and split/collapse hysteresis.  Those policies are
> intentionally kept outside this series.
> Changes since v2 [3]
> ====================
> 
> - Split-only series (collapse deferred).
> - Dropped SPE feedback (mechanism/policy separation).
> - DAMOS_MTHP_SPLIT -> DAMOS_SPLIT.
> - order field in existing union (no struct size increase).
> - Added functional selftest (damos_split.py).
> - checkpatch: 0 errors, 0 warnings.
> 
> [3] https://lore.kernel.org/20260701123000.00000-1-lianux.mm@gmail.com/
Could you add v1 as well?
> 
> Lian Wang (Processmission) (3):
>   mm/damon: introduce DAMOS_SPLIT action
>   mm/damon/vaddr: implement DAMOS_SPLIT handler
>   selftests/damon: add functional test for DAMOS_SPLIT
> 
>  .../ABI/testing/sysfs-kernel-mm-damon         |  7 ++
>  Documentation/mm/damon/design.rst             |  5 +
>  include/linux/damon.h                         | 15 ++-
>  mm/damon/core.c                               |  2 +
>  mm/damon/sysfs-schemes.c                      | 48 +++++++
>  mm/damon/vaddr.c                              | 90 +++++++++++++
>  tools/testing/selftests/damon/Makefile        |  1 +
>  tools/testing/selftests/damon/_damon_sysfs.py |  9 +-
>  tools/testing/selftests/damon/damos_split.py  | 99 +++++++++++++++
>  tools/testing/selftests/damon/sysfs.py        | 11 +-
>  10 files changed, 279 insertions(+), 8 deletions(-)
> 

-- 
Asier Gutierrez
Huawei



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20  9:28 ` [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Gutierrez Asier
@ 2026-07-20  9:43   ` Lian Wang
  0 siblings, 0 replies; 14+ messages in thread
From: Lian Wang @ 2026-07-20  9:43 UTC (permalink / raw)
  To: gutierrez.asier
  Cc: damon, linux-mm, sj, akpm, linux-kernel, david, ljs, liam, vbabka,
	rppt, surenb, mhocko, npache, ziy, baolin.wang, ryan.roberts,
	daichaobing, wangkefeng.wang, zengheng4, kasong, corbet, skhan,
	linux-doc, linux-kselftest, lianux.mm, lianux.wang, kunwu.chan

Hi Asier,

Thanks for the quick feedback.

On 7/20/2026 12:28 PM, Gutierrez Asier wrote:
> You should mention why page split is be needed. The fact that page
> collapsing exist doesn't necessarily mean that split should exist.

Fair point.  The underlying problem I'm trying to address is that
DAMON's vaddr monitoring loses accuracy under PMD-mapped THP:
multiple sampled addresses share a single Accessed bit, so the
observed hot set is coarser than the true working set.  Split is one
way to restore fine-grain monitoring -- by dismantling the PMD
mapping, each base page gets its own PTE Accessed bit and DAMON can
see the real access distribution again.

Split is not the only possible approach, and it is certainly not
intended to be "the opposite of collapse".  It is just one concrete
proposal to start the discussion.  What I really care about is
whether the community agrees that this monitoring granularity problem
is worth solving.  If there are better ways to address it, I'm very
open to that direction.

The RFC is as much about the problem as it is about the mechanism.
Feedback on real workloads that suffer from this coarsening, and on
alternative approaches, is exactly what I'm hoping for.

> Could you add v1 as well?

Good catch, will add in the next revision.

[1] https://lore.kernel.org/20260620203915.82947-1-sj@kernel.org/

Thanks,
Lian Wang


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20  3:03 [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Lian Wang
                   ` (3 preceding siblings ...)
  2026-07-20  9:28 ` [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Gutierrez Asier
@ 2026-07-20  9:44 ` David Hildenbrand (Arm)
  2026-07-20  9:56   ` Lian Wang
  2026-07-21  1:08 ` SJ Park
  5 siblings, 1 reply; 14+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-20  9:44 UTC (permalink / raw)
  To: Lian Wang, damon, linux-mm
  Cc: sj, akpm, linux-kernel, ljs, liam, vbabka, rppt, surenb, mhocko,
	npache, ziy, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, gutierrez.asier, zengheng4, kasong, corbet,
	skhan, linux-doc, linux-kselftest, lianux.wang, kunwu.chan

On 7/20/26 04:03, Lian Wang wrote:
> DAMOS_SPLIT splits large folios in a target region down to a
> configured target order, using the existing split_folio_to_order().
> No new core-mm code or exported symbols are introduced.
> 
> Based on mm-unstable at 61cccb8363fc ("mm/swap, PM: hibernate:
> atomically replace hibernation pin").
> 
> Different addresses within a PMD-mapped folio resolve to the same
> PMD Accessed bit.  Accesses to a small part of the folio can
> therefore coarsen DAMON's observed hot set relative to the actual
> working set.
> 
> DAMOS already provides promotion actions (HUGEPAGE, COLLAPSE) but
> has no corresponding demotion action.  DAMOS_SPLIT fills this gap.
> It is a mechanism, not a policy -- it does not decide which folios
> to split.  Selection is left to DAMON's existing access patterns,
> filters, and future probe/PMU signals.
> 
> target_order selects the split target: 0 for order-0 base pages,
> or a supported smaller mTHP order.  Both anonymous and file-backed
> folios are supported.  The locking follows split_huge_pages_in_pid()
> in mm/huge_memory.c.
> 
> Tests
> =====
> 
> damos_split.py (VM + Kunpeng 920):
>   anon THP -> order-0 split: PASS
> 
> sangfor_exp.py (Kunpeng 920, tmpfs, 4096 MiB):
> 
>   - Created a 4 GiB PMD-mapped tmpfs workload.
>   - Applied DAMOS_SPLIT with target_order=0.
>   - ShmemPmdMapped dropped from 4194304 KiB to 0 in every round.
>   - Repeated for five rounds without functional failures.
> 
> The functional selftest (damos_split.py) is included in this series.
> Additional experiment scripts and raw results are available on
> request.  Performance characterization using masim [1] and KMB [2]
> is in progress.
> 
> [1] https://github.com/sjp38/masim
> [2] https://gitee.com/OpenCloudOS/kernel-multi-bench

Hi,

you give no real motivation and evaluation why this is required or why this
gives the user any benefit.

HUGEPAGE + COLLAPSE is clear: give me THPs in a size not controlled by user
space, because the expectation is that this memory will be performance sensitive.

A SPLIT with an explicit order is not really want we want and it does not fit
the existing primitives.

-- 
Cheers,

David


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 1/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20  3:03 ` [RFC PATCH v3 1/3] " Lian Wang
@ 2026-07-20  9:47   ` Gutierrez Asier
  2026-07-20 10:02     ` Lian Wang
  0 siblings, 1 reply; 14+ messages in thread
From: Gutierrez Asier @ 2026-07-20  9:47 UTC (permalink / raw)
  To: Lian Wang, damon, linux-mm
  Cc: sj, akpm, linux-kernel, david, ljs, liam, vbabka, rppt, surenb,
	mhocko, npache, ziy, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, zengheng4, kasong, corbet, skhan, linux-doc,
	linux-kselftest, lianux.wang, kunwu.chan



On 7/20/2026 6:03 AM, Lian Wang wrote:
> Add a new DAMOS action, DAMOS_SPLIT, that splits large folios in the
> target region down to a smaller order.  A new per-scheme field and sysfs
> file, target_order, selects the split target: 0 for order-0 base pages,
> or 2..HPAGE_PMD_ORDER-1 for a smaller mTHP order (order-1 is rejected at
> store time since anonymous THPs cannot be split to order-1).
> 
> Unlike DAMOS_COLLAPSE which builds large folios, splitting only dismantles
> them; it allocates nothing, copies nothing, and no THP admission policy
> applies.  khugepaged deliberately refuses to collapse a folio to a
> smaller order, leaving the shrink direction to callers like this one.
> 
> This patch adds the DAMOS_SPLIT action, the ``order`` field (placed in
> the existing union alongside target_nid and migrate_dests), its sysfs
> interface, and documentation.  The vaddr operations set handler is added
> in the next patch.
> 
> Link: https://lore.kernel.org/20260605161422.213817-9-npache@redhat.com/
>  ("mm/khugepaged: skip collapsing mTHP to smaller orders")
> 
> 
> 
> Co-developed-by: Kunwu Chan <kunwu.chan@linux.dev>
> Signed-off-by: Kunwu Chan <kunwu.chan@linux.dev>
> Signed-off-by: Lian Wang (Processmission) <lianux.mm@gmail.com>
> ---
>  .../ABI/testing/sysfs-kernel-mm-damon         |  7 +++
>  Documentation/mm/damon/design.rst             |  5 ++
>  include/linux/damon.h                         | 15 ++++--
>  mm/damon/core.c                               |  2 +
>  mm/damon/sysfs-schemes.c                      | 48 +++++++++++++++++++
>  tools/testing/selftests/damon/sysfs.py        | 11 +++--
>  6 files changed, 79 insertions(+), 9 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon
> index 907a504fb64c..ee8e260a631c 100644
> --- a/Documentation/ABI/testing/sysfs-kernel-mm-damon
> +++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon
> @@ -265,6 +265,13 @@ Contact:	SJ Park <sj@kernel.org>
>  Description:	Action's target NUMA node id.  Supported by only relevant
>  		actions.
>  
> +What:		/sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/target_order
> +Date:		Jul 2026
> +Contact:	SJ Park <sj@kernel.org>
Shouldn't this point to your email address?
> +Description:	Target folio order for the ``split`` action.  Large folios in
> +		the target region are split down to this order.  Valid values
> +		are 0 (order-0 base pages) and 2..HPAGE_PMD_ORDER-1.
> +
>  What:		/sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/apply_interval_us
>  Date:		Sep 2023
>  Contact:	SJ Park <sj@kernel.org>
> diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst
> index aed6cb1cf483..87a801954b1b 100644
> --- a/Documentation/mm/damon/design.rst
> +++ b/Documentation/mm/damon/design.rst
> @@ -549,6 +549,11 @@ that supports each action are as below.
>     Supported by ``vaddr`` and ``fvaddr`` operations set. When
>     TRANSPARENT_HUGEPAGE is disabled, the application of the action will just
>     fail.
> + - ``split``: Split each large folio in the region down to the order specified
> +   by the scheme's ``target_order`` (``0`` for order-0 base pages, or
> +   ``2..HPAGE_PMD_ORDER-1``).  Supported by ``vaddr`` and ``fvaddr`` operations
> +   set. When TRANSPARENT_HUGEPAGE is disabled, the application of the action
> +   will just fail.
>   - ``lru_prio``: Prioritize the region on its LRU lists.
>     Supported by ``paddr`` operations set.
>   - ``lru_deprio``: Deprioritize the region on its LRU lists.
> diff --git a/include/linux/damon.h b/include/linux/damon.h
> index f69442a9d431..88b61b900a36 100644
> --- a/include/linux/damon.h
> +++ b/include/linux/damon.h
> @@ -110,6 +110,7 @@ struct damon_target {
>   * @DAMOS_HUGEPAGE:	Call ``madvise()`` for the region with MADV_HUGEPAGE.
>   * @DAMOS_NOHUGEPAGE:	Call ``madvise()`` for the region with MADV_NOHUGEPAGE.
>   * @DAMOS_COLLAPSE:	Call ``madvise()`` for the region with MADV_COLLAPSE.
> + * @DAMOS_SPLIT:	Split each large folio in the region to a smaller order.
>   * @DAMOS_LRU_PRIO:	Prioritize the region on its LRU lists.
>   * @DAMOS_LRU_DEPRIO:	Deprioritize the region on its LRU lists.
>   * @DAMOS_MIGRATE_HOT:  Migrate the regions prioritizing warmer regions.
> @@ -130,6 +131,7 @@ enum damos_action {
>  	DAMOS_HUGEPAGE,
>  	DAMOS_NOHUGEPAGE,
>  	DAMOS_COLLAPSE,
> +	DAMOS_SPLIT,
>  	DAMOS_LRU_PRIO,
>  	DAMOS_LRU_DEPRIO,
>  	DAMOS_MIGRATE_HOT,
> @@ -582,10 +584,15 @@ struct damos {
>  	struct damos_quota quota;
>  	struct damos_watermarks wmarks;
>  	union {
> -		struct {
> -			int target_nid;
> -			struct damos_migrate_dests migrate_dests;
> -		};
> +		int target_nid;
> +		struct damos_migrate_dests migrate_dests;
Shouldn't we keep target_nid and migrate_dests together using a
struct, as in the initial code?
> +		/*
> +		 * @order: target folio order for DAMOS_SPLIT.
> +		 * Split large folios down to this order.  0 for
> +		 * order-0 base pages, 2..HPAGE_PMD_ORDER-1 for
> +		 * smaller mTHP.  Order-1 is rejected.
> +		 */
> +		unsigned int order;
>  	};
>  	struct list_head core_filters;
>  	struct list_head ops_filters;
> diff --git a/mm/damon/core.c b/mm/damon/core.c
> index 806a67d02a6e..394c62455ad5 100644
> --- a/mm/damon/core.c
> +++ b/mm/damon/core.c
> @@ -693,6 +693,7 @@ struct damos *damon_new_scheme(struct damos_access_pattern *pattern,
>  		return NULL;
>  	scheme->pattern = *pattern;
>  	scheme->action = action;
> +	scheme->order = 0;
>  	scheme->apply_interval_us = apply_interval_us;
>  	/*
>  	 * next_apply_sis will be set when kdamond starts.  While kdamond is
> @@ -1446,6 +1447,7 @@ static int damos_commit(struct damos *dst, struct damos *src)
>  
>  	dst->pattern = src->pattern;
>  	dst->action = src->action;
> +	dst->order = src->order;
>  	dst->apply_interval_us = src->apply_interval_us;
>  
>  	err = damos_commit_quota(&dst->quota, &src->quota);
> diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c
> index 32f495a96b17..725ed81ccf6c 100644
> --- a/mm/damon/sysfs-schemes.c
> +++ b/mm/damon/sysfs-schemes.c
> @@ -4,7 +4,9 @@
>   */
>  
>  #include <linux/slab.h>
> +#include <linux/mm.h>
>  #include <linux/numa.h>
> +#include <linux/huge_mm.h>
>  
>  #include "sysfs-common.h"
>  
> @@ -2260,6 +2262,7 @@ struct damon_sysfs_scheme {
>  	struct damon_sysfs_stats *stats;
>  	struct damon_sysfs_scheme_regions *tried_regions;
>  	int target_nid;
> +	unsigned int target_order;
>  	struct damos_sysfs_dests *dests;
>  };
>  
> @@ -2293,6 +2296,10 @@ static struct damos_sysfs_action_name damos_sysfs_action_names[] = {
>  		.action = DAMOS_COLLAPSE,
>  		.name = "collapse",
>  	},
> +	{
> +		.action = DAMOS_SPLIT,
> +		.name = "split",
> +	},
>  	{
>  		.action = DAMOS_LRU_PRIO,
>  		.name = "lru_prio",
> @@ -2326,6 +2333,7 @@ static struct damon_sysfs_scheme *damon_sysfs_scheme_alloc(
>  	scheme->action = action;
>  	scheme->apply_interval_us = apply_interval_us;
>  	scheme->target_nid = NUMA_NO_NODE;
> +	scheme->target_order = 0;
>  	return scheme;
>  }
>  
> @@ -2645,6 +2653,40 @@ static ssize_t target_nid_store(struct kobject *kobj,
>  	return err ? err : count;
>  }
>  
> +static ssize_t target_order_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_scheme *scheme = container_of(kobj,
> +			struct damon_sysfs_scheme, kobj);
> +
> +	return sysfs_emit(buf, "%u\n", scheme->target_order);
> +}
> +
> +static ssize_t target_order_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_scheme *scheme = container_of(kobj,
> +			struct damon_sysfs_scheme, kobj);
> +	unsigned int val;
> +	int err;
Move the variable declaration to the top of the function.
> +
> +	err = kstrtouint(buf, 0, &val);
> +	if (err)
> +		return err;
> +
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> +	/* Valid split targets: 0 (order-0 base pages) or 2..HPAGE_PMD_ORDER-1. */
> +	if (val != 0 && (val < 2 || val >= HPAGE_PMD_ORDER))
> +		return -EINVAL;
> +#else
> +	if (val != 0)
> +		return -EINVAL;
> +#endif
> +
> +	scheme->target_order = val;
> +	return count;
> +}
> +
>  static void damon_sysfs_scheme_release(struct kobject *kobj)
>  {
>  	kfree(container_of(kobj, struct damon_sysfs_scheme, kobj));
> @@ -2659,10 +2701,14 @@ static struct kobj_attribute damon_sysfs_scheme_apply_interval_us_attr =
>  static struct kobj_attribute damon_sysfs_scheme_target_nid_attr =
>  		__ATTR_RW_MODE(target_nid, 0600);
>  
> +static struct kobj_attribute damon_sysfs_scheme_target_order_attr =
> +		__ATTR_RW_MODE(target_order, 0600);
> +
>  static struct attribute *damon_sysfs_scheme_attrs[] = {
>  	&damon_sysfs_scheme_action_attr.attr,
>  	&damon_sysfs_scheme_apply_interval_us_attr.attr,
>  	&damon_sysfs_scheme_target_nid_attr.attr,
> +	&damon_sysfs_scheme_target_order_attr.attr,
>  	NULL,
>  };
>  ATTRIBUTE_GROUPS(damon_sysfs_scheme);
> @@ -3011,6 +3057,8 @@ static struct damos *damon_sysfs_mk_scheme(
>  	if (!scheme)
>  		return NULL;
>  
> +	scheme->order = sysfs_scheme->target_order;
> +
>  	err = damos_sysfs_add_quota_score(sysfs_quotas->goals, &scheme->quota);
>  	if (err) {
>  		damon_destroy_scheme(scheme);
> diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py
> index 3ffa054b6386..49d3c319dfd8 100755
> --- a/tools/testing/selftests/damon/sysfs.py
> +++ b/tools/testing/selftests/damon/sysfs.py
> @@ -131,11 +131,12 @@ def assert_scheme_committed(scheme, dump):
>              'hugepage': 3,
>              'nohugepage': 4,
>              'collapse': 5,
> -            'lru_prio': 6,
> -            'lru_deprio': 7,
> -            'migrate_hot': 8,
> -            'migrate_cold': 9,
> -            'stat': 10,
> +            'split': 6,
> +            'lru_prio': 7,
> +            'lru_deprio': 8,
> +            'migrate_hot': 9,
> +            'migrate_cold': 10,
> +            'stat': 11,
>              }
>      assert_true(dump['action'] == action_val[scheme.action], 'action', dump)
>      assert_true(dump['apply_interval_us'] == scheme. apply_interval_us,
> 

-- 
Asier Gutierrez
Huawei



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20  9:44 ` David Hildenbrand (Arm)
@ 2026-07-20  9:56   ` Lian Wang
  2026-07-20 19:12     ` Zi Yan
  0 siblings, 1 reply; 14+ messages in thread
From: Lian Wang @ 2026-07-20  9:56 UTC (permalink / raw)
  To: david
  Cc: damon, linux-mm, sj, akpm, linux-kernel, ljs, liam, vbabka, rppt,
	surenb, mhocko, npache, ziy, baolin.wang, ryan.roberts,
	daichaobing, wangkefeng.wang, gutierrez.asier, zengheng4, kasong,
	corbet, skhan, linux-doc, linux-kselftest, lianux.mm, lianux.wang,
	kunwu.chan

Hi David,

On 7/20/2026 10:44 AM, David Hildenbrand (Arm) wrote:
> you give no real motivation and evaluation why this is required or
> why this gives the user any benefit.
> A SPLIT with an explicit order is not really want we want and it
> does not fit the existing primitives.

Thank you for the direct feedback.  Let me explain where this came
from -- the cover letter should have included this context.

This started from a real problem at Sangfor.  The scenario is:

  KVM-QEMU virtualization on Kunpeng 920, with KVM guest memory
  backed by tmpfs shared mappings (THP=always on the host).  An
  Oracle database runs inside the VM.  DAMON monitors the KVM
  process on the host to measure the hot-memory ratio.

  The KVM process allocates and uses a large amount of memory.
  Under the same workload, DAMON reports a significantly higher
  hot-memory ratio with THP enabled versus THP disabled.  Direct
  tmpfs write tests inside the VM -- touching at 4K and 2M
  strides -- show a clear gap between the two cases.

  DAMON parameters used:

    operations=vaddr
    monitoring_attrs/nr_regions/min=500
    monitoring_attrs/nr_regions/max=2000
    monitoring_attrs/intervals/sample_us=500000
    monitoring_attrs/intervals/aggr_us=20000000
    monitoring_attrs/intervals/update_us=60000000
    schemes/0/action=stat
    schemes/0/access_pattern/nr_accesses/min=1
    schemes/0/access_pattern/nr_accesses/max=max

The underlying issue is that under PMD-mapped THP, DAMON's monitoring
granularity is coarser than the actual working set -- a single
Accessed bit covers 512 base pages.  Before SJ's probe infrastructure
arrives, there is a gap: DAMON cannot distinguish hot sub-pages from
cold ones within a single THP.

Split is one possible mechanism to bridge that gap -- by dismantling
the PMD mapping, each base page gets its own PTE Accessed bit and
DAMON recovers fine-grain monitoring.  It is not intended to be a
permanent API, and certainly not "the opposite of collapse".

I did not write this scenario into the cover letter because our test
results do not yet show a clear quantitative benefit worth claiming,
and I did not want to oversell.  Without the context, I understand it
looks like I randomly proposed a new primitive -- that was not the
intention.

SJ acknowledged [1] that the monitoring problem under THP is real.
My RFC is a concrete proposal to start the discussion.  If split with
an explicit order is not the right primitive, I would appreciate your
thoughts on what the correct DAMOS abstraction for this should be.

[1] https://lore.kernel.org/20260620203915.82947-1-sj@kernel.org/

Thanks,
Lian Wang


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 1/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20  9:47   ` Gutierrez Asier
@ 2026-07-20 10:02     ` Lian Wang
  0 siblings, 0 replies; 14+ messages in thread
From: Lian Wang @ 2026-07-20 10:02 UTC (permalink / raw)
  To: Gutierrez Asier
  Cc: Lian Wang, damon, linux-mm, sj, akpm, linux-kernel, david, ljs,
	liam, vbabka, rppt, surenb, mhocko, npache, ziy, baolin.wang,
	ryan.roberts, daichaobing, wangkefeng.wang, zengheng4, kasong,
	corbet, skhan, linux-doc, linux-kselftest, lianux.wang,
	kunwu.chan

On Mon, 20 Jul 2026 12:47:23 +0300 Gutierrez Asier <gutierrez.asier@huawei-partners.com> wrote:

> 
> 
> On 7/20/2026 6:03 AM, Lian Wang wrote:
> > Add a new DAMOS action, DAMOS_SPLIT, that splits large folios in the
> > target region down to a smaller order.  A new per-scheme field and sysfs
> > file, target_order, selects the split target: 0 for order-0 base pages,
> > or 2..HPAGE_PMD_ORDER-1 for a smaller mTHP order (order-1 is rejected at
> > store time since anonymous THPs cannot be split to order-1).
> > 
> > Unlike DAMOS_COLLAPSE which builds large folios, splitting only dismantles
> > them; it allocates nothing, copies nothing, and no THP admission policy
> > applies.  khugepaged deliberately refuses to collapse a folio to a
> > smaller order, leaving the shrink direction to callers like this one.
> > 
> > This patch adds the DAMOS_SPLIT action, the ``order`` field (placed in
> > the existing union alongside target_nid and migrate_dests), its sysfs
> > interface, and documentation.  The vaddr operations set handler is added
> > in the next patch.
> > 
> > Link: https://lore.kernel.org/20260605161422.213817-9-npache@redhat.com/
> >  ("mm/khugepaged: skip collapsing mTHP to smaller orders")

Hi Asier,

Thanks for the review.  All three points noted -- will address them
in the next revision along with feedback from David and others.

Thanks,
Lian Wang


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20  9:56   ` Lian Wang
@ 2026-07-20 19:12     ` Zi Yan
  2026-07-21  0:47       ` SJ Park
  0 siblings, 1 reply; 14+ messages in thread
From: Zi Yan @ 2026-07-20 19:12 UTC (permalink / raw)
  To: Lian Wang, david
  Cc: damon, linux-mm, sj, akpm, linux-kernel, ljs, liam, vbabka, rppt,
	surenb, mhocko, npache, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, gutierrez.asier, zengheng4, kasong, corbet,
	skhan, linux-doc, linux-kselftest, lianux.wang, kunwu.chan

On Mon Jul 20, 2026 at 5:56 AM EDT, Lian Wang wrote:
> Hi David,
>
> On 7/20/2026 10:44 AM, David Hildenbrand (Arm) wrote:
>> you give no real motivation and evaluation why this is required or
>> why this gives the user any benefit.
>> A SPLIT with an explicit order is not really want we want and it
>> does not fit the existing primitives.
>
> Thank you for the direct feedback.  Let me explain where this came
> from -- the cover letter should have included this context.
>
> This started from a real problem at Sangfor.  The scenario is:
>
>   KVM-QEMU virtualization on Kunpeng 920, with KVM guest memory
>   backed by tmpfs shared mappings (THP=always on the host).  An
>   Oracle database runs inside the VM.  DAMON monitors the KVM
>   process on the host to measure the hot-memory ratio.
>
>   The KVM process allocates and uses a large amount of memory.
>   Under the same workload, DAMON reports a significantly higher
>   hot-memory ratio with THP enabled versus THP disabled.  Direct
>   tmpfs write tests inside the VM -- touching at 4K and 2M
>   strides -- show a clear gap between the two cases.
>
>   DAMON parameters used:
>
>     operations=vaddr
>     monitoring_attrs/nr_regions/min=500
>     monitoring_attrs/nr_regions/max=2000
>     monitoring_attrs/intervals/sample_us=500000
>     monitoring_attrs/intervals/aggr_us=20000000
>     monitoring_attrs/intervals/update_us=60000000
>     schemes/0/action=stat
>     schemes/0/access_pattern/nr_accesses/min=1
>     schemes/0/access_pattern/nr_accesses/max=max
>
> The underlying issue is that under PMD-mapped THP, DAMON's monitoring
> granularity is coarser than the actual working set -- a single
> Accessed bit covers 512 base pages.  Before SJ's probe infrastructure
> arrives, there is a gap: DAMON cannot distinguish hot sub-pages from
> cold ones within a single THP.
>
> Split is one possible mechanism to bridge that gap -- by dismantling
> the PMD mapping, each base page gets its own PTE Accessed bit and
> DAMON recovers fine-grain monitoring.  It is not intended to be a
> permanent API, and certainly not "the opposite of collapse".

If you just want PTE level access bit information, why not split PMD
mapping instead of the THP itself?

In addition, the issue is about access monitoring granularity in DAMON,
why should user care and know about THP split operations? I would expect
DAMON detects the inability of getting fine grain access information and
split the PMD mapping itself instead of a user initiated DAMON_SPLIT. If
that is not possible with DAMON, an alternative is to provide something
more generic like DAMON_SAMPLE, which does the split under the hood,
instead of exposing MM internal operations.

>
> I did not write this scenario into the cover letter because our test
> results do not yet show a clear quantitative benefit worth claiming,
> and I did not want to oversell.  Without the context, I understand it
> looks like I randomly proposed a new primitive -- that was not the
> intention.
>
> SJ acknowledged [1] that the monitoring problem under THP is real.
> My RFC is a concrete proposal to start the discussion.  If split with
> an explicit order is not the right primitive, I would appreciate your
> thoughts on what the correct DAMOS abstraction for this should be.
>
> [1] https://lore.kernel.org/20260620203915.82947-1-sj@kernel.org/
>
> Thanks,
> Lian Wang




-- 
Best Regards,
Yan, Zi



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20 19:12     ` Zi Yan
@ 2026-07-21  0:47       ` SJ Park
  2026-07-21  1:34         ` Lian Wang
  0 siblings, 1 reply; 14+ messages in thread
From: SJ Park @ 2026-07-21  0:47 UTC (permalink / raw)
  To: Zi Yan
  Cc: SJ Park, Lian Wang, david, damon, linux-mm, akpm, linux-kernel,
	ljs, liam, vbabka, rppt, surenb, mhocko, npache, baolin.wang,
	ryan.roberts, daichaobing, wangkefeng.wang, gutierrez.asier,
	zengheng4, kasong, corbet, skhan, linux-doc, linux-kselftest,
	lianux.wang, kunwu.chan

Hello,

On Mon, 20 Jul 2026 15:12:50 -0400 "Zi Yan" <ziy@nvidia.com> wrote:

> On Mon Jul 20, 2026 at 5:56 AM EDT, Lian Wang wrote:
> > Hi David,
> >
> > On 7/20/2026 10:44 AM, David Hildenbrand (Arm) wrote:
> >> you give no real motivation and evaluation why this is required or
> >> why this gives the user any benefit.
> >> A SPLIT with an explicit order is not really want we want and it
> >> does not fit the existing primitives.
> >
> > Thank you for the direct feedback.  Let me explain where this came
> > from -- the cover letter should have included this context.
> >
> > This started from a real problem at Sangfor.  The scenario is:
> >
> >   KVM-QEMU virtualization on Kunpeng 920, with KVM guest memory
> >   backed by tmpfs shared mappings (THP=always on the host).  An
> >   Oracle database runs inside the VM.  DAMON monitors the KVM
> >   process on the host to measure the hot-memory ratio.
> >
> >   The KVM process allocates and uses a large amount of memory.
> >   Under the same workload, DAMON reports a significantly higher
> >   hot-memory ratio with THP enabled versus THP disabled.  Direct
> >   tmpfs write tests inside the VM -- touching at 4K and 2M
> >   strides -- show a clear gap between the two cases.
> >
> >   DAMON parameters used:
> >
> >     operations=vaddr
> >     monitoring_attrs/nr_regions/min=500
> >     monitoring_attrs/nr_regions/max=2000
> >     monitoring_attrs/intervals/sample_us=500000
> >     monitoring_attrs/intervals/aggr_us=20000000
> >     monitoring_attrs/intervals/update_us=60000000

Thank you for sharing your detailed setup.  It is helpful.  Btw, have you
considered using intervals auto-tuning [1]?

> >     schemes/0/action=stat
> >     schemes/0/access_pattern/nr_accesses/min=1
> >     schemes/0/access_pattern/nr_accesses/max=max
> >
> > The underlying issue is that under PMD-mapped THP, DAMON's monitoring
> > granularity is coarser than the actual working set -- a single
> > Accessed bit covers 512 base pages.  Before SJ's probe infrastructure
> > arrives, there is a gap: DAMON cannot distinguish hot sub-pages from
> > cold ones within a single THP.
> >
> > Split is one possible mechanism to bridge that gap -- by dismantling
> > the PMD mapping, each base page gets its own PTE Accessed bit and
> > DAMON recovers fine-grain monitoring.

Thank you for clarifying the motivation of this series.

To me, it's still unclear what is the real user impact, though.  I mean, I can
understand DAMON suddenly reporting more hot memory can surprise some people.
But, why that matters in what extent for your use case?  You may not run DAMON
on your system only to read the information.  You may run it to do something
beneficial using the information.  What is that, and how badly degraded DAMON's
monitoring results affect it?

Overall, unless the real impact is serious, splitting huge pages only for
better DAMON monitoring sounds like not a good tradeoff.  You will increase
DAMON overhead and lose THP benefits in some extent.

> > It is not intended to be a
> > permanent API, and certainly not "the opposite of collapse".

Once it is added to the kernel, we have to support it for long term.  Let's not
introduce something for only temporal use.

> 
> If you just want PTE level access bit information, why not split PMD
> mapping instead of the THP itself?
> 
> In addition, the issue is about access monitoring granularity in DAMON,
> why should user care and know about THP split operations? I would expect
> DAMON detects the inability of getting fine grain access information and
> split the PMD mapping itself instead of a user initiated DAMON_SPLIT. If
> that is not possible with DAMON, an alternative is to provide something
> more generic like DAMON_SAMPLE, which does the split under the hood,
> instead of exposing MM internal operations.

Thank you for good opinion, Zi.  I agree all the points.  That said, I still
want to understand the problem first.

> 
> >
> > I did not write this scenario into the cover letter because our test
> > results do not yet show a clear quantitative benefit worth claiming,
> > and I did not want to oversell.  Without the context, I understand it
> > looks like I randomly proposed a new primitive -- that was not the
> > intention.
> >
> > SJ acknowledged [1] that the monitoring problem under THP is real.

Yes, the behavior is real and I agree your theory of how it happens.  I don't
clearly understand if it is really bad in what situations, though.

> > My RFC is a concrete proposal to start the discussion.  If split with
> > an explicit order is not the right primitive, I would appreciate your
> > thoughts on what the correct DAMOS abstraction for this should be.

Only after understanding what is the problem and how bad it is, we will be able
to think of different approaches and assess those.  To me, it is still unclear
what is the real problem and how bad it is.  I will wait for your further
clarifications of those.

> >
> > [1] https://lore.kernel.org/20260620203915.82947-1-sj@kernel.org/

[1] https://origin.kernel.org/doc/html/latest/mm/damon/design.html#monitoring-intervals-auto-tuning


Thanks,
SJ

[...]


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-20  3:03 [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Lian Wang
                   ` (4 preceding siblings ...)
  2026-07-20  9:44 ` David Hildenbrand (Arm)
@ 2026-07-21  1:08 ` SJ Park
  5 siblings, 0 replies; 14+ messages in thread
From: SJ Park @ 2026-07-21  1:08 UTC (permalink / raw)
  To: Lian Wang
  Cc: SJ Park, damon, linux-mm, akpm, linux-kernel, david, ljs, liam,
	vbabka, rppt, surenb, mhocko, npache, ziy, baolin.wang,
	ryan.roberts, daichaobing, wangkefeng.wang, gutierrez.asier,
	zengheng4, kasong, corbet, skhan, linux-doc, linux-kselftest,
	lianux.wang, kunwu.chan

You were warned at not Cc-ing THP developers in the previous revision, since it
was modifying THP source code.  Since this version is modifying only DAMON
source code, you don't really need to Cc more than DAMON developers.  Asking
wider inputs is good practice.  And it worked very well.  We got great inputs
from Asier, David and Zi.  But some people don't really like having too much
mails in their inbox.  My personal rule of thumb is just running
get_maintainer.pl via 'hkml patch format' [1].  You must have your own rule,
though :)

On Mon, 20 Jul 2026 11:03:24 +0800 Lian Wang <lianux.mm@gmail.com> wrote:

> DAMOS_SPLIT splits large folios in a target region down to a
> configured target order, using the existing split_folio_to_order().
> No new core-mm code or exported symbols are introduced.

The last sentence may better to go to changelog.  If you want to highlight, you
can put changelog at the top of the cover letter.

> 
> Based on mm-unstable at 61cccb8363fc ("mm/swap, PM: hibernate:
> atomically replace hibernation pin").

This is also not feasible to be the cover letter main content.

> 
> Different addresses within a PMD-mapped folio resolve to the same
> PMD Accessed bit.  Accesses to a small part of the folio can
> therefore coarsen DAMON's observed hot set relative to the actual
> working set.
> 
> DAMOS already provides promotion actions (HUGEPAGE, COLLAPSE) but
> has no corresponding demotion action.  DAMOS_SPLIT fills this gap.
> It is a mechanism, not a policy -- it does not decide which folios
> to split.  Selection is left to DAMON's existing access patterns,
> filters, and future probe/PMU signals.

As I commented to the previous version [2], this sounds like you are saying two
very different things.  Monitoring quality degradation issue and better THP
handling of DAMOS.  This makes understanding the motivation of this series
difficult, as other people also pointed out.

Based on your replies to others, now I understand you are proposing DAMOS_SPLIT
as a way for improving the monitoring results.  I'm waiting for your more
clarification of the issue, to better assess if this makes sense or not, as I
commented on the reply to Zi's reply.

> 
> target_order selects the split target: 0 for order-0 base pages,
> or a supported smaller mTHP order.  Both anonymous and file-backed
> folios are supported.  The locking follows split_huge_pages_in_pid()
> in mm/huge_memory.c.
> 
> Tests
> =====
> 
> damos_split.py (VM + Kunpeng 920):
>   anon THP -> order-0 split: PASS
> 
> sangfor_exp.py (Kunpeng 920, tmpfs, 4096 MiB):
> 
>   - Created a 4 GiB PMD-mapped tmpfs workload.
>   - Applied DAMOS_SPLIT with target_order=0.
>   - ShmemPmdMapped dropped from 4194304 KiB to 0 in every round.
>   - Repeated for five rounds without functional failures.
> 
> The functional selftest (damos_split.py) is included in this series.
> Additional experiment scripts and raw results are available on
> request.  Performance characterization using masim [1] and KMB [2]
> is in progress.

It is completely fine to keep having tests in progress.  But, please make the
story complete.  What damos_split.py and sangfor_exp.py do?  What the results
mean?  What the performance tests will do with what expectation?

> 
> [1] https://github.com/sjp38/masim
> [2] https://gitee.com/OpenCloudOS/kernel-multi-bench
> 
> Open questions
> ==============
> 
> - Selection policy: this series keeps folio selection outside the
>   action and relies on DAMOS access patterns, filters, and quotas.
>   Is this the appropriate layering for future probe-based signals?

You mentioned this series is for monitoring quality improvement.  If so,
shouldn't you just apply it to all THPs, regardless of the access pattern?  I'm
again being confused.  More clarification of the motivation would be useful.

> 
> - Hysteresis: khugepaged may re-collapse a just-split folio.
>   Should cooldown live in DAMON policy or khugepaged?

Ditto.

> 
> - File-backed folios: adjust target_order upward to filesystem
>   minimum, or keep current "fail and skip"?

I don't fully understand the question.  Could you please elaborate more?

> 
> Beyond the action API itself, feedback on real workloads that need
> proactive large-folio demotion is particularly welcome.  Follow-up
> work will evaluate candidate selection signals, including DAMON
> probes and hardware-assisted sampling, as well as target-order
> selection and split/collapse hysteresis.  Those policies are
> intentionally kept outside this series.

You mentioned this work is for monitoring quality improvement.  Now you are
saying somewhat followup.  I'm again being confused.

> 
> Changes since v2 [3]
> ====================
> 
> - Split-only series (collapse deferred).
> - Dropped SPE feedback (mechanism/policy separation).
> - DAMOS_MTHP_SPLIT -> DAMOS_SPLIT.
> - order field in existing union (no struct size increase).
> - Added functional selftest (damos_split.py).
> - checkpatch: 0 errors, 0 warnings.
> 
> [3] https://lore.kernel.org/20260701123000.00000-1-lianux.mm@gmail.com/
> 
> Lian Wang (Processmission) (3):
>   mm/damon: introduce DAMOS_SPLIT action
>   mm/damon/vaddr: implement DAMOS_SPLIT handler
>   selftests/damon: add functional test for DAMOS_SPLIT
> 
>  .../ABI/testing/sysfs-kernel-mm-damon         |  7 ++
>  Documentation/mm/damon/design.rst             |  5 +
>  include/linux/damon.h                         | 15 ++-
>  mm/damon/core.c                               |  2 +
>  mm/damon/sysfs-schemes.c                      | 48 +++++++
>  mm/damon/vaddr.c                              | 90 +++++++++++++
>  tools/testing/selftests/damon/Makefile        |  1 +
>  tools/testing/selftests/damon/_damon_sysfs.py |  9 +-
>  tools/testing/selftests/damon/damos_split.py  | 99 +++++++++++++++
>  tools/testing/selftests/damon/sysfs.py        | 11 +-
>  10 files changed, 279 insertions(+), 8 deletions(-)

[1] https://github.com/sjp38/hackermail/blob/master/USAGE.md#formatting-patches
[2] https://lore.kernel.org/20260702183551.91007-1-sj@kernel.org


Thanks,
SJ


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
  2026-07-21  0:47       ` SJ Park
@ 2026-07-21  1:34         ` Lian Wang
  0 siblings, 0 replies; 14+ messages in thread
From: Lian Wang @ 2026-07-21  1:34 UTC (permalink / raw)
  To: sj, david, ziy
  Cc: damon, linux-mm, akpm, linux-kernel, ljs, liam, vbabka, rppt,
	surenb, mhocko, npache, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, gutierrez.asier, zengheng4, kasong, corbet,
	skhan, linux-doc, linux-kselftest, lianux.mm, lianux.wang,
	kunwu.chan

Hi SJ,

On 7/20/2026 5:47 PM, SJ Park wrote:
> To me, it's still unclear what is the real user impact, though.
> ...
> Only after understanding what is the problem and how bad it is, we
> will be able to think of different approaches and assess those.

You are right.  I will work with Sangfor to quantify the real impact
in their production scenario -- what DAMOS action is driven by the
inflated hot-memory readings, and how badly the monitoring error
affects the actual outcome.

Thank you, David, and Zi Yan for the honest and constructive feedback.
It made me realize I should have led with the problem, not the
mechanism.

Let me take a step back, gather concrete data on the problem severity,
and come back with a clearer picture.  I will follow up in this thread
once I have something solid to share.

Thanks,
Lian Wang


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-07-21  1:34 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-20  3:03 [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Lian Wang
2026-07-20  3:03 ` [RFC PATCH v3 1/3] " Lian Wang
2026-07-20  9:47   ` Gutierrez Asier
2026-07-20 10:02     ` Lian Wang
2026-07-20  3:03 ` [RFC PATCH v3 2/3] mm/damon/vaddr: implement DAMOS_SPLIT handler Lian Wang
2026-07-20  3:03 ` [RFC PATCH v3 3/3] selftests/damon: add functional test for DAMOS_SPLIT Lian Wang
2026-07-20  9:28 ` [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action Gutierrez Asier
2026-07-20  9:43   ` Lian Wang
2026-07-20  9:44 ` David Hildenbrand (Arm)
2026-07-20  9:56   ` Lian Wang
2026-07-20 19:12     ` Zi Yan
2026-07-21  0:47       ` SJ Park
2026-07-21  1:34         ` Lian Wang
2026-07-21  1:08 ` SJ Park

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox