mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] mm/damon: fix zero quota for PSI goals under the temporal tuner
@ 2026-09-15  6:09 Karl Mehltretter
  2026-09-15  6:09 ` [PATCH 1/2] mm/damon/core: score an unmeasured PSI goal as not achieved for " Karl Mehltretter
  2026-09-15  6:09 ` [PATCH 2/2] mm/damon/tests/core-kunit: test PSI goal rounds under " Karl Mehltretter
  0 siblings, 2 replies; 9+ messages in thread
From: Karl Mehltretter @ 2026-09-15  6:09 UTC (permalink / raw)
  To: SJ Park
  Cc: Karl Mehltretter, Andrew Morton, Lian Wang, Kunwu Chan, damon,
	linux-mm, linux-kernel

A PSI quota goal without a previous sample is scored as achieved.
With the temporal tuner, this sets the quota to zero for the first
charge window and the window after each goal commit.

The series is based on next-20260911. It fixes commit a68878f83ae6
("mm/damon/core: handle uninitialized damos_quota_goal->last_psi_total"),
which is in mm-new and tagged for stable. Please carry this fix with
it.

Patch 1 keeps the temporal quota active until a PSI delta is
available. Patch 2 adds a regression test for the first round, the
next measured round, the round after a goal commit, a measured round
that reaches the target, and the consist tuner's unmeasured round.

Reproduced with damo 3.4.0 (b41965a) in a QEMU guest running
next-20260911 (x86_64, CONFIG_DAMON_SYSFS=y, CONFIG_PSI=y):

  damo start --damos_action stat --damos_quota_interval 120000 \
      --damos_quota_space 1048576 \
      --damos_quota_goal some_mem_psi_us 100000000000 \
      --damos_quota_goal_tuner temporal

then reading quotas/effective_bytes after update_schemes_effective_quotas
every 18 seconds. Without patch 1 the effective quota is 0 for the whole
first 120 second window and again for the window after "damo tune" with
the same parameters. With patch 1 it is 1048576 throughout.

The KUnit test in patch 2 fails without patch 1 and passes with it on
x86_64 and i386 (CONFIG_DAMON_KUNIT_TEST=y, built-in KUnit run at boot).

Karl Mehltretter (2):
  mm/damon/core: score an unmeasured PSI goal as not achieved for the
    temporal tuner
  mm/damon/tests/core-kunit: test PSI goal rounds under the temporal
    tuner

 mm/damon/core.c             | 14 +++++---
 mm/damon/tests/core-kunit.h | 69 +++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+), 4 deletions(-)


base-commit: 68142f986ff04b2b70b31db00f719bf690f64a9a
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/2] mm/damon/core: score an unmeasured PSI goal as not achieved for the temporal tuner
  2026-09-15  6:09 [PATCH 0/2] mm/damon: fix zero quota for PSI goals under the temporal tuner Karl Mehltretter
@ 2026-09-15  6:09 ` Karl Mehltretter
  2026-09-15 14:50   ` SJ Park
  2026-09-15  6:09 ` [PATCH 2/2] mm/damon/tests/core-kunit: test PSI goal rounds under " Karl Mehltretter
  1 sibling, 1 reply; 9+ messages in thread
From: Karl Mehltretter @ 2026-09-15  6:09 UTC (permalink / raw)
  To: SJ Park
  Cc: Karl Mehltretter, Andrew Morton, Lian Wang, Kunwu Chan, damon,
	linux-mm, linux-kernel

A newly created or committed PSI quota goal has no previous sample.
Commit a68878f83ae6 ("mm/damon/core: handle uninitialized
damos_quota_goal->last_psi_total") scores the first tuning round as
achieved to preserve the consist tuner's quota, since the feedback
loop returns its input unchanged at that score.

The temporal tuner sets the quota to zero for an achieved goal. This
skips the first charge window and the window after each goal commit.
Committing the PSI goal before every tuning round keeps the scheme
idle indefinitely.

Score an unmeasured PSI goal as not achieved for the temporal tuner,
so a new scheme can run in its first window. Keep the consist tuner's
behaviour unchanged.

Fixes: a68878f83ae6 ("mm/damon/core: handle uninitialized damos_quota_goal->last_psi_total")
Cc: <stable@vger.kernel.org> # 7.1.x
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
 mm/damon/core.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/mm/damon/core.c b/mm/damon/core.c
index 06cf2ab7e97d..e6d87fd3992e 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -3161,12 +3161,18 @@ static void damos_set_quota_goal_current_value(struct damon_ctx *c,
 		break;
 	case DAMOS_QUOTA_SOME_MEM_PSI_US:
 		now_psi_total = damos_get_some_mem_psi_total();
-		/* uninitialized last_psi_total; make no effect this round */
-		if (goal->last_psi_total == U64_MAX)
-			goal->current_value = goal->target_value;
-		else
+		if (goal->last_psi_total == U64_MAX) {
+			/* uninitialized last_psi_total; make no effect this round */
+			if (s->quota.goal_tuner ==
+					DAMOS_QUOTA_GOAL_TUNER_TEMPORAL)
+				/* an achieved score would zero the temporal quota */
+				goal->current_value = 0;
+			else
+				goal->current_value = goal->target_value;
+		} else {
 			goal->current_value = now_psi_total -
 				goal->last_psi_total;
+		}
 		goal->last_psi_total = now_psi_total;
 		break;
 	case DAMOS_QUOTA_NODE_MEM_USED_BP:
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 2/2] mm/damon/tests/core-kunit: test PSI goal rounds under the temporal tuner
  2026-09-15  6:09 [PATCH 0/2] mm/damon: fix zero quota for PSI goals under the temporal tuner Karl Mehltretter
  2026-09-15  6:09 ` [PATCH 1/2] mm/damon/core: score an unmeasured PSI goal as not achieved for " Karl Mehltretter
@ 2026-09-15  6:09 ` Karl Mehltretter
  2026-09-15  7:04   ` KunWu Chan
  2026-09-16  0:21   ` SJ Park
  1 sibling, 2 replies; 9+ messages in thread
From: Karl Mehltretter @ 2026-09-15  6:09 UTC (permalink / raw)
  To: SJ Park
  Cc: Karl Mehltretter, Andrew Morton, Lian Wang, Kunwu Chan, damon,
	linux-mm, linux-kernel

Exercise a PSI quota goal under the temporal tuner on its first
round, its next measured round, and after a goal commit. Use
ULONG_MAX as the target so the goal is not reached during the test.
Then check that a measured round which does reach the target sets
the quota to zero, and that the consist tuner keeps its quota over
an unmeasured round.

Without the previous patch, the first and the post-commit checks see
a zero quota.

Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
 mm/damon/tests/core-kunit.h | 69 +++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index c01e6a75cadc..2b9b24ce7dad 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -889,6 +889,74 @@ static void damos_test_commit_quota_goal(struct kunit *test)
 			});
 }
 
+/*
+ * Unmeasured PSI goals must not disable the temporal quota.
+ * Keep sz * 10000 within a 32-bit unsigned long.
+ */
+static void damos_test_set_effective_quota_temporal_psi(struct kunit *test)
+{
+	struct damon_ctx *c = damon_new_ctx();
+	struct damos_access_pattern pattern = {};
+	struct damos_quota quota = {
+		.sz = SZ_64K,
+		.goal_tuner = DAMOS_QUOTA_GOAL_TUNER_TEMPORAL,
+	};
+	struct damos_watermarks wmarks = {};
+	struct damos_quota_goal src = {
+		.metric = DAMOS_QUOTA_SOME_MEM_PSI_US,
+		.target_value = ULONG_MAX,
+	};
+	struct damos_quota_goal *goal;
+	struct damos *s;
+
+	if (!c)
+		kunit_skip(test, "ctx alloc fail");
+	s = damon_new_scheme(&pattern, DAMOS_STAT, 0, &quota, &wmarks,
+			NUMA_NO_NODE);
+	if (!s) {
+		damon_destroy_ctx(c);
+		kunit_skip(test, "scheme alloc fail");
+	}
+	damon_add_scheme(c, s);
+	goal = damos_new_quota_goal(DAMOS_QUOTA_SOME_MEM_PSI_US, ULONG_MAX);
+	if (!goal) {
+		damon_destroy_ctx(c);
+		kunit_skip(test, "goal alloc fail");
+	}
+	damos_add_quota_goal(&s->quota, goal);
+
+	/* fresh goal, first tuning round */
+	damos_set_effective_quota(c, s);
+	KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_64K);
+
+	/* second round: last_psi_total is initialised now */
+	damos_set_effective_quota(c, s);
+	KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_64K);
+
+	/* commit a PSI goal onto the initialised PSI goal */
+	damos_commit_quota_goal(goal, &src);
+	damos_set_effective_quota(c, s);
+	KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_64K);
+
+	/* a measured round that reaches the target disables the quota */
+	goal->target_value = 10;
+	goal->last_psi_total = damos_get_some_mem_psi_total() - 10;
+	/* U64_MAX marks an unmeasured goal, keep the sample away from it */
+	if (goal->last_psi_total == U64_MAX)
+		goal->last_psi_total--;
+	damos_set_effective_quota(c, s);
+	KUNIT_EXPECT_EQ(test, s->quota.esz, 0ul);
+
+	/* the consist tuner keeps its quota over an unmeasured round */
+	s->quota.goal_tuner = DAMOS_QUOTA_GOAL_TUNER_CONSIST;
+	s->quota.esz_bp = SZ_32K * 10000;
+	damos_commit_quota_goal(goal, &src);
+	damos_set_effective_quota(c, s);
+	KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_32K);
+
+	damon_destroy_ctx(c);
+}
+
 static void damos_test_commit_quota_goals_for(struct kunit *test,
 		struct damos_quota_goal *dst_goals, int nr_dst_goals,
 		struct damos_quota_goal *src_goals, int nr_src_goals)
@@ -1887,6 +1955,7 @@ static struct kunit_case damon_test_cases[] = {
 	KUNIT_CASE(damos_test_new_filter),
 	KUNIT_CASE(damos_test_commit_quota_goal),
 	KUNIT_CASE(damos_test_commit_quota_goals),
+	KUNIT_CASE(damos_test_set_effective_quota_temporal_psi),
 	KUNIT_CASE(damos_test_commit_quota),
 	KUNIT_CASE(damos_test_commit_dests),
 	KUNIT_CASE(damos_test_commit_filter),
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2] mm/damon/tests/core-kunit: test PSI goal rounds under the temporal tuner
  2026-09-15  6:09 ` [PATCH 2/2] mm/damon/tests/core-kunit: test PSI goal rounds under " Karl Mehltretter
@ 2026-09-15  7:04   ` KunWu Chan
  2026-09-15 21:31     ` Karl Mehltretter
  2026-09-16  0:21   ` SJ Park
  1 sibling, 1 reply; 9+ messages in thread
From: KunWu Chan @ 2026-09-15  7:04 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: SJ Park, Andrew Morton, Lian Wang, damon, linux-mm, linux-kernel

On Tue, Sep 15, 2026 at 2:09 PM Karl Mehltretter <kmehltretter@gmail.com> wrote:
>
> Exercise a PSI quota goal under the temporal tuner on its first
> round, its next measured round, and after a goal commit. Use
> ULONG_MAX as the target so the goal is not reached during the test.
> Then check that a measured round which does reach the target sets
> the quota to zero, and that the consist tuner keeps its quota over
> an unmeasured round.
>
> Without the previous patch, the first and the post-commit checks see
> a zero quota.
>
> Assisted-by: LLM
> Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
> ---
>  mm/damon/tests/core-kunit.h | 69 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 69 insertions(+)
>
> diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
> index c01e6a75cadc..2b9b24ce7dad 100644
> --- a/mm/damon/tests/core-kunit.h
> +++ b/mm/damon/tests/core-kunit.h
> @@ -889,6 +889,74 @@ static void damos_test_commit_quota_goal(struct kunit *test)
>                         });
>  }
>
> +/*
> + * Unmeasured PSI goals must not disable the temporal quota.
> + * Keep sz * 10000 within a 32-bit unsigned long.
> + */
> +static void damos_test_set_effective_quota_temporal_psi(struct kunit *test)
> +{
> +       struct damon_ctx *c = damon_new_ctx();
> +       struct damos_access_pattern pattern = {};
> +       struct damos_quota quota = {
> +               .sz = SZ_64K,
> +               .goal_tuner = DAMOS_QUOTA_GOAL_TUNER_TEMPORAL,
> +       };
> +       struct damos_watermarks wmarks = {};
> +       struct damos_quota_goal src = {
> +               .metric = DAMOS_QUOTA_SOME_MEM_PSI_US,
> +               .target_value = ULONG_MAX,
> +       };
> +       struct damos_quota_goal *goal;
> +       struct damos *s;
> +
> +       if (!c)
> +               kunit_skip(test, "ctx alloc fail");
> +       s = damon_new_scheme(&pattern, DAMOS_STAT, 0, &quota, &wmarks,
> +                       NUMA_NO_NODE);
> +       if (!s) {
> +               damon_destroy_ctx(c);
> +               kunit_skip(test, "scheme alloc fail");
> +       }
> +       damon_add_scheme(c, s);
> +       goal = damos_new_quota_goal(DAMOS_QUOTA_SOME_MEM_PSI_US, ULONG_MAX);
> +       if (!goal) {
> +               damon_destroy_ctx(c);
> +               kunit_skip(test, "goal alloc fail");
> +       }
> +       damos_add_quota_goal(&s->quota, goal);
> +
> +       /* fresh goal, first tuning round */
> +       damos_set_effective_quota(c, s);
> +       KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_64K);
> +
> +       /* second round: last_psi_total is initialised now */

Could we also check goal->last_psi_total here?

The first tuning round should consume the U64_MAX sentinel
and establish the measured state.

Testing this explicitly would lock in that state transition.

Thanks,
Kunwu


> +       damos_set_effective_quota(c, s);
> +       KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_64K);
> +
> +       /* commit a PSI goal onto the initialised PSI goal */
> +       damos_commit_quota_goal(goal, &src);
> +       damos_set_effective_quota(c, s);
> +       KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_64K);
> +
> +       /* a measured round that reaches the target disables the quota */
> +       goal->target_value = 10;
> +       goal->last_psi_total = damos_get_some_mem_psi_total() - 10;
> +       /* U64_MAX marks an unmeasured goal, keep the sample away from it */
> +       if (goal->last_psi_total == U64_MAX)
> +               goal->last_psi_total--;
> +       damos_set_effective_quota(c, s);
> +       KUNIT_EXPECT_EQ(test, s->quota.esz, 0ul);
> +
> +       /* the consist tuner keeps its quota over an unmeasured round */
> +       s->quota.goal_tuner = DAMOS_QUOTA_GOAL_TUNER_CONSIST;
> +       s->quota.esz_bp = SZ_32K * 10000;
> +       damos_commit_quota_goal(goal, &src);
> +       damos_set_effective_quota(c, s);
> +       KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_32K);
> +
> +       damon_destroy_ctx(c);
> +}
> +
>  static void damos_test_commit_quota_goals_for(struct kunit *test,
>                 struct damos_quota_goal *dst_goals, int nr_dst_goals,
>                 struct damos_quota_goal *src_goals, int nr_src_goals)
> @@ -1887,6 +1955,7 @@ static struct kunit_case damon_test_cases[] = {
>         KUNIT_CASE(damos_test_new_filter),
>         KUNIT_CASE(damos_test_commit_quota_goal),
>         KUNIT_CASE(damos_test_commit_quota_goals),
> +       KUNIT_CASE(damos_test_set_effective_quota_temporal_psi),
>         KUNIT_CASE(damos_test_commit_quota),
>         KUNIT_CASE(damos_test_commit_dests),
>         KUNIT_CASE(damos_test_commit_filter),
> --
> 2.53.0
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] mm/damon/core: score an unmeasured PSI goal as not achieved for the temporal tuner
  2026-09-15  6:09 ` [PATCH 1/2] mm/damon/core: score an unmeasured PSI goal as not achieved for " Karl Mehltretter
@ 2026-09-15 14:50   ` SJ Park
  2026-09-15 21:19     ` Karl Mehltretter
  0 siblings, 1 reply; 9+ messages in thread
From: SJ Park @ 2026-09-15 14:50 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: SJ Park, Andrew Morton, Lian Wang, Kunwu Chan, damon, linux-mm,
	linux-kernel

Hello Karl,

On Tue, 15 Sep 2026 08:09:36 +0200 Karl Mehltretter <kmehltretter@gmail.com> wrote:

> A newly created or committed PSI quota goal has no previous sample.
> Commit a68878f83ae6 ("mm/damon/core: handle uninitialized
> damos_quota_goal->last_psi_total") scores the first tuning round as
> achieved to preserve the consist tuner's quota, since the feedback
> loop returns its input unchanged at that score.
> 
> The temporal tuner sets the quota to zero for an achieved goal. This
> skips the first charge window and the window after each goal commit.
> Committing the PSI goal before every tuning round keeps the scheme
> idle indefinitely.
> 
> Score an unmeasured PSI goal as not achieved for the temporal tuner,
> so a new scheme can run in its first window. Keep the consist tuner's
> behaviour unchanged.

Good catch, thank you.

> 
> Fixes: a68878f83ae6 ("mm/damon/core: handle uninitialized damos_quota_goal->last_psi_total")
> Cc: <stable@vger.kernel.org> # 7.1.x
> Assisted-by: LLM
> Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
> ---
>  mm/damon/core.c | 14 ++++++++++----
>  1 file changed, 10 insertions(+), 4 deletions(-)
> 
> diff --git a/mm/damon/core.c b/mm/damon/core.c
> index 06cf2ab7e97d..e6d87fd3992e 100644
> --- a/mm/damon/core.c
> +++ b/mm/damon/core.c
> @@ -3161,12 +3161,18 @@ static void damos_set_quota_goal_current_value(struct damon_ctx *c,
>  		break;
>  	case DAMOS_QUOTA_SOME_MEM_PSI_US:
>  		now_psi_total = damos_get_some_mem_psi_total();
> -		/* uninitialized last_psi_total; make no effect this round */
> -		if (goal->last_psi_total == U64_MAX)
> -			goal->current_value = goal->target_value;
> -		else
> +		if (goal->last_psi_total == U64_MAX) {
> +			/* uninitialized last_psi_total; make no effect this round */
> +			if (s->quota.goal_tuner ==
> +					DAMOS_QUOTA_GOAL_TUNER_TEMPORAL)
> +				/* an achieved score would zero the temporal quota */
> +				goal->current_value = 0;
> +			else
> +				goal->current_value = goal->target_value;
> +		} else {

The intention is to make no effect this round.  Setting current_value to zero
to temporal tuner means it will now have highest quota it could have.  If the
effective quota before this was zero, it gets an effect.

Ideally, we should somehow remember what was the last esz and keep it.  I have
no good idea for doing that with minimum change.  This is a corner case in my
opinion (correct me if I'm wrong) so I want to keep the change as simple as
possible.

If there is not easy way to do that, I think just keeping the behavior but
making it explicitly explained might be better.

What do you think, Karl?


Thanks,
SJ

[...]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] mm/damon/core: score an unmeasured PSI goal as not achieved for the temporal tuner
  2026-09-15 14:50   ` SJ Park
@ 2026-09-15 21:19     ` Karl Mehltretter
  2026-09-16  0:13       ` SJ Park
  0 siblings, 1 reply; 9+ messages in thread
From: Karl Mehltretter @ 2026-09-15 21:19 UTC (permalink / raw)
  To: SJ Park
  Cc: Andrew Morton, Lian Wang, Kunwu Chan, damon, linux-mm, linux-kernel

On Tue, Sep 15, 2026 at 07:50:54AM +0100, SJ Park wrote:
> 
> The intention is to make no effect this round.  Setting current_value to zero
> to temporal tuner means it will now have highest quota it could have.  If the
> effective quota before this was zero, it gets an effect.
> 
> Ideally, we should somehow remember what was the last esz and keep it.  I have
> no good idea for doing that with minimum change.  This is a corner case in my
> opinion (correct me if I'm wrong) so I want to keep the change as simple as
> possible.
> 
> If there is not easy way to do that, I think just keeping the behavior but
> making it explicitly explained might be better.
> 

Hello SJ,

I believe I have a version that addresses your concern without adding
too much complexity.

In short, it leaves the tuners as they are and instead keeps
last_psi_total across a commit of an existing PSI goal, as the code
did before a68878f83ae6. The U64_MAX round then happens only once per
new goal.

That also covers anything that commits before every tuning round,
like the user_input feedback path or "damo tune", which currently
never gets a measured round on either tuner.

I will test that and send it as v2.

Thanks,
Karl

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2] mm/damon/tests/core-kunit: test PSI goal rounds under the temporal tuner
  2026-09-15  7:04   ` KunWu Chan
@ 2026-09-15 21:31     ` Karl Mehltretter
  0 siblings, 0 replies; 9+ messages in thread
From: Karl Mehltretter @ 2026-09-15 21:31 UTC (permalink / raw)
  To: KunWu Chan
  Cc: SJ Park, Andrew Morton, Lian Wang, damon, linux-mm, linux-kernel

On Tue, Sep 15, 2026 at 03:04:06PM +0100, KunWu Chan wrote:
> Could we also check goal->last_psi_total here?
> 
> The first tuning round should consume the U64_MAX sentinel
> and establish the measured state.
> 
> Testing this explicitly would lock in that state transition.
> 

Thanks for the review, good idea! The test only inferred the state
from the effective quota, a direct check of last_psi_total is clearer
and stricter.

It also fits the v2 fix. I plan to keep last_psi_total across a
commit of an existing PSI goal instead of touching the tuners, so
that value is what the test should assert.

For the v2 test I plan to check last_psi_total at each transition:
not U64_MAX after the first round, still the kept sample after a
commit of the same metric, U64_MAX again after a commit that changes
the metric and back. The quota checks stay to cover the tuners.

Karl

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] mm/damon/core: score an unmeasured PSI goal as not achieved for the temporal tuner
  2026-09-15 21:19     ` Karl Mehltretter
@ 2026-09-16  0:13       ` SJ Park
  0 siblings, 0 replies; 9+ messages in thread
From: SJ Park @ 2026-09-16  0:13 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: SJ Park, Andrew Morton, Lian Wang, Kunwu Chan, damon, linux-mm,
	linux-kernel

On Tue, 15 Sep 2026 23:19:27 +0200 Karl Mehltretter <kmehltretter@gmail.com> wrote:

> On Tue, Sep 15, 2026 at 07:50:54AM +0100, SJ Park wrote:
> > 
> > The intention is to make no effect this round.  Setting current_value to zero
> > to temporal tuner means it will now have highest quota it could have.  If the
> > effective quota before this was zero, it gets an effect.
> > 
> > Ideally, we should somehow remember what was the last esz and keep it.  I have
> > no good idea for doing that with minimum change.  This is a corner case in my
> > opinion (correct me if I'm wrong) so I want to keep the change as simple as
> > possible.
> > 
> > If there is not easy way to do that, I think just keeping the behavior but
> > making it explicitly explained might be better.
> > 
> 
> Hello SJ,
> 
> I believe I have a version that addresses your concern without adding
> too much complexity.

Thank you for keep pursuing on improving DAMON, Karl!

> 
> In short, it leaves the tuners as they are and instead keeps
> last_psi_total across a commit of an existing PSI goal, as the code
> did before a68878f83ae6. The U64_MAX round then happens only once per
> new goal.

I actually considered this option when working on commit a68878f83ae6.  I had
two following concerns though.  First, if the user commits multiple times
before the next quota reset interval, the next tuning round will work with
pressure times that cumulated for longer than the quota reset interval.  This
is a quite rare corner case, but I didn't feel that comfortable.  Second, the
behavior for new commit and update commit is different.

So I'd still prefer to just making no effect this round.  And my commit was
failed at doing that, because it didn't aware of temporal tuner.  Maybe we
could show whether the goal was achieved or not, using esz and let the tuner
show same achieveness?  What about something like below?

'''
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -3187,6 +3187,25 @@ static inline u64 damos_get_some_mem_psi_total(void)

 #endif /* CONFIG_PSI */

+static void damos_set_psi_current_val(u64 now_psi_total, struct
+               damos_quota_goal *goal, struct damos *s)
+{
+       if (goal->last_psi_total != U64_MAX) {
+               goal->current_value = now_psi_total - goal->last_psi_total;
+               return;
+       }
+       /* Uninitialized last_psi_total; make no effect this round */
+       if (s->quota.goal_tuner == DAMOS_QUOTA_GOAL_TUNER_CONSIST) {
+               goal->current_value = goal->target_value;
+               return;
+       }
+       /* Let temporal tuner show goal achieveness same to the last round */
+       if (!s->quota.esz)
+               goal->current_value = goal->target_value;
+       else
+               goal->current_value = 0;
+}
+
 #ifdef CONFIG_NUMA
 static bool invalid_mem_node(int nid)
 {
@@ -3439,12 +3458,7 @@ static void damos_set_quota_goal_current_value(struct damon_ctx *c,
                break;
        case DAMOS_QUOTA_SOME_MEM_PSI_US:
                now_psi_total = damos_get_some_mem_psi_total();
-               /* uninitialized last_psi_total; make no effect this round */
-               if (goal->last_psi_total == U64_MAX)
-                       goal->current_value = goal->target_value;
-               else
-                       goal->current_value = now_psi_total -
-                               goal->last_psi_total;
+               damos_set_psi_current_val(now_psi_total, goal, s);
                goal->last_psi_total = now_psi_total;
                break;
        case DAMOS_QUOTA_NODE_MEM_USED_BP:
'''

The code could further cleaned and optimized, but hopefully that will give you
my rough idea.

What do you think?


Thanks,
SJ

[...]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2] mm/damon/tests/core-kunit: test PSI goal rounds under the temporal tuner
  2026-09-15  6:09 ` [PATCH 2/2] mm/damon/tests/core-kunit: test PSI goal rounds under " Karl Mehltretter
  2026-09-15  7:04   ` KunWu Chan
@ 2026-09-16  0:21   ` SJ Park
  1 sibling, 0 replies; 9+ messages in thread
From: SJ Park @ 2026-09-16  0:21 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: SJ Park, Andrew Morton, Lian Wang, Kunwu Chan, damon, linux-mm,
	linux-kernel

On Tue, 15 Sep 2026 08:09:37 +0200 Karl Mehltretter <kmehltretter@gmail.com> wrote:

> Exercise a PSI quota goal under the temporal tuner on its first
> round, its next measured round, and after a goal commit. Use
> ULONG_MAX as the target so the goal is not reached during the test.
> Then check that a measured round which does reach the target sets
> the quota to zero, and that the consist tuner keeps its quota over
> an unmeasured round.
> 
> Without the previous patch, the first and the post-commit checks see
> a zero quota.

Thank you for adding this test.

> 
> Assisted-by: LLM
> Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
> ---
>  mm/damon/tests/core-kunit.h | 69 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 69 insertions(+)
> 
> diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
> index c01e6a75cadc..2b9b24ce7dad 100644
> --- a/mm/damon/tests/core-kunit.h
> +++ b/mm/damon/tests/core-kunit.h
> @@ -889,6 +889,74 @@ static void damos_test_commit_quota_goal(struct kunit *test)
>  			});
>  }
>  
> +/*
> + * Unmeasured PSI goals must not disable the temporal quota.
> + * Keep sz * 10000 within a 32-bit unsigned long.
> + */
> +static void damos_test_set_effective_quota_temporal_psi(struct kunit *test)
> +{
	[...]
> +	/* fresh goal, first tuning round */
> +	damos_set_effective_quota(c, s);
> +	KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_64K);
> +
> +	/* second round: last_psi_total is initialised now */
> +	damos_set_effective_quota(c, s);
> +	KUNIT_EXPECT_EQ(test, s->quota.esz, (unsigned long)SZ_64K);

So, this test depends on the memory pressure level of the testing system?  I
concern if that could make the test result not very reliable.  Maybe we could
split out the corner case handling logic to explicitly receive the current
memory PSI total time.  For example, like what I suggested in the reply to the
first patch of this series.  With it, we could build a kunit test for only the
target logic, without depending on the test system's status.


Thanks,
SJ

[...]

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-09-16  0:21 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15  6:09 [PATCH 0/2] mm/damon: fix zero quota for PSI goals under the temporal tuner Karl Mehltretter
2026-09-15  6:09 ` [PATCH 1/2] mm/damon/core: score an unmeasured PSI goal as not achieved for " Karl Mehltretter
2026-09-15 14:50   ` SJ Park
2026-09-15 21:19     ` Karl Mehltretter
2026-09-16  0:13       ` SJ Park
2026-09-15  6:09 ` [PATCH 2/2] mm/damon/tests/core-kunit: test PSI goal rounds under " Karl Mehltretter
2026-09-15  7:04   ` KunWu Chan
2026-09-15 21:31     ` Karl Mehltretter
2026-09-16  0:21   ` 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®