summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSJ Park <sj@kernel.org>2026-06-29 21:07:54 -0700
committerAndrew Morton <akpm@linux-foundation.org>2026-07-02 23:27:33 -0700
commitce9cb95178930ad4a4b235dd4fb1c2c1f6c50e83 (patch)
treea969dcd3e9a98b66e5903cf45c60bae01a598b22
parentcacb66e2e2bfd9d3ebdc223e7dd02d1f863ec7b9 (diff)
downloadlinux-next-ce9cb95178930ad4a4b235dd4fb1c2c1f6c50e83.tar.gz
linux-next-ce9cb95178930ad4a4b235dd4fb1c2c1f6c50e83.zip
mm/damon/core: introduce damon_nr_accesses_mvsum()
Patch series "mm/damon: optimize out nr_accesses_bp". TLDR: Replace damon_region->nr_accesses_bp, which is easy to be wrong, with a simpler on-demand moving sum function, damon_nr_accesses_mvsum(). Background ========== DAMON's monitoring output (access pattern snapshot, or more technically speaking, damon_region->nr_accesses) is completed once per aggregation interval, which is 100 ms by default. Users can arbitrarily increase the interval for demand. Under the suggested intervals auto-tuning setup, it can span up to 200 seconds. If the aggregation interval is too long, the snapshot users cannot use it in reasonable time. To mitigate this, we introduced a new field of damon_region, namely nr_accesses_bp. It contains a pseudo moving sum of nr_accesses in bp units and is updated for each sampling interval. It turned out keeping it correctly updated every sampling interval is not that easy. From online parameter update feature development and more experimental hacks, we found it is easy to be corrupted. Once it is corrupted, DAMON's monitoring outputs become quite insane. Hence we added a few validation checks. It is easy to be corrupted because it requires every update per sampling interval to be correct. Solution ======== There is no real reason to keep it updated every sampling interval. Due to the simple pseudo-moving sum mechanism and existing helper field (last_nr_accesses), we can also calculate the pseudo moving sum on demand in a much simpler way. Implement a function for getting the pseudo moving sum on demand, and replace nr_accessses_bp uses with the new function. Also remove no more needed tests for nr_accesses_bp and the per-sampling interval update functions. Finally, remove the nr_accesses_bp. The new function is quite simple. Discussion ========== Depending on the use case, multiple nr_accesses readers could be executed in the same kdamond_fn() main loop iteration, which is executed once per sampling interval. Such readers include DAMON region exporting tracepoints (damon_[region_]aggregated and damos_before_apply), DAMOS, and DAMON sysfs interface logic for update_schemes_tried_regions command. In this case, the new function will be called multiple times and this could be overhead compared to the old logic, which simply reads the field without any additional work. Nonetheless, the new function is quite simple. And the new approach does nothing while there is no need to read. The old approach had to execute its update function for each region for every sampling interval. Hence the new approach is believed to be even more lightweight in common case, and the overhead is anyway negligible. One more advantage of this change is that one field from the damon_region struct is removed. On setups that uses a high number of DAMON regions, this could be a potential memory space benefit. Patches Sequence ================ Patch 1 introduces the new function for getting the pseudo moving sum of nr_accesses on demands. Patch 2 implements a unit test for the new function's internal logic. Patch 3 and 4 update monitoring logic and the new function to ready for safe use on the existing logic. Patches 5-7 replace uses of nr_accesses_bp in DAMOS, tracepoints and DAMON sysfs interface with the new function, respectively. Patches 8-10 removes nr_accesses_bp validation functions in DAMON core, one by one. Patches 11 and 12 further remove tests and test helper for nr_accesses_bp, respectively. Patches 13 removes the setups and updates or nr_accesses_bp field. Patches 14-16 cleans up function parameters that are no more being used due to the previous patch. Patch 17 removes the function that was used for updating nr_accesses_bp field with its unit test, which is the single remaining caller of the function. Finally, patch 18 removes damon_region->nr_accesses_bp field. This patch (of 18): 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. Link: https://lore.kernel.org/20260630040812.149729-1-sj@kernel.org Link: https://lore.kernel.org/20260630040812.149729-2-sj@kernel.org Signed-off-by: SJ Park <sj@kernel.org> Cc: Brendan Higgins <brendan.higgins@linux.dev> Cc: "Masami Hiramatsu (Google)" <mhiramat@kernel.org> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Cc: Shuah Khan <shuah@kernel.org> Cc: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-rw-r--r--include/linux/damon.h2
-rw-r--r--mm/damon/core.c62
2 files changed, 64 insertions, 0 deletions
diff --git a/include/linux/damon.h b/include/linux/damon.h
index cfbbf8ba28f6..87c1ff479da8 100644
--- a/include/linux/damon.h
+++ b/include/linux/damon.h
@@ -1011,6 +1011,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 1cd756e0c6c0..f18866b5289d 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -208,6 +208,68 @@ 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;
+ if (time_after_eq(ctx->passed_sample_intervals,
+ ctx->next_aggregation_sis))
+ left_window = 0;
+ else
+ 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)
{