* [PATCH v3 0/2] memblock tests: cover low-address allocations
@ 2026-09-09 23:55 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 ` [PATCH v3 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen
0 siblings, 2 replies; 3+ messages in thread
From: Tianyi Chen @ 2026-09-09 23:55 UTC (permalink / raw)
To: Mike Rapoport; +Cc: akpm, linux-mm, linux-kernel, hi
Model a low allocation limit within the memory registered by the simulator,
then exercise memblock_alloc_low() in both allocation directions.
Changes in v3:
- Describe the low limit and simulated RAM alignment directly in comments
and README.
- Use Assisted-by: LLM in both patches.
- Start a separate thread for this revision.
The pointer casts remain unchanged. With BUILD=32, pointers are 32-bit and
phys_addr_t is 64-bit: the tested compiler sign-extends a direct cast, while
the conversion through uintptr_t zero-extends it. The direct casts also
introduce compiler warnings, so this revision preserves the existing
conversion behavior. There are no executable code changes from v2.
Validation with ASan and UBSan enabled:
- Default and 32BIT_PHYS_ADDR_T=1: 189 tests pass in each runtime mode.
- NUMA=1, 32BIT_PHYS_ADDR_T=1 with NUMA=1, and MEMBLOCK_DEBUG=1 with NUMA=1:
272 tests pass in each runtime mode.
- Both ./main -v and ./main -v -m were run for those configurations.
- BUILD=32 builds without warnings from alloc_low_api.c. Its runtime still
hits the pre-existing basic_api.c memblock_free_near_max_check assertion
before reaching the low-allocation tests.
v2: https://lore.kernel.org/r/178874362959.1415955.18132997964995947947.memblock-v2-0@tychen.cc
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 | 12 +-
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, 178 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] 3+ messages in thread
* [PATCH v3 1/2] memblock tests: model the low allocation limit within dummy memory
2026-09-09 23:55 [PATCH v3 0/2] memblock tests: cover low-address allocations Tianyi Chen
@ 2026-09-09 23:55 ` Tianyi Chen
2026-09-09 23:55 ` [PATCH v3 2/2] memblock tests: cover allocations below the low address limit Tianyi Chen
1 sibling, 0 replies; 3+ messages in thread
From: Tianyi Chen @ 2026-09-09 23:55 UTC (permalink / raw)
To: Mike Rapoport; +Cc: akpm, 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 memory registered by setup_memblock(). Resolve the limit
after allocating the buffer, leaving registered memory on both sides.
Assisted-by: LLM
Signed-off-by: Tianyi Chen <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 13ff8e5d22ef..6607408dcf25 100644
--- a/tools/testing/memblock/asm/dma.h
+++ b/tools/testing/memblock/asm/dma.h
@@ -2,4 +2,10 @@
#ifndef _TOOLS_DMA_H
#define _TOOLS_DMA_H
+#include <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 3250c8e5124b..ac032610a56e 100644
--- a/tools/testing/memblock/tests/common.c
+++ b/tools/testing/memblock/tests/common.c
@@ -118,6 +118,12 @@ phys_addr_t dummy_physical_memory_base(void)
return (phys_addr_t)memory_block.base;
}
+phys_addr_t dummy_physical_memory_low_limit(void)
+{
+ /* The low limit is halfway through memory registered by setup_memblock(). */
+ return dummy_physical_memory_base() + MEM_SIZE / 2;
+}
+
static void usage(const char *prog)
{
BUILD_BUG_ON(ARRAY_SIZE(help_opts) != ARRAY_SIZE(long_opts) - 1);
--
2.55.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [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
1 sibling, 0 replies; 3+ 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] 3+ messages in thread
end of thread, other threads:[~2026-09-10 0:01 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH v3 2/2] memblock tests: cover allocations below the low address limit 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®