[RFC PATCH 14/45] mm: page_alloc: extract claim_whole_block from try_to_claim_block

Rik van Riel
2026-04-30T20:22:50Z

Extract the whole-pageblock claiming logic from try_to_claim_block()
into a standalone claim_whole_block() function. This handles the
PB_all_free → used transition, pageblock migratetype change, and
block splitting for orders >= pageblock_order.

Pure refactoring, no functional change. Prepares for reuse of this
logic in the per-superpageblock free lists patch.

Signed-off-by: Rik van Riel <riel@surriel.com>
Assisted-by: Claude:claude-opus-4.7 syzkaller
---
mm/page_alloc.c | 90 +++++++++++++++++++++++++++++--------------------
1 file changed, 54 insertions(+), 36 deletions(-)

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 8b10322d5221..907ce46c060f 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2731,6 +2731,57 @@ int find_suitable_fallback(struct free_area *area, unsigned int order,
return -1;
}

+/*
+ * claim_whole_block - claim a free block (>= pageblock_order) for a new type
+ * @zone: zone containing the page
+ * @page: free page to claim
+ * @current_order: order of the free page
+ * @order: requested allocation order
+ * @new_type: migratetype to assign
+ * @old_type: current migratetype of the block (for free list removal)
+ *
+ * Handle the PB_all_free → used transition, change the pageblock
+ * migratetype, split the block down to @order, and return the page.
+ */
+static struct page *
+claim_whole_block(struct zone *zone, struct page *page,
+ int current_order, int order, int new_type, int old_type)
+{
+ struct superpageblock *sb;
+ unsigned int nr_added;
+ unsigned long pb_pfn;
+
+ VM_WARN_ON_ONCE(current_order < order);
+
+ /*
+ * Clear PB_all_free for pageblocks being claimed.
+ * This path bypasses page_del_and_expand(), so we
+ * must handle the free→used transition here.
+ */
+ for (pb_pfn = page_to_pfn(page);
+ pb_pfn < page_to_pfn(page) + (1 << current_order);
+ pb_pfn += pageblock_nr_pages) {
+ struct page *pb_page = pfn_to_page(pb_pfn);
+
+ if (get_pfnblock_bit(pb_page, pb_pfn, PB_all_free)) {
+ clear_pfnblock_bit(pb_page, pb_pfn, PB_all_free);
+ superpageblock_pb_now_used(pb_page);
+ }
+ __spb_set_has_type(pb_page, new_type);
+ }
+
+ del_page_from_free_list(page, zone, current_order, old_type);
+ change_pageblock_range(page, current_order, new_type);
+ nr_added = expand(zone, page, order, current_order, new_type);
+ account_freepages(zone, nr_added, new_type);
+
+ /* Single list update after all pageblocks processed */
+ sb = pfn_to_superpageblock(zone, page_to_pfn(page));
+ if (sb)
+ spb_update_list(sb);
+ return page;
+}
+
/*
* This function implements actual block claiming behaviour. If order is large
* enough, we can claim the whole pageblock for the requested migratetype. If
@@ -2754,42 +2805,9 @@ try_to_claim_block(struct zone *zone, struct page *page,
return NULL;

/* Take ownership for orders >= pageblock_order */
- if (current_order >= pageblock_order) {
- unsigned int nr_added;
- unsigned long pb_pfn;
-
- /*
- * Clear PB_all_free for pageblocks being claimed.
- * This path bypasses page_del_and_expand(), so we
- * must handle the free→used transition here.
- * Use block_type (the original migratetype) because
- * that's what was decremented when PB_all_free was set.
- */
- for (pb_pfn = page_to_pfn(page);
- pb_pfn < page_to_pfn(page) + (1 << current_order);
- pb_pfn += pageblock_nr_pages) {
- struct page *pb_page = pfn_to_page(pb_pfn);
-
- if (get_pfnblock_bit(pb_page, pb_pfn, PB_all_free)) {
- clear_pfnblock_bit(pb_page, pb_pfn, PB_all_free);
- superpageblock_pb_now_used(pb_page);
- }
- __spb_set_has_type(pb_page, start_type);
- }
- /* Single list update after all pageblocks processed */
- {
- struct superpageblock *sb =
- pfn_to_superpageblock(zone, page_to_pfn(page));
- if (sb)
- spb_update_list(sb);
- }
-
- del_page_from_free_list(page, zone, current_order, block_type);
- change_pageblock_range(page, current_order, start_type);
- nr_added = expand(zone, page, order, current_order, start_type);
- account_freepages(zone, nr_added, start_type);
- return page;
- }
+ if (current_order >= pageblock_order)
+ return claim_whole_block(zone, page, current_order, order,
+ start_type, block_type);

/* moving whole block can fail due to zone boundary conditions */
if (!prep_move_freepages_block(zone, page, &start_pfn, &free_pages,
--
2.52.0