* [PATCH v3 2/2] memblock tests: cover allocations below the low address limit
2026-09-09 23:55 [PATCH v3 0/2] memblock tests: cover low-address allocations Tianyi Chen
2026-09-09 23:55 ` [PATCH v3 1/2] memblock tests: model the low allocation limit within dummy memory Tianyi Chen
@ 2026-09-09 23:55 ` Tianyi Chen
2026-09-10 9:58 ` [PATCH v3 0/2] memblock tests: cover low-address allocations Mike Rapoport
2 siblings, 0 replies; 4+ messages in thread
From: Tianyi Chen @ 2026-09-09 23:55 UTC (permalink / raw)
To: Mike Rapoport; +Cc: akpm, 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 simulated limit and remove the completed TODO.
Assisted-by: LLM
Signed-off-by: Tianyi Chen <hi@tychen.cc>
---
tools/testing/memblock/Makefile | 3 +-
tools/testing/memblock/README | 12 +-
tools/testing/memblock/TODO | 5 -
tools/testing/memblock/main.c | 2 +
tools/testing/memblock/tests/alloc_low_api.c | 148 +++++++++++++++++++
tools/testing/memblock/tests/alloc_low_api.h | 9 ++
6 files changed, 166 insertions(+), 13 deletions(-)
delete mode 100644 tools/testing/memblock/TODO
create mode 100644 tools/testing/memblock/tests/alloc_low_api.c
create mode 100644 tools/testing/memblock/tests/alloc_low_api.h
diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile
index d80982ccdc20..20524fcbe3c7 100644
--- a/tools/testing/memblock/Makefile
+++ b/tools/testing/memblock/Makefile
@@ -7,7 +7,8 @@ CFLAGS += -I. -I../../include -Wall -O2 -fsanitize=address \
LDFLAGS += -fsanitize=address -fsanitize=undefined
TARGETS = main
TEST_OFILES = tests/alloc_nid_api.o tests/alloc_helpers_api.o tests/alloc_api.o \
- tests/basic_api.o tests/common.o tests/alloc_exact_nid_api.o
+ tests/basic_api.o tests/common.o tests/alloc_exact_nid_api.o \
+ tests/alloc_low_api.o
DEP_OFILES = memblock.o lib/slab.o mmzone.o slab.o cmdline.o
OFILES = main.o $(DEP_OFILES) $(TEST_OFILES)
EXTR_SRC = ../../../mm/memblock.c
diff --git a/tools/testing/memblock/README b/tools/testing/memblock/README
index b435f48d8a70..4ea293209548 100644
--- a/tools/testing/memblock/README
+++ b/tools/testing/memblock/README
@@ -67,13 +67,13 @@ memblock
|-- tests
| |-- alloc_api.(c|h) -- memblock_alloc tests
| |-- alloc_helpers_api.(c|h) -- memblock_alloc_from tests
+| |-- alloc_low_api.(c|h) -- memblock_alloc_low tests
| |-- alloc_nid_api.(c|h) -- memblock_alloc_try_nid tests
| |-- basic_api.(c|h) -- memblock_add/memblock_reserve/... tests
| |-- common.(c|h) -- helper functions for resetting memblock;
|-- main.c --------------. dummy physical memory definition
|-- Makefile `- test runner
|-- README
-|-- TODO
|-- .gitignore
Simulating physical memory
@@ -101,12 +101,10 @@ There's no need to explicitly free the dummy memory from memblock via
memblock_free() call. The entry will be erased by reset_memblock_regions(),
called at the beginning of each test.
-Known issues
-============
-
-1. Tests for memblock_alloc_low() can't be easily implemented. The function uses
- ARCH_LOW_ADDRESS_LIMIT marco, which can't be changed to point at the low
- memory of the memory_block.
+The simulator defines ARCH_LOW_ADDRESS_LIMIT in asm/dma.h using the midpoint
+of the MEM_SIZE range registered by setup_memblock(). This leaves registered
+memory both below and above the limit. The limit is the first address an
+allocation must not use; an allocation's base plus its size may equal the limit.
References
==========
diff --git a/tools/testing/memblock/TODO b/tools/testing/memblock/TODO
deleted file mode 100644
index c13ad0dae776..000000000000
--- a/tools/testing/memblock/TODO
+++ /dev/null
@@ -1,5 +0,0 @@
-TODO
-=====
-
-1. Add tests for memblock_alloc_low() once the simulator can model
- ARCH_LOW_ADDRESS_LIMIT against the low memory in memory_block
diff --git a/tools/testing/memblock/main.c b/tools/testing/memblock/main.c
index 278f9dec5008..9a96e178551b 100644
--- a/tools/testing/memblock/main.c
+++ b/tools/testing/memblock/main.c
@@ -4,6 +4,7 @@
#include "tests/alloc_helpers_api.h"
#include "tests/alloc_nid_api.h"
#include "tests/alloc_exact_nid_api.h"
+#include "tests/alloc_low_api.h"
#include "tests/common.h"
int main(int argc, char **argv)
@@ -12,6 +13,7 @@ int main(int argc, char **argv)
memblock_basic_checks();
memblock_alloc_checks();
memblock_alloc_helpers_checks();
+ memblock_alloc_low_checks();
memblock_alloc_nid_checks();
memblock_alloc_exact_nid_checks();
diff --git a/tools/testing/memblock/tests/alloc_low_api.c b/tools/testing/memblock/tests/alloc_low_api.c
new file mode 100644
index 000000000000..a117431f1f71
--- /dev/null
+++ b/tools/testing/memblock/tests/alloc_low_api.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "alloc_low_api.h"
+#include <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();
+
+ /* Simulated physical RAM is not necessarily SMP_CACHE_BYTES aligned. */
+ if (memblock_bottom_up())
+ expected = ALIGN(memblock_start_of_DRAM(), SMP_CACHE_BYTES);
+ else
+ expected = ALIGN_DOWN(ARCH_LOW_ADDRESS_LIMIT - size,
+ SMP_CACHE_BYTES);
+
+ allocated_ptr = memblock_alloc_low(size, SMP_CACHE_BYTES);
+
+ ASSERT_NE(allocated_ptr, NULL);
+ ASSERT_EQ((phys_addr_t)(uintptr_t)allocated_ptr, expected);
+ ASSERT_MEM_EQ(allocated_ptr, 0, size);
+ ASSERT_EQ(rgn->base, expected);
+ ASSERT_EQ(rgn->size, size);
+ ASSERT_LE(region_end(rgn), ARCH_LOW_ADDRESS_LIMIT);
+ ASSERT_EQ(memblock.reserved.cnt, 1);
+ ASSERT_EQ(memblock.reserved.total_size, size);
+
+ test_pass_pop();
+ return 0;
+}
+
+/* The last byte of the allocation is immediately below the low limit. */
+static int alloc_low_exact_limit_check(void)
+{
+ phys_addr_t limit = ARCH_LOW_ADDRESS_LIMIT;
+ phys_addr_t base = ALIGN_DOWN(limit - SZ_64, SMP_CACHE_BYTES);
+ phys_addr_t size = limit - base;
+ void *allocated_ptr;
+
+ PREFIX_PUSH();
+ setup_memblock();
+ ASSERT_EQ(memblock_remove(memblock_start_of_DRAM(),
+ base - memblock_start_of_DRAM()), 0);
+
+ allocated_ptr = memblock_alloc_low(size, SMP_CACHE_BYTES);
+
+ ASSERT_NE(allocated_ptr, NULL);
+ ASSERT_EQ((phys_addr_t)(uintptr_t)allocated_ptr, base);
+ ASSERT_MEM_EQ(allocated_ptr, 0, size);
+ ASSERT_EQ(memblock.reserved.regions[0].base, base);
+ ASSERT_EQ(region_end(&memblock.reserved.regions[0]), limit);
+ ASSERT_EQ(memblock.reserved.cnt, 1);
+ ASSERT_EQ(memblock.reserved.total_size, size);
+
+ test_pass_pop();
+ return 0;
+}
+
+/*
+ * There are size bytes below the limit, but aligning the start makes the
+ * allocation cross it. Memory above the limit must not satisfy the request.
+ */
+static int alloc_low_alignment_crosses_limit_check(void)
+{
+ phys_addr_t limit = ARCH_LOW_ADDRESS_LIMIT;
+ phys_addr_t base = ALIGN_DOWN(limit, SMP_CACHE_BYTES) - 1;
+ phys_addr_t size = limit - base;
+ void *allocated_ptr;
+
+ PREFIX_PUSH();
+ setup_memblock();
+ ASSERT_EQ(memblock_remove(memblock_start_of_DRAM(),
+ base - memblock_start_of_DRAM()), 0);
+
+ allocated_ptr = memblock_alloc_low(size, SMP_CACHE_BYTES);
+
+ ASSERT_EQ(allocated_ptr, NULL);
+ ASSERT_EQ(memblock.reserved.cnt, 0);
+ ASSERT_EQ(memblock.reserved.total_size, 0);
+ ASSERT_MEM_EQ((void *)(uintptr_t)base, 1, memblock_end_of_DRAM() - base);
+
+ test_pass_pop();
+ return 0;
+}
+
+/* Allocation must fail after reserving all low memory, with high memory free. */
+static int alloc_low_reserved_check(void)
+{
+ phys_addr_t limit = ARCH_LOW_ADDRESS_LIMIT;
+ phys_addr_t base = dummy_physical_memory_base();
+ phys_addr_t size = SZ_64;
+ void *allocated_ptr;
+
+ PREFIX_PUSH();
+ setup_memblock();
+ ASSERT_EQ(memblock_reserve(base, limit - base), 0);
+
+ allocated_ptr = memblock_alloc_low(size, SMP_CACHE_BYTES);
+
+ ASSERT_EQ(allocated_ptr, NULL);
+ ASSERT_EQ(memblock.reserved.cnt, 1);
+ ASSERT_EQ(memblock.reserved.regions[0].base, base);
+ ASSERT_EQ(memblock.reserved.regions[0].size, limit - base);
+ ASSERT_EQ(memblock.reserved.total_size, limit - base);
+ ASSERT_MEM_EQ((void *)(uintptr_t)base, 1, MEM_SIZE);
+
+ allocated_ptr = memblock_alloc(size, SMP_CACHE_BYTES);
+ ASSERT_NE(allocated_ptr, NULL);
+ ASSERT_LE(limit, (phys_addr_t)(uintptr_t)allocated_ptr);
+ ASSERT_MEM_EQ(allocated_ptr, 0, size);
+
+ test_pass_pop();
+ return 0;
+}
+
+static int alloc_low_checks(void)
+{
+ alloc_low_simple_check();
+ alloc_low_exact_limit_check();
+ alloc_low_alignment_crosses_limit_check();
+ alloc_low_reserved_check();
+
+ return 0;
+}
+
+int memblock_alloc_low_checks(void)
+{
+ prefix_reset();
+ prefix_push("memblock_alloc_low");
+ test_print("Running memblock_alloc_low tests...\n");
+
+ reset_memblock_attributes();
+ dummy_physical_memory_init();
+
+ run_top_down(alloc_low_checks);
+ run_bottom_up(alloc_low_checks);
+
+ dummy_physical_memory_cleanup();
+ prefix_pop();
+
+ return 0;
+}
diff --git a/tools/testing/memblock/tests/alloc_low_api.h b/tools/testing/memblock/tests/alloc_low_api.h
new file mode 100644
index 000000000000..2e3cbe336f5d
--- /dev/null
+++ b/tools/testing/memblock/tests/alloc_low_api.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _MEMBLOCK_ALLOC_LOW_H
+#define _MEMBLOCK_ALLOC_LOW_H
+
+#include "common.h"
+
+int memblock_alloc_low_checks(void);
+
+#endif
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread