memblock tests: add NUMA tests for memblock_alloc_try_nid*
v6 cover memblock tests: add NUMA tests for memblock_alloc_try_nid*
These patches add additional tests for memblock_alloc_try_nid() and memblock_alloc_try_nid_raw() that use a simulated physical memory that is set up with multiple NUMA nodes. Additionally, most of these tests set nid != NUMA_NO_NODE.
To set up a simulated physical memory with multiple NUMA nodes, patch 1 introduces setup_numa_memblock(). This function uses a previously allocated dummy physical memory. It can be used in place of setup_memblock() in tests that need to simulate a NUMA system.
These tests are run twice, once for memblock_alloc_try_nid() and once for memblock_alloc_try_nid_raw(), so that both functions are tested with the same set of tests. When the tests run memblock_alloc_try_nid(), they test that the entire memory region is zero. When the tests run memblock_alloc_try_nid_raw(), they test that the entire memory region is nonzero.
---
Changelog
v5 -> v6
Based on feedback from David Hildenbrand:
PATCH 1:
- tests/common.c, tests/common.h:
+ setup_numa_memblock():
* Change nodes[] from const int to const unsigned int
* Remove "&& node_fracs[i] > 0" from the assert()
PATCH 2:
- tests/alloc_nid_api.c:
+ Update node_fractions[] type to static const unsigned int to reflect changes in PATCH 1
v4 -> v5
Based on feedback from David Hildenbrand:
PATCH 1:
- tests/common.c:
+ setup_numa_memblock():
* Change nodes[] to int basis points (one hundredth of 1% or 1/10000) instead of phys_addr_t fraction denominators
* Rename parameter nodes[] to node_fracs[]
PATCH 2:
- tests/alloc_nid_api.c:
+ Update node_fractions[] to reflect changes in PATCH 1
v3 -> v4
Based on feedback from David Hildenbrand:
PATCH 1:
- tests/common.h:
+ Remove #define MEM_FACTOR
PATCH 2:
- tests/alloc_nid_api.c:
+ Add missing blank line after alloc_nid_test_flags = flags;
PATCH 2, PATCH 3, PATCH 4:
- tests/alloc_nid_api.c:
+ Define variables in new tests in a single block
+ Change "cleared memory region" to "memory region" in comment blocks for new tests
Based on feedback from Mike Rapoport:
PATCH 1:
- tests/common.c:
+ Remove node_sizes[] array
- tests/common.c, tests/common.h:
+ Combine setup_numa_memblock_generic() and setup_numa_memblock():
* Remove setup_numa_memblock(void)
* Change setup_numa_memblock_generic() to setup_numa_memblock() and remove the factor and node_cnt parameters
* Change nodes[] parameter to an array containing the
* denominators
of the fractions of MEM_SIZE contained in each node e.g., if nodes[0] = SZ_8, node 0 will contain 1/8th ofMEM_SIZE
PATCH 2:
- tests/alloc_nid_api.c:
+ Add node_fractions[] array:
* Based on values from node_sizes[] array previously in PATCH 1
* Update to fit the new nodes[] parameter of
* setup_numa_memblock()
v2 -> v3
PATCH 2, PATCH 3, PATCH 4:
- tests/alloc_nid_api.c:
+ Edits for consistency between tests and test naming styles:
* Change "numa_top_down" to "top_down_numa"
* Change "numa_bottom_up" to "bottom_up_numa"
* Move "generic" to immediately before "check"
* alloc_try_nid_bottom_up_numa_small_node_check() and alloc_try_nid_bottom_up_numa_node_reserved_check():
- Separate size definition from its declaration
Updates based on changes to the dependent patch set noted above:
PATCH 2, PATCH 3, PATCH 4:
- tests/alloc_nid_api.c:
+ get_func_testing():
* Rename to get_memblock_alloc_try_nid_name().
+ Update calls to verify_mem_content() to assert_mem_content().
v1 -> v2
Updates based on changes to the dependent patch set noted above:
PATCH 2, PATCH 3, PATCH 4:
- tests/alloc_nid_api.c:
+ Update calls to verify_mem_content() to include the flags parameter required by the new, common verify_mem_content() function.
---
Rebecca Mckeever (4):
memblock tests: add simulation of physical memory with multiple NUMA
nodes
memblock tests: add top-down NUMA tests for memblock_alloc_try_nid* memblock tests: add bottom-up NUMA tests for memblock_alloc_try_nid* memblock tests: add generic NUMA tests for memblock_alloc_try_nid*
.../testing/memblock/scripts/Makefile.include | 2 +- tools/testing/memblock/tests/alloc_nid_api.c | 1466 ++++++++++++++++- tools/testing/memblock/tests/alloc_nid_api.h | 16 + tools/testing/memblock/tests/common.c | 31 + tools/testing/memblock/tests/common.h | 22 +- 5 files changed, 1524 insertions(+), 13 deletions(-)
Signature · 1 line
2.25.1
PATCH v6 1/4 memblock tests: add simulation of physical memory with multiple NUMA nodes
Add function setup_numa_memblock() for setting up a memory layout with multiple NUMA nodes in a previously allocated dummy physical memory. This function can be used in place of setup_memblock() in tests that need to simulate a NUMA system.
setup_numa_memblock():
- allows for setting up a memory layout by specifying the fraction of MEM_SIZE in each node
Set CONFIG_NODES_SHIFT to 4 when building with NUMA=1 to allow for up to 16 NUMA nodes.
--- .../testing/memblock/scripts/Makefile.include | 2 +- tools/testing/memblock/tests/common.c | 31 +++++++++++++++++++ tools/testing/memblock/tests/common.h | 4 ++- 3 files changed, 35 insertions(+), 2 deletions(-)
tools/testing/memblock/scripts/Makefile.include +1 -1
@@ -3,7 +3,7 @@ # Simulate CONFIG_NUMA=y ifeq ($(NUMA), 1) - CFLAGS += -D CONFIG_NUMA + CFLAGS += -D CONFIG_NUMA -D CONFIG_NODES_SHIFT=4 endif # Use 32 bit physical addresses.
tools/testing/memblock/tests/common.c +31 -0
@@ -9,6 +9,7 @@#define INIT_MEMBLOCK_RESERVED_REGIONS INIT_MEMBLOCK_REGIONS#define PREFIXES_MAX 15#define DELIM ": " +#define BASIS 10000static struct test_memory memory_block;static const char __maybe_unused *prefixes[PREFIXES_MAX ];
@@ -72,6 +73,36 @@ void setup_memblock(void)fill_memblock (); } +/** +* setup_numa_memblock: +* Set up a memory layout with multiple NUMA nodes in a previously allocated +* dummy physical memory. +* @node_fracs: an array representing the fraction of MEM_SIZE contained in +* each node in basis point units (one hundredth of 1% or 1/10000). +* For example, if node 0 should contain 1/8 of MEM_SIZE, +* node_fracs[0] = 1250. +* +* The nids will be set to 0 through NUMA_NODES - 1. +*/ +void setup_numa_memblock (const unsigned int node_fracs[]) +{ +phys_addr_t base; +int flags; + +reset_memblock_regions (); + base = (phys_addr_t )memory_block.base; + flags = (movable_node_is_enabled ()) ?MEMBLOCK_NONE :MEMBLOCK_HOTPLUG ; + +for (int i =0 ; i <NUMA_NODES ; i++) { +assert (node_fracs[i] <=BASIS ); +phys_addr_t size =MEM_SIZE * node_fracs[i] /BASIS ; + +memblock_add_node (base, size, i, flags); + base += size; + } +fill_memblock (); +} +void dummy_physical_memory_init (void ) { memory_block.base =malloc (MEM_SIZE );
tools/testing/memblock/tests/common.h +3 -1
@@ -10,7 +10,8 @@#include <linux/printk.h> #include <../selftests/kselftest.h> -#define MEM_SIZE SZ_16K +#define MEM_SIZE SZ_16K +#define NUMA_NODES 8enum test_flags {/* No special request. */
@@ -102,6 +103,7 @@ struct region {void reset_memblock_regions (void );void reset_memblock_attributes (void );void setup_memblock (void ); +void setup_numa_memblock (const unsigned int node_fracs[]);void dummy_physical_memory_init (void );void dummy_physical_memory_cleanup (void );void parse_args (int argc,char **argv);
Signature · 1 line
2.25.1
PATCH v6 2/4 memblock tests: add top-down NUMA tests for memblock_alloc_try_nid*
In reply to Rebecca MckeeverAdd tests for memblock_alloc_try_nid() and memblock_alloc_try_nid_raw() where the simulated physical memory is set up with multiple NUMA nodes. Additionally, all of these tests set nid != NUMA_NO_NODE. These tests are run with a top-down allocation direction.
The tested scenarios are:
Range unrestricted:
- region can be allocated in the specific node requested:
+ there are no previously reserved regions
+ the requested node is partially reserved but has enough space
- the specific node requested cannot accommodate the request, but the region can be allocated in a different node:
+ there are no previously reserved regions, but node is too small
+ the requested node is fully reserved
+ the requested node is partially reserved and does not have enough space
Range restricted:
- region can be allocated in the specific node requested after dropping min_addr:
+ range partially overlaps with two different nodes, where the first node is the requested node
+ range partially overlaps with two different nodes, where the requested node ends before min_addr
- region cannot be allocated in the specific node requested, but it can be allocated in the requested range:
+ range overlaps with multiple nodes along node boundaries, and the requested node ends before min_addr
+ range overlaps with multiple nodes along node boundaries, and the requested node starts after max_addr
- region cannot be allocated in the specific node requested, but it can be allocated after dropping min_addr:
+ range partially overlaps with two different nodes, where the second node is the requested node
--- tools/testing/memblock/tests/alloc_nid_api.c | 701 ++++++++++++++++++- tools/testing/memblock/tests/alloc_nid_api.h | 16 + tools/testing/memblock/tests/common.h | 18 + 3 files changed, 724 insertions(+), 11 deletions(-)
tools/testing/memblock/tests/alloc_nid_api.c +690 -11
@@ -3,6 +3,21 @@static int alloc_nid_test_flags =TEST_F_NONE ; +/* +* contains the fraction of MEM_SIZE contained in each node in basis point +* units (one hundredth of 1% or 1/10000) +*/ +static const unsigned int node_fractions[] = { +2500 ,/* 1/4 */ +625 ,/* 1/16 */ +1250 ,/* 1/8 */ +1250 ,/* 1/8 */ +625 ,/* 1/16 */ +625 ,/* 1/16 */ +2500 ,/* 1/4 */ +625 ,/* 1/16 */ +}; +static inline const char *const get_memblock_alloc_try_nid_name (int flags) {if (flags &TEST_F_RAW )
@@ -1054,7 +1069,7 @@ static int alloc_try_nid_bottom_up_cap_min_check(void)return 0 ; } -/* Test case wrappers */ +/* Test case wrappers for range tests */ static int alloc_try_nid_simple_check (void ) {test_print ("\tRunning %s...\n" , __func__);
@@ -1186,17 +1201,10 @@ static int alloc_try_nid_low_max_check(void)return 0 ; } -static int memblock_alloc_nid_checks_internal (int flags) +static int memblock_alloc_nid_range_checks (void ) { -const char *func =get_memblock_alloc_try_nid_name (flags); - - alloc_nid_test_flags = flags; -prefix_reset (); -prefix_push (func); -test_print ("Running %s tests...\n" , func); - -reset_memblock_attributes (); -dummy_physical_memory_init (); +test_print ("Running %s range tests...\n" , +get_memblock_alloc_try_nid_name (alloc_nid_test_flags));alloc_try_nid_simple_check ();alloc_try_nid_misaligned_check ();
@@ -1213,6 +1221,677 @@ static int memblock_alloc_nid_checks_internal(int flags)alloc_try_nid_reserved_all_check ();alloc_try_nid_low_max_check (); +return 0 ; +} + +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* has enough memory to allocate a region of the requested size. +* Expect to allocate an aligned region at the end of the requested node. +*/ +static int alloc_try_nid_top_down_numa_simple_check (void ) +{ +int nid_req =3 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +void *allocated_ptr =NULL ; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + +ASSERT_LE (SZ_4 , req_node->size); + size = req_node->size /SZ_4 ; + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base,region_end (req_node) - size); +ASSERT_LE (req_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* does not have enough memory to allocate a region of the requested size: +* +* | +-----+ +------------------+ | +* | | req | | expected | | +* +---+-----+----------+------------------+-----+ +* +* | +---------+ | +* | | rgn | | +* +-----------------------------+---------+-----+ +* +* Expect to allocate an aligned region at the end of the last node that has +* enough memory (in this case, nid = 6) after falling back to NUMA_NO_NODE. +*/ +static int alloc_try_nid_top_down_numa_small_node_check (void ) +{ +int nid_req =1 ; +int nid_exp =6 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *exp_node = &memblock.memory.regions[nid_exp]; +void *allocated_ptr =NULL ; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + size =SZ_2 * req_node->size; + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base,region_end (exp_node) - size); +ASSERT_LE (exp_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* is fully reserved: +* +* | +---------+ +------------------+ | +* | |requested| | expected | | +* +--------------+---------+------------+------------------+-----+ +* +* | +---------+ +---------+ | +* | | reserved| | new | | +* +--------------+---------+---------------------+---------+-----+ +* +* Expect to allocate an aligned region at the end of the last node that is +* large enough and has enough unreserved memory (in this case, nid = 6) after +* falling back to NUMA_NO_NODE. The region count and total size get updated. +*/ +static int alloc_try_nid_top_down_numa_node_reserved_check (void ) +{ +int nid_req =2 ; +int nid_exp =6 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[1 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *exp_node = &memblock.memory.regions[nid_exp]; +void *allocated_ptr =NULL ; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + size = req_node->size; + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + +memblock_reserve (req_node->base, req_node->size); + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base,region_end (exp_node) - size); +ASSERT_LE (exp_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,2 ); +ASSERT_EQ (memblock.reserved.total_size, size + req_node->size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* is partially reserved but has enough memory for the allocated region: +* +* | +---------------------------------------+ | +* | | requested | | +* +-----------+---------------------------------------+----------+ +* +* | +------------------+ +-----+ | +* | | reserved | | new | | +* +-----------+------------------+--------------+-----+----------+ +* +* Expect to allocate an aligned region at the end of the requested node. The +* region count and total size get updated. +*/ +static int alloc_try_nid_top_down_numa_part_reserved_check (void ) +{ +int nid_req =4 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[1 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +void *allocated_ptr =NULL ; +struct region r1; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + +ASSERT_LE (SZ_8 , req_node->size); + r1.base = req_node->base; + r1.size = req_node->size /SZ_2 ; + size = r1.size /SZ_4 ; + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + +memblock_reserve (r1.base, r1.size); + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base,region_end (req_node) - size); +ASSERT_LE (req_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,2 ); +ASSERT_EQ (memblock.reserved.total_size, size + r1.size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* is partially reserved and does not have enough contiguous memory for the +* allocated region: +* +* | +-----------------------+ +----------------------| +* | | requested | | expected | +* +-----------+-----------------------+---------+----------------------+ +* +* | +----------+ +-----------| +* | | reserved | | new | +* +-----------------+----------+---------------------------+-----------+ +* +* Expect to allocate an aligned region at the end of the last node that is +* large enough and has enough unreserved memory (in this case, +* nid = NUMA_NODES - 1) after falling back to NUMA_NO_NODE. The region count +* and total size get updated. +*/ +static int alloc_try_nid_top_down_numa_part_reserved_fallback_check (void ) +{ +int nid_req =4 ; +int nid_exp =NUMA_NODES -1 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[1 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *exp_node = &memblock.memory.regions[nid_exp]; +void *allocated_ptr =NULL ; +struct region r1; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + +ASSERT_LE (SZ_4 , req_node->size); + size = req_node->size /SZ_2 ; + r1.base = req_node->base + (size /SZ_2 ); + r1.size = size; + + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + +memblock_reserve (r1.base, r1.size); + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base,region_end (exp_node) - size); +ASSERT_LE (exp_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,2 ); +ASSERT_EQ (memblock.reserved.total_size, size + r1.size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region that spans over the min_addr +* and max_addr range and overlaps with two different nodes, where the first +* node is the requested node: +* +* min_addr +* | max_addr +* | | +* v v +* | +-----------------------+-----------+ | +* | | requested | node3 | | +* +-----------+-----------------------+-----------+--------------+ +* + + +* | +-----------+ | +* | | rgn | | +* +-----------------------+-----------+--------------------------+ +* +* Expect to drop the lower limit and allocate a memory region that ends at +* the end of the requested node. +*/ +static int alloc_try_nid_top_down_numa_split_range_low_check (void ) +{ +int nid_req =2 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +void *allocated_ptr =NULL ; +phys_addr_t size =SZ_512 ; +phys_addr_t min_addr; +phys_addr_t max_addr; +phys_addr_t req_node_end; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + req_node_end =region_end (req_node); + min_addr = req_node_end -SZ_256 ; + max_addr = min_addr + size; + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, req_node_end - size); +ASSERT_LE (req_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region that spans over the min_addr +* and max_addr range and overlaps with two different nodes, where the second +* node is the requested node: +* +* min_addr +* | max_addr +* | | +* v v +* | +--------------------------+---------+ | +* | | expected |requested| | +* +------+--------------------------+---------+----------------+ +* + + +* | +---------+ | +* | | rgn | | +* +-----------------------+---------+--------------------------+ +* +* Expect to drop the lower limit and allocate a memory region that +* ends at the end of the first node that overlaps with the range. +*/ +static int alloc_try_nid_top_down_numa_split_range_high_check (void ) +{ +int nid_req =3 ; +int nid_exp = nid_req -1 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *exp_node = &memblock.memory.regions[nid_exp]; +void *allocated_ptr =NULL ; +phys_addr_t size =SZ_512 ; +phys_addr_t min_addr; +phys_addr_t max_addr; +phys_addr_t exp_node_end; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + exp_node_end =region_end (exp_node); + min_addr = exp_node_end -SZ_256 ; + max_addr = min_addr + size; + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, exp_node_end - size); +ASSERT_LE (exp_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region that spans over the min_addr +* and max_addr range and overlaps with two different nodes, where the requested +* node ends before min_addr: +* +* min_addr +* | max_addr +* | | +* v v +* | +---------------+ +-------------+---------+ | +* | | requested | | node1 | node2 | | +* +----+---------------+--------+-------------+---------+----------+ +* + + +* | +---------+ | +* | | rgn | | +* +----------+---------+-------------------------------------------+ +* +* Expect to drop the lower limit and allocate a memory region that ends at +* the end of the requested node. +*/ +static int alloc_try_nid_top_down_numa_no_overlap_split_check (void ) +{ +int nid_req =2 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *node2 = &memblock.memory.regions[6 ]; +void *allocated_ptr =NULL ; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + size =SZ_512 ; + min_addr = node2->base -SZ_256 ; + max_addr = min_addr + size; + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base,region_end (req_node) - size); +ASSERT_LE (req_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate memory within min_addr and max_add range when +* the requested node and the range do not overlap, and requested node ends +* before min_addr. The range overlaps with multiple nodes along node +* boundaries: +* +* min_addr +* | max_addr +* | | +* v v +* |-----------+ +----------+----...----+----------+ | +* | requested | | min node | ... | max node | | +* +-----------+-----------+----------+----...----+----------+------+ +* + + +* | +-----+ | +* | | rgn | | +* +---------------------------------------------------+-----+------+ +* +* Expect to allocate a memory region at the end of the final node in +* the range after falling back to NUMA_NO_NODE. +*/ +static int alloc_try_nid_top_down_numa_no_overlap_low_check (void ) +{ +int nid_req =0 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *min_node = &memblock.memory.regions[2 ]; +struct memblock_region *max_node = &memblock.memory.regions[5 ]; +void *allocated_ptr =NULL ; +phys_addr_t size =SZ_64 ; +phys_addr_t max_addr; +phys_addr_t min_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + min_addr = min_node->base; + max_addr =region_end (max_node); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, max_addr - size); +ASSERT_LE (max_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate memory within min_addr and max_add range when +* the requested node and the range do not overlap, and requested node starts +* after max_addr. The range overlaps with multiple nodes along node +* boundaries: +* +* min_addr +* | max_addr +* | | +* v v +* | +----------+----...----+----------+ +-----------+ | +* | | min node | ... | max node | | requested | | +* +-----+----------+----...----+----------+--------+-----------+---+ +* + + +* | +-----+ | +* | | rgn | | +* +---------------------------------+-----+------------------------+ +* +* Expect to allocate a memory region at the end of the final node in +* the range after falling back to NUMA_NO_NODE. +*/ +static int alloc_try_nid_top_down_numa_no_overlap_high_check (void ) +{ +int nid_req =7 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *min_node = &memblock.memory.regions[2 ]; +struct memblock_region *max_node = &memblock.memory.regions[5 ]; +void *allocated_ptr =NULL ; +phys_addr_t size =SZ_64 ; +phys_addr_t max_addr; +phys_addr_t min_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + min_addr = min_node->base; + max_addr =region_end (max_node); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, max_addr - size); +ASSERT_LE (max_node->base, new_rgn->base); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* Test case wrappers for NUMA tests */ +static int alloc_try_nid_numa_simple_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_simple_check (); + +return 0 ; +} + +static int alloc_try_nid_numa_small_node_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_small_node_check (); + +return 0 ; +} + +static int alloc_try_nid_numa_node_reserved_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_node_reserved_check (); + +return 0 ; +} + +static int alloc_try_nid_numa_part_reserved_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_part_reserved_check (); + +return 0 ; +} + +static int alloc_try_nid_numa_part_reserved_fallback_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_part_reserved_fallback_check (); + +return 0 ; +} + +static int alloc_try_nid_numa_split_range_low_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_split_range_low_check (); + +return 0 ; +} + +static int alloc_try_nid_numa_split_range_high_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_split_range_high_check (); + +return 0 ; +} + +static int alloc_try_nid_numa_no_overlap_split_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_no_overlap_split_check (); + +return 0 ; +} + +static int alloc_try_nid_numa_no_overlap_low_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_no_overlap_low_check (); + +return 0 ; +} + +static int alloc_try_nid_numa_no_overlap_high_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +memblock_set_bottom_up (false); +alloc_try_nid_top_down_numa_no_overlap_high_check (); + +return 0 ; +} + +int __memblock_alloc_nid_numa_checks (void ) +{ +test_print ("Running %s NUMA tests...\n" , +get_memblock_alloc_try_nid_name (alloc_nid_test_flags)); + +alloc_try_nid_numa_simple_check (); +alloc_try_nid_numa_small_node_check (); +alloc_try_nid_numa_node_reserved_check (); +alloc_try_nid_numa_part_reserved_check (); +alloc_try_nid_numa_part_reserved_fallback_check (); +alloc_try_nid_numa_split_range_low_check (); +alloc_try_nid_numa_split_range_high_check (); + +alloc_try_nid_numa_no_overlap_split_check (); +alloc_try_nid_numa_no_overlap_low_check (); +alloc_try_nid_numa_no_overlap_high_check (); + +return 0 ; +} + +static int memblock_alloc_nid_checks_internal (int flags) +{ + alloc_nid_test_flags = flags; + +prefix_reset (); +prefix_push (get_memblock_alloc_try_nid_name (flags)); + +reset_memblock_attributes (); +dummy_physical_memory_init (); + +memblock_alloc_nid_range_checks (); +memblock_alloc_nid_numa_checks (); +dummy_physical_memory_cleanup ();prefix_pop ();
tools/testing/memblock/tests/alloc_nid_api.h +16 -0
@@ -5,5 +5,21 @@#include "common.h" int memblock_alloc_nid_checks (void ); +int __memblock_alloc_nid_numa_checks (void ); + +#ifdef CONFIG_NUMA +static inline int memblock_alloc_nid_numa_checks (void ) +{ +__memblock_alloc_nid_numa_checks (); +return 0 ; +} + +#else +static inline int memblock_alloc_nid_numa_checks (void ) +{ +return 0 ; +} + +#endif /* CONFIG_NUMA */ #endif
tools/testing/memblock/tests/common.h +18 -0
@@ -59,6 +59,19 @@ enum test_flags {assert ((_expected) < (_seen)); \ }while (0 ) +/** +* ASSERT_LE(): +* Check the condition +* @_expected <= @_seen +* If false, print failed test message (if running with --verbose) and then +* assert. + */ +#define ASSERT_LE (_expected, _seen) do { \ + if ((_expected) > (_seen)) \ + test_fail(); \ + assert((_expected) <= (_seen)); \ +} while (0) +/** * ASSERT_MEM_EQ(): * Check that the first @_size bytes of @_seen are all equal to @_expected.
@@ -100,6 +113,11 @@ struct region {phys_addr_t size; }; +static inline phys_addr_t __maybe_unused region_end (struct memblock_region *rgn) +{ +return rgn->base + rgn->size; +} +void reset_memblock_regions (void );void reset_memblock_attributes (void );void setup_memblock (void );
Signature · 1 line
2.25.1
PATCH v6 3/4 memblock tests: add bottom-up NUMA tests for memblock_alloc_try_nid*
In reply to Rebecca MckeeverAdd tests for memblock_alloc_try_nid() and memblock_alloc_try_nid_raw() where the simulated physical memory is set up with multiple NUMA nodes. Additionally, all of these tests set nid != NUMA_NO_NODE. These tests are run with a bottom-up allocation direction.
The tested scenarios are:
Range unrestricted:
- region can be allocated in the specific node requested:
+ there are no previously reserved regions
+ the requested node is partially reserved but has enough space
- the specific node requested cannot accommodate the request, but the region can be allocated in a different node:
+ there are no previously reserved regions, but node is too small
+ the requested node is fully reserved
+ the requested node is partially reserved and does not have enough space
Range restricted:
- region can be allocated in the specific node requested after dropping min_addr:
+ range partially overlaps with two different nodes, where the first node is the requested node
+ range partially overlaps with two different nodes, where the requested node ends before min_addr
- region cannot be allocated in the specific node requested, but it can be allocated in the requested range:
+ range overlaps with multiple nodes along node boundaries, and the requested node ends before min_addr
+ range overlaps with multiple nodes along node boundaries, and the requested node starts after max_addr
- region cannot be allocated in the specific node requested, but it can be allocated after dropping min_addr:
+ range partially overlaps with two different nodes, where the second node is the requested node
--- tools/testing/memblock/tests/alloc_nid_api.c | 568 +++++++++++++++++++ 1 file changed, 568 insertions(+)
tools/testing/memblock/tests/alloc_nid_api.c +568 -0
@@ -1768,12 +1768,562 @@ static int alloc_try_nid_top_down_numa_no_overlap_high_check(void)return 0 ; } +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* has enough memory to allocate a region of the requested size. +* Expect to allocate an aligned region at the beginning of the requested node. +*/ +static int alloc_try_nid_bottom_up_numa_simple_check (void ) +{ +int nid_req =3 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +void *allocated_ptr =NULL ; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + +ASSERT_LE (SZ_4 , req_node->size); + size = req_node->size /SZ_4 ; + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, req_node->base); +ASSERT_LE (region_end (new_rgn),region_end (req_node)); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* does not have enough memory to allocate a region of the requested size: +* +* |----------------------+-----+ | +* | expected | req | | +* +----------------------+-----+----------------+ +* +* |---------+ | +* | rgn | | +* +---------+-----------------------------------+ +* +* Expect to allocate an aligned region at the beginning of the first node that +* has enough memory (in this case, nid = 0) after falling back to NUMA_NO_NODE. +*/ +static int alloc_try_nid_bottom_up_numa_small_node_check (void ) +{ +int nid_req =1 ; +int nid_exp =0 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *exp_node = &memblock.memory.regions[nid_exp]; +void *allocated_ptr =NULL ; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + size =SZ_2 * req_node->size; + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, exp_node->base); +ASSERT_LE (region_end (new_rgn),region_end (exp_node)); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* is fully reserved: +* +* |----------------------+ +-----------+ | +* | expected | | requested | | +* +----------------------+-----+-----------+--------------------+ +* +* |-----------+ +-----------+ | +* | new | | reserved | | +* +-----------+----------------+-----------+--------------------+ +* +* Expect to allocate an aligned region at the beginning of the first node that +* is large enough and has enough unreserved memory (in this case, nid = 0) +* after falling back to NUMA_NO_NODE. The region count and total size get +* updated. +*/ +static int alloc_try_nid_bottom_up_numa_node_reserved_check (void ) +{ +int nid_req =2 ; +int nid_exp =0 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *exp_node = &memblock.memory.regions[nid_exp]; +void *allocated_ptr =NULL ; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + size = req_node->size; + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + +memblock_reserve (req_node->base, req_node->size); + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, exp_node->base); +ASSERT_LE (region_end (new_rgn),region_end (exp_node)); + +ASSERT_EQ (memblock.reserved.cnt,2 ); +ASSERT_EQ (memblock.reserved.total_size, size + req_node->size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* is partially reserved but has enough memory for the allocated region: +* +* | +---------------------------------------+ | +* | | requested | | +* +-----------+---------------------------------------+---------+ +* +* | +------------------+-----+ | +* | | reserved | new | | +* +-----------+------------------+-----+------------------------+ +* +* Expect to allocate an aligned region in the requested node that merges with +* the existing reserved region. The total size gets updated. +*/ +static int alloc_try_nid_bottom_up_numa_part_reserved_check (void ) +{ +int nid_req =4 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +void *allocated_ptr =NULL ; +struct region r1; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; +phys_addr_t total_size; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + +ASSERT_LE (SZ_8 , req_node->size); + r1.base = req_node->base; + r1.size = req_node->size /SZ_2 ; + size = r1.size /SZ_4 ; + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + total_size = size + r1.size; + +memblock_reserve (r1.base, r1.size); + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, total_size); +ASSERT_EQ (new_rgn->base, req_node->base); +ASSERT_LE (region_end (new_rgn),region_end (req_node)); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, total_size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* is partially reserved and does not have enough contiguous memory for the +* allocated region: +* +* |----------------------+ +-----------------------+ | +* | expected | | requested | | +* +----------------------+-------+-----------------------+---------+ +* +* |-----------+ +----------+ | +* | new | | reserved | | +* +-----------+------------------------+----------+----------------+ +* +* Expect to allocate an aligned region at the beginning of the first +* node that is large enough and has enough unreserved memory (in this case, +* nid = 0) after falling back to NUMA_NO_NODE. The region count and total size +* get updated. +*/ +static int alloc_try_nid_bottom_up_numa_part_reserved_fallback_check (void ) +{ +int nid_req =4 ; +int nid_exp =0 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *exp_node = &memblock.memory.regions[nid_exp]; +void *allocated_ptr =NULL ; +struct region r1; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + +ASSERT_LE (SZ_4 , req_node->size); + size = req_node->size /SZ_2 ; + r1.base = req_node->base + (size /SZ_2 ); + r1.size = size; + + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + +memblock_reserve (r1.base, r1.size); + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, exp_node->base); +ASSERT_LE (region_end (new_rgn),region_end (exp_node)); + +ASSERT_EQ (memblock.reserved.cnt,2 ); +ASSERT_EQ (memblock.reserved.total_size, size + r1.size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region that spans over the min_addr +* and max_addr range and overlaps with two different nodes, where the first +* node is the requested node: +* +* min_addr +* | max_addr +* | | +* v v +* | +-----------------------+-----------+ | +* | | requested | node3 | | +* +-----------+-----------------------+-----------+--------------+ +* + + +* | +-----------+ | +* | | rgn | | +* +-----------+-----------+--------------------------------------+ +* +* Expect to drop the lower limit and allocate a memory region at the beginning +* of the requested node. +*/ +static int alloc_try_nid_bottom_up_numa_split_range_low_check (void ) +{ +int nid_req =2 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +void *allocated_ptr =NULL ; +phys_addr_t size =SZ_512 ; +phys_addr_t min_addr; +phys_addr_t max_addr; +phys_addr_t req_node_end; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + req_node_end =region_end (req_node); + min_addr = req_node_end -SZ_256 ; + max_addr = min_addr + size; + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, req_node->base); +ASSERT_LE (region_end (new_rgn), req_node_end); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region that spans over the min_addr +* and max_addr range and overlaps with two different nodes, where the second +* node is the requested node: +* +* min_addr +* | max_addr +* | | +* v v +* |------------------+ +----------------------+---------+ | +* | expected | | previous |requested| | +* +------------------+--------+----------------------+---------+------+ +* + + +* |---------+ | +* | rgn | | +* +---------+---------------------------------------------------------+ +* +* Expect to drop the lower limit and allocate a memory region at the beginning +* of the first node that has enough memory. +*/ +static int alloc_try_nid_bottom_up_numa_split_range_high_check (void ) +{ +int nid_req =3 ; +int nid_exp =0 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *exp_node = &memblock.memory.regions[nid_exp]; +void *allocated_ptr =NULL ; +phys_addr_t size =SZ_512 ; +phys_addr_t min_addr; +phys_addr_t max_addr; +phys_addr_t exp_node_end; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + exp_node_end =region_end (req_node); + min_addr = req_node->base -SZ_256 ; + max_addr = min_addr + size; + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, exp_node->base); +ASSERT_LE (region_end (new_rgn), exp_node_end); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate a memory region that spans over the min_addr +* and max_addr range and overlaps with two different nodes, where the requested +* node ends before min_addr: +* +* min_addr +* | max_addr +* | | +* v v +* | +---------------+ +-------------+---------+ | +* | | requested | | node1 | node2 | | +* +----+---------------+--------+-------------+---------+---------+ +* + + +* | +---------+ | +* | | rgn | | +* +----+---------+------------------------------------------------+ +* +* Expect to drop the lower limit and allocate a memory region that starts at +* the beginning of the requested node. +*/ +static int alloc_try_nid_bottom_up_numa_no_overlap_split_check (void ) +{ +int nid_req =2 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *node2 = &memblock.memory.regions[6 ]; +void *allocated_ptr =NULL ; +phys_addr_t size; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + size =SZ_512 ; + min_addr = node2->base -SZ_256 ; + max_addr = min_addr + size; + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, req_node->base); +ASSERT_LE (region_end (new_rgn),region_end (req_node)); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate memory within min_addr and max_add range when +* the requested node and the range do not overlap, and requested node ends +* before min_addr. The range overlaps with multiple nodes along node +* boundaries: +* +* min_addr +* | max_addr +* | | +* v v +* |-----------+ +----------+----...----+----------+ | +* | requested | | min node | ... | max node | | +* +-----------+-----------+----------+----...----+----------+------+ +* + + +* | +-----+ | +* | | rgn | | +* +-----------------------+-----+----------------------------------+ +* +* Expect to allocate a memory region at the beginning of the first node +* in the range after falling back to NUMA_NO_NODE. +*/ +static int alloc_try_nid_bottom_up_numa_no_overlap_low_check (void ) +{ +int nid_req =0 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *min_node = &memblock.memory.regions[2 ]; +struct memblock_region *max_node = &memblock.memory.regions[5 ]; +void *allocated_ptr =NULL ; +phys_addr_t size =SZ_64 ; +phys_addr_t max_addr; +phys_addr_t min_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + min_addr = min_node->base; + max_addr =region_end (max_node); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, min_addr); +ASSERT_LE (region_end (new_rgn),region_end (min_node)); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate memory within min_addr and max_add range when +* the requested node and the range do not overlap, and requested node starts +* after max_addr. The range overlaps with multiple nodes along node +* boundaries: +* +* min_addr +* | max_addr +* | | +* v v +* | +----------+----...----+----------+ +---------+ | +* | | min node | ... | max node | |requested| | +* +-----+----------+----...----+----------+---------+---------+---+ +* + + +* | +-----+ | +* | | rgn | | +* +-----+-----+---------------------------------------------------+ +* +* Expect to allocate a memory region at the beginning of the first node +* in the range after falling back to NUMA_NO_NODE. +*/ +static int alloc_try_nid_bottom_up_numa_no_overlap_high_check (void ) +{ +int nid_req =7 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *min_node = &memblock.memory.regions[2 ]; +struct memblock_region *max_node = &memblock.memory.regions[5 ]; +void *allocated_ptr =NULL ; +phys_addr_t size =SZ_64 ; +phys_addr_t max_addr; +phys_addr_t min_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + min_addr = min_node->base; + max_addr =region_end (max_node); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, size); +ASSERT_EQ (new_rgn->base, min_addr); +ASSERT_LE (region_end (new_rgn),region_end (min_node)); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, size); + +test_pass_pop (); + +return 0 ; +} +/* Test case wrappers for NUMA tests */ static int alloc_try_nid_numa_simple_check (void ) {test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_simple_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_simple_check ();return 0 ; }
@@ -1783,6 +2333,8 @@ static int alloc_try_nid_numa_small_node_check(void)test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_small_node_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_small_node_check ();return 0 ; }
@@ -1792,6 +2344,8 @@ static int alloc_try_nid_numa_node_reserved_check(void)test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_node_reserved_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_node_reserved_check ();return 0 ; }
@@ -1801,6 +2355,8 @@ static int alloc_try_nid_numa_part_reserved_check(void)test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_part_reserved_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_part_reserved_check ();return 0 ; }
@@ -1810,6 +2366,8 @@ static int alloc_try_nid_numa_part_reserved_fallback_check(void)test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_part_reserved_fallback_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_part_reserved_fallback_check ();return 0 ; }
@@ -1819,6 +2377,8 @@ static int alloc_try_nid_numa_split_range_low_check(void)test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_split_range_low_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_split_range_low_check ();return 0 ; }
@@ -1828,6 +2388,8 @@ static int alloc_try_nid_numa_split_range_high_check(void)test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_split_range_high_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_split_range_high_check ();return 0 ; }
@@ -1837,6 +2399,8 @@ static int alloc_try_nid_numa_no_overlap_split_check(void)test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_no_overlap_split_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_no_overlap_split_check ();return 0 ; }
@@ -1846,6 +2410,8 @@ static int alloc_try_nid_numa_no_overlap_low_check(void)test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_no_overlap_low_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_no_overlap_low_check ();return 0 ; }
@@ -1855,6 +2421,8 @@ static int alloc_try_nid_numa_no_overlap_high_check(void)test_print ("\tRunning %s...\n" , __func__);memblock_set_bottom_up (false);alloc_try_nid_top_down_numa_no_overlap_high_check (); +memblock_set_bottom_up (true); +alloc_try_nid_bottom_up_numa_no_overlap_high_check ();return 0 ; }
Signature · 1 line
2.25.1
PATCH v6 4/4 memblock tests: add generic NUMA tests for memblock_alloc_try_nid*
In reply to Rebecca MckeeverAdd tests for memblock_alloc_try_nid() and memblock_alloc_try_nid_raw() where the simulated physical memory is set up with multiple NUMA nodes. Additionally, two of these tests set nid != NUMA_NO_NODE. All tests are run for both top-down and bottom-up allocation directions.
The tested scenarios are:
Range unrestricted:
- region cannot be allocated:
+ none of the nodes have enough memory to allocate the region
Range restricted:
- region can be allocated in the specific node requested without dropping min_addr:
+ the range fully overlaps with the node, and there are adjacent reserved regions
- region cannot be allocated:
+ nid is set to NUMA_NO_NODE and the total range can fit the region, but the range is split between two nodes and everything else is reserved
--- tools/testing/memblock/tests/alloc_nid_api.c | 197 +++++++++++++++++++ 1 file changed, 197 insertions(+)
tools/testing/memblock/tests/alloc_nid_api.c +197 -0
@@ -2316,6 +2316,173 @@ static int alloc_try_nid_bottom_up_numa_no_overlap_high_check(void)return 0 ; } +/* +* A test that tries to allocate a memory region in a specific NUMA node that +* does not have enough memory to allocate a region of the requested size. +* Additionally, none of the nodes have enough memory to allocate the region: +* +* +-----------------------------------+ +* | new | +* +-----------------------------------+ +* |-------+-------+-------+-------+-------+-------+-------+-------| +* | node0 | node1 | node2 | node3 | node4 | node5 | node6 | node7 | +* +-------+-------+-------+-------+-------+-------+-------+-------+ +* +* Expect no allocation to happen. +*/ +static int alloc_try_nid_numa_large_region_generic_check (void ) +{ +int nid_req =3 ; +void *allocated_ptr =NULL ; +phys_addr_t size =MEM_SIZE /SZ_2 ; +phys_addr_t min_addr; +phys_addr_t max_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + min_addr =memblock_start_of_DRAM (); + max_addr =memblock_end_of_DRAM (); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); +ASSERT_EQ (allocated_ptr,NULL ); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate memory within min_addr and max_addr range when +* there are two reserved regions at the borders. The requested node starts at +* min_addr and ends at max_addr and is the same size as the region to be +* allocated: +* +* min_addr +* | max_addr +* | | +* v v +* | +-----------+-----------------------+-----------------------| +* | | node5 | requested | node7 | +* +------+-----------+-----------------------+-----------------------+ +* + + +* | +----+-----------------------+----+ | +* | | r2 | new | r1 | | +* +-------------+----+-----------------------+----+------------------+ +* +* Expect to merge all of the regions into one. The region counter and total +* size fields get updated. +*/ +static int alloc_try_nid_numa_reserved_full_merge_generic_check (void ) +{ +int nid_req =6 ; +int nid_next = nid_req +1 ; +struct memblock_region *new_rgn = &memblock.reserved.regions[0 ]; +struct memblock_region *req_node = &memblock.memory.regions[nid_req]; +struct memblock_region *next_node = &memblock.memory.regions[nid_next]; +void *allocated_ptr =NULL ; +struct region r1, r2; +phys_addr_t size = req_node->size; +phys_addr_t total_size; +phys_addr_t max_addr; +phys_addr_t min_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + r1.base = next_node->base; + r1.size =SZ_128 ; + + r2.size =SZ_128 ; + r2.base = r1.base - (size + r2.size); + + total_size = r1.size + r2.size + size; + min_addr = r2.base + r2.size; + max_addr = r1.base; + +memblock_reserve (r1.base, r1.size); +memblock_reserve (r2.base, r2.size); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, nid_req); + +ASSERT_NE (allocated_ptr,NULL ); +assert_mem_content (allocated_ptr, size, alloc_nid_test_flags); + +ASSERT_EQ (new_rgn->size, total_size); +ASSERT_EQ (new_rgn->base, r2.base); + +ASSERT_LE (new_rgn->base, req_node->base); +ASSERT_LE (region_end (req_node),region_end (new_rgn)); + +ASSERT_EQ (memblock.reserved.cnt,1 ); +ASSERT_EQ (memblock.reserved.total_size, total_size); + +test_pass_pop (); + +return 0 ; +} + +/* +* A test that tries to allocate memory within min_addr and max_add range, +* where the total range can fit the region, but it is split between two nodes +* and everything else is reserved. Additionally, nid is set to NUMA_NO_NODE +* instead of requesting a specific node: +* +* +-----------+ +* | new | +* +-----------+ +* | +---------------------+-----------| +* | | prev node | next node | +* +------+---------------------+-----------+ +* + + +* |----------------------+ +-----| +* | r1 | | r2 | +* +----------------------+-----------+-----+ +* ^ ^ +* | | +* | max_addr +* | +* min_addr +* +* Expect no allocation to happen. +*/ +static int alloc_try_nid_numa_split_all_reserved_generic_check (void ) +{ +void *allocated_ptr =NULL ; +struct memblock_region *next_node = &memblock.memory.regions[7 ]; +struct region r1, r2; +phys_addr_t size =SZ_256 ; +phys_addr_t max_addr; +phys_addr_t min_addr; + +PREFIX_PUSH (); +setup_numa_memblock (node_fractions); + + r2.base = next_node->base +SZ_128 ; + r2.size =memblock_end_of_DRAM () - r2.base; + + r1.size =MEM_SIZE - (r2.size + size); + r1.base =memblock_start_of_DRAM (); + + min_addr = r1.base + r1.size; + max_addr = r2.base; + +memblock_reserve (r1.base, r1.size); +memblock_reserve (r2.base, r2.size); + + allocated_ptr =run_memblock_alloc_try_nid (size,SMP_CACHE_BYTES , + min_addr, max_addr, +NUMA_NO_NODE ); + +ASSERT_EQ (allocated_ptr,NULL ); + +test_pass_pop (); + +return 0 ; +} +/* Test case wrappers for NUMA tests */ static int alloc_try_nid_numa_simple_check (void ) {
@@ -2427,6 +2594,33 @@ static int alloc_try_nid_numa_no_overlap_high_check(void)return 0 ; } +static int alloc_try_nid_numa_large_region_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +run_top_down (alloc_try_nid_numa_large_region_generic_check); +run_bottom_up (alloc_try_nid_numa_large_region_generic_check); + +return 0 ; +} + +static int alloc_try_nid_numa_reserved_full_merge_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +run_top_down (alloc_try_nid_numa_reserved_full_merge_generic_check); +run_bottom_up (alloc_try_nid_numa_reserved_full_merge_generic_check); + +return 0 ; +} + +static int alloc_try_nid_numa_split_all_reserved_check (void ) +{ +test_print ("\tRunning %s...\n" , __func__); +run_top_down (alloc_try_nid_numa_split_all_reserved_generic_check); +run_bottom_up (alloc_try_nid_numa_split_all_reserved_generic_check); + +return 0 ; +} +int __memblock_alloc_nid_numa_checks (void ) { test_print("Running %s NUMA tests...\n",
@@ -2443,6 +2637,9 @@ int __memblock_alloc_nid_numa_checks(void)alloc_try_nid_numa_no_overlap_split_check ();alloc_try_nid_numa_no_overlap_low_check ();alloc_try_nid_numa_no_overlap_high_check (); +alloc_try_nid_numa_large_region_check (); +alloc_try_nid_numa_reserved_full_merge_check (); +alloc_try_nid_numa_split_all_reserved_check ();return 0 ; }
Signature · 1 line
2.25.1
Quoted 24 lines · Rebecca Mckeever
These patches add additional tests for
memblock_alloc_try_nid()andmemblock_alloc_try_nid_raw()that use a simulated physical memory that is set up with multiple NUMA nodes. Additionally, most of these tests set nid !=NUMA_NO_NODE.To set up a simulated physical memory with multiple NUMA nodes, patch 1 introduces
setup_numa_memblock(). This function uses a previously allocated dummy physical memory. It can be used in place ofsetup_memblock()in tests that need to simulate a NUMA system.These tests are run twice, once for
memblock_alloc_try_nid()and once formemblock_alloc_try_nid_raw(), so that both functions are tested with the same set of tests. When the tests runmemblock_alloc_try_nid(), they test that the entire memory region is zero. When the tests runmemblock_alloc_try_nid_raw(), they test that the entire memory region is nonzero.Rebecca Mckeever (4):
memblock tests: add simulation of physical memory with multiple NUMA
nodes
memblock tests: add top-down NUMA tests for memblock_alloc_try_nid* memblock tests: add bottom-up NUMA tests for memblock_alloc_try_nid* memblock tests: add generic NUMA tests for memblock_alloc_try_nid*
Quoted 6 lines
.../testing/memblock/scripts/Makefile.include | 2 +- tools/testing/memblock/tests/alloc_nid_api.c | 1466 ++++++++++++++++- tools/testing/memblock/tests/alloc_nid_api.h | 16 + tools/testing/memblock/tests/common.c | 31 + tools/testing/memblock/tests/common.h | 22 +- 5 files changed, 1524 insertions(+), 13 deletions(-)
Applied, thanks!
Signature · 2 lines
Sincerely yours, Mike.