linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: SeongJae Park <sj@kernel.org>
Cc: SeongJae Park <sj@kernel.org>,
	Andrew Morton <akpm@linux-foundation.org>,
	damon@lists.linux.dev, kernel-team@meta.com,
	linux-kernel@vger.kernel.org, linux-mm@kvack.org
Subject: [RFC PATCH 03/13] mm/damon/core: make damon_set_attrs() be safe to be called from damon_call()
Date: Tue, 25 Feb 2025 22:36:41 -0800	[thread overview]
Message-ID: <20250226063651.513178-4-sj@kernel.org> (raw)
In-Reply-To: <20250226063651.513178-1-sj@kernel.org>

Currently all DAMON kernel API callers do online DAMON parameters commit
from damon_callback->after_aggregation because only those are safe place
to call the DAMON monitoring attributes update function, namely
damon_set_attrs().

Because damon_callback hooks provides no synchronization, the callers
works in asynchronous ways or implement their own inefficient and
complicated synchronization mechanisms.  It also means online DAMON
parameters commit can take up to one aggregation interval.  On large
systems having long aggregation intervals, that can be too slow.  The
synchronization can be done in more efficient and simple way while
removing the latency constraint if it can be done using damon_call().

The fact that damon_call() can be executed in the middle of the
aggregation makes damon_set_attrs() unsafe to be called from it, though.
Two real problems can occur in the case.  First, converting the not yet
completely aggregated nr_accesses for new user-set intervals can
arguably degrade the accuracy or at least make the logic complicated.
Second, kdamond_reset_aggregated() will not be called after the
monitoring results update, so next aggregation starts from unclean
state.  This can result in inconsistent and unexpected nr_accesses.

Make it safe as follows.  Catch the middle-of-the-aggregation case from
damon_set_attrs() and pass the information to nr_accesses conversion
logic.  The logic works as before if this is not the case (called after
the current aggregation is completed).  If not, it drops the nr_accesses
information that so far aggregated, and make the status same to the
beginning of this aggregation, but as if the last aggregation was ran
with the updated sampling/aggregation intervals.

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 mm/damon/core.c             | 38 ++++++++++++++++++++++++++-----------
 mm/damon/tests/core-kunit.h |  6 +++---
 2 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/mm/damon/core.c b/mm/damon/core.c
index 0578e89dff13..5b807caaec95 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -602,11 +602,25 @@ static unsigned int damon_nr_accesses_for_new_attrs(unsigned int nr_accesses,
 }
 
 static void damon_update_monitoring_result(struct damon_region *r,
-		struct damon_attrs *old_attrs, struct damon_attrs *new_attrs)
+		struct damon_attrs *old_attrs, struct damon_attrs *new_attrs,
+		bool aggregating)
 {
-	r->nr_accesses = damon_nr_accesses_for_new_attrs(r->nr_accesses,
-			old_attrs, new_attrs);
-	r->nr_accesses_bp = r->nr_accesses * 10000;
+	if (!aggregating) {
+		r->nr_accesses = damon_nr_accesses_for_new_attrs(
+				r->nr_accesses, old_attrs, new_attrs);
+		r->nr_accesses_bp = r->nr_accesses * 10000;
+	} else {
+		/*
+		 * if this is called in the middle of the aggregation, reset
+		 * the aggregations we made so far for this aggregation
+		 * interval.  In other words, make the status like
+		 * kdamond_reset_aggregated() is called.
+		 */
+		r->last_nr_accesses = damon_nr_accesses_for_new_attrs(
+				r->last_nr_accesses, old_attrs, new_attrs);
+		r->nr_accesses_bp = r->last_nr_accesses * 10000;
+		r->nr_accesses = 0;
+	}
 	r->age = damon_age_for_new_attrs(r->age, old_attrs, new_attrs);
 }
 
@@ -619,7 +633,7 @@ static void damon_update_monitoring_result(struct damon_region *r,
  * ->nr_accesses and ->age of given damon_ctx's regions for new damon_attrs.
  */
 static void damon_update_monitoring_results(struct damon_ctx *ctx,
-		struct damon_attrs *new_attrs)
+		struct damon_attrs *new_attrs, bool aggregating)
 {
 	struct damon_attrs *old_attrs = &ctx->attrs;
 	struct damon_target *t;
@@ -634,7 +648,7 @@ static void damon_update_monitoring_results(struct damon_ctx *ctx,
 	damon_for_each_target(t, ctx)
 		damon_for_each_region(r, t)
 			damon_update_monitoring_result(
-					r, old_attrs, new_attrs);
+					r, old_attrs, new_attrs, aggregating);
 }
 
 /*
@@ -661,10 +675,10 @@ static bool damon_valid_intervals_goal(struct damon_attrs *attrs)
  * @ctx:		monitoring context
  * @attrs:		monitoring attributes
  *
- * This function should be called while the kdamond is not running, or an
- * access check results aggregation is not ongoing (e.g., from
- * &struct damon_callback->after_aggregation or
- * &struct damon_callback->after_wmarks_check callbacks).
+ * This function should be called while the kdamond is not running, an access
+ * check results aggregation is not ongoing (e.g., from &struct
+ * damon_callback->after_aggregation or &struct
+ * damon_callback->after_wmarks_check callbacks), or from damon_call().
  *
  * Every time interval is in micro-seconds.
  *
@@ -675,6 +689,8 @@ int damon_set_attrs(struct damon_ctx *ctx, struct damon_attrs *attrs)
 	unsigned long sample_interval = attrs->sample_interval ?
 		attrs->sample_interval : 1;
 	struct damos *s;
+	bool aggregating = ctx->passed_sample_intervals <
+		ctx->next_aggregation_sis;
 
 	if (!damon_valid_intervals_goal(attrs))
 		return -EINVAL;
@@ -695,7 +711,7 @@ int damon_set_attrs(struct damon_ctx *ctx, struct damon_attrs *attrs)
 	ctx->next_ops_update_sis = ctx->passed_sample_intervals +
 		attrs->ops_update_interval / sample_interval;
 
-	damon_update_monitoring_results(ctx, attrs);
+	damon_update_monitoring_results(ctx, attrs, aggregating);
 	ctx->attrs = *attrs;
 
 	damon_for_each_scheme(s, ctx)
diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index 532c6a6f21f9..be0fea9ee5fc 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -348,19 +348,19 @@ static void damon_test_update_monitoring_result(struct kunit *test)
 
 	new_attrs = (struct damon_attrs){
 		.sample_interval = 100, .aggr_interval = 10000,};
-	damon_update_monitoring_result(r, &old_attrs, &new_attrs);
+	damon_update_monitoring_result(r, &old_attrs, &new_attrs, false);
 	KUNIT_EXPECT_EQ(test, r->nr_accesses, 15);
 	KUNIT_EXPECT_EQ(test, r->age, 2);
 
 	new_attrs = (struct damon_attrs){
 		.sample_interval = 1, .aggr_interval = 1000};
-	damon_update_monitoring_result(r, &old_attrs, &new_attrs);
+	damon_update_monitoring_result(r, &old_attrs, &new_attrs, false);
 	KUNIT_EXPECT_EQ(test, r->nr_accesses, 150);
 	KUNIT_EXPECT_EQ(test, r->age, 2);
 
 	new_attrs = (struct damon_attrs){
 		.sample_interval = 1, .aggr_interval = 100};
-	damon_update_monitoring_result(r, &old_attrs, &new_attrs);
+	damon_update_monitoring_result(r, &old_attrs, &new_attrs, false);
 	KUNIT_EXPECT_EQ(test, r->nr_accesses, 150);
 	KUNIT_EXPECT_EQ(test, r->age, 20);
 
-- 
2.39.5


  parent reply	other threads:[~2025-02-26  6:37 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-26  6:36 [RFC PATCH 00/13] mm/damon/sysfs: commit parameters online via damon_call() SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 01/13] mm/damon/sysfs: validate user inputs from damon_sysfs_commit_input() SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 02/13] mm/damon/core: invoke kdamond_call() after merging is done if possible SeongJae Park
2025-02-26  6:36 ` SeongJae Park [this message]
2025-03-02 21:41   ` [RFC PATCH 03/13] mm/damon/core: make damon_set_attrs() be safe to be called from damon_call() SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 04/13] mm/damon/sysfs: handle commit command using damon_call() SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 05/13] mm/damon/sysfs: remove damon_sysfs_cmd_request code from damon_sysfs_handle_cmd() SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 06/13] mm/damon/sysfs: remove damon_sysfs_cmd_request_callback() and its callers SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 07/13] mm/damon/sysfs: remove damon_sysfs_cmd_request and its readers SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 08/13] mm/damon/sysfs-schemes: remove obsolete comment for damon_sysfs_schemes_clear_regions() SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 09/13] mm/damon: remove damon_callback->private SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 10/13] mm/damon: remove ->before_start of damon_callback SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 11/13] mm/damon: remove damon_callback->after_sampling SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 12/13] mm/damon: remove damon_callback->before_damos_apply SeongJae Park
2025-02-26  6:36 ` [RFC PATCH 13/13] mm/damon: remove damon_operations->reset_aggregated SeongJae Park

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250226063651.513178-4-sj@kernel.org \
    --to=sj@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=damon@lists.linux.dev \
    --cc=kernel-team@meta.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox