]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mm/damon/core: split regions for min_nr_regions
authorSeongJae Park <sj@kernel.org>
Sat, 28 Feb 2026 22:28:25 +0000 (14:28 -0800)
committerAndrew Morton <akpm@linux-foundation.org>
Sun, 5 Apr 2026 20:53:11 +0000 (13:53 -0700)
Patch series "mm/damon: strictly respect min_nr_regions".

DAMON core respects min_nr_regions only at merge operation.  DAMON API
callers are therefore responsible to respect or ignore that.  Only vaddr
ops is respecting that, but only for initial start time.  DAMON sysfs
interface allows users to setup the initial regions that DAMON core also
respects.  But, again, it works for only the initial time.  Users setting
the regions for min_nr_regions can be difficult and inefficient, when the
min_nr_regions value is high.  There was actually a report [1] from a
user.  The use case was page granular access monitoring with a large
aggregation interval.

Make the following three changes for resolving the issue.  First (patch
1), make DAMON core split regions at the beginning and every aggregation
interval, to respect the min_nr_regions.  Second (patch 2), drop the
vaddr's split operations and related code that are no more needed.  Third
(patch 3), add a kunit test for the newly introduced function.

This patch (of 3):

DAMON core layer respects the min_nr_regions parameter by setting the
maximum size of each region as total monitoring region size divided by the
parameter value.  And the limit is applied by preventing merge of regions
that result in a region larger than the maximum size.  The limit is
updated per ops update interval, because vaddr updates the monitoring
regions on the ops update callback.

It does nothing for the beginning state.  That's because the users can set
the initial monitoring regions as they want.  That is, if the users really
care about the min_nr_regions, they are supposed to set the initial
monitoring regions to have more than min_nr_regions regions.  The virtual
address space operation set, vaddr, has an exceptional case.  Users can
ask the ops set to configure the initial regions on its own.  For the
case, vaddr sets up the initial regions to meet the min_nr_regions.  So,
vaddr has exceptional support, but basically users are required to set the
regions on their own if they want min_nr_regions to be respected.

When 'min_nr_regions' is high, such initial setup is difficult.  If DAMON
sysfs interface is used for that, the memory for saving the initial setup
is also a waste.

Even if the user forgives the setup, DAMON will eventually make more than
min_nr_regions regions by splitting operations.  But it will take time.
If the aggregation interval is long, the delay could be problematic.
There was actually a report [1] of the case.  The reporter wanted to do
page granular monitoring with a large aggregation interval.

Also, DAMON is doing nothing for online changes on monitoring regions and
min_nr_regions.  For example, the user can remove a monitoring region or
increase min_nr_regions while DAMON is running.

Split regions larger than the size at the beginning of the kdamond main
loop, to fix the initial setup issue.  Also do the split every aggregation
interval, for online changes.  This means the behavior is slightly
changed.  It is difficult to imagine a use case that actually depends on
the old behavior, though.  So this change is arguably fine.

Note that the size limit is aligned by damon_ctx->min_region_sz and cannot
be zero.  That is, if min_nr_region is larger than the total size of
monitoring regions divided by ->min_region_sz, that cannot be respected.

Link: https://lkml.kernel.org/r/20260228222831.7232-1-sj@kernel.org
Link: https://lkml.kernel.org/r/20260228222831.7232-2-sj@kernel.org
Link: https://lore.kernel.org/CAC5umyjmJE9SBqjbetZZecpY54bHpn2AvCGNv3aF6J=1cfoPXQ@mail.gmail.com
Signed-off-by: SeongJae Park <sj@kernel.org>
Cc: Brendan Higgins <brendan.higgins@linux.dev>
Cc: David Gow <davidgow@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/damon/core.c

index a97d8799c228896ddcfb5f5375e18b2dc6015607..71ccea40368da7c3e18f28be0055ac5173074fb3 100644 (file)
@@ -1316,6 +1316,40 @@ static unsigned long damon_region_sz_limit(struct damon_ctx *ctx)
        return sz;
 }
 
+static void damon_split_region_at(struct damon_target *t,
+                                 struct damon_region *r, unsigned long sz_r);
+
+/*
+ * damon_apply_min_nr_regions() - Make effect of min_nr_regions parameter.
+ * @ctx:       monitoring context.
+ *
+ * This function implement min_nr_regions (minimum number of damon_region
+ * objects in the given monitoring context) behavior.  It first calculates
+ * maximum size of each region for enforcing the min_nr_regions as total size
+ * of the regions divided by the min_nr_regions.  After that, this function
+ * splits regions to ensure all regions are equal to or smaller than the size
+ * limit.  Finally, this function returns the maximum size limit.
+ *
+ * Returns: maximum size of each region for convincing min_nr_regions.
+ */
+static unsigned long damon_apply_min_nr_regions(struct damon_ctx *ctx)
+{
+       unsigned long max_region_sz = damon_region_sz_limit(ctx);
+       struct damon_target *t;
+       struct damon_region *r, *next;
+
+       max_region_sz = ALIGN(max_region_sz, ctx->min_region_sz);
+       damon_for_each_target(t, ctx) {
+               damon_for_each_region_safe(r, next, t) {
+                       while (damon_sz_region(r) > max_region_sz) {
+                               damon_split_region_at(t, r, max_region_sz);
+                               r = damon_next_region(r);
+                       }
+               }
+       }
+       return max_region_sz;
+}
+
 static int kdamond_fn(void *data);
 
 /*
@@ -1672,9 +1706,6 @@ static void kdamond_tune_intervals(struct damon_ctx *c)
        damon_set_attrs(c, &new_attrs);
 }
 
-static void damon_split_region_at(struct damon_target *t,
-                                 struct damon_region *r, unsigned long sz_r);
-
 static bool __damos_valid_target(struct damon_region *r, struct damos *s)
 {
        unsigned long sz;
@@ -2763,7 +2794,7 @@ static int kdamond_fn(void *data)
        if (!ctx->regions_score_histogram)
                goto done;
 
-       sz_limit = damon_region_sz_limit(ctx);
+       sz_limit = damon_apply_min_nr_regions(ctx);
 
        while (!kdamond_need_stop(ctx)) {
                /*
@@ -2788,10 +2819,13 @@ static int kdamond_fn(void *data)
                if (ctx->ops.check_accesses)
                        max_nr_accesses = ctx->ops.check_accesses(ctx);
 
-               if (ctx->passed_sample_intervals >= next_aggregation_sis)
+               if (ctx->passed_sample_intervals >= next_aggregation_sis) {
                        kdamond_merge_regions(ctx,
                                        max_nr_accesses / 10,
                                        sz_limit);
+                       /* online updates might be made */
+                       sz_limit = damon_apply_min_nr_regions(ctx);
+               }
 
                /*
                 * do kdamond_call() and kdamond_apply_schemes() after
@@ -2850,7 +2884,6 @@ static int kdamond_fn(void *data)
                                sample_interval;
                        if (ctx->ops.update)
                                ctx->ops.update(ctx);
-                       sz_limit = damon_region_sz_limit(ctx);
                }
        }
 done: