From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
To: "David Hildenbrand" <david@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
"Vlastimil Babka" <vbabka@kernel.org>,
"Mike Rapoport" <rppt@kernel.org>,
"Suren Baghdasaryan" <surenb@google.com>,
"Michal Hocko" <mhocko@suse.com>,
"David Rientjes" <rientjes@google.com>,
"Shakeel Butt" <shakeel.butt@linux.dev>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Jann Horn" <jannh@google.com>,
"Pedro Falcato" <pfalcato@suse.de>,
"Matthew Wilcox (Oracle)" <willy@infradead.org>,
"Jan Kara" <jack@suse.cz>, "Nhat Pham" <nphamcs@gmail.com>,
"Johannes Weiner" <hannes@cmpxchg.org>, "Zi Yan" <ziy@nvidia.com>,
"Matthew Brost" <matthew.brost@intel.com>,
"Joshua Hahn" <joshua.hahnjy@gmail.com>,
"Byungchul Park" <byungchul@sk.com>,
"Gregory Price" <gourry@gourry.net>,
"Ying Huang" <ying.huang@linux.alibaba.com>,
"Alistair Popple" <apopple@nvidia.com>,
"Usama Arif" <usama.arif@linux.dev>,
"Kairui Song" <kasong@tencent.com>,
"Balbir Singh" <balbirs@nvidia.com>,
"Jérôme Glisse" <jglisse@redhat.com>,
"Kiryl Shutsemau" <kas@kernel.org>,
"Yu-cheng Yu" <yu-cheng.yu@intel.com>,
"Dave Hansen" <dave.hansen@linux.intel.com>,
"Dennis Zhou" <dennis@kernel.org>, "Tejun Heo" <tj@kernel.org>,
"Christoph Lameter" <cl@gentwo.org>,
"Peng Fan" <peng.fan@nxp.com>, "Josef Bacik" <jbacik@fb.com>,
"Chris Mason" <mason@kernel.org>
Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org,
linux-fsdevel@vger.kernel.org,
"Borislav Petkov (AMD)" <bp@alien8.de>,
Mark Brown <broonie@kernel.org>,
"Dennis Zhou (Facebook)" <dennisszhou@gmail.com>,
Rick Edgecombe <rick.p.edgecombe@intel.com>,
"Lorenzo Stoakes (ARM)" <ljs@kernel.org>
Subject: [PATCH 11/13] percpu: fix contig hints that are too low
Date: Wed, 11 Nov 2026 18:13:25 +0000 [thread overview]
Message-ID: <20261111-mason-migrate-et-al-fixes-v1-11-3d2454362e8d@kernel.org> (raw)
In-Reply-To: <20261111-mason-migrate-et-al-fixes-v1-0-3d2454362e8d@kernel.org>
pcpu_find_block_fit() skips a chunk or block whose contig_hint is too
small for the request, so a contig_hint must never be smaller than the
largest free area.
If it is, atomic and reserved chunk allocations can fail spuriously, and
others can create a chunk which is not needed.
There are three ways in which this can happen:
Firstly, when an allocation breaks a contig_hint, both
pcpu_block_refresh_hint() and pcpu_chunk_refresh_hint() promote the
scan_hint to contig_hint and scan only what follows it. However a free
can set the scan_hint regardless of what precedes it:
free areas A = 10 bits, then C = 50 bits
hints contig_hint = C
scan_hint = 0, cleared by an earlier allocation
free B 5 bits between A and C: scan_hint = B
allocate C contig_hint = B, the scan starts after B
result contig_hint = 5, but A has 10 bits
Secondly, when a free stays within one block,
pcpu_block_update_hint_free() passes the end of the free area to the
chunk hints as a block offset rather than a chunk offset, so in any block
but the first the chunk hints are not updated at all.
Thirdly, that free area stops at the block boundary, even if the free
bits continue into a neighbouring block.
Fix these by always rescanning when a contig_hint is broken, passing the
correct offset, and refreshing the chunk hints whenever a free area
reaches a block boundary.
Reported-by: Chris Mason <mason@kernel.org>
Fixes: da3afdd5bb54 ("percpu: use block scan_hint to only scan forward")
Fixes: 92c14cab4326 ("percpu: convert chunk hints to be based on pcpu_block_md")
Fixes: d33d9f3dd96b ("percpu: use chunk scan_hint to skip some scanning")
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
mm/percpu.c | 59 +++++++++++++----------------------------------------------
1 file changed, 13 insertions(+), 46 deletions(-)
diff --git a/mm/percpu.c b/mm/percpu.c
index d7883507dc15..dfd3ff0a1ac0 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -733,30 +733,16 @@ static void pcpu_block_update_scan(struct pcpu_chunk *chunk, int bit_off,
/**
* pcpu_chunk_refresh_hint - updates metadata about a chunk
* @chunk: chunk of interest
- * @full_scan: if we should scan from the beginning
*
* Iterates over the metadata blocks to find the largest contig area.
- * A full scan can be avoided on the allocation path as this is triggered
- * if we broke the contig_hint. In doing so, the scan_hint will be before
- * the contig_hint or after if the scan_hint == contig_hint. This cannot
- * be prevented on freeing as we want to find the largest area possibly
- * spanning blocks.
*/
-static void pcpu_chunk_refresh_hint(struct pcpu_chunk *chunk, bool full_scan)
+static void pcpu_chunk_refresh_hint(struct pcpu_chunk *chunk)
{
struct pcpu_block_md *chunk_md = &chunk->chunk_md;
- int bit_off, bits;
+ int bit_off = chunk_md->first_free, bits;
- /* promote scan_hint to contig_hint */
- if (!full_scan && chunk_md->scan_hint) {
- bit_off = chunk_md->scan_hint_start + chunk_md->scan_hint;
- chunk_md->contig_hint_start = chunk_md->scan_hint_start;
- chunk_md->contig_hint = chunk_md->scan_hint;
- chunk_md->scan_hint = 0;
- } else {
- bit_off = chunk_md->first_free;
- chunk_md->contig_hint = 0;
- }
+ chunk_md->contig_hint = 0;
+ chunk_md->scan_hint = 0;
pcpu_for_each_md_free_region(chunk, bit_off, bits)
pcpu_block_update(chunk_md, bit_off, bit_off + bits);
@@ -774,19 +760,10 @@ static void pcpu_block_refresh_hint(struct pcpu_chunk *chunk, int index)
{
struct pcpu_block_md *block = chunk->md_blocks + index;
unsigned long *alloc_map = pcpu_index_alloc_map(chunk, index);
- unsigned int start, end; /* region start, region end */
-
- /* promote scan_hint to contig_hint */
- if (block->scan_hint) {
- start = block->scan_hint_start + block->scan_hint;
- block->contig_hint_start = block->scan_hint_start;
- block->contig_hint = block->scan_hint;
- block->scan_hint = 0;
- } else {
- start = block->first_free;
- block->contig_hint = 0;
- }
+ unsigned int start = block->first_free, end;
+ block->contig_hint = 0;
+ block->scan_hint = 0;
block->right_free = 0;
/* iterate over free areas and update the contig hints */
@@ -938,7 +915,7 @@ static void pcpu_block_update_hint_alloc(struct pcpu_chunk *chunk, int bit_off,
chunk_md->contig_hint,
bit_off,
bit_off + bits))
- pcpu_chunk_refresh_hint(chunk, false);
+ pcpu_chunk_refresh_hint(chunk);
}
/**
@@ -952,12 +929,8 @@ static void pcpu_block_update_hint_alloc(struct pcpu_chunk *chunk, int bit_off,
* forward and backward to determine the extent of the free area. This is
* capped at the boundary of blocks.
*
- * A chunk update is triggered if a page becomes free, a block becomes free,
- * or the free spans across blocks. This tradeoff is to minimize iterating
- * over the block metadata to update chunk_md->contig_hint.
- * chunk_md->contig_hint may be off by up to a page, but it will never be more
- * than the available space. If the contig hint is contained in one block, it
- * will be accurate.
+ * The chunk metadata is refreshed if the free area reaches a block boundary,
+ * as it may then extend into neighbouring blocks.
*/
static void pcpu_block_update_hint_free(struct pcpu_chunk *chunk, int bit_off,
int bits)
@@ -1042,18 +1015,12 @@ static void pcpu_block_update_hint_free(struct pcpu_chunk *chunk, int bit_off,
if (nr_empty_pages)
pcpu_update_empty_pages(chunk, nr_empty_pages);
- /*
- * Refresh chunk metadata when the free makes a block free or spans
- * across blocks. The contig_hint may be off by up to a page, but if
- * the contig_hint is contained in a block, it will be accurate with
- * the else condition below.
- */
- if (((end - start) >= PCPU_BITMAP_BLOCK_BITS) || s_index != e_index)
- pcpu_chunk_refresh_hint(chunk, true);
+ if (!start || end == PCPU_BITMAP_BLOCK_BITS || s_index != e_index)
+ pcpu_chunk_refresh_hint(chunk);
else
pcpu_block_update(&chunk->chunk_md,
pcpu_block_off_to_off(s_index, start),
- end);
+ pcpu_block_off_to_off(s_index, end));
}
/**
--
2.56.0
next prev parent reply other threads:[~2026-11-11 18:15 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 18:13 [PATCH 00/13] mm: batch of (urgentish?) fixes in oomk, cachestat, dev-migrate, percpu Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 01/13] mm/mmap: free gathered pages before MMF_OOM_SKIP Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 02/13] mm: cachestat: count each page only once Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 03/13] mm: cachestat: restart the walk after each entry Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 04/13] mm/migrate_device: allocate THP deferred split heads Lorenzo Stoakes (ARM)
2026-11-11 19:30 ` Kiryl Shutsemau
2026-11-11 19:40 ` David Hildenbrand (Arm)
2026-11-11 18:13 ` [PATCH 05/13] mm/migrate_device: don't collect a range twice Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 06/13] mm/migrate_device: don't let go of the fault page Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 07/13] mm/migrate_device: look up fault folio after a split Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 08/13] mm/migrate_device: fix ksmd oops on a migrated KSM page Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 09/13] mm/migrate_device: fix use-after-free of the anon rmap Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 10/13] mm/migrate_device: refuse to migrate shadow stacks Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` Lorenzo Stoakes (ARM) [this message]
2026-11-11 18:13 ` [PATCH 12/13] percpu: search the whole reserved chunk Lorenzo Stoakes (ARM)
2026-11-11 18:13 ` [PATCH 13/13] percpu: queue the balance work on an unbound workqueue Lorenzo Stoakes (ARM)
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=20261111-mason-migrate-et-al-fixes-v1-11-3d2454362e8d@kernel.org \
--to=ljs@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=apopple@nvidia.com \
--cc=balbirs@nvidia.com \
--cc=bp@alien8.de \
--cc=broonie@kernel.org \
--cc=byungchul@sk.com \
--cc=cl@gentwo.org \
--cc=dave.hansen@linux.intel.com \
--cc=david@kernel.org \
--cc=dennis@kernel.org \
--cc=dennisszhou@gmail.com \
--cc=gourry@gourry.net \
--cc=hannes@cmpxchg.org \
--cc=jack@suse.cz \
--cc=jannh@google.com \
--cc=jbacik@fb.com \
--cc=jglisse@redhat.com \
--cc=joshua.hahnjy@gmail.com \
--cc=kas@kernel.org \
--cc=kasong@tencent.com \
--cc=liam@infradead.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mason@kernel.org \
--cc=matthew.brost@intel.com \
--cc=mhocko@suse.com \
--cc=nphamcs@gmail.com \
--cc=peng.fan@nxp.com \
--cc=pfalcato@suse.de \
--cc=rick.p.edgecombe@intel.com \
--cc=rientjes@google.com \
--cc=rppt@kernel.org \
--cc=shakeel.butt@linux.dev \
--cc=surenb@google.com \
--cc=tj@kernel.org \
--cc=usama.arif@linux.dev \
--cc=vbabka@kernel.org \
--cc=willy@infradead.org \
--cc=ying.huang@linux.alibaba.com \
--cc=yu-cheng.yu@intel.com \
--cc=ziy@nvidia.com \
/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
all inboxes | Powered by JetHome®