From 69c61d4b7e42ed308cd9b9d5dc46bc5e1dc4bae5 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Thu, 20 Aug 2026 00:44:34 +0900 Subject: [PATCH 01/15] memblock: simplify reset_all_zones_managed_pages() reset_all_zones_managed_pages() is only called from memblock_free_all(), which runs once during boot. So remove the reset_managed_pages_done guard, make the function static and drop its declaration. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260819154436.3225746-1-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/memblock.h | 1 - mm/memblock.c | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/include/linux/memblock.h b/include/linux/memblock.h index d62db9e776cf..aa845f488327 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -162,7 +162,6 @@ int memblock_mark_kho_scratch(phys_addr_t base, phys_addr_t size); int memblock_clear_kho_scratch(phys_addr_t base, phys_addr_t size); void memblock_free(void *ptr, size_t size); -void reset_all_zones_managed_pages(void); /* Low level functions */ void __next_mem_range(u64 *idx, int nid, enum memblock_flags flags, diff --git a/mm/memblock.c b/mm/memblock.c index 9ce86349a29f..fd9774f1583b 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -2465,8 +2465,6 @@ static unsigned long __init free_low_memory_core_early(void) return count; } -static int reset_managed_pages_done __initdata; - static void __init reset_node_managed_pages(pg_data_t *pgdat) { struct zone *z; @@ -2475,17 +2473,12 @@ static void __init reset_node_managed_pages(pg_data_t *pgdat) atomic_long_set(&z->managed_pages, 0); } -void __init reset_all_zones_managed_pages(void) +static void __init reset_all_zones_managed_pages(void) { struct pglist_data *pgdat; - if (reset_managed_pages_done) - return; - for_each_online_pgdat(pgdat) reset_node_managed_pages(pgdat); - - reset_managed_pages_done = 1; } /** From c1a6b4ca3b68ef794f5e754af00592721dddc10d Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Mon, 24 Aug 2026 00:48:38 +0900 Subject: [PATCH 02/15] memblock: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260823154840.3793140-1-ekffu200098@gmail.com Reviewed-by: Andrew Morton Signed-off-by: Mike Rapoport (Microsoft) --- mm/memblock.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mm/memblock.c b/mm/memblock.c index fd9774f1583b..4302fb4ab85c 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -2693,15 +2693,10 @@ static int __init prepare_kho_fdt(void) static int __init reserve_mem_init(void) { - int err; - if (!kho_is_enabled() || !reserved_mem_count) return 0; - err = prepare_kho_fdt(); - if (err) - return err; - return err; + return prepare_kho_fdt(); } late_initcall(reserve_mem_init); From dbffc67777cd752ef3f77818109e1d2ef13e2949 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Wed, 2 Sep 2026 01:51:49 +0900 Subject: [PATCH 03/15] mm/mm_init: remove zone_absent_pages_in_node() zone_absent_pages_in_node() returns 0 when the zone is empty and otherwise calls __absent_pages_in_range(), which already returns 0 for an empty range. So add an early return to __absent_pages_in_range() to avoid looping over the memory ranges, then remove zone_absent_pages_in_node() and call __absent_pages_in_range() directly. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260901165205.1025640-1-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/mm_init.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 1533aebafb68..c00ac947a7d6 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1166,6 +1166,10 @@ static unsigned long __init __absent_pages_in_range(int nid, unsigned long start_pfn, end_pfn; int i; + /* range is empty, nothing to do */ + if (!nr_absent) + return 0; + for_each_mem_pfn_range(i, nid, &start_pfn, &end_pfn, NULL) { start_pfn = clamp(start_pfn, range_start_pfn, range_end_pfn); end_pfn = clamp(end_pfn, range_start_pfn, range_end_pfn); @@ -1187,22 +1191,9 @@ unsigned long __init absent_pages_in_range(unsigned long start_pfn, return __absent_pages_in_range(MAX_NUMNODES, start_pfn, end_pfn); } -/* Return the number of page frames in holes in a zone on a node */ -static unsigned long __init zone_absent_pages_in_node(int nid, - unsigned long zone_type, - unsigned long zone_start_pfn, - unsigned long zone_end_pfn) -{ - /* zone is empty, we don't have any absent pages */ - if (zone_start_pfn == zone_end_pfn) - return 0; - - return __absent_pages_in_range(nid, zone_start_pfn, zone_end_pfn); -} - /* * Return the number of pages a zone spans in a node, including holes - * present_pages = zone_spanned_pages_in_node() - zone_absent_pages_in_node() + * present_pages = zone_spanned_pages_in_node() - __absent_pages_in_range() */ static unsigned long __init zone_spanned_pages_in_node(int nid, unsigned long zone_type, @@ -1292,9 +1283,8 @@ static void __init calculate_node_totalpages(struct pglist_data *pgdat, node_end_pfn, &zone_start_pfn, &zone_end_pfn); - absent = zone_absent_pages_in_node(pgdat->node_id, i, - zone_start_pfn, - zone_end_pfn); + absent = __absent_pages_in_range(pgdat->node_id, zone_start_pfn, + zone_end_pfn); real_size = spanned - absent; From f68c01000a6c3bddbf415b6e50a201196ec78358 Mon Sep 17 00:00:00 2001 From: Tianyi Chen Date: Thu, 10 Sep 2026 07:55:28 +0800 Subject: [PATCH 04/15] memblock tests: model the low allocation limit within dummy memory The simulator represents physical addresses using the address of its allocated buffer. That buffer can lie above the default ARCH_LOW_ADDRESS_LIMIT, preventing meaningful memblock_alloc_low() tests. Use the architecture override in asm/dma.h to place the limit halfway through the memory registered by setup_memblock(). Resolve the limit after allocating the buffer, leaving registered memory on both sides. Assisted-by: LLM Signed-off-by: Tianyi Chen Link: https://patch.msgid.link/f16c272cd52316b83c35d87987b4d8687c4a3182.1788997523.git.hi@tychen.cc Signed-off-by: Mike Rapoport (Microsoft) --- tools/testing/memblock/asm/dma.h | 6 ++++++ tools/testing/memblock/tests/common.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/tools/testing/memblock/asm/dma.h b/tools/testing/memblock/asm/dma.h index 13ff8e5d22ef..6607408dcf25 100644 --- a/tools/testing/memblock/asm/dma.h +++ b/tools/testing/memblock/asm/dma.h @@ -2,4 +2,10 @@ #ifndef _TOOLS_DMA_H #define _TOOLS_DMA_H +#include + +phys_addr_t dummy_physical_memory_low_limit(void); + +#define ARCH_LOW_ADDRESS_LIMIT dummy_physical_memory_low_limit() + #endif diff --git a/tools/testing/memblock/tests/common.c b/tools/testing/memblock/tests/common.c index 3250c8e5124b..ac032610a56e 100644 --- a/tools/testing/memblock/tests/common.c +++ b/tools/testing/memblock/tests/common.c @@ -118,6 +118,12 @@ phys_addr_t dummy_physical_memory_base(void) return (phys_addr_t)memory_block.base; } +phys_addr_t dummy_physical_memory_low_limit(void) +{ + /* The low limit is halfway through memory registered by setup_memblock(). */ + return dummy_physical_memory_base() + MEM_SIZE / 2; +} + static void usage(const char *prog) { BUILD_BUG_ON(ARRAY_SIZE(help_opts) != ARRAY_SIZE(long_opts) - 1); From 2345d7e354e84c140de225022a38754eab499a94 Mon Sep 17 00:00:00 2001 From: Tianyi Chen Date: Thu, 10 Sep 2026 07:55:33 +0800 Subject: [PATCH 05/15] memblock tests: cover allocations below the low address limit Add memblock_alloc_low() coverage using the simulator's low address limit. Exercise aligned allocation, an allocation whose last byte is immediately below the limit, an alignment constraint that prevents fitting below it, and fully reserved low memory with high memory still available. Run each case with bottom-up and top-down allocation. Check zeroing and reserved-region accounting as well as returned addresses. Verify that an unrestricted allocation can use the free high memory after the low allocation fails. Document the simulated limit and remove the completed TODO. Assisted-by: LLM Signed-off-by: Tianyi Chen Link: https://patch.msgid.link/142ab1025fd1e4b5dcd2a1591b964626ebce9d23.1788997523.git.hi@tychen.cc Signed-off-by: Mike Rapoport (Microsoft) --- tools/testing/memblock/Makefile | 3 +- tools/testing/memblock/README | 12 +- tools/testing/memblock/TODO | 5 - tools/testing/memblock/main.c | 2 + tools/testing/memblock/tests/alloc_low_api.c | 148 +++++++++++++++++++ tools/testing/memblock/tests/alloc_low_api.h | 9 ++ 6 files changed, 166 insertions(+), 13 deletions(-) delete mode 100644 tools/testing/memblock/TODO create mode 100644 tools/testing/memblock/tests/alloc_low_api.c create mode 100644 tools/testing/memblock/tests/alloc_low_api.h diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile index d80982ccdc20..20524fcbe3c7 100644 --- a/tools/testing/memblock/Makefile +++ b/tools/testing/memblock/Makefile @@ -7,7 +7,8 @@ CFLAGS += -I. -I../../include -Wall -O2 -fsanitize=address \ LDFLAGS += -fsanitize=address -fsanitize=undefined TARGETS = main TEST_OFILES = tests/alloc_nid_api.o tests/alloc_helpers_api.o tests/alloc_api.o \ - tests/basic_api.o tests/common.o tests/alloc_exact_nid_api.o + tests/basic_api.o tests/common.o tests/alloc_exact_nid_api.o \ + tests/alloc_low_api.o DEP_OFILES = memblock.o lib/slab.o mmzone.o slab.o cmdline.o OFILES = main.o $(DEP_OFILES) $(TEST_OFILES) EXTR_SRC = ../../../mm/memblock.c diff --git a/tools/testing/memblock/README b/tools/testing/memblock/README index b435f48d8a70..4ea293209548 100644 --- a/tools/testing/memblock/README +++ b/tools/testing/memblock/README @@ -67,13 +67,13 @@ memblock |-- tests | |-- alloc_api.(c|h) -- memblock_alloc tests | |-- alloc_helpers_api.(c|h) -- memblock_alloc_from tests +| |-- alloc_low_api.(c|h) -- memblock_alloc_low tests | |-- alloc_nid_api.(c|h) -- memblock_alloc_try_nid tests | |-- basic_api.(c|h) -- memblock_add/memblock_reserve/... tests | |-- common.(c|h) -- helper functions for resetting memblock; |-- main.c --------------. dummy physical memory definition |-- Makefile `- test runner |-- README -|-- TODO |-- .gitignore Simulating physical memory @@ -101,12 +101,10 @@ There's no need to explicitly free the dummy memory from memblock via memblock_free() call. The entry will be erased by reset_memblock_regions(), called at the beginning of each test. -Known issues -============ - -1. Tests for memblock_alloc_low() can't be easily implemented. The function uses - ARCH_LOW_ADDRESS_LIMIT marco, which can't be changed to point at the low - memory of the memory_block. +The simulator defines ARCH_LOW_ADDRESS_LIMIT in asm/dma.h using the midpoint +of the MEM_SIZE range registered by setup_memblock(). This leaves registered +memory both below and above the limit. The limit is the first address an +allocation must not use; an allocation's base plus its size may equal the limit. References ========== diff --git a/tools/testing/memblock/TODO b/tools/testing/memblock/TODO deleted file mode 100644 index c13ad0dae776..000000000000 --- a/tools/testing/memblock/TODO +++ /dev/null @@ -1,5 +0,0 @@ -TODO -===== - -1. Add tests for memblock_alloc_low() once the simulator can model - ARCH_LOW_ADDRESS_LIMIT against the low memory in memory_block diff --git a/tools/testing/memblock/main.c b/tools/testing/memblock/main.c index 278f9dec5008..9a96e178551b 100644 --- a/tools/testing/memblock/main.c +++ b/tools/testing/memblock/main.c @@ -4,6 +4,7 @@ #include "tests/alloc_helpers_api.h" #include "tests/alloc_nid_api.h" #include "tests/alloc_exact_nid_api.h" +#include "tests/alloc_low_api.h" #include "tests/common.h" int main(int argc, char **argv) @@ -12,6 +13,7 @@ int main(int argc, char **argv) memblock_basic_checks(); memblock_alloc_checks(); memblock_alloc_helpers_checks(); + memblock_alloc_low_checks(); memblock_alloc_nid_checks(); memblock_alloc_exact_nid_checks(); diff --git a/tools/testing/memblock/tests/alloc_low_api.c b/tools/testing/memblock/tests/alloc_low_api.c new file mode 100644 index 000000000000..a117431f1f71 --- /dev/null +++ b/tools/testing/memblock/tests/alloc_low_api.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "alloc_low_api.h" +#include + +/* Allocate at the first or last aligned address below the low limit. */ +static int alloc_low_simple_check(void) +{ + struct memblock_region *rgn = &memblock.reserved.regions[0]; + phys_addr_t size = SZ_64; + phys_addr_t expected; + void *allocated_ptr; + + PREFIX_PUSH(); + setup_memblock(); + + /* Simulated physical RAM is not necessarily SMP_CACHE_BYTES aligned. */ + if (memblock_bottom_up()) + expected = ALIGN(memblock_start_of_DRAM(), SMP_CACHE_BYTES); + else + expected = ALIGN_DOWN(ARCH_LOW_ADDRESS_LIMIT - size, + SMP_CACHE_BYTES); + + allocated_ptr = memblock_alloc_low(size, SMP_CACHE_BYTES); + + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ((phys_addr_t)(uintptr_t)allocated_ptr, expected); + ASSERT_MEM_EQ(allocated_ptr, 0, size); + ASSERT_EQ(rgn->base, expected); + ASSERT_EQ(rgn->size, size); + ASSERT_LE(region_end(rgn), ARCH_LOW_ADDRESS_LIMIT); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); + + test_pass_pop(); + return 0; +} + +/* The last byte of the allocation is immediately below the low limit. */ +static int alloc_low_exact_limit_check(void) +{ + phys_addr_t limit = ARCH_LOW_ADDRESS_LIMIT; + phys_addr_t base = ALIGN_DOWN(limit - SZ_64, SMP_CACHE_BYTES); + phys_addr_t size = limit - base; + void *allocated_ptr; + + PREFIX_PUSH(); + setup_memblock(); + ASSERT_EQ(memblock_remove(memblock_start_of_DRAM(), + base - memblock_start_of_DRAM()), 0); + + allocated_ptr = memblock_alloc_low(size, SMP_CACHE_BYTES); + + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ((phys_addr_t)(uintptr_t)allocated_ptr, base); + ASSERT_MEM_EQ(allocated_ptr, 0, size); + ASSERT_EQ(memblock.reserved.regions[0].base, base); + ASSERT_EQ(region_end(&memblock.reserved.regions[0]), limit); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); + + test_pass_pop(); + return 0; +} + +/* + * There are size bytes below the limit, but aligning the start makes the + * allocation cross it. Memory above the limit must not satisfy the request. + */ +static int alloc_low_alignment_crosses_limit_check(void) +{ + phys_addr_t limit = ARCH_LOW_ADDRESS_LIMIT; + phys_addr_t base = ALIGN_DOWN(limit, SMP_CACHE_BYTES) - 1; + phys_addr_t size = limit - base; + void *allocated_ptr; + + PREFIX_PUSH(); + setup_memblock(); + ASSERT_EQ(memblock_remove(memblock_start_of_DRAM(), + base - memblock_start_of_DRAM()), 0); + + allocated_ptr = memblock_alloc_low(size, SMP_CACHE_BYTES); + + ASSERT_EQ(allocated_ptr, NULL); + ASSERT_EQ(memblock.reserved.cnt, 0); + ASSERT_EQ(memblock.reserved.total_size, 0); + ASSERT_MEM_EQ((void *)(uintptr_t)base, 1, memblock_end_of_DRAM() - base); + + test_pass_pop(); + return 0; +} + +/* Allocation must fail after reserving all low memory, with high memory free. */ +static int alloc_low_reserved_check(void) +{ + phys_addr_t limit = ARCH_LOW_ADDRESS_LIMIT; + phys_addr_t base = dummy_physical_memory_base(); + phys_addr_t size = SZ_64; + void *allocated_ptr; + + PREFIX_PUSH(); + setup_memblock(); + ASSERT_EQ(memblock_reserve(base, limit - base), 0); + + allocated_ptr = memblock_alloc_low(size, SMP_CACHE_BYTES); + + ASSERT_EQ(allocated_ptr, NULL); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.regions[0].base, base); + ASSERT_EQ(memblock.reserved.regions[0].size, limit - base); + ASSERT_EQ(memblock.reserved.total_size, limit - base); + ASSERT_MEM_EQ((void *)(uintptr_t)base, 1, MEM_SIZE); + + allocated_ptr = memblock_alloc(size, SMP_CACHE_BYTES); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_LE(limit, (phys_addr_t)(uintptr_t)allocated_ptr); + ASSERT_MEM_EQ(allocated_ptr, 0, size); + + test_pass_pop(); + return 0; +} + +static int alloc_low_checks(void) +{ + alloc_low_simple_check(); + alloc_low_exact_limit_check(); + alloc_low_alignment_crosses_limit_check(); + alloc_low_reserved_check(); + + return 0; +} + +int memblock_alloc_low_checks(void) +{ + prefix_reset(); + prefix_push("memblock_alloc_low"); + test_print("Running memblock_alloc_low tests...\n"); + + reset_memblock_attributes(); + dummy_physical_memory_init(); + + run_top_down(alloc_low_checks); + run_bottom_up(alloc_low_checks); + + dummy_physical_memory_cleanup(); + prefix_pop(); + + return 0; +} diff --git a/tools/testing/memblock/tests/alloc_low_api.h b/tools/testing/memblock/tests/alloc_low_api.h new file mode 100644 index 000000000000..2e3cbe336f5d --- /dev/null +++ b/tools/testing/memblock/tests/alloc_low_api.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _MEMBLOCK_ALLOC_LOW_H +#define _MEMBLOCK_ALLOC_LOW_H + +#include "common.h" + +int memblock_alloc_low_checks(void); + +#endif From 5728d3523f12826a2b180c1c5738f74e33197e14 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sat, 5 Sep 2026 23:15:24 +0900 Subject: [PATCH 06/15] memblock: reject non-page-aligned reserve_mem regions When map->start or map->size is not page aligned, reserved_mem_preserve() does not preserve the last one or two pages of the region from map->start to map->start + map->size. After kexec the new kernel reserves only the preserved pages, so those pages are released to the buddy allocator. But reserve_mem_find_by_name() still returns the whole region including them, so they can be allocated while the subsystem is still using them. So reject a reserve_mem= size or alignment that is not a multiple of PAGE_SIZE to keep both map->start and map->size page aligned. Fixes: 8375b76517cb ("kho: replace kho_preserve_phys() with kho_preserve_pages()") Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260905141526.1546478-1-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- Documentation/admin-guide/kernel-parameters.txt | 3 +++ mm/memblock.c | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 68647ff4bdd2..f969085fffb7 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6648,6 +6648,9 @@ Kernel parameters reserve_mem=12M:4096:oops ramoops.mem_name=oops + Both the size and the alignment must be multiples of + the page size. + reservetop= [X86-32,EARLY] Format: nn[KMG] Reserves a hole at the top of the kernel virtual diff --git a/mm/memblock.c b/mm/memblock.c index 31961ed628b0..22d14a637f24 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -2816,12 +2816,17 @@ static int __init reserve_mem(char *p) if (*p != ':') goto err_param; + if (!IS_ALIGNED(size, PAGE_SIZE) || !IS_ALIGNED(align, PAGE_SIZE)) { + pr_err("reserve_mem: size and align must be multiples of the page size\n"); + return -EINVAL; + } + /* * memblock_phys_alloc() doesn't like a zero size align, * but it is OK for this command to have it. */ - if (align < SMP_CACHE_BYTES) - align = SMP_CACHE_BYTES; + if (!align) + align = PAGE_SIZE; name = p + 1; len = strlen(name); From 9357596afac61154e7f54048c0add9f1293b9eba Mon Sep 17 00:00:00 2001 From: Tianyi Chen Date: Wed, 23 Sep 2026 11:31:23 +0900 Subject: [PATCH 07/15] memblock tests: use physical addresses for region removal With BUILD=32, phys_addr_t remains 64-bit while pointers are 32-bit. Casting the address near PHYS_ADDR_MAX to void * truncates it before memblock_free() converts it back with __pa(). The reserved region is therefore left unchanged and memblock_free_near_max_check() fails. Use memblock_phys_free() for all synthetic physical ranges in the basic API tests. This also removes the integer-to-pointer cast warnings from the other removal checks. Retain the checks that freeing past PHYS_ADDR_MAX clips the removed region. Assisted-by: LLM Signed-off-by: Tianyi Chen Link: https://patch.msgid.link/20260923023126.1910311-2-hi@tychen.cc Signed-off-by: Mike Rapoport (Microsoft) --- tools/testing/memblock/tests/basic_api.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 01e836fba488..47bcdcc115a5 100644 --- a/tools/testing/memblock/tests/basic_api.c +++ b/tools/testing/memblock/tests/basic_api.c @@ -1730,7 +1730,7 @@ static int memblock_free_simple_check(void) reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_reserve(r2.base, r2.size); - memblock_free((void *)r1.base, r1.size); + memblock_phys_free(r1.base, r1.size); ASSERT_EQ(rgn->base, r2.base); ASSERT_EQ(rgn->size, r2.size); @@ -1778,7 +1778,7 @@ static int memblock_free_absent_check(void) reset_memblock_regions(); memblock_reserve(r1.base, r1.size); - memblock_free((void *)r2.base, r2.size); + memblock_phys_free(r2.base, r2.size); ASSERT_EQ(rgn->base, r1.base); ASSERT_EQ(rgn->size, r1.size); @@ -1832,7 +1832,7 @@ static int memblock_free_overlap_top_check(void) reset_memblock_regions(); memblock_reserve(r1.base, r1.size); - memblock_free((void *)r2.base, r2.size); + memblock_phys_free(r2.base, r2.size); ASSERT_EQ(rgn->base, r2.base + r2.size); ASSERT_EQ(rgn->size, total_size); @@ -1881,7 +1881,7 @@ static int memblock_free_overlap_bottom_check(void) reset_memblock_regions(); memblock_reserve(r1.base, r1.size); - memblock_free((void *)r2.base, r2.size); + memblock_phys_free(r2.base, r2.size); ASSERT_EQ(rgn->base, r1.base); ASSERT_EQ(rgn->size, total_size); @@ -1938,7 +1938,7 @@ static int memblock_free_within_check(void) reset_memblock_regions(); memblock_reserve(r1.base, r1.size); - memblock_free((void *)r2.base, r2.size); + memblock_phys_free(r2.base, r2.size); ASSERT_EQ(rgn1->base, r1.base); ASSERT_EQ(rgn1->size, r1_size); @@ -1976,7 +1976,7 @@ static int memblock_free_only_region_check(void) reset_memblock_regions(); memblock_reserve(r1.base, r1.size); - memblock_free((void *)r1.base, r1.size); + memblock_phys_free(r1.base, r1.size); ASSERT_EQ(rgn->base, 0); ASSERT_EQ(rgn->size, 0); @@ -2003,7 +2003,7 @@ static int memblock_free_only_region_check(void) * Expect the total size of reserved memory to be updated and the counter to * not be updated. */ -static int memblock_free_near_max_check(void) +static int memblock_phys_free_near_max_check(void) { struct memblock_region *rgn; phys_addr_t total_size; @@ -2026,7 +2026,7 @@ static int memblock_free_near_max_check(void) reset_memblock_regions(); memblock_reserve(r1.base, r1.size); - memblock_free((void *)r2.base, r2.size); + memblock_phys_free(r2.base, r2.size); ASSERT_EQ(rgn->base, r1.base); ASSERT_EQ(rgn->size, total_size); @@ -2086,7 +2086,7 @@ static int memblock_free_overlap_two_check(void) reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_reserve(r2.base, r2.size); - memblock_free((void *)r3.base, r3.size); + memblock_phys_free(r3.base, r3.size); ASSERT_EQ(rgn1->base, r1.base); ASSERT_EQ(rgn1->size, new_r1_size); @@ -2114,7 +2114,7 @@ static int memblock_free_checks(void) memblock_free_overlap_bottom_check(); memblock_free_within_check(); memblock_free_only_region_check(); - memblock_free_near_max_check(); + memblock_phys_free_near_max_check(); memblock_free_overlap_two_check(); prefix_pop(); From 6957416ae43de1830174f527b5b8cf737401a8dd Mon Sep 17 00:00:00 2001 From: Tianyi Chen Date: Wed, 23 Sep 2026 11:31:24 +0900 Subject: [PATCH 08/15] memblock tests: zero-extend simulated memory addresses Converting a 32-bit pointer directly to the 64-bit phys_addr_t can sign-extend it when BUILD=32 is used. A dummy allocation above 2 GiB then gets registered at an address different from its unsigned pointer value. The low-address tests compare that value against the registered range and fail once the earlier maximum-address free test is fixed. Convert through uintptr_t when registering and reporting dummy memory. This preserves the pointer bits and agrees with the simulator __pa() conversion and the allocation tests. Assisted-by: LLM Signed-off-by: Tianyi Chen Link: https://patch.msgid.link/20260923023126.1910311-3-hi@tychen.cc Signed-off-by: Mike Rapoport (Microsoft) --- tools/testing/memblock/tests/common.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/memblock/tests/common.c b/tools/testing/memblock/tests/common.c index ac032610a56e..2a248973eb4c 100644 --- a/tools/testing/memblock/tests/common.c +++ b/tools/testing/memblock/tests/common.c @@ -67,7 +67,7 @@ static inline void fill_memblock(void) void setup_memblock(void) { reset_memblock_regions(); - memblock_add((phys_addr_t)memory_block.base, MEM_SIZE); + memblock_add((phys_addr_t)(uintptr_t)memory_block.base, MEM_SIZE); fill_memblock(); } @@ -88,7 +88,7 @@ void setup_numa_memblock(const unsigned int node_fracs[]) int flags; reset_memblock_regions(); - base = (phys_addr_t)memory_block.base; + base = (phys_addr_t)(uintptr_t)memory_block.base; flags = (movable_node_is_enabled()) ? MEMBLOCK_NONE : MEMBLOCK_HOTPLUG; for (int i = 0; i < NUMA_NODES; i++) { @@ -115,7 +115,7 @@ void dummy_physical_memory_cleanup(void) phys_addr_t dummy_physical_memory_base(void) { - return (phys_addr_t)memory_block.base; + return (phys_addr_t)(uintptr_t)memory_block.base; } phys_addr_t dummy_physical_memory_low_limit(void) From a10f3a08d248fcb3006f587faf42437837fd12c6 Mon Sep 17 00:00:00 2001 From: Tianyi Chen Date: Wed, 23 Sep 2026 11:31:25 +0900 Subject: [PATCH 09/15] tools: use __pa() in virt_to_phys() With 32-bit pointers and a 64-bit phys_addr_t, GCC warns about the pointer-to-integer cast in virt_to_phys(). It can also sign-extend pointers whose top bit is set, turning 0x80000000 into 0xffffffff80000000. Use __pa() to convert through unsigned long before widening the result. This preserves the unsigned pointer value and matches the other address conversion helpers. Reported-by: Mike Rapoport Link: https://lore.kernel.org/r/179007180279.508753.4595268983388589280.b4-review@b4 Assisted-by: LLM Signed-off-by: Tianyi Chen Link: https://patch.msgid.link/20260923023126.1910311-4-hi@tychen.cc Signed-off-by: Mike Rapoport (Microsoft) --- tools/include/linux/mm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/include/linux/mm.h b/tools/include/linux/mm.h index 84b5954f66c3..480526de8d65 100644 --- a/tools/include/linux/mm.h +++ b/tools/include/linux/mm.h @@ -30,7 +30,7 @@ static inline void *phys_to_virt(unsigned long address) #define virt_to_phys virt_to_phys static inline phys_addr_t virt_to_phys(volatile void *address) { - return (phys_addr_t)address; + return __pa(address); } static inline void totalram_pages_inc(void) From ba94f68b754985f77c3f6d3354df74952a1e0752 Mon Sep 17 00:00:00 2001 From: Tianyi Chen Date: Wed, 23 Sep 2026 11:31:26 +0900 Subject: [PATCH 10/15] memblock: initialize the region merge end index GCC 13 reports that end_rgn may be used uninitialized when building the memblock simulator with BUILD=32 and its default sanitizer flags. Every insertion sets end_rgn, and the nr_new check prevents merging when there were no insertions, so this is not an uninitialized read. Initialize the index to zero to avoid the warning without disabling the diagnostic. Reported-by: Mike Rapoport Link: https://lore.kernel.org/r/179007180279.508753.4595268983388589280.b4-review@b4 Assisted-by: LLM Signed-off-by: Tianyi Chen Link: https://patch.msgid.link/20260923023126.1910311-5-hi@tychen.cc Signed-off-by: Mike Rapoport (Microsoft) --- mm/memblock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memblock.c b/mm/memblock.c index 22d14a637f24..aee1fe36130b 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -614,7 +614,7 @@ static int __init_memblock memblock_add_range(struct memblock_type *type, bool insert = false; phys_addr_t obase = base; phys_addr_t end = base + memblock_cap_size(base, &size); - int idx, nr_new, start_rgn = -1, end_rgn; + int idx, nr_new, start_rgn = -1, end_rgn = 0; struct memblock_region *rgn; if (!size) From 063b5e8bbd216c27e885f45e29e23d053aa565fc Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Sat, 19 Sep 2026 21:53:25 +0800 Subject: [PATCH 11/15] mm: Remove unused exact_nid parameter from memmap_alloc() Commit ffe55393137c ("mm/sparse: remove sparse buffer pre-allocation mechanism") removed sparse_buffer_init(), which was the only caller of memmap_alloc() that passed true for exact_nid. All remaining callers pass false, leaving the exact-node allocation branch unreachable. Remove the exact_nid parameter and unconditionally use memblock_alloc_try_nid_raw(). This has no functional impact. Signed-off-by: Kaitao Cheng Link: https://patch.msgid.link/20260919135327.74089-2-kaitao.cheng@linux.dev Signed-off-by: Mike Rapoport (Microsoft) --- mm/mm_init.c | 14 ++++---------- mm/mm_init.h | 4 ++-- mm/sparse-vmemmap.c | 17 +---------------- mm/sparse.c | 2 +- 4 files changed, 8 insertions(+), 29 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index c00ac947a7d6..37bb9786345a 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1549,7 +1549,7 @@ static void __init free_area_init_core(struct pglist_data *pgdat) } void __init *memmap_alloc(phys_addr_t size, phys_addr_t align, - phys_addr_t min_addr, int nid, bool exact_nid) + phys_addr_t min_addr, int nid) { void *ptr; @@ -1557,14 +1557,8 @@ void __init *memmap_alloc(phys_addr_t size, phys_addr_t align, * Kmemleak will explicitly scan mem_map by traversing all valid * `struct *page`,so memblock does not need to be added to the scan list. */ - if (exact_nid) - ptr = memblock_alloc_exact_nid_raw(size, align, min_addr, - MEMBLOCK_ALLOC_NOLEAKTRACE, - nid); - else - ptr = memblock_alloc_try_nid_raw(size, align, min_addr, - MEMBLOCK_ALLOC_NOLEAKTRACE, - nid); + ptr = memblock_alloc_try_nid_raw(size, align, min_addr, + MEMBLOCK_ALLOC_NOLEAKTRACE, nid); if (ptr && size > 0) page_init_poison(ptr, size); @@ -1592,7 +1586,7 @@ static void __init alloc_node_mem_map(struct pglist_data *pgdat) end = ALIGN(pgdat_end_pfn(pgdat), MAX_ORDER_NR_PAGES); size = (end - start) * sizeof(struct page); map = memmap_alloc(size, SMP_CACHE_BYTES, MEMBLOCK_LOW_LIMIT, - pgdat->node_id, false); + pgdat->node_id); if (!map) panic("Failed to allocate %ld bytes for node %d memory map\n", size, pgdat->node_id); diff --git a/mm/mm_init.h b/mm/mm_init.h index 39f75df9be1c..3207ae33957b 100644 --- a/mm/mm_init.h +++ b/mm/mm_init.h @@ -31,8 +31,8 @@ static inline void clear_zone_contiguous(struct zone *zone) void memblock_free_pages(unsigned long pfn, unsigned int order); -void *memmap_alloc(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, - int nid, bool exact_nid); +void *memmap_alloc(phys_addr_t size, phys_addr_t align, + phys_addr_t min_addr, int nid); void memmap_init_range(unsigned long size, int nid, unsigned long zone, unsigned long start_pfn, unsigned long zone_end_pfn, diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 5a2469fb1838..87e1ff20156b 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -44,20 +44,6 @@ #include "mm_init.h" #include "sparse.h" -/* - * Allocate a block of memory to be used to back the virtual memory map - * or to back the page tables that are used to create the mapping. - * Uses the main allocators if they are available, else bootmem. - */ - -static void * __ref __earlyonly_bootmem_alloc(int node, - unsigned long size, - unsigned long align, - unsigned long goal) -{ - return memmap_alloc(size, align, goal, node, false); -} - void * __meminit vmemmap_alloc_block(unsigned long size, int node) { /* If the main allocator is up use that, fallback to bootmem. */ @@ -78,8 +64,7 @@ void * __meminit vmemmap_alloc_block(unsigned long size, int node) } return NULL; } else - return __earlyonly_bootmem_alloc(node, size, size, - __pa(MAX_DMA_ADDRESS)); + return memmap_alloc(size, size, __pa(MAX_DMA_ADDRESS), node); } static void * __meminit altmap_alloc_block_buf(unsigned long size, diff --git a/mm/sparse.c b/mm/sparse.c index 7c15406e77f5..117bc93f55d0 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -231,7 +231,7 @@ struct page __init *__populate_section_memmap(unsigned long pfn, { unsigned long size = section_map_size(); - return memmap_alloc(size, size, __pa(MAX_DMA_ADDRESS), nid, false); + return memmap_alloc(size, size, __pa(MAX_DMA_ADDRESS), nid); } #endif /* !CONFIG_SPARSEMEM_VMEMMAP */ From fba3e86fb848f79c4573243affcd7d79d378958d Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Sat, 19 Sep 2026 21:53:26 +0800 Subject: [PATCH 12/15] memblock tests: Move exact nid tests to memblock_alloc_range_nid() After memmap_alloc() stopped requesting allocations from an exact NUMA node, memblock_alloc_exact_nid_raw() no longer has any in-kernel users and is being removed. Preserve coverage of exact-node allocation by converting the applicable NUMA tests to call memblock_alloc_range_nid() with exact_nid set. Remove the tests that depend on memblock_alloc_internal() retrying below min_addr, as memblock_alloc_range_nid() treats its address range as a hard constraint. Remove the now-unused TEST_F_EXACT support from the shared allocation tests. Suggested-by: Lorenzo Stoakes Signed-off-by: Kaitao Cheng Link: https://patch.msgid.link/20260919135327.74089-3-kaitao.cheng@linux.dev Signed-off-by: Mike Rapoport (Microsoft) --- .../memblock/tests/alloc_exact_nid_api.c | 454 ++---------------- tools/testing/memblock/tests/alloc_nid_api.c | 20 - tools/testing/memblock/tests/alloc_nid_api.h | 1 - tools/testing/memblock/tests/common.h | 2 - 4 files changed, 37 insertions(+), 440 deletions(-) diff --git a/tools/testing/memblock/tests/alloc_exact_nid_api.c b/tools/testing/memblock/tests/alloc_exact_nid_api.c index 0c46c73b5e04..52c63d011870 100644 --- a/tools/testing/memblock/tests/alloc_exact_nid_api.c +++ b/tools/testing/memblock/tests/alloc_exact_nid_api.c @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "alloc_exact_nid_api.h" -#include "alloc_nid_api.h" -#define FUNC_NAME "memblock_alloc_exact_nid_raw" +#define FUNC_NAME "memblock_alloc_range_nid(exact)" /* * contains the fraction of MEM_SIZE contained in each node in basis point @@ -19,6 +18,18 @@ static const unsigned int node_fractions[] = { 625, /* 1/16 */ }; +static void *alloc_range_exact_nid(phys_addr_t size, phys_addr_t align, + phys_addr_t min_addr, + phys_addr_t max_addr, int nid) +{ + phys_addr_t addr; + + addr = memblock_alloc_range_nid(size, align, min_addr, max_addr, nid, + true); + + return addr ? phys_to_virt(addr) : NULL; +} + /* * A test that tries to allocate a memory region in a specific NUMA node that * has enough memory to allocate a region of the requested size. @@ -42,9 +53,8 @@ static int alloc_exact_nid_top_down_numa_simple_check(void) min_addr = memblock_start_of_DRAM(); max_addr = memblock_end_of_DRAM(); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_NE(allocated_ptr, NULL); ASSERT_MEM_NE(allocated_ptr, 0, size); @@ -98,9 +108,8 @@ static int alloc_exact_nid_top_down_numa_part_reserved_check(void) max_addr = memblock_end_of_DRAM(); memblock_reserve(r1.base, r1.size); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_NE(allocated_ptr, NULL); ASSERT_MEM_NE(allocated_ptr, 0, size); @@ -117,177 +126,6 @@ static int alloc_exact_nid_top_down_numa_part_reserved_check(void) return 0; } -/* - * A test that tries to allocate a memory region that spans over the min_addr - * and max_addr range and overlaps with two different nodes, where the first - * node is the requested node: - * - * min_addr - * | max_addr - * | | - * v v - * | +-----------------------+-----------+ | - * | | requested | node3 | | - * +-----------+-----------------------+-----------+--------------+ - * + + - * | +-----------+ | - * | | rgn | | - * +-----------------------+-----------+--------------------------+ - * - * Expect to drop the lower limit and allocate a memory region that ends at - * the end of the requested node. - */ -static int alloc_exact_nid_top_down_numa_split_range_low_check(void) -{ - int nid_req = 2; - struct memblock_region *new_rgn = &memblock.reserved.regions[0]; - struct memblock_region *req_node = &memblock.memory.regions[nid_req]; - void *allocated_ptr = NULL; - phys_addr_t size = SZ_512; - phys_addr_t min_addr; - phys_addr_t max_addr; - phys_addr_t req_node_end; - - PREFIX_PUSH(); - setup_numa_memblock(node_fractions); - - req_node_end = region_end(req_node); - min_addr = req_node_end - SZ_256; - max_addr = min_addr + size; - - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); - - ASSERT_NE(allocated_ptr, NULL); - ASSERT_MEM_NE(allocated_ptr, 0, size); - - ASSERT_EQ(new_rgn->size, size); - ASSERT_EQ(new_rgn->base, req_node_end - size); - ASSERT_LE(req_node->base, new_rgn->base); - - ASSERT_EQ(memblock.reserved.cnt, 1); - ASSERT_EQ(memblock.reserved.total_size, size); - - test_pass_pop(); - - return 0; -} - -/* - * A test that tries to allocate a memory region that spans over the min_addr - * and max_addr range and overlaps with two different nodes, where the requested - * node ends before min_addr: - * - * min_addr - * | max_addr - * | | - * v v - * | +---------------+ +-------------+---------+ | - * | | requested | | node1 | node2 | | - * +----+---------------+--------+-------------+---------+----------+ - * + + - * | +---------+ | - * | | rgn | | - * +----------+---------+-------------------------------------------+ - * - * Expect to drop the lower limit and allocate a memory region that ends at - * the end of the requested node. - */ -static int alloc_exact_nid_top_down_numa_no_overlap_split_check(void) -{ - int nid_req = 2; - struct memblock_region *new_rgn = &memblock.reserved.regions[0]; - struct memblock_region *req_node = &memblock.memory.regions[nid_req]; - struct memblock_region *node2 = &memblock.memory.regions[6]; - void *allocated_ptr = NULL; - phys_addr_t size; - phys_addr_t min_addr; - phys_addr_t max_addr; - - PREFIX_PUSH(); - setup_numa_memblock(node_fractions); - - size = SZ_512; - min_addr = node2->base - SZ_256; - max_addr = min_addr + size; - - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); - - ASSERT_NE(allocated_ptr, NULL); - ASSERT_MEM_NE(allocated_ptr, 0, size); - - ASSERT_EQ(new_rgn->size, size); - ASSERT_EQ(new_rgn->base, region_end(req_node) - size); - ASSERT_LE(req_node->base, new_rgn->base); - - ASSERT_EQ(memblock.reserved.cnt, 1); - ASSERT_EQ(memblock.reserved.total_size, size); - - test_pass_pop(); - - return 0; -} - -/* - * A test that tries to allocate memory within min_addr and max_add range when - * the requested node and the range do not overlap, and requested node ends - * before min_addr. The range overlaps with multiple nodes along node - * boundaries: - * - * min_addr - * | max_addr - * | | - * v v - * |-----------+ +----------+----...----+----------+ | - * | requested | | min node | ... | max node | | - * +-----------+-----------+----------+----...----+----------+------+ - * + + - * | +-----+ | - * | | rgn | | - * +-----+-----+----------------------------------------------------+ - * - * Expect to drop the lower limit and allocate a memory region that ends at - * the end of the requested node. - */ -static int alloc_exact_nid_top_down_numa_no_overlap_low_check(void) -{ - int nid_req = 0; - struct memblock_region *new_rgn = &memblock.reserved.regions[0]; - struct memblock_region *req_node = &memblock.memory.regions[nid_req]; - struct memblock_region *min_node = &memblock.memory.regions[2]; - struct memblock_region *max_node = &memblock.memory.regions[5]; - void *allocated_ptr = NULL; - phys_addr_t size = SZ_64; - phys_addr_t max_addr; - phys_addr_t min_addr; - - PREFIX_PUSH(); - setup_numa_memblock(node_fractions); - - min_addr = min_node->base; - max_addr = region_end(max_node); - - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); - - ASSERT_NE(allocated_ptr, NULL); - ASSERT_MEM_NE(allocated_ptr, 0, size); - - ASSERT_EQ(new_rgn->size, size); - ASSERT_EQ(new_rgn->base, region_end(req_node) - size); - - ASSERT_EQ(memblock.reserved.cnt, 1); - ASSERT_EQ(memblock.reserved.total_size, size); - - test_pass_pop(); - - return 0; -} - /* * A test that tries to allocate a memory region in a specific NUMA node that * has enough memory to allocate a region of the requested size. @@ -311,9 +149,8 @@ static int alloc_exact_nid_bottom_up_numa_simple_check(void) min_addr = memblock_start_of_DRAM(); max_addr = memblock_end_of_DRAM(); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_NE(allocated_ptr, NULL); ASSERT_MEM_NE(allocated_ptr, 0, size); @@ -369,9 +206,8 @@ static int alloc_exact_nid_bottom_up_numa_part_reserved_check(void) total_size = size + r1.size; __memblock_reserve(r1.base, r1.size, nid_req, MEMBLOCK_RSRV_KERN); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_NE(allocated_ptr, NULL); ASSERT_MEM_NE(allocated_ptr, 0, size); @@ -388,178 +224,6 @@ static int alloc_exact_nid_bottom_up_numa_part_reserved_check(void) return 0; } -/* - * A test that tries to allocate a memory region that spans over the min_addr - * and max_addr range and overlaps with two different nodes, where the first - * node is the requested node: - * - * min_addr - * | max_addr - * | | - * v v - * | +-----------------------+-----------+ | - * | | requested | node3 | | - * +-----------+-----------------------+-----------+--------------+ - * + + - * | +-----------+ | - * | | rgn | | - * +-----------+-----------+--------------------------------------+ - * - * Expect to drop the lower limit and allocate a memory region at the beginning - * of the requested node. - */ -static int alloc_exact_nid_bottom_up_numa_split_range_low_check(void) -{ - int nid_req = 2; - struct memblock_region *new_rgn = &memblock.reserved.regions[0]; - struct memblock_region *req_node = &memblock.memory.regions[nid_req]; - void *allocated_ptr = NULL; - phys_addr_t size = SZ_512; - phys_addr_t min_addr; - phys_addr_t max_addr; - phys_addr_t req_node_end; - - PREFIX_PUSH(); - setup_numa_memblock(node_fractions); - - req_node_end = region_end(req_node); - min_addr = req_node_end - SZ_256; - max_addr = min_addr + size; - - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); - - ASSERT_NE(allocated_ptr, NULL); - ASSERT_MEM_NE(allocated_ptr, 0, size); - - ASSERT_EQ(new_rgn->size, size); - ASSERT_EQ(new_rgn->base, req_node->base); - ASSERT_LE(region_end(new_rgn), req_node_end); - - ASSERT_EQ(memblock.reserved.cnt, 1); - ASSERT_EQ(memblock.reserved.total_size, size); - - test_pass_pop(); - - return 0; -} - -/* - * A test that tries to allocate a memory region that spans over the min_addr - * and max_addr range and overlaps with two different nodes, where the requested - * node ends before min_addr: - * - * min_addr - * | max_addr - * | | - * v v - * | +---------------+ +-------------+---------+ | - * | | requested | | node1 | node2 | | - * +----+---------------+--------+-------------+---------+---------+ - * + + - * | +---------+ | - * | | rgn | | - * +----+---------+------------------------------------------------+ - * - * Expect to drop the lower limit and allocate a memory region that starts at - * the beginning of the requested node. - */ -static int alloc_exact_nid_bottom_up_numa_no_overlap_split_check(void) -{ - int nid_req = 2; - struct memblock_region *new_rgn = &memblock.reserved.regions[0]; - struct memblock_region *req_node = &memblock.memory.regions[nid_req]; - struct memblock_region *node2 = &memblock.memory.regions[6]; - void *allocated_ptr = NULL; - phys_addr_t size; - phys_addr_t min_addr; - phys_addr_t max_addr; - - PREFIX_PUSH(); - setup_numa_memblock(node_fractions); - - size = SZ_512; - min_addr = node2->base - SZ_256; - max_addr = min_addr + size; - - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); - - ASSERT_NE(allocated_ptr, NULL); - ASSERT_MEM_NE(allocated_ptr, 0, size); - - ASSERT_EQ(new_rgn->size, size); - ASSERT_EQ(new_rgn->base, req_node->base); - ASSERT_LE(region_end(new_rgn), region_end(req_node)); - - ASSERT_EQ(memblock.reserved.cnt, 1); - ASSERT_EQ(memblock.reserved.total_size, size); - - test_pass_pop(); - - return 0; -} - -/* - * A test that tries to allocate memory within min_addr and max_add range when - * the requested node and the range do not overlap, and requested node ends - * before min_addr. The range overlaps with multiple nodes along node - * boundaries: - * - * min_addr - * | max_addr - * | | - * v v - * |-----------+ +----------+----...----+----------+ | - * | requested | | min node | ... | max node | | - * +-----------+-----------+----------+----...----+----------+------+ - * + + - * |-----+ | - * | rgn | | - * +-----+----------------------------------------------------------+ - * - * Expect to drop the lower limit and allocate a memory region that starts at - * the beginning of the requested node. - */ -static int alloc_exact_nid_bottom_up_numa_no_overlap_low_check(void) -{ - int nid_req = 0; - struct memblock_region *new_rgn = &memblock.reserved.regions[0]; - struct memblock_region *req_node = &memblock.memory.regions[nid_req]; - struct memblock_region *min_node = &memblock.memory.regions[2]; - struct memblock_region *max_node = &memblock.memory.regions[5]; - void *allocated_ptr = NULL; - phys_addr_t size = SZ_64; - phys_addr_t max_addr; - phys_addr_t min_addr; - - PREFIX_PUSH(); - setup_numa_memblock(node_fractions); - - min_addr = min_node->base; - max_addr = region_end(max_node); - - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); - - ASSERT_NE(allocated_ptr, NULL); - ASSERT_MEM_NE(allocated_ptr, 0, size); - - ASSERT_EQ(new_rgn->size, size); - ASSERT_EQ(new_rgn->base, req_node->base); - ASSERT_LE(region_end(new_rgn), region_end(req_node)); - - ASSERT_EQ(memblock.reserved.cnt, 1); - ASSERT_EQ(memblock.reserved.total_size, size); - - test_pass_pop(); - - return 0; -} - /* * A test that tries to allocate a memory region in a specific NUMA node that * does not have enough memory to allocate a region of the requested size: @@ -590,9 +254,8 @@ static int alloc_exact_nid_numa_small_node_generic_check(void) min_addr = memblock_start_of_DRAM(); max_addr = memblock_end_of_DRAM(); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_EQ(allocated_ptr, NULL); @@ -632,9 +295,8 @@ static int alloc_exact_nid_numa_node_reserved_generic_check(void) max_addr = memblock_end_of_DRAM(); memblock_reserve(req_node->base, req_node->size); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_EQ(allocated_ptr, NULL); @@ -680,9 +342,8 @@ static int alloc_exact_nid_numa_part_reserved_fail_generic_check(void) max_addr = memblock_end_of_DRAM(); memblock_reserve(r1.base, r1.size); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_EQ(allocated_ptr, NULL); @@ -721,9 +382,8 @@ static int alloc_exact_nid_numa_split_range_high_generic_check(void) min_addr = req_node->base - SZ_256; max_addr = min_addr + size; - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_EQ(allocated_ptr, NULL); @@ -764,9 +424,8 @@ static int alloc_exact_nid_numa_no_overlap_high_generic_check(void) min_addr = min_node->base; max_addr = region_end(max_node); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_EQ(allocated_ptr, NULL); @@ -803,9 +462,8 @@ static int alloc_exact_nid_numa_large_region_generic_check(void) min_addr = memblock_start_of_DRAM(); max_addr = memblock_end_of_DRAM(); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_EQ(allocated_ptr, NULL); test_pass_pop(); @@ -864,9 +522,8 @@ static int alloc_exact_nid_numa_reserved_full_merge_generic_check(void) __memblock_reserve(r1.base, r1.size, nid_req, MEMBLOCK_RSRV_KERN); __memblock_reserve(r2.base, r2.size, nid_req, MEMBLOCK_RSRV_KERN); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - nid_req); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, nid_req); ASSERT_NE(allocated_ptr, NULL); ASSERT_MEM_NE(allocated_ptr, 0, size); @@ -933,9 +590,8 @@ static int alloc_exact_nid_numa_split_all_reserved_generic_check(void) memblock_reserve(r1.base, r1.size); memblock_reserve(r2.base, r2.size); - allocated_ptr = memblock_alloc_exact_nid_raw(size, SMP_CACHE_BYTES, - min_addr, max_addr, - NUMA_NO_NODE); + allocated_ptr = alloc_range_exact_nid(size, SMP_CACHE_BYTES, + min_addr, max_addr, NUMA_NO_NODE); ASSERT_EQ(allocated_ptr, NULL); @@ -967,38 +623,6 @@ static int alloc_exact_nid_numa_part_reserved_check(void) return 0; } -static int alloc_exact_nid_numa_split_range_low_check(void) -{ - test_print("\tRunning %s...\n", __func__); - memblock_set_bottom_up(false); - alloc_exact_nid_top_down_numa_split_range_low_check(); - memblock_set_bottom_up(true); - alloc_exact_nid_bottom_up_numa_split_range_low_check(); - - return 0; -} - -static int alloc_exact_nid_numa_no_overlap_split_check(void) -{ - test_print("\tRunning %s...\n", __func__); - memblock_set_bottom_up(false); - alloc_exact_nid_top_down_numa_no_overlap_split_check(); - memblock_set_bottom_up(true); - alloc_exact_nid_bottom_up_numa_no_overlap_split_check(); - - return 0; -} - -static int alloc_exact_nid_numa_no_overlap_low_check(void) -{ - test_print("\tRunning %s...\n", __func__); - memblock_set_bottom_up(false); - alloc_exact_nid_top_down_numa_no_overlap_low_check(); - memblock_set_bottom_up(true); - alloc_exact_nid_bottom_up_numa_no_overlap_low_check(); - - return 0; -} static int alloc_exact_nid_numa_small_node_check(void) { @@ -1078,9 +702,6 @@ int __memblock_alloc_exact_nid_numa_checks(void) alloc_exact_nid_numa_simple_check(); alloc_exact_nid_numa_part_reserved_check(); - alloc_exact_nid_numa_split_range_low_check(); - alloc_exact_nid_numa_no_overlap_split_check(); - alloc_exact_nid_numa_no_overlap_low_check(); alloc_exact_nid_numa_small_node_check(); alloc_exact_nid_numa_node_reserved_check(); @@ -1102,7 +723,6 @@ int memblock_alloc_exact_nid_checks(void) reset_memblock_attributes(); dummy_physical_memory_init(); - memblock_alloc_exact_nid_range_checks(); memblock_alloc_exact_nid_numa_checks(); dummy_physical_memory_cleanup(); diff --git a/tools/testing/memblock/tests/alloc_nid_api.c b/tools/testing/memblock/tests/alloc_nid_api.c index c04923532159..3dd4c0bebc41 100644 --- a/tools/testing/memblock/tests/alloc_nid_api.c +++ b/tools/testing/memblock/tests/alloc_nid_api.c @@ -20,8 +20,6 @@ static const unsigned int node_fractions[] = { static inline const char * const get_memblock_alloc_nid_name(int flags) { - if (flags & TEST_F_EXACT) - return "memblock_alloc_exact_nid_raw"; if (flags & TEST_F_RAW) return "memblock_alloc_try_nid_raw"; return "memblock_alloc_try_nid"; @@ -32,15 +30,6 @@ static inline void *run_memblock_alloc_nid(phys_addr_t size, phys_addr_t min_addr, phys_addr_t max_addr, int nid) { - assert(!(alloc_nid_test_flags & TEST_F_EXACT) || - (alloc_nid_test_flags & TEST_F_RAW)); - /* - * TEST_F_EXACT should be checked before TEST_F_RAW since - * memblock_alloc_exact_nid_raw() performs raw allocations. - */ - if (alloc_nid_test_flags & TEST_F_EXACT) - return memblock_alloc_exact_nid_raw(size, align, min_addr, - max_addr, nid); if (alloc_nid_test_flags & TEST_F_RAW) return memblock_alloc_try_nid_raw(size, align, min_addr, max_addr, nid); @@ -2722,12 +2711,3 @@ int memblock_alloc_nid_checks(void) return 0; } - -int memblock_alloc_exact_nid_range_checks(void) -{ - alloc_nid_test_flags = (TEST_F_RAW | TEST_F_EXACT); - - memblock_alloc_nid_range_checks(); - - return 0; -} diff --git a/tools/testing/memblock/tests/alloc_nid_api.h b/tools/testing/memblock/tests/alloc_nid_api.h index 2b8cabacacb8..92d07d230e18 100644 --- a/tools/testing/memblock/tests/alloc_nid_api.h +++ b/tools/testing/memblock/tests/alloc_nid_api.h @@ -5,7 +5,6 @@ #include "common.h" int memblock_alloc_nid_checks(void); -int memblock_alloc_exact_nid_range_checks(void); int __memblock_alloc_nid_numa_checks(void); #ifdef CONFIG_NUMA diff --git a/tools/testing/memblock/tests/common.h b/tools/testing/memblock/tests/common.h index e1138e06c903..c3cca972dcca 100644 --- a/tools/testing/memblock/tests/common.h +++ b/tools/testing/memblock/tests/common.h @@ -23,8 +23,6 @@ enum test_flags { TEST_F_NONE = 0x0, /* Perform raw allocations (no zeroing of memory). */ TEST_F_RAW = 0x1, - /* Perform allocations on the exact node specified. */ - TEST_F_EXACT = 0x2 }; /** From ea27be9f7ce90a05e779d20b60969ce2a9834f77 Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Sat, 19 Sep 2026 21:53:27 +0800 Subject: [PATCH 13/15] memblock: Remove unused memblock_alloc_exact_nid_raw() memmap_alloc() was the only in-kernel caller of memblock_alloc_exact_nid_raw(). After removing its unused exact_nid argument, the function no longer has any callers. Remove memblock_alloc_exact_nid_raw() and its declaration. The remaining callers of memblock_alloc_internal() always allow allocation to fall back to other NUMA nodes, so remove its exact_nid argument and pass false directly to memblock_alloc_range_nid(). Suggested-by: Lorenzo Stoakes Signed-off-by: Kaitao Cheng Link: https://patch.msgid.link/20260919135327.74089-4-kaitao.cheng@linux.dev Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/memblock.h | 3 --- mm/memblock.c | 44 +++++----------------------------------- 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/include/linux/memblock.h b/include/linux/memblock.h index aa845f488327..01a2dea5aa11 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -412,9 +412,6 @@ static __always_inline phys_addr_t memblock_phys_alloc(phys_addr_t size, MEMBLOCK_ALLOC_ACCESSIBLE); } -void *memblock_alloc_exact_nid_raw(phys_addr_t size, phys_addr_t align, - phys_addr_t min_addr, phys_addr_t max_addr, - int nid); void *memblock_alloc_try_nid_raw(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid); diff --git a/mm/memblock.c b/mm/memblock.c index 22d14a637f24..51f816ce0cd9 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -1659,7 +1659,6 @@ phys_addr_t __init memblock_phys_alloc_try_nid(phys_addr_t size, phys_addr_t ali * @min_addr: the lower bound of the memory region to allocate (phys address) * @max_addr: the upper bound of the memory region to allocate (phys address) * @nid: nid of the free area to find, %NUMA_NO_NODE for any node - * @exact_nid: control the allocation fall back to other nodes * * Allocates memory block using memblock_alloc_range_nid() and * converts the returned physical address to virtual. @@ -1675,7 +1674,7 @@ phys_addr_t __init memblock_phys_alloc_try_nid(phys_addr_t size, phys_addr_t ali static void * __init memblock_alloc_internal( phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, - int nid, bool exact_nid) + int nid) { phys_addr_t alloc; @@ -1684,12 +1683,12 @@ static void * __init memblock_alloc_internal( max_addr = memblock.current_limit; alloc = memblock_alloc_range_nid(size, align, min_addr, max_addr, nid, - exact_nid); + false); /* retry allocation without lower limit */ if (!alloc && min_addr) alloc = memblock_alloc_range_nid(size, align, 0, max_addr, nid, - exact_nid); + false); if (!alloc) return NULL; @@ -1697,37 +1696,6 @@ static void * __init memblock_alloc_internal( return phys_to_virt(alloc); } -/** - * memblock_alloc_exact_nid_raw - allocate boot memory block on the exact node - * without zeroing memory - * @size: size of memory block to be allocated in bytes - * @align: alignment of the region and block's size - * @min_addr: the lower bound of the memory region from where the allocation - * is preferred (phys address) - * @max_addr: the upper bound of the memory region from where the allocation - * is preferred (phys address), or %MEMBLOCK_ALLOC_ACCESSIBLE to - * allocate only from memory limited by memblock.current_limit value - * @nid: nid of the free area to find, %NUMA_NO_NODE for any node - * - * Public function, provides additional debug information (including caller - * info), if enabled. Does not zero allocated memory. - * - * Return: - * Virtual address of allocated memory block on success, NULL on failure. - */ -void * __init memblock_alloc_exact_nid_raw( - phys_addr_t size, phys_addr_t align, - phys_addr_t min_addr, phys_addr_t max_addr, - int nid) -{ - memblock_dbg("%s: %llu bytes align=0x%llx nid=%d from=%pa max_addr=%pa %pS\n", - __func__, (u64)size, (u64)align, nid, &min_addr, - &max_addr, (void *)_RET_IP_); - - return memblock_alloc_internal(size, align, min_addr, max_addr, nid, - true); -} - /** * memblock_alloc_try_nid_raw - allocate boot memory block without zeroing * memory and without panicking @@ -1756,8 +1724,7 @@ void * __init memblock_alloc_try_nid_raw( __func__, (u64)size, (u64)align, nid, &min_addr, &max_addr, (void *)_RET_IP_); - return memblock_alloc_internal(size, align, min_addr, max_addr, nid, - false); + return memblock_alloc_internal(size, align, min_addr, max_addr, nid); } /** @@ -1858,8 +1825,7 @@ void * __init memblock_alloc_try_nid( memblock_dbg("%s: %llu bytes align=0x%llx nid=%d from=%pa max_addr=%pa %pS\n", __func__, (u64)size, (u64)align, nid, &min_addr, &max_addr, (void *)_RET_IP_); - ptr = memblock_alloc_internal(size, align, - min_addr, max_addr, nid, false); + ptr = memblock_alloc_internal(size, align, min_addr, max_addr, nid); if (ptr) memset(ptr, 0, size); From aedb8dde08f8cd93ce1cc3a23273f2ff7238f832 Mon Sep 17 00:00:00 2001 From: Tarun Sahu Date: Thu, 8 Oct 2026 19:08:27 +0000 Subject: [PATCH 14/15] memblock: drop for_each_memblock_type() and open code its users for_each_memblock_type() always starts the iteration at index 0 and its body is a trivial 'for' loop, so it hides very little. It also gets in the way of iterating from an arbitrary index, which the next patch needs. Remove the macro and open code its three users: memblock_add_range(), memblock_isolate_range() and memblock_dump(). While at it, move the region pointer into the loop body scope. No functional change. Reviewed-by: Pratyush Yadav Signed-off-by: Tarun Sahu Link: https://patch.msgid.link/20261008190828.3221718-1-tarunsahu@google.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/memblock.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/mm/memblock.c b/mm/memblock.c index 1fabe09f1d52..5acd6718a148 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -155,11 +155,6 @@ struct memblock_type physmem = { */ static __refdata struct memblock_type *memblock_memory = &memblock.memory; -#define for_each_memblock_type(i, memblock_type, rgn) \ - for (i = 0, rgn = &memblock_type->regions[0]; \ - i < memblock_type->cnt; \ - i++, rgn = &memblock_type->regions[i]) - #define memblock_dbg(fmt, ...) \ do { \ if (memblock_debug) \ @@ -615,7 +610,6 @@ static int __init_memblock memblock_add_range(struct memblock_type *type, phys_addr_t obase = base; phys_addr_t end = base + memblock_cap_size(base, &size); int idx, nr_new, start_rgn = -1, end_rgn = 0; - struct memblock_region *rgn; if (!size) return 0; @@ -651,7 +645,8 @@ static int __init_memblock memblock_add_range(struct memblock_type *type, base = obase; nr_new = 0; - for_each_memblock_type(idx, type, rgn) { + for (idx = 0; idx < type->cnt; idx++) { + struct memblock_region *rgn = &type->regions[idx]; phys_addr_t rbase = rgn->base; phys_addr_t rend = rbase + rgn->size; @@ -815,7 +810,6 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type, { phys_addr_t end = base + memblock_cap_size(base, &size); int idx; - struct memblock_region *rgn; *start_rgn = *end_rgn = 0; @@ -827,7 +821,8 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type, if (memblock_double_array(type, base, size) < 0) return -ENOMEM; - for_each_memblock_type(idx, type, rgn) { + for (idx = 0; idx < type->cnt; idx++) { + struct memblock_region *rgn = &type->regions[idx]; phys_addr_t rbase = rgn->base; phys_addr_t rend = rbase + rgn->size; @@ -2160,11 +2155,11 @@ static void __init_memblock memblock_dump(struct memblock_type *type) phys_addr_t base, end, size; enum memblock_flags flags; int idx; - struct memblock_region *rgn; pr_info(" %s.cnt = 0x%lx\n", type->name, type->cnt); - for_each_memblock_type(idx, type, rgn) { + for (idx = 0; idx < type->cnt; idx++) { + struct memblock_region *rgn = &type->regions[idx]; char nid_buf[32] = ""; base = rgn->base; From 7933ad3f669670ca5f5f13de2b5cb0f92b91abf5 Mon Sep 17 00:00:00 2001 From: Tarun Sahu Date: Thu, 8 Oct 2026 19:08:28 +0000 Subject: [PATCH 15/15] memblock: use binary search to locate candidate regions Use binary search in memblock_add_range() and memblock_isolate_range() to locate candidate regions instead of linearly scanning from index 0. Under heavy memory fragmentation, such as KHO page preservation registering hundreds of thousands of disjoint folios, scanning from index 0 on every insertion and isolation results in O(N^2) complexity, causing boot-time memory retrieval to take several minutes (~268s for 393k pages). Using binary search reduces the candidate search complexity to O(log N) (from O(N)), cutting KHO memory retrieval time from ~268s to ~50ms. memblock_search() open codes the same binary search, so reimplement it on top of the new helper. Signed-off-by: Tarun Sahu Reviewed-by: Pratyush Yadav Link: https://patch.msgid.link/20261008190828.3221718-2-tarunsahu@google.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/memblock.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/mm/memblock.c b/mm/memblock.c index 5acd6718a148..8ce808673d30 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -586,6 +586,29 @@ static void __init_memblock memblock_insert_region(struct memblock_type *type, type->total_size += size; } +/** + * __memblock_search - Find the first region index where rend > base + * @type: memblock type to search + * @base: base physical address of the candidate range + * + * Return: the first region index that could potentially overlap @base. + */ +static int __init_memblock __memblock_search(struct memblock_type *type, + phys_addr_t base) +{ + int mid, low = 0; + int high = type->cnt; + + while (low < high) { + mid = (low + high) / 2; + if (type->regions[mid].base + type->regions[mid].size <= base) + low = mid + 1; + else + high = mid; + } + return low; +} + /** * memblock_add_range - add new memblock region * @type: memblock type to add new region into @@ -645,7 +668,7 @@ static int __init_memblock memblock_add_range(struct memblock_type *type, base = obase; nr_new = 0; - for (idx = 0; idx < type->cnt; idx++) { + for (idx = __memblock_search(type, base); idx < type->cnt; idx++) { struct memblock_region *rgn = &type->regions[idx]; phys_addr_t rbase = rgn->base; phys_addr_t rend = rbase + rgn->size; @@ -809,7 +832,6 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type, int *start_rgn, int *end_rgn) { phys_addr_t end = base + memblock_cap_size(base, &size); - int idx; *start_rgn = *end_rgn = 0; @@ -821,7 +843,7 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type, if (memblock_double_array(type, base, size) < 0) return -ENOMEM; - for (idx = 0; idx < type->cnt; idx++) { + for (int idx = __memblock_search(type, base); idx < type->cnt; idx++) { struct memblock_region *rgn = &type->regions[idx]; phys_addr_t rbase = rgn->base; phys_addr_t rend = rbase + rgn->size; @@ -2028,19 +2050,10 @@ void __init memblock_mem_limit_remove_map(phys_addr_t limit) static int __init_memblock memblock_search(struct memblock_type *type, phys_addr_t addr) { - unsigned int left = 0, right = type->cnt; + int idx = __memblock_search(type, addr); - do { - unsigned int mid = (right + left) / 2; - - if (addr < type->regions[mid].base) - right = mid; - else if (addr >= (type->regions[mid].base + - type->regions[mid].size)) - left = mid + 1; - else - return mid; - } while (left < right); + if (idx < type->cnt && addr >= type->regions[idx].base) + return idx; return -1; }