Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: DaeMyung Kang <charsyam@gmail.com>
To: SJ Park <sj@kernel.org>
Cc: damon@lists.linux.dev, linux-mm@kvack.org,
	Andrew Morton <akpm@linux-foundation.org>,
	Asier Gutierrez <gutierrez.asier@huawei-partners.com>,
	DaeMyung Kang <charsyam@gmail.com>
Subject: [RFC PATCH 1/3] mm/damon/ops-common: avoid overflow in weighted hotness score
Date: Tue, 27 Oct 2026 22:59:30 +0900	[thread overview]
Message-ID: <20261027135932.2301646-2-charsyam@gmail.com> (raw)
In-Reply-To: <20261027135932.2301646-1-charsyam@gmail.com>

damon_hot_score() combines the frequency and age subscores using the
user supplied DAMOS quota weights.  The weights are unsigned int, and
both the weighted sum and the total weight are calculated in unsigned
int.  This is independent of the architecture word size.

A frequency weight of 50,000,000 makes the weighted sum wrap, so a
region that is accessed in every sample gets a score of 13 instead of
99.  If the two weights add up to exactly 2^32, the total wraps to zero
and the averaging division is skipped.

Accumulate both values in u64 and divide the weighted sum by the full
total weight.  Add a KUnit case covering a large weight, weights summing
above UINT_MAX, and zero weights.

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/20260623141527.044D11F000E9@smtp.kernel.org

Fixes: 198f0f4c58b9 ("mm/damon/vaddr,paddr: support pageout prioritization")
Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
---
 mm/damon/ops-common.c             | 10 +++++--
 mm/damon/tests/ops-common-kunit.h | 47 +++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 3 deletions(-)
 create mode 100644 mm/damon/tests/ops-common-kunit.h

diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
index 2f3bf86a221b..af170b112e08 100644
--- a/mm/damon/ops-common.c
+++ b/mm/damon/ops-common.c
@@ -170,6 +170,7 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r,
 	int age_in_log, age_subscore;
 	unsigned int freq_weight = s->quota.weight_nr_accesses;
 	unsigned int age_weight = s->quota.weight_age;
+	u64 total_weight, weighted_sum;
 	int hotness;
 
 	freq_subscore = mult_frac(damon_nr_accesses_mvsum(r, c),
@@ -197,9 +198,10 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r,
 	age_subscore = age_in_log * DAMON_MAX_SUBSCORE /
 		DAMON_MAX_AGE_IN_LOG / 2;
 
-	hotness = (freq_weight * freq_subscore + age_weight * age_subscore);
-	if (freq_weight + age_weight)
-		hotness /= freq_weight + age_weight;
+	total_weight = (u64)freq_weight + age_weight;
+	weighted_sum = (u64)freq_weight * freq_subscore +
+			(u64)age_weight * age_subscore;
+	hotness = total_weight ? div64_u64(weighted_sum, total_weight) : 0;
 	/*
 	 * Transform it to fit in [0, DAMOS_MAX_SCORE]
 	 */
@@ -576,3 +578,5 @@ bool damon_ops_filter_match(struct damon_filter *filter, struct folio *folio)
 	}
 	return matched == filter->matching;
 }
+
+#include "tests/ops-common-kunit.h"
diff --git a/mm/damon/tests/ops-common-kunit.h b/mm/damon/tests/ops-common-kunit.h
new file mode 100644
index 000000000000..813bad69cdcd
--- /dev/null
+++ b/mm/damon/tests/ops-common-kunit.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifdef CONFIG_DAMON_KUNIT_TEST
+
+#include <kunit/test.h>
+
+static void damon_test_hot_score_large_weight(struct kunit *test)
+{
+	struct damon_ctx ctx = {
+		.attrs = {
+			.sample_interval = 1000,
+			.aggr_interval = 100000,
+		},
+	};
+	struct damon_region region = {
+		.nr_accesses = 100,
+	};
+	struct damos scheme = {
+		.quota = {
+			.weight_nr_accesses = 50000000,
+		},
+	};
+
+	KUNIT_EXPECT_EQ(test, damon_hot_score(&ctx, &region, &scheme), 99);
+
+	scheme.quota.weight_nr_accesses = UINT_MAX;
+	scheme.quota.weight_age = UINT_MAX;
+	KUNIT_EXPECT_EQ(test, damon_hot_score(&ctx, &region, &scheme), 74);
+
+	scheme.quota.weight_nr_accesses = 0;
+	scheme.quota.weight_age = 0;
+	KUNIT_EXPECT_EQ(test, damon_hot_score(&ctx, &region, &scheme), 0);
+}
+
+static struct kunit_case damon_ops_common_test_cases[] = {
+	KUNIT_CASE(damon_test_hot_score_large_weight),
+	{}
+};
+
+static struct kunit_suite damon_ops_common_test_suite = {
+	.name = "damon-ops-common",
+	.test_cases = damon_ops_common_test_cases,
+};
+
+kunit_test_suite(damon_ops_common_test_suite);
+
+#endif /* CONFIG_DAMON_KUNIT_TEST */
-- 
2.43.0



  reply	other threads:[~2026-10-27 13:59 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 13:59 [RFC PATCH 0/3] mm/damon: fix overflows in score and ratio calculations DaeMyung Kang
2026-10-27 13:59 ` DaeMyung Kang [this message]
2026-10-27 13:59 ` [RFC PATCH 2/3] mm/damon/core: avoid overflows in monitoring results calculations DaeMyung Kang
2026-10-27 14:18   ` Gutierrez Asier
2026-10-27 13:59 ` [RFC PATCH 3/3] mm/damon/core: avoid overflows in quota goal metrics and scores DaeMyung Kang
2026-10-27 14:23 ` [RFC PATCH 0/3] mm/damon: fix overflows in score and ratio calculations SJ 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=20261027135932.2301646-2-charsyam@gmail.com \
    --to=charsyam@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=damon@lists.linux.dev \
    --cc=gutierrez.asier@huawei-partners.com \
    --cc=linux-mm@kvack.org \
    --cc=sj@kernel.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