Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] mm, page_alloc: several stable fixes
@ 2026-11-03 16:16 Vlastimil Babka (SUSE)
  2026-11-03 16:16 ` [PATCH v2 1/5] mm/page_alloc: clear ->private of deferred free pages Vlastimil Babka (SUSE)
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-11-03 16:16 UTC (permalink / raw)
  To: Johannes Weiner, Gregory Price (Meta), Zi Yan, Shakeel Butt,
	Mike Rapoport, David Hildenbrand, Oscar Salvador,
	Matthew Wilcox (Oracle)
  Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, linux-mm,
	linux-kernel, Vlastimil Babka (SUSE), Josef Bacik, stable

A bunch of fixes from Josef, filtered for page allocator by David.

I have tweaked some commit logs and a comment in one case.

In v2 some were dropped as they were merged already, the last one was
changed to fix things differently. "mm/compaction: trust
zone->contiguous only in the span" was dropped due to lots of sashiko
feedback. compaction (as a pfn scanner) needs much more thought

Based on page_alloc/for-next-fixes, to be applied there as well.

Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
---
Changes in v2:
- rebased on page_alloc/for-next-fixes
- dropped 3 patches that were already merged from Gregory
- dropped "mm/compaction: trust zone->contiguous only in the span" as it
  needs more thougt
- in the last patch, remove the VM_BUG_ON_PAGE completely
- Link to v1: https://patch.msgid.link/20261027-pgalloc-ai-hotfixes-v1-0-9c7d1aca801e@kernel.org

---
Josef Bacik (4):
      mm/page_alloc: clear ->private of deferred free pages
      mm: page_frag: refill the cache for large fragments
      mm: page_isolation: do not take slab pages for free
      mm/memory_hotplug: resize zones under their seqlock

Vlastimil Babka (SUSE) (1):
      mm/page_alloc: remove the zone_spans_pfn() check from get_pfnblock_bitmap_bitidx()

 mm/memory_hotplug.c  | 51 +++++++++++++++++++++++++++++++++++----------------
 mm/page_alloc.c      |  3 ++-
 mm/page_frag_cache.c | 21 +++++++++++++--------
 mm/page_isolation.c  |  7 +++++++
 4 files changed, 57 insertions(+), 25 deletions(-)
---
base-commit: 743a99d7e339e81acaf76f3c902b23abb29abc15
change-id: 20261027-pgalloc-ai-hotfixes-59bcd873a355



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

* [PATCH v2 1/5] mm/page_alloc: clear ->private of deferred free pages
  2026-11-03 16:16 [PATCH v2 0/5] mm, page_alloc: several stable fixes Vlastimil Babka (SUSE)
@ 2026-11-03 16:16 ` Vlastimil Babka (SUSE)
  2026-11-03 18:27   ` Johannes Weiner
  2026-11-03 16:16 ` [PATCH v2 2/5] mm: page_frag: refill the cache for large fragments Vlastimil Babka (SUSE)
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 8+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-11-03 16:16 UTC (permalink / raw)
  To: Johannes Weiner, Gregory Price (Meta), Zi Yan, Shakeel Butt,
	Mike Rapoport, David Hildenbrand, Oscar Salvador,
	Matthew Wilcox (Oracle)
  Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, linux-mm,
	linux-kernel, Vlastimil Babka (SUSE), Josef Bacik, stable

From: Josef Bacik <josef@toxicpanda.com>

free_pages_nolock() and free_frozen_pages_nolock() put the page on
zone->trylock_free_pages if they get neither the pcp lock nor zone->lock.
add_page_to_zone_llist() keeps the order of the page in page->private.
The next free_one_page() which does get zone->lock passes these pages to
split_large_buddy() with ->private still set.

__free_one_page() writes ->private of the head of the resulting free page
only. If the deferred page is merged with its lower buddy, it becomes a
tail of that free page and keeps the order in ->private.

All other free pages which are not the head of a buddy have a ->private
of zero, and since commit 819bfbd47e49 ("mm/page_alloc: remove
set_page_private() in prep_compound_tail()") the allocator relies on it.
When the stale page is allocated again as a tail of a larger page:

 - prep_compound_tail() hits VM_WARN_ON_ONCE(tail->private);
 - with the page checks enabled, __free_pages_prepare() reports
   "BUG: Bad page state", "nonzero private", and leaks the whole page;
 - without them the value is seen by whoever uses that page next, e.g. as
   folio->private of a folio which is split off a large folio.

Only pages of an order above zero are affected. Those can be freed
through _nolock() by the stack depot (order 2) and by SLUB (a new slab
which cannot be added to the partial list by kmalloc_nolock()).

Clear ->private when the page is taken off the list.

Fixes: 8c57b687e833 ("mm, bpf: Introduce free_pages_nolock()")
Cc: <stable@vger.kernel.org>
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Zi Yan <ziy@nvidia.com>
Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
---
 mm/page_alloc.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 55bd558306f5..41fd80c0c762 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1562,6 +1562,8 @@ static void free_one_page(struct zone *zone, struct page *page,
 		llist_for_each_entry_safe(p, tmp, llnode, pcp_llist) {
 			unsigned int p_order = p->private;
 
+			/* It may end up as a tail of the merged free page. */
+			p->private = 0;
 			split_large_buddy(zone, p, page_to_pfn(p), p_order, fpi_flags);
 			__count_vm_events(PGFREE, 1 << p_order);
 		}

-- 
2.56.0



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

* [PATCH v2 2/5] mm: page_frag: refill the cache for large fragments
  2026-11-03 16:16 [PATCH v2 0/5] mm, page_alloc: several stable fixes Vlastimil Babka (SUSE)
  2026-11-03 16:16 ` [PATCH v2 1/5] mm/page_alloc: clear ->private of deferred free pages Vlastimil Babka (SUSE)
@ 2026-11-03 16:16 ` Vlastimil Babka (SUSE)
  2026-11-03 16:16 ` [PATCH v2 3/5] mm: page_isolation: do not take slab pages for free Vlastimil Babka (SUSE)
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-11-03 16:16 UTC (permalink / raw)
  To: Johannes Weiner, Gregory Price (Meta), Zi Yan, Shakeel Butt,
	Mike Rapoport, David Hildenbrand, Oscar Salvador,
	Matthew Wilcox (Oracle)
  Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, linux-mm,
	linux-kernel, Vlastimil Babka (SUSE), Josef Bacik, stable

From: Josef Bacik <josef@toxicpanda.com>

A fragment which is larger than PAGE_SIZE can only be served from the
32K page of a page_frag_cache. If memory is low and the cache had to fall
back to a single page, __page_frag_alloc_align() returns NULL and keeps
that page.

Since the offset counts up, this check comes first and is made whenever
the fragment does not fit behind nc->offset, no matter how big the page
of the cache is. So the cache is never refilled on behalf of a large
fragment: with fragsz = 10000 the first three allocations succeed, the
fourth one and all the following ones fail although memory is plentiful.
nc->offset does not move either, so this lasts until smaller fragments
have used up the rest of the page, which never happens if the cache has
no other user.

The nfp driver allocates its receive buffers with napi_alloc_frag() and
netdev_alloc_frag(). They are larger than a page with a jumbo MTU.

Only give up if the fragment does not fit into the page at all, which is
what the comment describes. The fourth allocation in the scenario above
with a 32k page will thus now proceed to refill the cache.

Fixes: 8218f62c9c9b ("mm: page_frag: use initial zero offset for page_frag_alloc_align()")
Cc: <stable@vger.kernel.org>
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Johannes Weiner <hannes@cmpxchg.org>
[vbabka@kernel.org: make the comment more obvious]
Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
---
 mm/page_frag_cache.c | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/mm/page_frag_cache.c b/mm/page_frag_cache.c
index e63efe78b7d4..8914437ddc51 100644
--- a/mm/page_frag_cache.c
+++ b/mm/page_frag_cache.c
@@ -119,15 +119,20 @@ void *__page_frag_alloc_align(struct page_frag_cache *nc,
 	size = PAGE_SIZE << encoded_page_decode_order(encoded_page);
 	offset = __ALIGN_KERNEL_MASK(nc->offset, ~align_mask);
 	if (unlikely(offset + fragsz > size)) {
-		if (unlikely(fragsz > PAGE_SIZE)) {
+		if (unlikely(fragsz > size)) {
 			/*
-			 * The caller is trying to allocate a fragment
-			 * with fragsz > PAGE_SIZE but the cache isn't big
-			 * enough to satisfy the request, this may
-			 * happen in low memory conditions.
-			 * We don't release the cache page because
-			 * it could make memory pressure worse
-			 * so we simply return NULL here.
+			 * The caller is trying to allocate a fragment that's
+			 * larger than the whole cache page.
+			 *
+			 * Either fragsz is larger than PAGE_FRAG_CACHE_MAX_SIZE
+			 * so this cannot ever succeed.
+			 *
+			 * Or the refill has fallen back to order-0 due to a low
+			 * memory condition. Then we don't want to release the
+			 * cache page to attempt a new large-size refill because
+			 * it could make memory pressure worse.
+			 *
+			 * So in both cases we simply return NULL here.
 			 */
 			return NULL;
 		}

-- 
2.56.0



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

* [PATCH v2 3/5] mm: page_isolation: do not take slab pages for free
  2026-11-03 16:16 [PATCH v2 0/5] mm, page_alloc: several stable fixes Vlastimil Babka (SUSE)
  2026-11-03 16:16 ` [PATCH v2 1/5] mm/page_alloc: clear ->private of deferred free pages Vlastimil Babka (SUSE)
  2026-11-03 16:16 ` [PATCH v2 2/5] mm: page_frag: refill the cache for large fragments Vlastimil Babka (SUSE)
@ 2026-11-03 16:16 ` Vlastimil Babka (SUSE)
  2026-11-03 18:33   ` Johannes Weiner
  2026-11-03 16:16 ` [PATCH v2 4/5] mm/memory_hotplug: resize zones under their seqlock Vlastimil Babka (SUSE)
  2026-11-03 16:16 ` [PATCH v2 5/5] mm/page_alloc: remove the zone_spans_pfn() check from get_pfnblock_bitmap_bitidx() Vlastimil Babka (SUSE)
  4 siblings, 1 reply; 8+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-11-03 16:16 UTC (permalink / raw)
  To: Johannes Weiner, Gregory Price (Meta), Zi Yan, Shakeel Butt,
	Mike Rapoport, David Hildenbrand, Oscar Salvador,
	Matthew Wilcox (Oracle)
  Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, linux-mm,
	linux-kernel, Vlastimil Babka (SUSE), Josef Bacik

From: Josef Bacik <josef@toxicpanda.com>

page_is_unmovable() regards a page with a reference count of zero as free
or about to be freed. Since slab allocates frozen pages, they will
be wrongly considered to be freed if order 0 (higher-order compound
pages are caught earlier).

If such slabs are the only unmovable pages of a memory block, memory
offlining now isolates the block, this can lead to offline_pages()
looping without progress.

Thus detect the slab pages as removable to return -EBUSY, as would
happen before commit 9aec2fb0fd5e ("slab: allocate frozen pages").

Fixes: 9aec2fb0fd5e ("slab: allocate frozen pages")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Zi Yan <ziy@nvidia.com>
Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
---
 mm/memory_hotplug.c | 7 +++++++
 mm/page_isolation.c | 7 +++++++
 2 files changed, 14 insertions(+)

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index c9cc42e54f05..0ab9a6e09222 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1832,6 +1832,13 @@ static int scan_movable_pages(unsigned long start, unsigned long end,
 		if (PageOffline(page) && page_count(page))
 			return -EBUSY;
 
+		/*
+		 * A page that was allocated, but was not a slab page yet, when
+		 * its pageblock was isolated.
+		 */
+		if (PageSlab(page))
+			return -EBUSY;
+
 		folio = page_folio(page);
 		if (!folio_test_hugetlb(folio))
 			continue;
diff --git a/mm/page_isolation.c b/mm/page_isolation.c
index 5aaad037384e..f4f0571607ab 100644
--- a/mm/page_isolation.c
+++ b/mm/page_isolation.c
@@ -74,6 +74,13 @@ bool page_is_unmovable(struct zone *zone, struct page *page,
 		return false;
 	}
 
+	/*
+	 * Slab pages are frozen: do not take their reference count of zero
+	 * for that of a free page.
+	 */
+	if (PageSlab(page))
+		return true;
+
 	/*
 	 * We can't use page_count without pin a page
 	 * because another CPU can free compound page.

-- 
2.56.0



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

* [PATCH v2 4/5] mm/memory_hotplug: resize zones under their seqlock
  2026-11-03 16:16 [PATCH v2 0/5] mm, page_alloc: several stable fixes Vlastimil Babka (SUSE)
                   ` (2 preceding siblings ...)
  2026-11-03 16:16 ` [PATCH v2 3/5] mm: page_isolation: do not take slab pages for free Vlastimil Babka (SUSE)
@ 2026-11-03 16:16 ` Vlastimil Babka (SUSE)
  2026-11-03 16:16 ` [PATCH v2 5/5] mm/page_alloc: remove the zone_spans_pfn() check from get_pfnblock_bitmap_bitidx() Vlastimil Babka (SUSE)
  4 siblings, 0 replies; 8+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-11-03 16:16 UTC (permalink / raw)
  To: Johannes Weiner, Gregory Price (Meta), Zi Yan, Shakeel Butt,
	Mike Rapoport, David Hildenbrand, Oscar Salvador,
	Matthew Wilcox (Oracle)
  Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, linux-mm,
	linux-kernel, Vlastimil Babka (SUSE), Josef Bacik, stable

From: Josef Bacik <josef@toxicpanda.com>

With CONFIG_DEBUG_VM the page allocator checks that a page which is
freed, split or allocated lies within its zone. The check reads
zone->zone_start_pfn and zone->spanned_pages under zone->span_seqlock, as
memory hotplug can change the two at any time.

Commit 27cacaad16c5 ("mm,memory_hotplug: drop unneeded locking") removed
the write side, since all resizing is serialized by the hotplug lock. That
leaves the reader without protection. When memory below the start of a
zone goes online, the start is lowered first and the size is raised
afterwards. When the lowest memory of a zone goes offline, the size is
reduced first. In between, the zone seems to end before its last pages. If
one of them goes through the allocator at that moment, it is reported as
being outside of the zone triggers VM_BUG_ON_PAGE().

Write the two fields in a write section of the seqlock. Interrupts are
disabled as pages are freed from interrupts, too, and the reader would
wait for the writer which it has interrupted.

Fixes: 27cacaad16c5 ("mm,memory_hotplug: drop unneeded locking")
Cc: <stable@vger.kernel.org>
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
---
 mm/memory_hotplug.c | 44 ++++++++++++++++++++++++++++----------------
 1 file changed, 28 insertions(+), 16 deletions(-)

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 0ab9a6e09222..b9f02bc6ed0d 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -461,6 +461,22 @@ static unsigned long find_biggest_section_pfn(struct zone *zone,
 	return 0;
 }
 
+/*
+ * The page allocator checks pages against the span of their zone while the
+ * zone is resized, see page_outside_zone_boundaries(). It must not see the
+ * new value of one field together with the old value of the other one.
+ */
+static void zone_set_span(struct zone *zone, unsigned long start_pfn,
+			  unsigned long spanned_pages)
+{
+	unsigned long flags;
+
+	write_seqlock_irqsave(&zone->span_seqlock, flags);
+	zone->zone_start_pfn = start_pfn;
+	zone->spanned_pages = spanned_pages;
+	write_sequnlock_irqrestore(&zone->span_seqlock, flags);
+}
+
 static void shrink_zone_span(struct zone *zone, unsigned long start_pfn,
 			     unsigned long end_pfn)
 {
@@ -475,13 +491,10 @@ static void shrink_zone_span(struct zone *zone, unsigned long start_pfn,
 		 */
 		pfn = find_smallest_section_pfn(zone, end_pfn,
 						zone_end_pfn(zone));
-		if (pfn) {
-			zone->spanned_pages = zone_end_pfn(zone) - pfn;
-			zone->zone_start_pfn = pfn;
-		} else {
-			zone->zone_start_pfn = 0;
-			zone->spanned_pages = 0;
-		}
+		if (pfn)
+			zone_set_span(zone, pfn, zone_end_pfn(zone) - pfn);
+		else
+			zone_set_span(zone, 0, 0);
 	} else if (zone_end_pfn(zone) == end_pfn) {
 		/*
 		 * If the section is biggest section in the zone, it need
@@ -492,11 +505,10 @@ static void shrink_zone_span(struct zone *zone, unsigned long start_pfn,
 		pfn = find_biggest_section_pfn(zone, zone->zone_start_pfn,
 					       start_pfn);
 		if (pfn)
-			zone->spanned_pages = pfn - zone->zone_start_pfn + 1;
-		else {
-			zone->zone_start_pfn = 0;
-			zone->spanned_pages = 0;
-		}
+			zone_set_span(zone, zone->zone_start_pfn,
+				      pfn - zone->zone_start_pfn + 1);
+		else
+			zone_set_span(zone, 0, 0);
 	}
 }
 
@@ -684,12 +696,12 @@ static void online_pages_range(unsigned long start_pfn, unsigned long nr_pages)
 static void __meminit resize_zone_range(struct zone *zone, unsigned long start_pfn,
 		unsigned long nr_pages)
 {
-	unsigned long old_end_pfn = zone_end_pfn(zone);
+	unsigned long end_pfn = max(start_pfn + nr_pages, zone_end_pfn(zone));
 
-	if (zone_is_empty(zone) || start_pfn < zone->zone_start_pfn)
-		zone->zone_start_pfn = start_pfn;
+	if (!zone_is_empty(zone) && start_pfn > zone->zone_start_pfn)
+		start_pfn = zone->zone_start_pfn;
 
-	zone->spanned_pages = max(start_pfn + nr_pages, old_end_pfn) - zone->zone_start_pfn;
+	zone_set_span(zone, start_pfn, end_pfn - start_pfn);
 }
 
 static void __meminit resize_pgdat_range(struct pglist_data *pgdat, unsigned long start_pfn,

-- 
2.56.0



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

* [PATCH v2 5/5] mm/page_alloc: remove the zone_spans_pfn() check from get_pfnblock_bitmap_bitidx()
  2026-11-03 16:16 [PATCH v2 0/5] mm, page_alloc: several stable fixes Vlastimil Babka (SUSE)
                   ` (3 preceding siblings ...)
  2026-11-03 16:16 ` [PATCH v2 4/5] mm/memory_hotplug: resize zones under their seqlock Vlastimil Babka (SUSE)
@ 2026-11-03 16:16 ` Vlastimil Babka (SUSE)
  4 siblings, 0 replies; 8+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-11-03 16:16 UTC (permalink / raw)
  To: Johannes Weiner, Gregory Price (Meta), Zi Yan, Shakeel Butt,
	Mike Rapoport, David Hildenbrand, Oscar Salvador,
	Matthew Wilcox (Oracle)
  Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, linux-mm,
	linux-kernel, Vlastimil Babka (SUSE), Josef Bacik, stable

Whenever the flags of a pageblock are read or written, a kernel with
CONFIG_DEBUG_VM makes sure that the pfn lies within the zone of the page.
Unlike bad_range(), that check reads zone->zone_start_pfn and
zone->spanned_pages without zone->span_seqlock.

Memory hotplug changes the two fields one after the other. If the lower
end of a zone moves, the zone seems to end before its last pages in
between. A page of that part which is freed at that moment, on another
CPU, ends in

  page dumped because: VM_BUG_ON_PAGE(!zone_spans_pfn(page_zone(page), pfn))
  kernel BUG at mm/page_alloc.c:372!
   free_unref_folios+0x55e/0xed0
   folios_put_refs+0x2b5/0x530

An attempt has been made [1] to fix this with the span seqlock check.
But that can trigger all sorts of problems in restricted contexts, as
noted by sashiko [2].

Since this is just a sanity debug check and anything with BUG_ON is
frowned upon anyway, just remove it altogether.

Reported-by: Josef Bacik <josef@toxicpanda.com>
Closes: https://lore.kernel.org/all/20261027-pgalloc-ai-hotfixes-v1-9-9c7d1aca801e@kernel.org/ [1]
Link: https://lore.kernel.org/all/sashiko-outbox-188081@kernel.org/ [2]
Fixes: 86051ca5eaf5 ("mm: fix usemap initialization")
Cc: <stable@vger.kernel.org>
Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
---
 mm/page_alloc.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 41fd80c0c762..3a7b9b533368 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -370,7 +370,6 @@ get_pfnblock_bitmap_bitidx(const struct page *page, unsigned long pfn,
 	BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4);
 #endif
 	BUILD_BUG_ON(__MIGRATE_TYPE_END > PAGEBLOCK_MIGRATETYPE_MASK);
-	VM_BUG_ON_PAGE(!zone_spans_pfn(page_zone(page), pfn), page);
 
 	bitmap = get_pageblock_bitmap(page, pfn);
 	*bitidx = pfn_to_bitidx(page, pfn);

-- 
2.56.0



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

* Re: [PATCH v2 1/5] mm/page_alloc: clear ->private of deferred free pages
  2026-11-03 16:16 ` [PATCH v2 1/5] mm/page_alloc: clear ->private of deferred free pages Vlastimil Babka (SUSE)
@ 2026-11-03 18:27   ` Johannes Weiner
  0 siblings, 0 replies; 8+ messages in thread
From: Johannes Weiner @ 2026-11-03 18:27 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Gregory Price (Meta), Zi Yan, Shakeel Butt, Mike Rapoport,
	David Hildenbrand, Oscar Salvador, Matthew Wilcox (Oracle),
	Suren Baghdasaryan, Michal Hocko, Brendan Jackman, linux-mm,
	linux-kernel, Josef Bacik, stable

On Tue, Nov 03, 2026 at 05:16:47PM +0100, Vlastimil Babka (SUSE) wrote:
> From: Josef Bacik <josef@toxicpanda.com>
> 
> free_pages_nolock() and free_frozen_pages_nolock() put the page on
> zone->trylock_free_pages if they get neither the pcp lock nor zone->lock.
> add_page_to_zone_llist() keeps the order of the page in page->private.
> The next free_one_page() which does get zone->lock passes these pages to
> split_large_buddy() with ->private still set.
> 
> __free_one_page() writes ->private of the head of the resulting free page
> only. If the deferred page is merged with its lower buddy, it becomes a
> tail of that free page and keeps the order in ->private.
> 
> All other free pages which are not the head of a buddy have a ->private
> of zero, and since commit 819bfbd47e49 ("mm/page_alloc: remove
> set_page_private() in prep_compound_tail()") the allocator relies on it.
> When the stale page is allocated again as a tail of a larger page:
> 
>  - prep_compound_tail() hits VM_WARN_ON_ONCE(tail->private);
>  - with the page checks enabled, __free_pages_prepare() reports
>    "BUG: Bad page state", "nonzero private", and leaks the whole page;
>  - without them the value is seen by whoever uses that page next, e.g. as
>    folio->private of a folio which is split off a large folio.
> 
> Only pages of an order above zero are affected. Those can be freed
> through _nolock() by the stack depot (order 2) and by SLUB (a new slab
> which cannot be added to the partial list by kmalloc_nolock()).
> 
> Clear ->private when the page is taken off the list.
> 
> Fixes: 8c57b687e833 ("mm, bpf: Introduce free_pages_nolock()")
> Cc: <stable@vger.kernel.org>
> Assisted-by: LLM
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
> Reviewed-by: Zi Yan <ziy@nvidia.com>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>

Reviewed-by: Johannes Weiner <hannes@cmpxchg.org>


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

* Re: [PATCH v2 3/5] mm: page_isolation: do not take slab pages for free
  2026-11-03 16:16 ` [PATCH v2 3/5] mm: page_isolation: do not take slab pages for free Vlastimil Babka (SUSE)
@ 2026-11-03 18:33   ` Johannes Weiner
  0 siblings, 0 replies; 8+ messages in thread
From: Johannes Weiner @ 2026-11-03 18:33 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Gregory Price (Meta), Zi Yan, Shakeel Butt, Mike Rapoport,
	David Hildenbrand, Oscar Salvador, Matthew Wilcox (Oracle),
	Suren Baghdasaryan, Michal Hocko, Brendan Jackman, linux-mm,
	linux-kernel, Josef Bacik

On Tue, Nov 03, 2026 at 05:16:49PM +0100, Vlastimil Babka (SUSE) wrote:
> From: Josef Bacik <josef@toxicpanda.com>
> 
> page_is_unmovable() regards a page with a reference count of zero as free
> or about to be freed. Since slab allocates frozen pages, they will
> be wrongly considered to be freed if order 0 (higher-order compound
> pages are caught earlier).
> 
> If such slabs are the only unmovable pages of a memory block, memory
> offlining now isolates the block, this can lead to offline_pages()
> looping without progress.
> 
> Thus detect the slab pages as removable to return -EBUSY, as would
> happen before commit 9aec2fb0fd5e ("slab: allocate frozen pages").
> 
> Fixes: 9aec2fb0fd5e ("slab: allocate frozen pages")
> Assisted-by: LLM
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
> Reviewed-by: Zi Yan <ziy@nvidia.com>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>

Reviewed-by: Johannes Weiner <hannes@cmpxchg.org>


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

end of thread, other threads:[~2026-11-03 18:33 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-03 16:16 [PATCH v2 0/5] mm, page_alloc: several stable fixes Vlastimil Babka (SUSE)
2026-11-03 16:16 ` [PATCH v2 1/5] mm/page_alloc: clear ->private of deferred free pages Vlastimil Babka (SUSE)
2026-11-03 18:27   ` Johannes Weiner
2026-11-03 16:16 ` [PATCH v2 2/5] mm: page_frag: refill the cache for large fragments Vlastimil Babka (SUSE)
2026-11-03 16:16 ` [PATCH v2 3/5] mm: page_isolation: do not take slab pages for free Vlastimil Babka (SUSE)
2026-11-03 18:33   ` Johannes Weiner
2026-11-03 16:16 ` [PATCH v2 4/5] mm/memory_hotplug: resize zones under their seqlock Vlastimil Babka (SUSE)
2026-11-03 16:16 ` [PATCH v2 5/5] mm/page_alloc: remove the zone_spans_pfn() check from get_pfnblock_bitmap_bitidx() Vlastimil Babka (SUSE)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox