* [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 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 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
* [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
* [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 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
* 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
* 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®