* [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory
@ 2026-09-06 14:40 Tianyi Chen
2026-09-06 14:40 ` [PATCH 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen
` (2 more replies)
0 siblings, 3 replies; 12+ messages in thread
From: Tianyi Chen @ 2026-09-06 14:40 UTC (permalink / raw)
To: rppt; +Cc: Tianyi Chen, Andrew Morton, linux-mm, linux-kernel
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 existing architecture override in asm/dma.h to place the limit
halfway through the dummy memory buffer. Resolve it after the buffer
has been allocated, so both low and high memory can be represented
regardless of its address. The production allocator is unchanged.
Assisted-by: LLM
Signed-off-by: Tianyi Chen <hi@tychen.cc>
---
tools/testing/memblock/asm/dma.h | 6 ++++++
tools/testing/memblock/tests/common.c | 5 +++++
2 files changed, 11 insertions(+)
diff --git a/tools/testing/memblock/asm/dma.h b/tools/testing/memblock/asm/dma.h
index 13ff8e5d22e..6607408dcf2 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 <linux/types.h>
+
+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 3250c8e5124..4e6a920c459 100644
--- a/tools/testing/memblock/tests/common.c
+++ b/tools/testing/memblock/tests/common.c
@@ -118,6 +118,11 @@ phys_addr_t dummy_physical_memory_base(void)
return (phys_addr_t)memory_block.base;
}
+phys_addr_t dummy_physical_memory_low_limit(void)
+{
+ 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);
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 2/2] memblock tests: cover allocations below the low address limit 2026-09-06 14:40 [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen @ 2026-09-06 14:40 ` Tianyi Chen 2026-09-06 20:29 ` Mike Rapoport 2026-09-06 20:21 ` [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory Mike Rapoport 2026-09-07 1:20 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Tianyi Chen 2 siblings, 1 reply; 12+ messages in thread From: Tianyi Chen @ 2026-09-06 14:40 UTC (permalink / raw) To: rppt; +Cc: Tianyi Chen, Andrew Morton, linux-mm, linux-kernel Add memblock_alloc_low() coverage using the simulator's low address limit. Exercise aligned allocation, an allocation ending exactly at 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. The full suite passes with ASan and UBSan in default, NUMA, 32-bit physical address and debug configurations, with movable-node mode both enabled and disabled. Assisted-by: LLM Signed-off-by: Tianyi Chen <hi@tychen.cc> --- tools/testing/memblock/Makefile | 3 +- tools/testing/memblock/README | 13 +- tools/testing/memblock/TODO | 5 - tools/testing/memblock/main.c | 2 + tools/testing/memblock/tests/alloc_low_api.c | 147 +++++++++++++++++++ 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 d80982ccdc2..20524fcbe3c 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 b435f48d8a7..e4fb5f23fb2 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,11 @@ 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 beginning at the dummy physical memory base. This keeps +the low-address limit within the allocated buffer even when malloc() returns +an address above the kernel's default limit. The limit is an exclusive upper +bound, as in the memblock allocation API. References ========== diff --git a/tools/testing/memblock/TODO b/tools/testing/memblock/TODO deleted file mode 100644 index c13ad0dae77..00000000000 --- 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 278f9dec500..9a96e178551 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 00000000000..a0fae975a26 --- /dev/null +++ b/tools/testing/memblock/tests/alloc_low_api.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "alloc_low_api.h" +#include <linux/align.h> + +/* 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(); + + 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 only low memory available ends exactly at the exclusive 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; +} + +/* Reserving all low memory must fail even though high memory remains 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 00000000000..2e3cbe336f5 --- /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 -- 2.55.0 ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/2] memblock tests: cover allocations below the low address limit 2026-09-06 14:40 ` [PATCH 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen @ 2026-09-06 20:29 ` Mike Rapoport 0 siblings, 0 replies; 12+ messages in thread From: Mike Rapoport @ 2026-09-06 20:29 UTC (permalink / raw) To: Tianyi Chen; +Cc: Andrew Morton, linux-mm, linux-kernel On Sun, Sep 06, 2026 at 10:40:11PM +0800, Tianyi Chen wrote: > Add memblock_alloc_low() coverage using the simulator's low address > limit. Exercise aligned allocation, an allocation ending exactly at > 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. The full > suite passes with ASan and UBSan in default, NUMA, 32-bit physical > address and debug configurations, with movable-node mode both enabled > and disabled. > > Assisted-by: LLM > Signed-off-by: Tianyi Chen <hi@tychen.cc> > --- > tools/testing/memblock/Makefile | 3 +- > tools/testing/memblock/README | 13 +- > tools/testing/memblock/TODO | 5 - > tools/testing/memblock/main.c | 2 + > tools/testing/memblock/tests/alloc_low_api.c | 147 +++++++++++++++++++ > 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 d80982ccdc2..20524fcbe3c 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 b435f48d8a7..e4fb5f23fb2 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,11 @@ 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 beginning at the dummy physical memory base. This keeps > +the low-address limit within the allocated buffer even when malloc() returns > +an address above the kernel's default limit. The limit is an exclusive upper > +bound, as in the memblock allocation API. > > References > ========== > diff --git a/tools/testing/memblock/TODO b/tools/testing/memblock/TODO > deleted file mode 100644 > index c13ad0dae77..00000000000 > --- 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 278f9dec500..9a96e178551 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 00000000000..a0fae975a26 > --- /dev/null > +++ b/tools/testing/memblock/tests/alloc_low_api.c > @@ -0,0 +1,147 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +#include "alloc_low_api.h" > +#include <linux/align.h> > + > +/* 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(); > + > + if (memblock_bottom_up()) > + expected = ALIGN(memblock_start_of_DRAM(), SMP_CACHE_BYTES); Can we ever have memblock_start_of_DRAM() not aligned to 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 only low memory available ends exactly at the exclusive limit. */ What do you mean by "exclusive 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; > +} > + > +/* Reserving all low memory must fail even though high memory remains free. */ Is this what the test checks? > +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 00000000000..2e3cbe336f5 > --- /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 > -- > 2.55.0 > > -- Sincerely yours, Mike. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory 2026-09-06 14:40 [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen 2026-09-06 14:40 ` [PATCH 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen @ 2026-09-06 20:21 ` Mike Rapoport 2026-09-07 1:20 ` Tianyi Chen 2026-09-07 1:20 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Tianyi Chen 2 siblings, 1 reply; 12+ messages in thread From: Mike Rapoport @ 2026-09-06 20:21 UTC (permalink / raw) To: Tianyi Chen; +Cc: Andrew Morton, linux-mm, linux-kernel On Sun, Sep 06, 2026 at 10:40:10PM +0800, Tianyi Chen wrote: > 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 existing architecture override in asm/dma.h to place the limit > halfway through the dummy memory buffer. Resolve it after the buffer > has been allocated, so both low and high memory can be represented > regardless of its address. The production allocator is unchanged. > > Assisted-by: LLM Please add LLM attribution as per https://docs.kernel.org/process/coding-assistants.html#attribution > Signed-off-by: Tianyi Chen <hi@tychen.cc> > --- > tools/testing/memblock/asm/dma.h | 6 ++++++ > tools/testing/memblock/tests/common.c | 5 +++++ > 2 files changed, 11 insertions(+) > > diff --git a/tools/testing/memblock/asm/dma.h b/tools/testing/memblock/asm/dma.h > index 13ff8e5d22e..6607408dcf2 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 <linux/types.h> > + > +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 3250c8e5124..4e6a920c459 100644 > --- a/tools/testing/memblock/tests/common.c > +++ b/tools/testing/memblock/tests/common.c > @@ -118,6 +118,11 @@ phys_addr_t dummy_physical_memory_base(void) > return (phys_addr_t)memory_block.base; > } > > +phys_addr_t dummy_physical_memory_low_limit(void) > +{ > + return dummy_physical_memory_base() + MEM_SIZE / 2; Shouldn't this be PHYS_MEM_SIZE? > +} > + > static void usage(const char *prog) > { > BUILD_BUG_ON(ARRAY_SIZE(help_opts) != ARRAY_SIZE(long_opts) - 1); > -- > 2.55.0 > > -- Sincerely yours, Mike. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory 2026-09-06 20:21 ` [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory Mike Rapoport @ 2026-09-07 1:20 ` Tianyi Chen 0 siblings, 0 replies; 12+ messages in thread From: Tianyi Chen @ 2026-09-07 1:20 UTC (permalink / raw) To: rppt; +Cc: Tianyi Chen, Andrew Morton, linux-mm, linux-kernel Hi Mike, Thank you for reviewing both patches. I have addressed the attribution and wording in v2: https://lore.kernel.org/r/178874362959.1415955.18132997964995947947.memblock-v2-0@tychen.cc > Shouldn't this be PHYS_MEM_SIZE? MEM_SIZE is intentional here. setup_memblock() registers only MEM_SIZE (32 KiB), although malloc() allocates a PHYS_MEM_SIZE (16 MiB) backing buffer. Splitting the registered range leaves usable memory on both sides of the limit. PHYS_MEM_SIZE / 2 would put the limit above all registered memory. The comment, README and commit message now explain this. On the alignment question in patch 2: the registered base is the malloc() result, which is not guaranteed to have SMP_CACHE_BYTES alignment (32 bytes in this simulator). ALIGN() also matches the allocator's round_up(this_start, align) in __memblock_find_range_bottom_up(); it is harmless when the base is already aligned. By "exclusive limit" I meant the first address an allocation must not use. region_end() is base + size, so equality with the limit is valid. I have replaced that wording with the allocation's last byte being immediately below the limit. You are also right about the reserved-memory comment: reserving low memory succeeds; the subsequent low-memory allocation must fail. I have corrected that description. Both patches now use Assisted-by: Codex:GPT-6. The five configuration combinations described in the cover letter passed with ASan/UBSan, movable-node mode enabled and disabled, and both allocation directions. The cover also records the separate, pre-existing BUILD=32 basic_api assertion failure. Thanks, Tianyi ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 0/2] memblock tests: cover low-address allocations 2026-09-06 14:40 [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen 2026-09-06 14:40 ` [PATCH 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen 2026-09-06 20:21 ` [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory Mike Rapoport @ 2026-09-07 1:20 ` Tianyi Chen 2026-09-07 1:20 ` [PATCH v2 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen ` (2 more replies) 2 siblings, 3 replies; 12+ messages in thread From: Tianyi Chen @ 2026-09-07 1:20 UTC (permalink / raw) To: rppt; +Cc: Tianyi Chen, Andrew Morton, linux-mm, linux-kernel Model a low allocation limit within the dummy memory registered with memblock, then exercise memblock_alloc_low() in both allocation directions. Changes in v2, following Mike Rapoport's review: - Clarify why the limit splits MEM_SIZE, the region registered by setup_memblock(), rather than the larger PHYS_MEM_SIZE malloc buffer. - Explain why the malloc-backed base still needs alignment. - Describe the boundary in terms of the last usable byte and clarify that reserving low memory succeeds; the following allocation fails. - Update Assisted-by tags to the required tool:model format. No allocator or test behavior changes from v1. Validation with the existing Makefile and ASan/UBSan enabled: - Default, NUMA=1, 32BIT_PHYS_ADDR_T=1, 32BIT_PHYS_ADDR_T=1 with NUMA=1, and MEMBLOCK_DEBUG=1 with NUMA=1 builds passed. - Each configuration passed both ./main -v and ./main -v -m. - A separate BUILD=32 run still hits the pre-existing memblock_free_near_max_check assertion in basic_api.c, also seen on the baseline. This is distinct from the passing 32-bit physical-address configuration above. v1: https://lore.kernel.org/r/20260906144011.848898-1-hi@tychen.cc Review: https://lore.kernel.org/r/ap3LTQlKTpDbAEJZ@kernel.org Review: https://lore.kernel.org/r/ap3NJsaG10rLDVpd@kernel.org Tianyi Chen (2): memblock tests: model the low allocation limit within dummy memory memblock tests: cover allocations below the low address limit tools/testing/memblock/Makefile | 3 +- tools/testing/memblock/README | 14 +- tools/testing/memblock/TODO | 5 - tools/testing/memblock/asm/dma.h | 6 + tools/testing/memblock/main.c | 2 + tools/testing/memblock/tests/alloc_low_api.c | 148 +++++++++++++++++++ tools/testing/memblock/tests/alloc_low_api.h | 9 ++ tools/testing/memblock/tests/common.c | 6 + 8 files changed, 180 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 -- 2.55.0 ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 1/2] memblock tests: model the low allocation limit within dummy memory 2026-09-07 1:20 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Tianyi Chen @ 2026-09-07 1:20 ` Tianyi Chen 2026-09-09 17:14 ` Mike Rapoport 2026-09-07 1:20 ` [PATCH v2 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen 2026-09-09 17:31 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Mike Rapoport 2 siblings, 1 reply; 12+ messages in thread From: Tianyi Chen @ 2026-09-07 1:20 UTC (permalink / raw) To: rppt; +Cc: Tianyi Chen, Andrew Morton, linux-mm, linux-kernel 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 MEM_SIZE region registered by setup_memblock(). This leaves registered memory on both sides of the limit. PHYS_MEM_SIZE describes the larger backing allocation, rather than the registered region. Resolve the limit after allocating the buffer. The production allocator is unchanged. Assisted-by: Codex:GPT-6 Signed-off-by: Tianyi Chen <hi@tychen.cc> --- 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 13ff8e5d22e..6607408dcf2 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 <linux/types.h> + +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 3250c8e5124..17b95af216b 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) +{ + /* Split the MEM_SIZE region registered by setup_memblock(), not the buffer. */ + 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); -- 2.55.0 ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/2] memblock tests: model the low allocation limit within dummy memory 2026-09-07 1:20 ` [PATCH v2 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen @ 2026-09-09 17:14 ` Mike Rapoport 0 siblings, 0 replies; 12+ messages in thread From: Mike Rapoport @ 2026-09-09 17:14 UTC (permalink / raw) To: Tianyi Chen; +Cc: rppt, Andrew Morton, linux-mm, linux-kernel Hi, > 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 MEM_SIZE region registered by setup_memblock(). This leaves > registered memory on both sides of the limit. PHYS_MEM_SIZE describes > the larger backing allocation, rather than the registered region. > Resolve the limit after allocating the buffer. The production allocator > is unchanged. > > Assisted-by: Codex:GPT-6 > Signed-off-by: Tianyi Chen <hi@tychen.cc> > > diff --git a/tools/testing/memblock/asm/dma.h b/tools/testing/memblock/asm/dma.h > index 13ff8e5d22ef6..6607408dcf25d 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 <linux/types.h> > + > +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 3250c8e5124b3..17b95af216b2c 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) > +{ > + /* Split the MEM_SIZE region registered by setup_memblock(), not the buffer. */ The comment should not address the review comments ;-) It should state the universal truth, like "low limit is half of the memory registered with setup_memblock()" -- Sincerely yours, Mike. ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 2/2] memblock tests: cover allocations below the low address limit 2026-09-07 1:20 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Tianyi Chen 2026-09-07 1:20 ` [PATCH v2 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen @ 2026-09-07 1:20 ` Tianyi Chen 2026-09-09 17:14 ` Mike Rapoport 2026-09-09 17:31 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Mike Rapoport 2 siblings, 1 reply; 12+ messages in thread From: Tianyi Chen @ 2026-09-07 1:20 UTC (permalink / raw) To: rppt; +Cc: Tianyi Chen, Andrew Morton, linux-mm, linux-kernel 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 registered memory range and its simulated limit, clarify the allocation boundary, and remove the completed TODO. Assisted-by: Codex:GPT-6 Signed-off-by: Tianyi Chen <hi@tychen.cc> --- tools/testing/memblock/Makefile | 3 +- tools/testing/memblock/README | 14 +- 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, 168 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 d80982ccdc2..20524fcbe3c 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 b435f48d8a7..13b7f58b2ee 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,12 @@ 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(), rather than the larger +PHYS_MEM_SIZE backing buffer. This leaves registered memory both below and +above the limit, even when malloc() returns an address above the kernel's +default 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 c13ad0dae77..00000000000 --- 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 278f9dec500..9a96e178551 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 00000000000..1ea5a36fcf0 --- /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 <linux/align.h> + +/* 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(); + + /* malloc() does not guarantee SMP_CACHE_BYTES alignment. */ + 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 00000000000..2e3cbe336f5 --- /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 -- 2.55.0 ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/2] memblock tests: cover allocations below the low address limit 2026-09-07 1:20 ` [PATCH v2 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen @ 2026-09-09 17:14 ` Mike Rapoport 0 siblings, 0 replies; 12+ messages in thread From: Mike Rapoport @ 2026-09-09 17:14 UTC (permalink / raw) To: Tianyi Chen; +Cc: rppt, Andrew Morton, linux-mm, linux-kernel Hi, > 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 registered memory range and its simulated limit, clarify > the allocation boundary, and remove the completed TODO. > > Assisted-by: Codex:GPT-6 > Signed-off-by: Tianyi Chen <hi@tychen.cc> > > diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile > index d80982ccdc209..20524fcbe3c72 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 b435f48d8a700..13b7f58b2eed1 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,12 @@ 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(), rather than the larger > +PHYS_MEM_SIZE backing buffer. This leaves registered memory both below and "rather than ..." part is excessive > +above the limit, even when malloc() returns an address above the kernel's Why do we care about what malloc() returns here? > +default 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 c13ad0dae7763..0000000000000 > --- 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 278f9dec50087..9a96e178551b8 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 0000000000000..1ea5a36fcf0d8 > --- /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 <linux/align.h> > + > +/* 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(); > + > + /* malloc() does not guarantee SMP_CACHE_BYTES alignment. */ I'd drop malloc() and say "simulated physical RAM isn't 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); Isn't (phys_addr_t) cast enough? -- Sincerely yours, Mike. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 0/2] memblock tests: cover low-address allocations 2026-09-07 1:20 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Tianyi Chen 2026-09-07 1:20 ` [PATCH v2 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen 2026-09-07 1:20 ` [PATCH v2 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen @ 2026-09-09 17:31 ` Mike Rapoport 2026-09-09 23:55 ` Tianyi Chen 2 siblings, 1 reply; 12+ messages in thread From: Mike Rapoport @ 2026-09-09 17:31 UTC (permalink / raw) To: Tianyi Chen; +Cc: Andrew Morton, linux-mm, linux-kernel Hi, For the future postings, please don't send vN+1 as a reply to vN. On Mon, Sep 07, 2026 at 09:20:31AM +0800, Tianyi Chen wrote: > Model a low allocation limit within the dummy memory registered with > memblock, then exercise memblock_alloc_low() in both allocation directions. > > Changes in v2, following Mike Rapoport's review: > - Clarify why the limit splits MEM_SIZE, the region registered by > setup_memblock(), rather than the larger PHYS_MEM_SIZE malloc buffer. > - Explain why the malloc-backed base still needs alignment. > - Describe the boundary in terms of the last usable byte and clarify > that reserving low memory succeeds; the following allocation fails. > - Update Assisted-by tags to the required tool:model format. > > No allocator or test behavior changes from v1. > > Validation with the existing Makefile and ASan/UBSan enabled: > - Default, NUMA=1, 32BIT_PHYS_ADDR_T=1, 32BIT_PHYS_ADDR_T=1 with NUMA=1, > and MEMBLOCK_DEBUG=1 with NUMA=1 builds passed. > - Each configuration passed both ./main -v and ./main -v -m. > - A separate BUILD=32 run still hits the pre-existing > memblock_free_near_max_check assertion in basic_api.c, also seen on > the baseline. This is distinct from the passing 32-bit physical-address > configuration above. > > v1: https://lore.kernel.org/r/20260906144011.848898-1-hi@tychen.cc > Review: https://lore.kernel.org/r/ap3LTQlKTpDbAEJZ@kernel.org > Review: https://lore.kernel.org/r/ap3NJsaG10rLDVpd@kernel.org > > Tianyi Chen (2): > memblock tests: model the low allocation limit within dummy memory > memblock tests: cover allocations below the low address limit > > tools/testing/memblock/Makefile | 3 +- > tools/testing/memblock/README | 14 +- > tools/testing/memblock/TODO | 5 - > tools/testing/memblock/asm/dma.h | 6 + > tools/testing/memblock/main.c | 2 + > tools/testing/memblock/tests/alloc_low_api.c | 148 +++++++++++++++++++ > tools/testing/memblock/tests/alloc_low_api.h | 9 ++ > tools/testing/memblock/tests/common.c | 6 + > 8 files changed, 180 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 > > -- > 2.55.0 > > -- Sincerely yours, Mike. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 0/2] memblock tests: cover low-address allocations 2026-09-09 17:31 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Mike Rapoport @ 2026-09-09 23:55 ` Tianyi Chen 0 siblings, 0 replies; 12+ messages in thread From: Tianyi Chen @ 2026-09-09 23:55 UTC (permalink / raw) To: Mike Rapoport; +Cc: akpm, linux-mm, linux-kernel, hi Hi Mike, Sorry for sending v2 as a reply to v1. I have posted v3 as a separate thread: https://lore.kernel.org/r/cover.1788997523.git.hi@tychen.cc I shortened the comments and README to describe the low limit and simulated RAM alignment directly. Both patches now use Assisted-by: LLM. I also checked the pointer casts with BUILD=32, where pointers are 32-bit and phys_addr_t is 64-bit. The tested compiler sign-extends a direct cast to phys_addr_t, while conversion through uintptr_t zero-extends it. Direct casts also introduce warnings, so I retained uintptr_t to preserve the existing conversion behavior. There are no executable code changes from v2. Thanks for the review, Tianyi ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-09-09 23:55 UTC | newest] Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-06 14:40 [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen 2026-09-06 14:40 ` [PATCH 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen 2026-09-06 20:29 ` Mike Rapoport 2026-09-06 20:21 ` [PATCH 1/2] memblock tests: model the low allocation limit within dummy memory Mike Rapoport 2026-09-07 1:20 ` Tianyi Chen 2026-09-07 1:20 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Tianyi Chen 2026-09-07 1:20 ` [PATCH v2 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen 2026-09-09 17:14 ` Mike Rapoport 2026-09-07 1:20 ` [PATCH v2 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen 2026-09-09 17:14 ` Mike Rapoport 2026-09-09 17:31 ` [PATCH v2 0/2] memblock tests: cover low-address allocations Mike Rapoport 2026-09-09 23:55 ` Tianyi Chen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®