mm/damon/core: fix unconditionally skip last region
Subsystems (3)
SJ Park
maintainerPATCH v3 1/1 mm/damon/core: fix unconditionally skip last region
From: Liew Rui Yan <[email protected]>
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 resets the charge_{target,addr}_from when last region is reached, only skips when it applied or cannot split.
---
Changes from v2:
- v2: https://lore.kernel.org/[email protected]
- Collect R-b: from SJ.
Changes from v1:
- Simplify example in commit message.
- Add clarification regarding the severity of the bug to avoid causing users unnecessary afraid.
- Modify the patch code, each time the last region is reached, only charge_{target,addr}_from is reset, but the return value depends on the situation.
- Modify patch's title from 'mm/damon' to 'mm/damon/core', since it only changes core.c.
- v1: https://lore.kernel.org/damon/[email protected]
Changes from RFC v1:
- Minimal fix, only fixes the issue where the last-region is skipped.
- Add an example to the commit message to demonstrate that this error occurs very rarely.
- RFC v1: https://lore.kernel.org/damon/[email protected]
mm/damon/core.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)
mm/damon/core.c +14 -11
@@ -2452,36 +2452,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,
base-commit: ab263dd200a7a9101ce3c1374fd23139f29cdac4
Signature · 1 line
2.47.3
[email protected]
AI reviewThank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Quota tracking pointer charge_target_from can get permanently stuck if its corresponding target becomes invalid, freezing scheme application for all subsequent targets indefinitely. --
commit 4d9a4144cc83637a927f5110bc1cc97f4064d9e8
Author: Liew Rui Yan <[email protected]>
mm/damon/core: fix unconditionally skip last region
This commit modifies damos_skip_charged_region() to only reset the quota's charge_target_from and charge_addr_from when the last region is reached. It ensures that the last region of a tracked target is only skipped if it was actually applied or cannot be split.
Quoted 5 lines
mm/damon/core.c +0
-0
mm/damon/core.c static bool damos_skip_charged_region(struct damon_target *t, · line 2460
Quoted 9 lines
mm/damon/core.c +1
-0@@ -2452,36 +2452,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;
[Severity: High]
This isn't a bug introduced by this patch, but is it possible for quota->charge_target_from to remain stuck indefinitely if its corresponding target becomes invalid?
Looking at kdamond_apply_schemes() in mm/damon/core.c, if a target becomes invalid, it is skipped and damos_apply_target() is never called for it:
damon_for_each_target(t, c) {
if (c->ops.target_valid && c->ops.target_valid(t) == false)
continue;
damos_apply_target(c, t, max_region_sz);
}If the previous quota window ended with quota->charge_target_from pointing to this now-invalid target, wouldn't the 'return true' here in damos_skip_charged_region() cause all regions of all subsequent valid targets to be skipped?
Because the invalid target is never processed, t will never equal quota->charge_target_from again.
Does this mean the pointer is never cleared, freezing scheme application for all targets?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
SJ Park
maintainerQuoted 50 lines · On Tue, 08 Sep 2026 14:07:20 +0000 [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Quota tracking pointercharge_target_fromcan get permanently stuck if its corresponding target becomes invalid, freezing scheme application for all subsequent targets indefinitely. --commit
4d9a4144cc83637a927f5110bc1cc97f4064d9e8
Author: Liew Rui Yan <[email protected]>
mm/damon/core: fix unconditionally skip last regionThis commit modifies
damos_skip_charged_region()to only reset the quota'scharge_target_fromandcharge_addr_fromwhen the last region is reached. It ensures that the last region of a tracked target is only skipped if it was actually applied or cannot be split.Quoted 5 lines
mm/damon/core.c +0
-0mm/damon/core.c static bool damos_skip_charged_region(struct damon_target *t, · line 2460
Quoted 9 lines
mm/damon/core.c +1
-0@@ -2452,36 +2452,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;[Severity: High]
This isn't a bug introduced by this patch, but is it possible forquota->charge_target_fromto remain stuck indefinitely if its corresponding target becomes invalid?Looking at
kdamond_apply_schemes()inmm/damon/core.c, if a target becomes invalid, it is skipped anddamos_apply_target()is never called for it:damon_for_each_target(t, c) { if (c->ops.target_valid && c->ops.target_valid(t) == false) continue; damos_apply_target(c, t, max_region_sz); }If the previous quota window ended with
quota->charge_target_frompointing to this now-invalid target, wouldn't the 'return true' here indamos_skip_charged_region()cause all regions of all subsequent valid targets to be skipped?
Quoted 5 lines
Because the invalid target is never processed, t will never equal
quota->charge_target_fromagain.Does this mean the pointer is never cleared, freezing scheme application for all targets?
I'm working [1] on this issue.
[1] https://lore.kernel.org/[email protected]
Quoted 3 lines
Signature · 2 lines
-- Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Thanks,
SJ
SJ Park
maintainerQuoted 19 lines · On Tue, 8 Sep 2026 06:47:38 -0700 SJ Park <[email protected]> wrote:
From: Liew Rui Yan <[email protected]>
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 isdamon_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.
Quoted 7 lines
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 resets the charge_{target,addr}_from when last region is reached, only skips when it applied or cannot split.
Sashiko found a pre-existing issue that I'm separately working on. It is no blocker for this patch.
Thanks,
SJ
[...]