* [PATCH 6.6.y v3 0/2] mm/damon/core: fix last region handling of damos_skip_charged_regions()
[not found] <2026092948-moonrise-persecute-3597@gregkh>
@ 2026-10-09 13:49 ` SJ Park
2026-10-09 13:49 ` [PATCH 6.6.y v3 1/2] mm/damon/core: do non-safe region walk on kdamond_apply_schemes() SJ Park
2026-10-09 13:49 ` [PATCH 6.6.y v3 2/2] mm/damon/core: fix unconditionally skip last region SJ Park
0 siblings, 2 replies; 3+ messages in thread
From: SJ Park @ 2026-10-09 13:49 UTC (permalink / raw)
To: stable; +Cc: damon, SJ Park, Andrew Morton, linux-kernel, linux-mm
Patch 1 is a dependency of patch 2. Without it, patch 2 introduces
out-of-bounds memory access bug that was found by Sashiko. Patch 2
fixes a bug that categorized to be backported to stable@.
Changes from v2
- v2: https://lore.kernel.org/20260930101853.58786-1-sj@kernel.org
- Fix out-of-bounds memory access bug by adding patch 1.
Changes from v1
- v1: https://lore.kernel.org/20260930093707.48739-1-sj@kernel.org
- Add missed damon_sz_region(r) <= min_region_sz case change.
Liew Rui Yan (1):
mm/damon/core: fix unconditionally skip last region
SeongJae Park (1):
mm/damon/core: do non-safe region walk on kdamond_apply_schemes()
mm/damon/core.c | 45 ++++++++++++++++++++++++---------------------
1 file changed, 24 insertions(+), 21 deletions(-)
base-commit: ae7bc7c9b4336d7a8fb4bcfe0a6d8c925bde3ce9
--
2.47.3
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 6.6.y v3 1/2] mm/damon/core: do non-safe region walk on kdamond_apply_schemes()
2026-10-09 13:49 ` [PATCH 6.6.y v3 0/2] mm/damon/core: fix last region handling of damos_skip_charged_regions() SJ Park
@ 2026-10-09 13:49 ` SJ Park
2026-10-09 13:49 ` [PATCH 6.6.y v3 2/2] mm/damon/core: fix unconditionally skip last region SJ Park
1 sibling, 0 replies; 3+ messages in thread
From: SJ Park @ 2026-10-09 13:49 UTC (permalink / raw)
To: stable; +Cc: damon, SeongJae Park, Andrew Morton, linux-kernel, linux-mm
From: SeongJae Park <sj@kernel.org>
kdamond_apply_schemes() is using damon_for_each_region_safe(), which is
safe for deallocation of the region inside the loop. However, the loop
internal logic does not deallocate regions. Hence it is only wasting the
next pointer. Also, it causes a problem.
When an address filter is applied, and there is a region that intersects
with the filter, the filter splits the region on the filter boundary. The
intention is to let DAMOS apply action to only filtered-in address ranges.
However, it is using damon_for_each_region_safe(), which sets the next
region before the execution of the iteration. Hence, the region that
split and now will be next to the previous region, is simply ignored. As
a result, DAMOS applies the action to target regions bit slower than
expected, when the address filter is used. Shouldn't be a big problem but
definitely better to be fixed. damos_skip_charged_region() was working
around the issue using a double pointer hack.
Use damon_for_each_region(), which is safe for this use case. And drop
the work around in damos_skip_charged_region().
Link: https://lkml.kernel.org/r/20260227170623.95384-3-sj@kernel.org
Signed-off-by: SeongJae Park <sj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
(cherry picked from commit 1745ccbd2907db2bdaa843e4abccde4fdaccbe5d)
Signed-off-by: SJ Park <sj@kernel.org>
---
mm/damon/core.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index fe91b296f4c6..c86cac855b9b 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -871,16 +871,17 @@ static bool damos_valid_target(struct damon_ctx *c, struct damon_target *t,
* This function checks if a given region should be skipped or not for the
* reason. If only the starting part of the region has previously charged,
* this function splits the region into two so that the second one covers the
- * area that not charged in the previous charge widnow and saves the second
- * region in *rp and returns false, so that the caller can apply DAMON action
- * to the second one.
+ * area that not charged in the previous charge widnow, and return true. The
+ * caller can see the second one on the next iteration of the region walk.
+ * Note that this means the caller should use damon_for_each_region() instead
+ * of damon_for_each_region_safe(). If damon_for_each_region_safe() is used,
+ * the second region will just be ignored.
*
- * Return: true if the region should be entirely skipped, false otherwise.
+ * Return: true if the region should be skipped, false otherwise.
*/
static bool damos_skip_charged_region(struct damon_target *t,
- struct damon_region **rp, struct damos *s)
+ struct damon_region *r, struct damos *s)
{
- struct damon_region *r = *rp;
struct damos_quota *quota = &s->quota;
unsigned long sz_to_skip;
@@ -907,8 +908,7 @@ static bool damos_skip_charged_region(struct damon_target *t,
sz_to_skip = DAMON_MIN_REGION;
}
damon_split_region_at(t, r, sz_to_skip);
- r = damon_next_region(r);
- *rp = r;
+ return true;
}
quota->charge_target_from = NULL;
quota->charge_addr_from = 0;
@@ -1045,7 +1045,7 @@ static void damon_do_apply_schemes(struct damon_ctx *c,
if (quota->esz && quota->charged_sz >= quota->esz)
continue;
- if (damos_skip_charged_region(t, &r, s))
+ if (damos_skip_charged_region(t, r, s))
continue;
if (!damos_valid_target(c, t, r, s))
@@ -1134,7 +1134,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s)
static void kdamond_apply_schemes(struct damon_ctx *c)
{
struct damon_target *t;
- struct damon_region *r, *next_r;
+ struct damon_region *r;
struct damos *s;
unsigned long sample_interval = c->attrs.sample_interval ?
c->attrs.sample_interval : 1;
@@ -1156,7 +1156,7 @@ static void kdamond_apply_schemes(struct damon_ctx *c)
return;
damon_for_each_target(t, c) {
- damon_for_each_region_safe(r, next_r, t)
+ damon_for_each_region(r, t)
damon_do_apply_schemes(c, t, r);
}
--
2.47.3
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 6.6.y v3 2/2] mm/damon/core: fix unconditionally skip last region
2026-10-09 13:49 ` [PATCH 6.6.y v3 0/2] mm/damon/core: fix last region handling of damos_skip_charged_regions() SJ Park
2026-10-09 13:49 ` [PATCH 6.6.y v3 1/2] mm/damon/core: do non-safe region walk on kdamond_apply_schemes() SJ Park
@ 2026-10-09 13:49 ` SJ Park
1 sibling, 0 replies; 3+ messages in thread
From: SJ Park @ 2026-10-09 13:49 UTC (permalink / raw)
To: stable
Cc: damon, Liew Rui Yan, Andrew Morton, SeongJae Park, linux-kernel,
linux-mm
From: Liew Rui Yan <aethernet65535@gmail.com>
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.
Link: https://lore.kernel.org/20260908134739.96919-1-sj@kernel.org
Fixes: 50585192bc2e ("mm/damon/schemes: skip already charged targets and regions")
Signed-off-by: Liew Rui Yan <aethernet65535@gmail.com>
Reviewed-by: SJ Park <sj@kernel.org>
Signed-off-by: SJ Park <sj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: <stable@vger.kernel.org> # v5.16.x
(cherry picked from commit b3723b596b548c837a766aae3553c14a7b15af2b)
Signed-off-by: SJ Park <sj@kernel.org>
---
mm/damon/core.c | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index c86cac855b9b..c459eae4bbae 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -884,36 +884,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, DAMON_MIN_REGION);
if (!sz_to_skip) {
- if (damon_sz_region(r) <= DAMON_MIN_REGION)
- return true;
+ if (damon_sz_region(r) <= DAMON_MIN_REGION) {
+ skip = true;
+ goto out;
+ }
sz_to_skip = DAMON_MIN_REGION;
}
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,
--
2.47.3
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-09 13:50 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <2026092948-moonrise-persecute-3597@gregkh>
2026-10-09 13:49 ` [PATCH 6.6.y v3 0/2] mm/damon/core: fix last region handling of damos_skip_charged_regions() SJ Park
2026-10-09 13:49 ` [PATCH 6.6.y v3 1/2] mm/damon/core: do non-safe region walk on kdamond_apply_schemes() SJ Park
2026-10-09 13:49 ` [PATCH 6.6.y v3 2/2] mm/damon/core: fix unconditionally skip last region SJ Park
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®