* [RFC PATCH v1.1 01/13] mm/damon: introduce damon_nr_accesses_mvsum()
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 02/13] mm/damon/tests/core-kunit: test damon_mvsum() SeongJae Park
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, damon, linux-kernel, linux-mm
Introduce a new DAMON core function, damon_nr_accesses_mvsum(). It
returns a pseudo moving sum value of a given region's nr_accesses for
the last aggregation interval. The internal logic is the same to
nr_accesses_bp. The difference is that nr_accesses_bp is updated for
each sampling interval, while the new function needs to be executed only
when requested. Hence the function's return value is the same as the
value of nr_accesses_bp.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
include/linux/damon.h | 2 ++
mm/damon/core.c | 57 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+)
diff --git a/include/linux/damon.h b/include/linux/damon.h
index 02ac34537df9a..55a743d99b567 100644
--- a/include/linux/damon.h
+++ b/include/linux/damon.h
@@ -1013,6 +1013,8 @@ struct damon_probe *damon_new_probe(void);
void damon_add_probe(struct damon_ctx *ctx, struct damon_probe *probe);
struct damon_region *damon_new_region(unsigned long start, unsigned long end);
+unsigned int damon_nr_accesses_mvsum(struct damon_region *r,
+ struct damon_ctx *ctx);
int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges,
unsigned int nr_ranges, unsigned long min_region_sz);
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 5b84b3ce3fcff..687aa2ea1d013 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -210,6 +210,63 @@ static struct damon_probe *damon_nth_probe(int n, struct damon_ctx *ctx)
return NULL;
}
+/*
+ * damon_mvsum() - Returns pseudo moving sum value for a time window.
+ * @current_nr: The value of the current aggregation window.
+ * @last_nr: The value of the last aggregation window.
+ * @left_window_bp: Left time of the current aggregation window.
+ *
+ * This function calculates a pseudo moving sum value of a counter that is
+ * aggregated for each time window. @current_nr is the value of the counter
+ * that aggregated so far (maybe not yet complete), from the beginning of the
+ * current aggregation time window. @last_nr is the value of the counter that
+ * has completely aggregated in the last aggregation time window.
+ * @left_window_bp represents how much time is left for the current aggregation
+ * time window in bp (1/10,000). For example, the aggregation time window is
+ * for every 10 seconds and 7 seconds has passed since the beginning of the
+ * current window, this parameter will be 3000 ((10 - 7) / 10 * 10000).
+ *
+ * The logic assumes the aggregation in the last phase was made in a single
+ * speed. Based on the assumption, the value from the last window that needs
+ * to be added to the current value is calculated as a portion of the last
+ * value based on the remaining time window.
+ */
+static unsigned long damon_mvsum(unsigned long current_nr,
+ unsigned long last_nr, unsigned long left_window_bp)
+{
+ return current_nr + mult_frac(last_nr, left_window_bp, 10000);
+}
+
+/**
+ * damon_nr_accesses_mvsum() - Returns moving sum access frequency score.
+ * @r: Region to get the access frequency of.
+ * @ctx: DAMON context of @r.
+ *
+ * This function returns for how many sampling iterations in the last
+ * aggregation interval (&damon_attrs->aggr_interval) the region was found to
+ * be accessed. Hence the value can be interpreted as the relative access
+ * frequency score of the region (@r). The value is calculated as a pseudo
+ * moving sum, and hence it is not an exact value but just a best-effort
+ * reasonable estimation.
+ *
+ * Return: the pseudo moving sum access frequency score.
+ */
+unsigned int damon_nr_accesses_mvsum(struct damon_region *r,
+ struct damon_ctx *ctx)
+{
+ unsigned long sample_interval, aggr_interval;
+ unsigned long window_len, left_window, left_window_bp;
+
+ sample_interval = ctx->attrs.sample_interval ? : 1;
+ aggr_interval = ctx->attrs.aggr_interval ? : 1;
+ window_len = aggr_interval / sample_interval;
+ left_window = ctx->next_aggregation_sis - ctx->passed_sample_intervals;
+ left_window_bp = mult_frac(left_window, 10000, window_len);
+
+ return damon_mvsum(r->nr_accesses, r->last_nr_accesses,
+ left_window_bp);
+}
+
#ifdef CONFIG_DAMON_DEBUG_SANITY
static void damon_verify_new_region(unsigned long start, unsigned long end)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 02/13] mm/damon/tests/core-kunit: test damon_mvsum()
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 01/13] mm/damon: introduce damon_nr_accesses_mvsum() SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 03/13] mm/damon/core: use damon_nr_accesses_mvsum() in __damos_valid_target() SeongJae Park
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, Brendan Higgins, David Gow, damon,
kunit-dev, linux-kernel, linux-kselftest, linux-mm
Add a simple unit test for damon_nr_accesses_mvsum()'s internal core
logic, damon_mvsum(). The test contains cases for just-started windows,
partially completed windows, and just-completed windows.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/tests/core-kunit.h | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index 4e448c08c724a..cdab14396250f 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -623,6 +623,30 @@ static void damon_test_moving_sum(struct kunit *test)
}
}
+static void damon_test_mvsum(struct kunit *test)
+{
+ unsigned long input_expects[] = {
+ /* current value, last value, remaining window (bp) */
+ 0, 49, 10000, 49, /* 0 + 49 * 1 */
+ 3, 10, 7000, 10, /* 3 + 10 * 0.7 */
+ 3, 10, 5000, 8, /* 3 + 10 * 0.5 */
+ 32, 100, 1000, 42, /* 32 + 100 * 0.1 */
+ 42, 49, 0, 42, /* 42 + 49 * 0 */
+ };
+
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(input_expects); i += 4) {
+ unsigned long current_nr = input_expects[i];
+ unsigned long last_nr = input_expects[i + 1];
+ unsigned long left_window_bp = input_expects[i + 2];
+ unsigned long expect = input_expects[i + 3];
+
+ KUNIT_EXPECT_EQ(test, damon_mvsum(current_nr, last_nr,
+ left_window_bp), expect);
+ }
+}
+
static void damos_test_new_filter(struct kunit *test)
{
struct damos_filter *filter;
@@ -1501,6 +1525,7 @@ static struct kunit_case damon_test_cases[] = {
KUNIT_CASE(damon_test_update_monitoring_result),
KUNIT_CASE(damon_test_set_attrs),
KUNIT_CASE(damon_test_moving_sum),
+ KUNIT_CASE(damon_test_mvsum),
KUNIT_CASE(damos_test_new_filter),
KUNIT_CASE(damos_test_commit_quota_goal),
KUNIT_CASE(damos_test_commit_quota_goals),
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 03/13] mm/damon/core: use damon_nr_accesses_mvsum() in __damos_valid_target()
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 01/13] mm/damon: introduce damon_nr_accesses_mvsum() SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 02/13] mm/damon/tests/core-kunit: test damon_mvsum() SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 04/13] mm/damon/core: use damon_nr_accesses_mvsum() for damos region tracing SeongJae Park
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, damon, linux-kernel, linux-mm
damon_nr_accesses_mvsum() returns a value same to nr_accesses_bp. Also
the function is more simple and therefore more tolerant to errors.
Execution of the function would be more expensive than the simple read
of the field, but because the function is quite simple, the overhead
should be negligible. Use it in __damos_valid_target() instead of the
nr_accesses_bp.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/core.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 687aa2ea1d013..ce0e2a4c1d523 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -2120,10 +2120,11 @@ static noinline_for_stack void kdamond_tune_intervals(struct damon_ctx *c)
damon_set_attrs(c, &new_attrs);
}
-static bool __damos_valid_target(struct damon_region *r, struct damos *s)
+static bool __damos_valid_target(struct damon_region *r, struct damos *s,
+ struct damon_ctx *c)
{
unsigned long sz;
- unsigned int nr_accesses = r->nr_accesses_bp / 10000;
+ unsigned int nr_accesses = damon_nr_accesses_mvsum(r, c);
sz = damon_sz_region(r);
return s->pattern.min_sz_region <= sz &&
@@ -2149,7 +2150,7 @@ static bool damos_quota_is_set(struct damos_quota *quota)
static bool damos_valid_target(struct damon_ctx *c, struct damon_region *r,
struct damos *s)
{
- bool ret = __damos_valid_target(r, s);
+ bool ret = __damos_valid_target(r, s, c);
if (!ret || !damos_quota_is_set(&s->quota) || !c->ops.get_scheme_score)
return ret;
@@ -2735,7 +2736,7 @@ static phys_addr_t damos_calc_eligible_bytes(struct damon_ctx *c,
damon_for_each_region(r, t) {
phys_addr_t addr, end_addr;
- if (!__damos_valid_target(r, s))
+ if (!__damos_valid_target(r, s, c))
continue;
/* Convert from core address units to physical bytes */
@@ -3024,7 +3025,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s)
(DAMOS_MAX_SCORE + 1));
damon_for_each_target(t, c) {
damon_for_each_region(r, t) {
- if (!__damos_valid_target(r, s))
+ if (!__damos_valid_target(r, s, c))
continue;
if (damos_core_filter_out(c, t, r, s))
continue;
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 04/13] mm/damon/core: use damon_nr_accesses_mvsum() for damos region tracing
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (2 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 03/13] mm/damon/core: use damon_nr_accesses_mvsum() in __damos_valid_target() SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 05/13] mm/damon/sysfs-schemes: use damon_nr_accesses_mvsum() for damo regions SeongJae Park
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, Masami Hiramatsu, Mathieu Desnoyers,
Steven Rostedt, damon, linux-kernel, linux-mm, linux-trace-kernel
damon_nr_accesses_mvsum() returns a value same to nr_accesses_bp. Also
the function is more simple and therefore more tolerant to errors.
Execution of the function would be more expensive than the simple read
of the field, but because the function is quite simple, the overhead
should be negligible. Use it in the DAMON region exporting trace points
instead of the nr_accesses_bp.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
include/trace/events/damon.h | 8 +++++---
mm/damon/core.c | 5 +++--
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/include/trace/events/damon.h b/include/trace/events/damon.h
index 78388538acf44..8851727ae1627 100644
--- a/include/trace/events/damon.h
+++ b/include/trace/events/damon.h
@@ -78,9 +78,11 @@ TRACE_EVENT_CONDITION(damos_before_apply,
TP_PROTO(unsigned int context_idx, unsigned int scheme_idx,
unsigned int target_idx, struct damon_region *r,
- unsigned int nr_regions, bool do_trace),
+ unsigned int nr_accesses, unsigned int nr_regions,
+ bool do_trace),
- TP_ARGS(context_idx, scheme_idx, target_idx, r, nr_regions, do_trace),
+ TP_ARGS(context_idx, scheme_idx, target_idx, r, nr_accesses,
+ nr_regions, do_trace),
TP_CONDITION(do_trace),
@@ -101,7 +103,7 @@ TRACE_EVENT_CONDITION(damos_before_apply,
__entry->target_idx = target_idx;
__entry->start = r->ar.start;
__entry->end = r->ar.end;
- __entry->nr_accesses = r->nr_accesses_bp / 10000;
+ __entry->nr_accesses = nr_accesses;
__entry->age = r->age;
__entry->nr_regions = nr_regions;
),
diff --git a/mm/damon/core.c b/mm/damon/core.c
index ce0e2a4c1d523..710ec13e98281 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -2434,7 +2434,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t,
struct damos *siter; /* schemes iterator */
unsigned int sidx = 0;
struct damon_target *titer; /* targets iterator */
- unsigned int tidx = 0;
+ unsigned int tidx = 0, nr_accesses = 0;
bool do_trace = false;
/* get indices for trace_damos_before_apply() */
@@ -2449,6 +2449,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t,
break;
tidx++;
}
+ nr_accesses = damon_nr_accesses_mvsum(r, c);
do_trace = true;
}
@@ -2464,7 +2465,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t,
if (damos_core_filter_out(c, t, r, s))
return;
ktime_get_coarse_ts64(&begin);
- trace_damos_before_apply(cidx, sidx, tidx, r,
+ trace_damos_before_apply(cidx, sidx, tidx, r, nr_accesses,
damon_nr_regions(t), do_trace);
sz_applied = c->ops.apply_scheme(c, t, r, s,
&sz_ops_filter_passed);
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 05/13] mm/damon/sysfs-schemes: use damon_nr_accesses_mvsum() for damo regions
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (3 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 04/13] mm/damon/core: use damon_nr_accesses_mvsum() for damos region tracing SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 06/13] mm/damon/core: remove damon_warn_fix_nr_accesses_corruption() SeongJae Park
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, damon, linux-kernel, linux-mm
damon_nr_accesses_mvsum() returns a value same to nr_accesses_bp. Also
the function is more simple and therefore more tolerant to errors.
Execution of the function would be more expensive than the simple read
of the field, but because the function is quite simple, the overhead
should be negligible. Use it in the DAMON sysfs interface for
scheme-tried regions, instead of the nr_accesses_bp.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/sysfs-schemes.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c
index 9d8fab32b80f7..30a007bcf82f4 100644
--- a/mm/damon/sysfs-schemes.c
+++ b/mm/damon/sysfs-schemes.c
@@ -159,7 +159,7 @@ struct damon_sysfs_scheme_region {
};
static struct damon_sysfs_scheme_region *damon_sysfs_scheme_region_alloc(
- struct damon_region *region)
+ struct damon_region *region, struct damon_ctx *ctx)
{
struct damon_sysfs_scheme_region *sysfs_region = kmalloc_obj(*sysfs_region);
@@ -167,7 +167,7 @@ static struct damon_sysfs_scheme_region *damon_sysfs_scheme_region_alloc(
return NULL;
sysfs_region->kobj = (struct kobject){};
sysfs_region->ar = region->ar;
- sysfs_region->nr_accesses = region->nr_accesses_bp / 10000;
+ sysfs_region->nr_accesses = damon_nr_accesses_mvsum(region, ctx);
sysfs_region->age = region->age;
sysfs_region->probes = NULL;
INIT_LIST_HEAD(&sysfs_region->list);
@@ -3124,7 +3124,7 @@ void damos_sysfs_populate_region_dir(struct damon_sysfs_schemes *sysfs_schemes,
if (total_bytes_only)
return;
- region = damon_sysfs_scheme_region_alloc(r);
+ region = damon_sysfs_scheme_region_alloc(r, ctx);
if (!region)
return;
region->sz_filter_passed = sz_filter_passed;
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 06/13] mm/damon/core: remove damon_warn_fix_nr_accesses_corruption()
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (4 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 05/13] mm/damon/sysfs-schemes: use damon_nr_accesses_mvsum() for damo regions SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 07/13] mm/damon/core: remove damon_verify_reset_aggregated() SeongJae Park
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, damon, linux-kernel, linux-mm
nr_accesses_bp is delicate. Once it is corrupted, the consequence is
the bad madness of DAMON monitoring results. From developments of
features of size, we historically found nr_accesses_bp can be corrupted
by complicated bugs that are not easy to debug. Hence we added a
function for finding the corruption and fixing it right away.
There are no more uses of nr_accesses_bp. Hence the function for
corruption detection and fix is no more needed. Rip it out.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/core.c | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 710ec13e98281..e144f0079322d 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -1994,19 +1994,6 @@ int damos_walk(struct damon_ctx *ctx, struct damos_walk_control *control)
return 0;
}
-/*
- * Warn and fix corrupted ->nr_accesses[_bp] for investigations and preventing
- * the problem being propagated.
- */
-static void damon_warn_fix_nr_accesses_corruption(struct damon_region *r)
-{
- if (r->nr_accesses_bp == r->nr_accesses * 10000)
- return;
- WARN_ONCE(true, "invalid nr_accesses_bp at reset: %u %u\n",
- r->nr_accesses_bp, r->nr_accesses);
- r->nr_accesses_bp = r->nr_accesses * 10000;
-}
-
#ifdef CONFIG_DAMON_DEBUG_SANITY
static void damon_verify_reset_aggregated(struct damon_region *r,
struct damon_ctx *c)
@@ -2048,7 +2035,6 @@ static void kdamond_reset_aggregated(struct damon_ctx *c)
trace_damon_aggregated(ti, r, damon_nr_regions(t));
trace_damon_region_aggregated(ti, r,
damon_nr_regions(t), nr_probes);
- damon_warn_fix_nr_accesses_corruption(r);
r->last_nr_accesses = r->nr_accesses;
r->nr_accesses = 0;
for (i = 0; i < DAMON_MAX_PROBES; i++)
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 07/13] mm/damon/core: remove damon_verify_reset_aggregated()
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (5 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 06/13] mm/damon/core: remove damon_warn_fix_nr_accesses_corruption() SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 08/13] mm/damon/core: remove damon_verify_merge_regions_of() SeongJae Park
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, damon, linux-kernel, linux-mm
nr_accesse_bp is no longer being used in real use cases. Remove its
validation function.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/core.c | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index e144f0079322d..84d21d21ed6c9 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -1994,23 +1994,6 @@ int damos_walk(struct damon_ctx *ctx, struct damos_walk_control *control)
return 0;
}
-#ifdef CONFIG_DAMON_DEBUG_SANITY
-static void damon_verify_reset_aggregated(struct damon_region *r,
- struct damon_ctx *c)
-{
- WARN_ONCE(r->nr_accesses_bp != r->last_nr_accesses * 10000,
- "nr_accesses_bp %u last_nr_accesses %u sis %lu %lu\n",
- r->nr_accesses_bp, r->last_nr_accesses,
- c->passed_sample_intervals, c->next_aggregation_sis);
-}
-#else
-static void damon_verify_reset_aggregated(struct damon_region *r,
- struct damon_ctx *c)
-{
-}
-#endif
-
-
/*
* Reset the aggregated monitoring results ('nr_accesses' of each region).
*/
@@ -2039,7 +2022,6 @@ static void kdamond_reset_aggregated(struct damon_ctx *c)
r->nr_accesses = 0;
for (i = 0; i < DAMON_MAX_PROBES; i++)
r->probe_hits[i] = 0;
- damon_verify_reset_aggregated(r, c);
}
ti++;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 08/13] mm/damon/core: remove damon_verify_merge_regions_of()
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (6 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 07/13] mm/damon/core: remove damon_verify_reset_aggregated() SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 09/13] mm/damon/tests/core-kunit: remove nr_accesses_bp setup and tests SeongJae Park
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, damon, linux-kernel, linux-mm
damon_verify_merge_regions_of() is only for nr_accesses_bp validation.
But nr_accesses_bp is no more being used for a real purpose. Remove the
validation.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/core.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 84d21d21ed6c9..6db157ee25129 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -3109,20 +3109,6 @@ static void damon_merge_two_regions(struct damon_target *t,
damon_destroy_region(r, t);
}
-#ifdef CONFIG_DAMON_DEBUG_SANITY
-static void damon_verify_merge_regions_of(struct damon_region *r)
-{
- WARN_ONCE(r->nr_accesses != r->nr_accesses_bp / 10000,
- "nr_accesses (%u) != nr_accesses_bp (%u)\n",
- r->nr_accesses, r->nr_accesses_bp);
-}
-#else
-static void damon_verify_merge_regions_of(struct damon_region *r)
-{
-}
-#endif
-
-
/*
* Merge adjacent regions having similar access frequencies
*
@@ -3136,7 +3122,6 @@ static void damon_merge_regions_of(struct damon_target *t, unsigned int thres,
struct damon_region *r, *prev = NULL, *next;
damon_for_each_region_safe(r, next, t) {
- damon_verify_merge_regions_of(r);
if (abs(r->nr_accesses - r->last_nr_accesses) > thres)
r->age = 0;
else if ((r->nr_accesses == 0) != (r->last_nr_accesses == 0))
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 09/13] mm/damon/tests/core-kunit: remove nr_accesses_bp setup and tests
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (7 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 08/13] mm/damon/core: remove damon_verify_merge_regions_of() SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 10/13] selftests/damon/drgn_dump_damon_status: do not dump nr_accesses_bp SeongJae Park
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, Brendan Higgins, David Gow, damon,
kunit-dev, linux-kernel, linux-kselftest, linux-mm
DAMON core unit tests set up nr_accesses_bp for representing realistic
damon_region, and also test the field. nr_acceses_bp is no longer being
used for a real use case. Remove the setup and tests.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/tests/core-kunit.h | 8 --------
1 file changed, 8 deletions(-)
diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index cdab14396250f..d1f019ab6fc25 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -118,7 +118,6 @@ static void damon_test_aggregate(struct kunit *test)
kunit_skip(test, "region alloc fail");
}
r->nr_accesses = accesses[it][ir];
- r->nr_accesses_bp = accesses[it][ir] * 10000;
damon_add_region(r, t);
}
it++;
@@ -155,7 +154,6 @@ static void damon_test_split_at(struct kunit *test)
damon_free_target(t);
kunit_skip(test, "region alloc fail");
}
- r->nr_accesses_bp = 420000;
r->nr_accesses = 42;
r->last_nr_accesses = 15;
r->age = 10;
@@ -168,7 +166,6 @@ static void damon_test_split_at(struct kunit *test)
KUNIT_EXPECT_EQ(test, r_new->ar.start, 25ul);
KUNIT_EXPECT_EQ(test, r_new->ar.end, 100ul);
- KUNIT_EXPECT_EQ(test, r->nr_accesses_bp, r_new->nr_accesses_bp);
KUNIT_EXPECT_EQ(test, r->nr_accesses, r_new->nr_accesses);
KUNIT_EXPECT_EQ(test, r->last_nr_accesses, r_new->last_nr_accesses);
KUNIT_EXPECT_EQ(test, r->age, r_new->age);
@@ -191,7 +188,6 @@ static void damon_test_merge_two(struct kunit *test)
kunit_skip(test, "region alloc fail");
}
r->nr_accesses = 10;
- r->nr_accesses_bp = 100000;
r->age = 9;
damon_add_region(r, t);
r2 = damon_new_region(100, 300);
@@ -200,7 +196,6 @@ static void damon_test_merge_two(struct kunit *test)
kunit_skip(test, "second region alloc fail");
}
r2->nr_accesses = 20;
- r2->nr_accesses_bp = 200000;
r2->age = 21;
damon_add_region(r2, t);
@@ -208,7 +203,6 @@ static void damon_test_merge_two(struct kunit *test)
KUNIT_EXPECT_EQ(test, r->ar.start, 0ul);
KUNIT_EXPECT_EQ(test, r->ar.end, 300ul);
KUNIT_EXPECT_EQ(test, r->nr_accesses, 16u);
- KUNIT_EXPECT_EQ(test, r->nr_accesses_bp, 160000u);
KUNIT_EXPECT_EQ(test, r->age, 17u);
i = 0;
@@ -256,7 +250,6 @@ static void damon_test_merge_regions_of(struct kunit *test)
kunit_skip(test, "region alloc fail");
}
r->nr_accesses = nrs[i];
- r->nr_accesses_bp = nrs[i] * 10000;
damon_add_region(r, t);
}
@@ -556,7 +549,6 @@ static void damon_test_update_monitoring_result(struct kunit *test)
kunit_skip(test, "region alloc fail");
r->nr_accesses = 15;
- r->nr_accesses_bp = 150000;
r->age = 20;
new_attrs = (struct damon_attrs){
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 10/13] selftests/damon/drgn_dump_damon_status: do not dump nr_accesses_bp
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (8 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 09/13] mm/damon/tests/core-kunit: remove nr_accesses_bp setup and tests SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 11/13] mm/damon/core: remove nr_accesses_bp setups and updates SeongJae Park
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Shuah Khan, damon, linux-kernel, linux-kselftest,
linux-mm
drgn_dump_damon_status is dumping nr_accesses_bp field for future use
case. nr_accesses_bp is not being used for a real purpose, though.
Hence there will be no future test for it. Do not dump it.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
tools/testing/selftests/damon/drgn_dump_damon_status.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py
index 26b207e44268d..09552e91bc782 100755
--- a/tools/testing/selftests/damon/drgn_dump_damon_status.py
+++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py
@@ -59,7 +59,6 @@ def region_to_dict(region):
['ar', addr_range_to_dict],
['sampling_addr', int],
['nr_accesses', int],
- ['nr_accesses_bp', int],
['age', int],
])
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 11/13] mm/damon/core: remove nr_accesses_bp setups and updates
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (9 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 10/13] selftests/damon/drgn_dump_damon_status: do not dump nr_accesses_bp SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 12/13] mm/damon/core: remove damon_moving_sum() and its unit test SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 13/13] mm/damon: remove damon_region->nr_accesses_bp SeongJae Park
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, damon, linux-kernel, linux-mm
DAMON core sets and updates nr_accesses_bp in multiple places. It
explains how delicate it is. The field is no more being used for any
real purpose, and replaced by a simpler function. Remove the setups and
updates.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/core.c | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 6db157ee25129..471b58e8e31b9 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -296,7 +296,6 @@ struct damon_region *damon_new_region(unsigned long start, unsigned long end)
region->ar.start = start;
region->ar.end = end;
region->nr_accesses = 0;
- region->nr_accesses_bp = 0;
for (i = 0; i < DAMON_MAX_PROBES; i++)
region->probe_hits[i] = 0;
INIT_LIST_HEAD(®ion->list);
@@ -871,7 +870,6 @@ static void damon_update_monitoring_result(struct damon_region *r,
if (!aggregating) {
r->nr_accesses = damon_nr_accesses_for_new_attrs(
r->nr_accesses, old_attrs, new_attrs);
- r->nr_accesses_bp = r->nr_accesses * 10000;
} else {
/*
* if this is called in the middle of the aggregation, reset
@@ -881,7 +879,6 @@ static void damon_update_monitoring_result(struct damon_region *r,
*/
r->last_nr_accesses = damon_nr_accesses_for_new_attrs(
r->last_nr_accesses, old_attrs, new_attrs);
- r->nr_accesses_bp = r->last_nr_accesses * 10000;
r->nr_accesses = 0;
}
r->age = damon_age_for_new_attrs(r->age, old_attrs, new_attrs);
@@ -3098,7 +3095,6 @@ static void damon_merge_two_regions(struct damon_target *t,
l->nr_accesses = (l->nr_accesses * sz_l + r->nr_accesses * sz_r) /
(sz_l + sz_r);
- l->nr_accesses_bp = l->nr_accesses * 10000;
l->age = (l->age * sz_l + r->age * sz_r) / (sz_l + sz_r);
l->ar.end = r->ar.end;
/* todo: do this for only installed probes */
@@ -3210,7 +3206,6 @@ static void damon_split_region_at(struct damon_target *t,
new->age = r->age;
new->last_nr_accesses = r->last_nr_accesses;
- new->nr_accesses_bp = r->nr_accesses_bp;
new->nr_accesses = r->nr_accesses;
/* todo: do this for only installed probes */
memcpy(new->probe_hits, r->probe_hits, sizeof(r->probe_hits));
@@ -3779,18 +3774,6 @@ static unsigned int damon_moving_sum(unsigned int mvsum, unsigned int nomvsum,
void damon_update_region_access_rate(struct damon_region *r, bool accessed,
struct damon_attrs *attrs)
{
- unsigned int len_window = 1;
-
- /*
- * sample_interval can be zero, but cannot be larger than
- * aggr_interval, owing to validation of damon_set_attrs().
- */
- if (attrs->sample_interval)
- len_window = damon_max_nr_accesses(attrs);
- r->nr_accesses_bp = damon_moving_sum(r->nr_accesses_bp,
- r->last_nr_accesses * 10000, len_window,
- accessed ? 10000 : 0);
-
if (accessed)
r->nr_accesses++;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 12/13] mm/damon/core: remove damon_moving_sum() and its unit test
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (10 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 11/13] mm/damon/core: remove nr_accesses_bp setups and updates SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
2026-06-20 17:22 ` [RFC PATCH v1.1 13/13] mm/damon: remove damon_region->nr_accesses_bp SeongJae Park
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, damon, linux-kernel, linux-mm
damon_moving_sum() is no longer being called for real purpose but its
unit test. Testing a function that is not being used for real users
makes no sense. Remove the test and the function.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/core.c | 40 -------------------------------------
mm/damon/tests/core-kunit.h | 16 ---------------
2 files changed, 56 deletions(-)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 471b58e8e31b9..f96f54a7f178a 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -3720,46 +3720,6 @@ int damon_set_region_system_rams_default(struct damon_target *t,
return damon_set_regions(t, &addr_range, 1, min_region_sz);
}
-/*
- * damon_moving_sum() - Calculate an inferred moving sum value.
- * @mvsum: Inferred sum of the last @len_window values.
- * @nomvsum: Non-moving sum of the last discrete @len_window window values.
- * @len_window: The number of last values to take care of.
- * @new_value: New value that will be added to the pseudo moving sum.
- *
- * Moving sum (moving average * window size) is good for handling noise, but
- * the cost of keeping past values can be high for arbitrary window size. This
- * function implements a lightweight pseudo moving sum function that doesn't
- * keep the past window values.
- *
- * It simply assumes there was no noise in the past, and get the no-noise
- * assumed past value to drop from @nomvsum and @len_window. @nomvsum is a
- * non-moving sum of the last window. For example, if @len_window is 10 and we
- * have 25 values, @nomvsum is the sum of the 11th to 20th values of the 25
- * values. Hence, this function simply drops @nomvsum / @len_window from
- * given @mvsum and add @new_value.
- *
- * For example, if @len_window is 10 and @nomvsum is 50, the last 10 values for
- * the last window could be vary, e.g., 0, 10, 0, 10, 0, 10, 0, 0, 0, 20. For
- * calculating next moving sum with a new value, we should drop 0 from 50 and
- * add the new value. However, this function assumes it got value 5 for each
- * of the last ten times. Based on the assumption, when the next value is
- * measured, it drops the assumed past value, 5 from the current sum, and add
- * the new value to get the updated pseduo-moving average.
- *
- * This means the value could have errors, but the errors will be disappeared
- * for every @len_window aligned calls. For example, if @len_window is 10, the
- * pseudo moving sum with 11th value to 19th value would have an error. But
- * the sum with 20th value will not have the error.
- *
- * Return: Pseudo-moving average after getting the @new_value.
- */
-static unsigned int damon_moving_sum(unsigned int mvsum, unsigned int nomvsum,
- unsigned int len_window, unsigned int new_value)
-{
- return mvsum - nomvsum / len_window + new_value;
-}
-
/**
* damon_update_region_access_rate() - Update the access rate of a region.
* @r: The DAMON region to update for its access check result.
diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index d1f019ab6fc25..df16cc070eeb0 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -600,21 +600,6 @@ static void damon_test_set_attrs(struct kunit *test)
damon_destroy_ctx(c);
}
-static void damon_test_moving_sum(struct kunit *test)
-{
- unsigned int mvsum = 50000, nomvsum = 50000, len_window = 10;
- unsigned int new_values[] = {10000, 0, 10000, 0, 0, 0, 10000, 0, 0, 0};
- unsigned int expects[] = {55000, 50000, 55000, 50000, 45000, 40000,
- 45000, 40000, 35000, 30000};
- int i;
-
- for (i = 0; i < ARRAY_SIZE(new_values); i++) {
- mvsum = damon_moving_sum(mvsum, nomvsum, len_window,
- new_values[i]);
- KUNIT_EXPECT_EQ(test, mvsum, expects[i]);
- }
-}
-
static void damon_test_mvsum(struct kunit *test)
{
unsigned long input_expects[] = {
@@ -1516,7 +1501,6 @@ static struct kunit_case damon_test_cases[] = {
KUNIT_CASE(damon_test_nr_accesses_to_accesses_bp),
KUNIT_CASE(damon_test_update_monitoring_result),
KUNIT_CASE(damon_test_set_attrs),
- KUNIT_CASE(damon_test_moving_sum),
KUNIT_CASE(damon_test_mvsum),
KUNIT_CASE(damos_test_new_filter),
KUNIT_CASE(damos_test_commit_quota_goal),
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [RFC PATCH v1.1 13/13] mm/damon: remove damon_region->nr_accesses_bp
2026-06-20 17:22 [RFC PATCH v1.1 00/13] mm/damon: optimize out nr_accesses_bp SeongJae Park
` (11 preceding siblings ...)
2026-06-20 17:22 ` [RFC PATCH v1.1 12/13] mm/damon/core: remove damon_moving_sum() and its unit test SeongJae Park
@ 2026-06-20 17:22 ` SeongJae Park
12 siblings, 0 replies; 14+ messages in thread
From: SeongJae Park @ 2026-06-20 17:22 UTC (permalink / raw)
Cc: SeongJae Park, Andrew Morton, damon, linux-kernel, linux-mm
No code touches damon_region->nr_accesses_bp field. Remove it.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
include/linux/damon.h | 10 ----------
mm/damon/core.c | 3 +--
2 files changed, 1 insertion(+), 12 deletions(-)
diff --git a/include/linux/damon.h b/include/linux/damon.h
index 55a743d99b567..90d2910b3217a 100644
--- a/include/linux/damon.h
+++ b/include/linux/damon.h
@@ -47,8 +47,6 @@ struct damon_size_range {
* @ar: The address range of the region.
* @sampling_addr: Address of the sample for the next access check.
* @nr_accesses: Access frequency of this region.
- * @nr_accesses_bp: @nr_accesses in basis point (0.01%) that updated for
- * each sampling interval.
* @probe_hits: Number of probe-positive region samples.
* @list: List head for siblings.
* @age: Age of this region.
@@ -61,13 +59,6 @@ struct damon_size_range {
* not be done with direct access but with the helper function,
* damon_update_region_access_rate().
*
- * @nr_accesses_bp is another representation of @nr_accesses in basis point
- * (1 in 10,000) that updated for every &damon_attrs->sample_interval in a
- * manner similar to moving sum. By the algorithm, this value becomes
- * @nr_accesses * 10000 for every &struct damon_attrs->aggr_interval. This can
- * be used when the aggregation interval is too huge and therefore cannot wait
- * for it before getting the access monitoring results.
- *
* @age is initially zero, increased for each aggregation interval, and reset
* to zero again if the access frequency is significantly changed. If two
* regions are merged into a new region, both @nr_accesses and @age of the new
@@ -77,7 +68,6 @@ struct damon_region {
struct damon_addr_range ar;
unsigned long sampling_addr;
unsigned int nr_accesses;
- unsigned int nr_accesses_bp;
unsigned char probe_hits[DAMON_MAX_PROBES];
struct list_head list;
diff --git a/mm/damon/core.c b/mm/damon/core.c
index f96f54a7f178a..abccac9e9f9e8 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -3580,8 +3580,7 @@ static int kdamond_fn(void *data)
* aggregation, and make aggregation
* information reset for all regions. Then,
* following kdamond_reset_aggregated() call
- * will make the region information invalid,
- * particularly for ->nr_accesses_bp.
+ * will make the region information invalid.
*
* Reset ->next_aggregation_sis to avoid that.
* It will anyway correctly updated after this
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread