From: Joonwon Kang <joonwonkang@google.com>
To: dennis@kernel.org, tj@kernel.org, cl@gentwo.org
Cc: akpm@linux-foundation.org, linux-mm@kvack.org,
linux-kernel@vger.kernel.org, dodam@google.com,
Joonwon Kang <joonwonkang@google.com>
Subject: [PATCH v2] percpu: Fix hint invariant breakage
Date: Wed, 8 Apr 2026 10:06:42 +0000 [thread overview]
Message-ID: <20260408100642.83919-1-joonwonkang@google.com> (raw)
The invariant "scan_hint_start > contig_hint_start if and only if
scan_hint == contig_hint" should be kept for hint management. However,
it could be broken in some cases:
- if (new contig == contig_hint == scan_hint) && (contig_hint_start <
scan_hint_start < new contig start) && the new contig is to become a
new contig_hint due to its better alignment, then scan_hint should
be invalidated instead of keeping the old value.
- if (new contig == contig_hint > scan_hint) && (new contig start <
contig_hint_start) && the new contig is not to become a new
contig_hint, then scan_hint should be not updated to the new contig.
This commit refactors the percpu block update code to make it more
visible on what to consider, e.g. when the new contig overlaps with the
old contig_hint or scan_hint, fixes the invariant breakage and also
optimizes scan_hint further. Some of the optimization cases when no
overlap occurs are:
- if (new contig > contig_hint > scan_hint) && (scan_hint_start < new
contig start < contig_hint_start), then keep scan_hint instead of
invalidating it.
- if (new contig > contig_hint == scan_hint) && (contig_hint_start <
new contig start < scan_hint_start), then update scan_hint to the
old contig_hint instead of invalidating it.
- if (new contig == contig_hint > scan_hint) && (new contig start <
contig_hint_start) && the new contig is to become a new contig_hint
due to its better alignment, then update scan_hint to the old
contig_hint instead of invalidating or keeping it.
Signed-off-by: Joonwon Kang <joonwonkang@google.com>
---
v1 -> v2: Consider cases where the new contig overlaps with the existing
contig_hint or scan_hint.
mm/percpu.c | 124 +++++++++++++++++++++++++++++++++++-----------------
1 file changed, 85 insertions(+), 39 deletions(-)
diff --git a/mm/percpu.c b/mm/percpu.c
index 81462ce5866e..57fa67f3726d 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -630,6 +630,11 @@ static inline bool pcpu_region_overlap(int a, int b, int x, int y)
static void pcpu_block_update(struct pcpu_block_md *block, int start, int end)
{
int contig = end - start;
+ int scan_hint_cand_1 = 0;
+ int scan_hint_cand_1_start = 0;
+ int scan_hint_cand_2 = 0;
+ int scan_hint_cand_2_start = 0;
+ bool overlap_with_contig_hint;
block->first_free = min(block->first_free, start);
if (start == 0)
@@ -638,57 +643,98 @@ static void pcpu_block_update(struct pcpu_block_md *block, int start, int end)
if (end == block->nr_bits)
block->right_free = contig;
+ if (block->contig_hint == 0) {
+ block->contig_hint = contig;
+ block->contig_hint_start = start;
+ block->scan_hint = 0;
+ return;
+ }
+
+ overlap_with_contig_hint = pcpu_region_overlap(
+ start, end, block->contig_hint_start,
+ block->contig_hint_start + block->contig_hint);
+
if (contig > block->contig_hint) {
- /* promote the old contig_hint to be the new scan_hint */
- if (start > block->contig_hint_start) {
- if (block->contig_hint > block->scan_hint) {
- block->scan_hint_start =
- block->contig_hint_start;
- block->scan_hint = block->contig_hint;
- } else if (start < block->scan_hint_start) {
- /*
- * The old contig_hint == scan_hint. But, the
- * new contig is larger so hold the invariant
- * scan_hint_start < contig_hint_start.
- */
- block->scan_hint = 0;
- }
- } else {
- block->scan_hint = 0;
+ if (!overlap_with_contig_hint) {
+ scan_hint_cand_1 = block->contig_hint;
+ scan_hint_cand_1_start = block->contig_hint_start;
}
- block->contig_hint_start = start;
+
block->contig_hint = contig;
+ block->contig_hint_start = start;
} else if (contig == block->contig_hint) {
if (block->contig_hint_start &&
(!start ||
__ffs(start) > __ffs(block->contig_hint_start))) {
- /* start has a better alignment so use it */
+ scan_hint_cand_1 = block->contig_hint;
+ scan_hint_cand_1_start = block->contig_hint_start;
+
+ /* Start has a better alignment so use it. */
block->contig_hint_start = start;
- if (start < block->scan_hint_start &&
- block->contig_hint > block->scan_hint)
- block->scan_hint = 0;
- } else if (start > block->scan_hint_start ||
- block->contig_hint > block->scan_hint) {
- /*
- * Knowing contig == contig_hint, update the scan_hint
- * if it is farther than or larger than the current
- * scan_hint.
- */
- block->scan_hint_start = start;
- block->scan_hint = contig;
+ } else {
+ if (!overlap_with_contig_hint) {
+ scan_hint_cand_1 = contig;
+ scan_hint_cand_1_start = start;
+ }
}
} else {
/*
- * The region is smaller than the contig_hint. So only update
- * the scan_hint if it is larger than or equal and farther than
- * the current scan_hint.
+ * Consider only when the new contig is larger than or equal to
+ * the old scan hint.
*/
- if ((start < block->contig_hint_start &&
- (contig > block->scan_hint ||
- (contig == block->scan_hint &&
- start > block->scan_hint_start)))) {
- block->scan_hint_start = start;
- block->scan_hint = contig;
+ if (contig >= block->scan_hint) {
+ scan_hint_cand_1 = contig;
+ scan_hint_cand_1_start = start;
+ }
+ }
+
+ if (block->scan_hint &&
+ !pcpu_region_overlap(start, end, block->scan_hint_start,
+ block->scan_hint_start + block->scan_hint)) {
+ scan_hint_cand_2 = block->scan_hint;
+ scan_hint_cand_2_start = block->scan_hint_start;
+ }
+
+ /* Make scan_hint_cand_1 be the best candidate for the new scan hint. */
+ if ((scan_hint_cand_2 > scan_hint_cand_1) ||
+ (scan_hint_cand_2 == scan_hint_cand_1 &&
+ scan_hint_cand_2_start > scan_hint_cand_1_start)) {
+ int tmp_hint = scan_hint_cand_1;
+ int tmp_hint_start = scan_hint_cand_1_start;
+
+ scan_hint_cand_1 = scan_hint_cand_2;
+ scan_hint_cand_1_start = scan_hint_cand_2_start;
+ scan_hint_cand_2 = tmp_hint;
+ scan_hint_cand_2_start = tmp_hint_start;
+ }
+
+ /*
+ * At this point, it is guaranteed that none of the scan hint
+ * candidates overlaps with the new contig hint while they may overlap
+ * with the old scan hint, and that the first candidate is larger in
+ * size or, it equal, farther than the second one.
+ */
+
+ if (block->contig_hint > scan_hint_cand_1) {
+ if (scan_hint_cand_1_start < block->contig_hint_start) {
+ block->scan_hint = scan_hint_cand_1;
+ block->scan_hint_start = scan_hint_cand_1_start;
+ } else if (scan_hint_cand_2_start < block->contig_hint_start) {
+ block->scan_hint = scan_hint_cand_2;
+ block->scan_hint_start = scan_hint_cand_2_start;
+ } else {
+ block->scan_hint = 0;
+ }
+ } else if (block->contig_hint == scan_hint_cand_1) {
+ if (scan_hint_cand_1_start > block->contig_hint_start) {
+ block->scan_hint = scan_hint_cand_1;
+ block->scan_hint_start = scan_hint_cand_1_start;
+ } else if (scan_hint_cand_2 < block->contig_hint &&
+ scan_hint_cand_2_start < scan_hint_cand_1_start) {
+ block->scan_hint = scan_hint_cand_2;
+ block->scan_hint_start = scan_hint_cand_2_start;
+ } else {
+ block->scan_hint = 0;
}
}
}
--
2.53.0.1213.gd9a14994de-goog
next reply other threads:[~2026-04-08 10:06 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-08 10:06 Joonwon Kang [this message]
2026-04-08 11:04 ` Joonwon Kang
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=20260408100642.83919-1-joonwonkang@google.com \
--to=joonwonkang@google.com \
--cc=akpm@linux-foundation.org \
--cc=cl@gentwo.org \
--cc=dennis@kernel.org \
--cc=dodam@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=tj@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