FAILED: patch "[PATCH] mm/damon/core: fix unconditionally skip last region" failed to apply to 6.18-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x b3723b596b548c837a766aae3553c14a7b15af2b
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026092947-shush-encode-1e48@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From b3723b596b548c837a766aae3553c14a7b15af2b Mon Sep 17 00:00:00 2001
From: Liew Rui Yan <[email protected]>
Date: Tue, 8 Sep 2026 06:47:38 -0700
Subject: [PATCH] mm/damon/core: fix unconditionally skip last region
Once quota set, the charge_{target,addr}_from unconditionally skips and resets at the last region of the tracked target, so the last region can be skipped even when it has not been processed.
Example:
1. Target has 2 regions: R1 (0-100 bytes) and R2 (100-200 bytes).
2. Quota is configured to process only 100 bytes per window.
3. Window 1: Processes R1 (0-100). Quota is full. charge_{target, addr}_from is saved at (Target, 100).
4. Window 2: The loop reaches R2. Because R2 is damon_last_region(t), the old code unconditionally returns true, skipping R2 entirely and resetting the charge_{target,addr}_from.
Result: R2 is permanently skipped even though it has never been processed.
However, it is important to note that this is a very minor issue. This is because it is triggered only when the previous window saved/kept charge_{target,addr}_from, and in the next window, all regions except the last region were skipped by damos_skip_charged_region().
Fix this by only resetting the charge_{target,addr}_from when last region is reached, only skipping when it is applied or cannot split.
mm/damon/core.c +14 -11
@@ -2342,36 +2342,39 @@ static bool damos_skip_charged_region(struct damon_target *t, {struct damos_quota *quota = &s->quota;unsigned long sz_to_skip; +bool skip = false;/* Skip previously charged regions */ if (quota->charge_target_from) {if (t != quota->charge_target_from)return true; -if (r ==damon_last_region (t)) { - quota->charge_target_from =NULL ; - quota->charge_addr_from =0 ; -return true; - }if (quota->charge_addr_from && - r->ar.end <= quota->charge_addr_from) -return true; + r->ar.end <= quota->charge_addr_from) { + skip = true; + goto out; + }if (quota->charge_addr_from && r->ar.start < quota->charge_addr_from) { sz_to_skip =ALIGN_DOWN (quota->charge_addr_from - r->ar.start, min_region_sz);if (!sz_to_skip) { -if (damon_sz_region (r) <= min_region_sz) -return true; +if (damon_sz_region (r) <= min_region_sz) { + skip = true; + goto out; + } sz_to_skip = min_region_sz; }damon_split_region_at (t, r, sz_to_skip); -return true; + skip = true; } + } +out: +if (r ==damon_last_region (t)) { quota->charge_target_from =NULL ; quota->charge_addr_from =0 ; } -return false; +return skip; } static void damos_update_stat(struct damos *s,
SJ Park
maintainerQuoted 5 lines · On Tue, 29 Sep 2026 10:27:47 +0200 <[email protected]> wrote:
The patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
Thank you for this notification!
Quoted 9 lines
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkoutFETCH_HEAD
git cherry-pick -xb3723b596b548c837a766aae3553c14a7b15af2b
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026092947-shush-encode-1e48@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
I resubmitted the patch as part of another series:
https://lore.kernel.org/[email protected]
Thanks,
SJ
[...]
Sasha Levin
maintainerPATCH 1/5 mm/damon: rename damos core filter helpers to have word core
In reply to [email protected]From: SeongJae Park <[email protected]>
[ Upstream commit 8b02baf37311754518dfe78073583db03fbb0c07 ]
Patch series "mm/damon: misc cleanups".
Yet another batch of misc cleanups and refactoring for DAMON code, tests, and documents.
First two patches (1and 2) rename DAMOS core filters related code for readability.
Three following patches (3-5) refactor page table walk callback functions in DAMON, as suggested by Hugh and David, and I promised.
Next two patches (6 and 7) refactor DAMON core layer kunit test and sysfs interface selftest to be simple and deduplicated.
Final two patches (8 and 9) fix up sphinx and grammatical errors on documents.
This patch (of 9):
DAMOS filters handled by the core layer are called core filters, while those handled by the ops layer are called ops filters. They share the same type but are managed in different places since core filters are evaluated before the ops filters. They also have different helper functions that depend on their managed places.
The helper functions for ops filters have '_ops_' keyword on their name, so it is easy to know they are for ops filters. Meanwhile, the helper functions for core filters are not having the 'core' keyword on their name. This makes it easy to be mistakenly used for ops filters. Actually there was such a bug.
To avoid future mistakes from similar confusions, rename DAMOS core filters helper functions to have a keyword 'core' on their names.
Stable-dep-of: b3723b596b54 ("mm/damon/core: fix unconditionally skip last region")
--- .clang-format | 4 ++-- include/linux/damon.h | 4 ++-- mm/damon/core.c | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-)
.clang-format +2 -2
@@ -140,8 +140,8 @@ ForEachMacros: - 'damon_for_each_scheme_safe' - 'damon_for_each_target' - 'damon_for_each_target_safe' - - 'damos_for_each_filter' - - 'damos_for_each_filter_safe' + - 'damos_for_each_core_filter' + - 'damos_for_each_core_filter_safe' - 'damos_for_each_ops_filter' - 'damos_for_each_ops_filter_safe' - 'damos_for_each_quota_goal'
include/linux/damon.h +2 -2
@@ -858,10 +858,10 @@ static inline unsigned long damon_sz_region(struct damon_region *r)#define damos_for_each_quota_goal_safe (goal, next, quota) \ list_for_each_entry_safe(goal, next, &(quota)->goals, list) -#define damos_for_each_filter (f, scheme) \ +#define damos_for_each_core_filter (f, scheme) \ list_for_each_entry(f, &(scheme)->filters, list) -#define damos_for_each_filter_safe (f, next, scheme) \ +#define damos_for_each_core_filter_safe (f, next, scheme) \ list_for_each_entry_safe(f, next, &(scheme)->filters, list)#define damos_for_each_ops_filter (f, scheme) \
mm/damon/core.c +7 -7
@@ -463,7 +463,7 @@ void damon_destroy_scheme(struct damos *s)damos_for_each_quota_goal_safe (g, g_next, &s->quota)damos_destroy_quota_goal (g); -damos_for_each_filter_safe (f, next, s) +damos_for_each_core_filter_safe (f, next, s)damos_destroy_filter (f);damos_for_each_ops_filter_safe (f, next, s)
@@ -871,12 +871,12 @@ static int damos_commit_quota(struct damos_quota *dst, struct damos_quota *src)return 0 ; } -static struct damos_filter *damos_nth_filter (int n,struct damos *s) +static struct damos_filter *damos_nth_core_filter (int n,struct damos *s) {struct damos_filter *filter;int i =0 ; -damos_for_each_filter (filter, s) { +damos_for_each_core_filter (filter, s) {if (i++ == n)return filter; }
@@ -930,15 +930,15 @@ static int damos_commit_core_filters(struct damos *dst, struct damos *src)struct damos_filter *dst_filter, *next, *src_filter, *new_filter;int i =0 , j =0 ; -damos_for_each_filter_safe (dst_filter, next, dst) { - src_filter =damos_nth_filter (i++, src); +damos_for_each_core_filter_safe (dst_filter, next, dst) { + src_filter =damos_nth_core_filter (i++, src);if (src_filter)damos_commit_filter (dst_filter, src_filter);else damos_destroy_filter (dst_filter); } -damos_for_each_filter_safe (src_filter, next, src) { +damos_for_each_core_filter_safe (src_filter, next, src) {if (j++ < i)continue ;
@@ -1846,7 +1846,7 @@ static bool damos_filter_out(struct damon_ctx *ctx, struct damon_target *t,struct damos_filter *filter; s->core_filters_allowed = false; -damos_for_each_filter (filter, s) { +damos_for_each_core_filter (filter, s) {if (damos_filter_match (ctx, t, r, filter, ctx->min_sz_region)) {if (filter->allow) s->core_filters_allowed = true;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/5 mm/damon/core: fix wasteful CPU calls by skipping non-existent targets
From: Enze Li <[email protected]>
[ Upstream commit 6e4930e33329eec80dd245f28b52202271f5fb28 ]
Currently, DAMON does not proactively clean up invalid monitoring targets during its runtime. When some monitored processes exit, DAMON continues to make the following unnecessary function calls,
--damon_for_each_target--
--damon_for_each_region--
damon_do_apply_schemes
damos_apply_scheme
damon_va_apply_scheme
damos_madvise
damon_get_mm
it is only in the damon_get_mm() function that it may finally discover the target no longer exists, which wastes CPU resources. A simple idea is to check for the existence of monitoring targets within the kdamond_need_stop() function and promptly clean up non-existent targets.
However, SJ pointed out that this approach is problematic because the online commit logic incorrectly uses list indices to update the monitoring state. This can lead to data loss if the target list is changed concurrently. Meanwhile, SJ suggests checking for target existence at the damon_for_each_target level, and if a target does not exist, simply skip it and proceed to the next one.
Stable-dep-of: b3723b596b54 ("mm/damon/core: fix unconditionally skip last region")
--- mm/damon/core.c | 3 +++ 1 file changed, 3 insertions(+)
mm/damon/core.c +3 -0
@@ -2325,6 +2325,9 @@ static void kdamond_apply_schemes(struct damon_ctx *c)mutex_lock (&c->walk_control_lock);damon_for_each_target (t, c) { +if (c->ops.target_valid && c->ops.target_valid (t) == false) +continue ; +damon_for_each_region_safe (r, next_r, t)damon_do_apply_schemes (c, t, r); }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/5 mm/damon: rename DAMON_MIN_REGION to DAMON_MIN_REGION_SZ
In reply to Sasha LevinFrom: SeongJae Park <[email protected]>
[ Upstream commit dfb1b0c9dc0d61e422905640e1e7334b3cf6f384 ]
The macro is for the default minimum size of each DAMON region. There was a case that a reader was confused if it is the minimum number of total DAMON regions, which is set on damon_attrs->min_nr_regions. Make the name more explicit.
Stable adaptation for b3723b596b548c837a766aae3553c14a7b15af2b:
Preserve the READ_ONCE() snapshots of ops_id and addr_unit in sysfs and use ctx->addr_unit when calculating the minimum region size. Complete the macro rename for stable-only core, stat, and core KUnit references, including the one-byte minimum used by the existing tests.
Prepare the existing quota helper for the target's upstream patch:
rename its minimum-size parameter as in cc1db8dff8e7, and adapt the region-walk changes from 1745ccbd2907. Use damon_for_each_region() so newly split regions are visited, pass the region directly to the quota helper, and skip the charged prefix after a split. These changes keep the helper and its caller consistent and let the target apply cleanly. No functions are added; the target's last-region fix remains separate.
Stable-dep-of: b3723b596b54 ("mm/damon/core: fix unconditionally skip last region")
--- include/linux/damon.h | 2 +- mm/damon/core.c | 41 ++++++++++++++-------------- mm/damon/lru_sort.c | 2 +- mm/damon/reclaim.c | 2 +- mm/damon/stat.c | 2 +- mm/damon/sysfs.c | 2 +- mm/damon/tests/core-kunit.h | 52 ++++++++++++++++++------------------ mm/damon/tests/vaddr-kunit.h | 2 +- mm/damon/vaddr.c | 24 ++++++++--------- 9 files changed, 65 insertions(+), 64 deletions(-)
include/linux/damon.h +1 -1
@@ -15,7 +15,7 @@#include <linux/random.h> /* Minimal region size. Every damon_region is aligned by this. */ -#define DAMON_MIN_REGION PAGE_SIZE +#define DAMON_MIN_REGION_SZ PAGE_SIZE/* Max priority score for DAMON-based operation schemes */ #define DAMOS_MAX_SCORE (99)
mm/damon/core.c +21 -20
104 lines not on this page · raw message
mm/damon/lru_sort.c +1 -1
@@ -203,7 +203,7 @@ static int damon_lru_sort_apply_parameters(void)if (!monitor_region_start && !monitor_region_end) addr_unit =1 ; param_ctx->addr_unit = addr_unit; - param_ctx->min_sz_region =max (DAMON_MIN_REGION / addr_unit,1 ); + param_ctx->min_sz_region =max (DAMON_MIN_REGION_SZ / addr_unit,1 );if (!damon_lru_sort_mon_attrs.sample_interval) { err = -EINVAL ;
mm/damon/reclaim.c +1 -1
@@ -199,7 +199,7 @@ static int damon_reclaim_apply_parameters(void)if (!monitor_region_start && !monitor_region_end) addr_unit =1 ; param_ctx->addr_unit = addr_unit; - param_ctx->min_sz_region =max (DAMON_MIN_REGION / addr_unit,1 ); + param_ctx->min_sz_region =max (DAMON_MIN_REGION_SZ / addr_unit,1 );if (!damon_reclaim_mon_attrs.aggr_interval) { err = -EINVAL ;
mm/damon/stat.c +1 -1
@@ -178,7 +178,7 @@ static int damon_stat_set_monitoring_region(struct damon_target *t, addr_range.end = arg.res.end +1 ;if (addr_range.end <= addr_range.start)return -EINVAL ; -return damon_set_regions (t, &addr_range,1 ,DAMON_MIN_REGION ); +return damon_set_regions (t, &addr_range,1 ,DAMON_MIN_REGION_SZ ); }static struct damon_ctx *damon_stat_build_ctx (void )
mm/damon/sysfs.c +1 -1
@@ -1451,7 +1451,7 @@ static int damon_sysfs_apply_inputs(struct damon_ctx *ctx,/* addr_unit is respected by only DAMON_OPS_PADDR */ if (ops_id ==DAMON_OPS_PADDR ) ctx->min_sz_region =max ( -DAMON_MIN_REGION / ctx->addr_unit,1 ); +DAMON_MIN_REGION_SZ / ctx->addr_unit,1 ); err =damon_sysfs_set_attrs (ctx, sys_ctx->attrs);if (err)return err;
mm/damon/tests/core-kunit.h +26 -26
114 lines not on this page · raw message
mm/damon/tests/vaddr-kunit.h +1 -1
@@ -147,7 +147,7 @@ static void damon_do_test_apply_three_regions(struct kunit *test,damon_add_region (r, t); } -damon_set_regions (t, three_regions,3 ,DAMON_MIN_REGION ); +damon_set_regions (t, three_regions,3 ,DAMON_MIN_REGION_SZ );KUNIT_EXPECT_EQ (test,damon_nr_regions (t), nr_expected /2 );if (damon_nr_regions (t) != nr_expected /2 )
mm/damon/vaddr.c +12 -12
@@ -19,8 +19,8 @@#include "ops-common.h" #ifdef CONFIG_DAMON_VADDR_KUNIT_TEST -#undef DAMON_MIN_REGION -#define DAMON_MIN_REGION 1 +#undef DAMON_MIN_REGION_SZ +#define DAMON_MIN_REGION_SZ 1#endif /*
@@ -78,7 +78,7 @@ static int damon_va_evenly_split_region(struct damon_target *t, orig_end = r->ar.end; sz_orig =damon_sz_region (r); - sz_piece =ALIGN_DOWN (sz_orig / nr_pieces,DAMON_MIN_REGION ); + sz_piece =ALIGN_DOWN (sz_orig / nr_pieces,DAMON_MIN_REGION_SZ );if (!sz_piece)return -EINVAL ;
@@ -161,12 +161,12 @@ static int __damon_va_three_regions(struct mm_struct *mm,swap (first_gap, second_gap);/* Store the result */ - regions[0 ].start =ALIGN (start,DAMON_MIN_REGION ); - regions[0 ].end =ALIGN (first_gap.start,DAMON_MIN_REGION ); - regions[1 ].start =ALIGN (first_gap.end,DAMON_MIN_REGION ); - regions[1 ].end =ALIGN (second_gap.start,DAMON_MIN_REGION ); - regions[2 ].start =ALIGN (second_gap.end,DAMON_MIN_REGION ); - regions[2 ].end =ALIGN (prev->vm_end,DAMON_MIN_REGION ); + regions[0 ].start =ALIGN (start,DAMON_MIN_REGION_SZ ); + regions[0 ].end =ALIGN (first_gap.start,DAMON_MIN_REGION_SZ ); + regions[1 ].start =ALIGN (first_gap.end,DAMON_MIN_REGION_SZ ); + regions[1 ].end =ALIGN (second_gap.start,DAMON_MIN_REGION_SZ ); + regions[2 ].start =ALIGN (second_gap.end,DAMON_MIN_REGION_SZ ); + regions[2 ].end =ALIGN (prev->vm_end,DAMON_MIN_REGION_SZ );return 0 ; }
@@ -259,8 +259,8 @@ static void __damon_va_init_regions(struct damon_ctx *ctx, sz += regions[i].end - regions[i].start;if (ctx->attrs.min_nr_regions) sz /= ctx->attrs.min_nr_regions; -if (sz <DAMON_MIN_REGION ) - sz =DAMON_MIN_REGION ; +if (sz <DAMON_MIN_REGION_SZ ) + sz =DAMON_MIN_REGION_SZ ;/* Set the initial three regions of the target */ for (i =0 ; i <3 ; i++) {
@@ -299,7 +299,7 @@ static void damon_va_update(struct damon_ctx *ctx)damon_for_each_target (t, ctx) {if (damon_va_three_regions (t, three_regions))continue ; -damon_set_regions (t, three_regions,3 ,DAMON_MIN_REGION ); +damon_set_regions (t, three_regions,3 ,DAMON_MIN_REGION_SZ ); } }
Signature · 1 line
2.53.0
SJ Park
maintainerRe: [PATCH 6.18.y 3/5] mm/damon: rename DAMON_MIN_REGION to DAMON_MIN_REGION_SZ
Hello Sasha,
Quoted 9 lines · On Tue, 29 Sep 2026 20:03:52 -0400 Sasha Levin <[email protected]> wrote:
From: SeongJae Park <[email protected]>
[ Upstream commit
dfb1b0c9dc0d61e422905640e1e7334b3cf6f384]The macro is for the default minimum size of each DAMON region. There was a case that a reader was confused if it is the minimum number of total DAMON regions, which is set on
damon_attrs->min_nr_regions. Make the name more explicit.
Quoted 5 lines
Signed-off-by SeongJae Park <[email protected]>Signed-off-by Andrew Morton <[email protected]>Stable adaptation for
b3723b596b548c837a766aae3553c14a7b15af2b:
Thank you for this work for making DAMON fixes in stable kernels! However, this looks wrong backport to me. Please read below for details.
[...]
Quoted 7 lines
mm/damon/core.c +1
-1@@ -1763,15 +1765,14 @@ static bool damos_skip_charged_region(struct damon_target *t,if (quota->charge_addr_from && r->ar.start < quota->charge_addr_from) { sz_to_skip =ALIGN_DOWN (quota->charge_addr_from - - r->ar.start, min_sz_region); + r->ar.start, min_region_sz);if (!sz_to_skip) {
mm/damon/core.c static bool damos_skip_charged_region(struct damon_target *t, · line 1775
Quoted 10 lines
mm/damon/core.c +3
-4-if (damon_sz_region (r) <= min_sz_region) +if (damon_sz_region (r) <= min_region_sz)return true; - sz_to_skip = min_sz_region; + sz_to_skip = min_region_sz; }damon_split_region_at (t, r, sz_to_skip); - r =damon_next_region (r); - *rp = r; +return true;
In mainline, the above change was introduced with commit 1745ccbd2907d ("mm/damon/core: do non-safe region walk on kdamond_apply_schemes()"). This patch is trying to backport dfb1b0c9dc0d ("mm/damon: rename DAMON_MIN_REGION to DAMON_MIN_REGION_SZ"), so the above change shouldn't be in this patch. Also, seems not all changes in commit 1745ccbd2907d ("mm/damon/core: do non-safe region walk on kdamond_apply_schemes()") are added in this patch. Particualrly, seems this patch is not doing damon_for_each_region_safe() -> damon_for_each_region() change, which can introduce another subtle bug.
Could we use my manual backport [1] instead of this series?
[1] https://lore.kernel.org/[email protected]
Thanks,
SJ
[...]
Sasha Levin
maintainerPATCH 4/5 mm/damon: rename min_sz_region of damon_ctx to min_region_sz
In reply to Sasha LevinFrom: SeongJae Park <[email protected]>
[ Upstream commit cc1db8dff8e751ec3ab352483de366b7f23aefe2 ]
'min_sz_region' field of 'struct damon_ctx' represents the minimum size of each DAMON region for the context. 'struct damos_access_pattern' has a field of the same name. It confuses readers and makes 'grep' less optimal for them. Rename it to 'min_region_sz'.
Stable adaptation for b3723b596b548c837a766aae3553c14a7b15af2b:
Keep the context minimum-region-size rename and update stable-only users in range validation, context validation, and effective quota calculation. The access-pattern min_sz_region field retains its original name.
Drop hunks for the extra minimum-size argument of damon_set_region_biggest_system_ram_default(), which this stable tree has not gained. Retain the existing three-argument helper and callers, and keep stat.c's separate monitoring-region setup unchanged.
Preserve stable's context-commit error handling, sysfs READ_ONCE() snapshots, and the already-adapted direct-region quota helper and region walk. The target's last-region fix remains separate and applies cleanly. No functions are added.
[ sashal: Reduced backport -- upstream cc1db8dff8e75 touches 6 file(s), this
backport carries 5. Not backported here:
mm/damon/stat.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: b3723b596b54 ("mm/damon/core: fix unconditionally skip last region")
--- include/linux/damon.h | 6 ++--- mm/damon/core.c | 62 +++++++++++++++++++++---------------------- mm/damon/lru_sort.c | 2 +- mm/damon/reclaim.c | 2 +- mm/damon/sysfs.c | 9 ++++--- 5 files changed, 41 insertions(+), 40 deletions(-)
include/linux/damon.h +3 -3
@@ -749,7 +749,7 @@ struct damon_attrs {* * @ops: Set of monitoring operations for given use cases. * @addr_unit: Scale factor for core to ops address conversion. -* @min_sz_region: Minimum region size. +* @min_region_sz: Minimum region size. * @adaptive_targets: Head of monitoring targets (&damon_target) list. * @schemes: Head of schemes (&damos) list. */
@@ -799,7 +799,7 @@ struct damon_ctx {struct damon_operations ops;unsigned long addr_unit; -unsigned long min_sz_region; +unsigned long min_region_sz;struct list_head adaptive_targets;struct list_head schemes;
@@ -888,7 +888,7 @@ static inline void damon_insert_region(struct damon_region *r,void damon_add_region (struct damon_region *r,struct damon_target *t);void damon_destroy_region (struct damon_region *r,struct damon_target *t);int damon_set_regions (struct damon_target *t,struct damon_addr_range *ranges, -unsigned int nr_ranges,unsigned long min_sz_region); +unsigned int nr_ranges,unsigned long min_region_sz);void damon_update_region_access_rate (struct damon_region *r,bool accessed,struct damon_attrs *attrs);
mm/damon/core.c +31 -31
214 lines not on this page · raw message
mm/damon/lru_sort.c +1 -1
@@ -203,7 +203,7 @@ static int damon_lru_sort_apply_parameters(void)if (!monitor_region_start && !monitor_region_end) addr_unit =1 ; param_ctx->addr_unit = addr_unit; - param_ctx->min_sz_region =max (DAMON_MIN_REGION_SZ / addr_unit,1 ); + param_ctx->min_region_sz =max (DAMON_MIN_REGION_SZ / addr_unit,1 );if (!damon_lru_sort_mon_attrs.sample_interval) { err = -EINVAL ;
mm/damon/reclaim.c +1 -1
@@ -199,7 +199,7 @@ static int damon_reclaim_apply_parameters(void)if (!monitor_region_start && !monitor_region_end) addr_unit =1 ; param_ctx->addr_unit = addr_unit; - param_ctx->min_sz_region =max (DAMON_MIN_REGION_SZ / addr_unit,1 ); + param_ctx->min_region_sz =max (DAMON_MIN_REGION_SZ / addr_unit,1 );if (!damon_reclaim_mon_attrs.aggr_interval) { err = -EINVAL ;
mm/damon/sysfs.c +5 -4
@@ -1345,7 +1345,7 @@ static int damon_sysfs_set_attrs(struct damon_ctx *ctx,static int damon_sysfs_set_regions (struct damon_target *t,struct damon_sysfs_regions *sysfs_regions, -unsigned long min_sz_region) +unsigned long min_region_sz) {struct damon_addr_range *ranges =kmalloc_array (sysfs_regions->nr,sizeof (*ranges),GFP_KERNEL | __GFP_NOWARN);
@@ -1367,7 +1367,7 @@ static int damon_sysfs_set_regions(struct damon_target *t,if (ranges[i -1 ].end > ranges[i].start) goto out; } - err =damon_set_regions (t, ranges, sysfs_regions->nr, min_sz_region); + err =damon_set_regions (t, ranges, sysfs_regions->nr, min_region_sz); out:kfree (ranges);return err;
@@ -1388,7 +1388,8 @@ static int damon_sysfs_add_target(struct damon_sysfs_target *sys_target,/* caller will destroy targets */ return -EINVAL ; } -return damon_sysfs_set_regions (t, sys_target->regions, ctx->min_sz_region); +return damon_sysfs_set_regions (t, sys_target->regions, + ctx->min_region_sz); } static int damon_sysfs_add_targets(struct damon_ctx *ctx,
@@ -1450,7 +1451,7 @@ static int damon_sysfs_apply_inputs(struct damon_ctx *ctx, ctx->addr_unit =READ_ONCE (sys_ctx->addr_unit);/* addr_unit is respected by only DAMON_OPS_PADDR */ if (ops_id ==DAMON_OPS_PADDR ) - ctx->min_sz_region =max ( + ctx->min_region_sz =max (DAMON_MIN_REGION_SZ / ctx->addr_unit,1 ); err =damon_sysfs_set_attrs (ctx, sys_ctx->attrs);if (err)
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/5 mm/damon/core: fix unconditionally skip last region
In reply to Sasha LevinFrom: Liew Rui Yan <[email protected]>
[ Upstream commit b3723b596b548c837a766aae3553c14a7b15af2b ]
Once quota set, the charge_{target,addr}_from unconditionally skips and resets at the last region of the tracked target, so the last region can be skipped even when it has not been processed.
Example:
1. Target has 2 regions: R1 (0-100 bytes) and R2 (100-200 bytes).
2. Quota is configured to process only 100 bytes per window.
3. Window 1: Processes R1 (0-100). Quota is full. charge_{target, addr}_from is saved at (Target, 100).
4. Window 2: The loop reaches R2. Because R2 is damon_last_region(t), the old code unconditionally returns true, skipping R2 entirely and resetting the charge_{target,addr}_from.
Result: R2 is permanently skipped even though it has never been processed.
However, it is important to note that this is a very minor issue. This is because it is triggered only when the previous window saved/kept charge_{target,addr}_from, and in the next window, all regions except the last region were skipped by damos_skip_charged_region().
Fix this by only resetting the charge_{target,addr}_from when last region is reached, only skipping when it is applied or cannot split.
--- mm/damon/core.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)
mm/damon/core.c +14 -11
@@ -1748,36 +1748,39 @@ static bool damos_skip_charged_region(struct damon_target *t, {struct damos_quota *quota = &s->quota;unsigned long sz_to_skip; +bool skip = false;/* Skip previously charged regions */ if (quota->charge_target_from) {if (t != quota->charge_target_from)return true; -if (r ==damon_last_region (t)) { - quota->charge_target_from =NULL ; - quota->charge_addr_from =0 ; -return true; - }if (quota->charge_addr_from && - r->ar.end <= quota->charge_addr_from) -return true; + r->ar.end <= quota->charge_addr_from) { + skip = true; + goto out; + }if (quota->charge_addr_from && r->ar.start < quota->charge_addr_from) { sz_to_skip =ALIGN_DOWN (quota->charge_addr_from - r->ar.start, min_region_sz);if (!sz_to_skip) { -if (damon_sz_region (r) <= min_region_sz) -return true; +if (damon_sz_region (r) <= min_region_sz) { + skip = true; + goto out; + } sz_to_skip = min_region_sz; }damon_split_region_at (t, r, sz_to_skip); -return true; + skip = true; } + } +out: +if (r ==damon_last_region (t)) { quota->charge_target_from =NULL ; quota->charge_addr_from =0 ; } -return false; +return skip; } static void damos_update_stat(struct damos *s,
Signature · 1 line
2.53.0