Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

memblock test: Add test to memblock_add() 129th region

Add 129th region into the memblock, and this will trigger the
memblock_double_array() function, this needs valid memory regions. So
using dummy_physical_memory_init() to allocate a large enough memory
region, and split it into a large enough memory which can be choosed by
memblock_double_array(), and the left memory will be split into small
memory region, and add them into the memblock. It make sure the
memblock_double_array() will always choose the valid memory region that
is allocated by the dummy_physical_memory_init().
So memblock_double_array() must success.

Another thing should be done is to restore the memory.regions after
memblock_double_array(), due to now the memory.regions is pointing to a
memory region allocated by dummy_physical_memory_init(). And it will
affect the subsequent tests if we don't restore the memory region. So
simply record the origin region, and restore it after the test.

Signed-off-by: Shaoqin Huang <shaoqin.huang@intel.com>
Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
Link: https://lore.kernel.org/r/20221011062128.49359-2-shaoqin.huang@intel.com

authored by

Shaoqin Huang and committed by
Mike Rapoport
085bdaa6 30a0b95b

+103 -3
+93
tools/testing/memblock/tests/basic_api.c
··· 423 423 return 0; 424 424 } 425 425 426 + /* 427 + * A test that trying to add the 129th memory block. 428 + * Expect to trigger memblock_double_array() to double the 429 + * memblock.memory.max, find a new valid memory as 430 + * memory.regions. 431 + */ 432 + static int memblock_add_many_check(void) 433 + { 434 + int i; 435 + void *orig_region; 436 + struct region r = { 437 + .base = SZ_16K, 438 + .size = SZ_16K, 439 + }; 440 + phys_addr_t new_memory_regions_size; 441 + phys_addr_t base, size = SZ_64; 442 + phys_addr_t gap_size = SZ_64; 443 + 444 + PREFIX_PUSH(); 445 + 446 + reset_memblock_regions(); 447 + memblock_allow_resize(); 448 + 449 + dummy_physical_memory_init(); 450 + /* 451 + * We allocated enough memory by using dummy_physical_memory_init(), and 452 + * split it into small block. First we split a large enough memory block 453 + * as the memory region which will be choosed by memblock_double_array(). 454 + */ 455 + base = PAGE_ALIGN(dummy_physical_memory_base()); 456 + new_memory_regions_size = PAGE_ALIGN(INIT_MEMBLOCK_REGIONS * 2 * 457 + sizeof(struct memblock_region)); 458 + memblock_add(base, new_memory_regions_size); 459 + 460 + /* This is the base of small memory block. */ 461 + base += new_memory_regions_size + gap_size; 462 + 463 + orig_region = memblock.memory.regions; 464 + 465 + for (i = 0; i < INIT_MEMBLOCK_REGIONS; i++) { 466 + /* 467 + * Add these small block to fulfill the memblock. We keep a 468 + * gap between the nearby memory to avoid being merged. 469 + */ 470 + memblock_add(base, size); 471 + base += size + gap_size; 472 + 473 + ASSERT_EQ(memblock.memory.cnt, i + 2); 474 + ASSERT_EQ(memblock.memory.total_size, new_memory_regions_size + 475 + (i + 1) * size); 476 + } 477 + 478 + /* 479 + * At there, memblock_double_array() has been succeed, check if it 480 + * update the memory.max. 481 + */ 482 + ASSERT_EQ(memblock.memory.max, INIT_MEMBLOCK_REGIONS * 2); 483 + 484 + /* memblock_double_array() will reserve the memory it used. Check it. */ 485 + ASSERT_EQ(memblock.reserved.cnt, 1); 486 + ASSERT_EQ(memblock.reserved.total_size, new_memory_regions_size); 487 + 488 + /* 489 + * Now memblock_double_array() works fine. Let's check after the 490 + * double_array(), the memblock_add() still works as normal. 491 + */ 492 + memblock_add(r.base, r.size); 493 + ASSERT_EQ(memblock.memory.regions[0].base, r.base); 494 + ASSERT_EQ(memblock.memory.regions[0].size, r.size); 495 + 496 + ASSERT_EQ(memblock.memory.cnt, INIT_MEMBLOCK_REGIONS + 2); 497 + ASSERT_EQ(memblock.memory.total_size, INIT_MEMBLOCK_REGIONS * size + 498 + new_memory_regions_size + 499 + r.size); 500 + ASSERT_EQ(memblock.memory.max, INIT_MEMBLOCK_REGIONS * 2); 501 + 502 + dummy_physical_memory_cleanup(); 503 + 504 + /* 505 + * The current memory.regions is occupying a range of memory that 506 + * allocated from dummy_physical_memory_init(). After free the memory, 507 + * we must not use it. So restore the origin memory region to make sure 508 + * the tests can run as normal and not affected by the double array. 509 + */ 510 + memblock.memory.regions = orig_region; 511 + memblock.memory.cnt = INIT_MEMBLOCK_REGIONS; 512 + 513 + test_pass_pop(); 514 + 515 + return 0; 516 + } 517 + 426 518 static int memblock_add_checks(void) 427 519 { 428 520 prefix_reset(); ··· 530 438 memblock_add_twice_check(); 531 439 memblock_add_between_check(); 532 440 memblock_add_near_max_check(); 441 + memblock_add_many_check(); 533 442 534 443 prefix_pop(); 535 444
+5 -2
tools/testing/memblock/tests/common.c
··· 5 5 #include <linux/memory_hotplug.h> 6 6 #include <linux/build_bug.h> 7 7 8 - #define INIT_MEMBLOCK_REGIONS 128 9 - #define INIT_MEMBLOCK_RESERVED_REGIONS INIT_MEMBLOCK_REGIONS 10 8 #define PREFIXES_MAX 15 11 9 #define DELIM ": " 12 10 #define BASIS 10000 ··· 111 113 void dummy_physical_memory_cleanup(void) 112 114 { 113 115 free(memory_block.base); 116 + } 117 + 118 + phys_addr_t dummy_physical_memory_base(void) 119 + { 120 + return (phys_addr_t)memory_block.base; 114 121 } 115 122 116 123 static void usage(const char *prog)
+5 -1
tools/testing/memblock/tests/common.h
··· 10 10 #include <linux/printk.h> 11 11 #include <../selftests/kselftest.h> 12 12 13 - #define MEM_SIZE SZ_16K 13 + #define MEM_SIZE SZ_32K 14 14 #define NUMA_NODES 8 15 + 16 + #define INIT_MEMBLOCK_REGIONS 128 17 + #define INIT_MEMBLOCK_RESERVED_REGIONS INIT_MEMBLOCK_REGIONS 15 18 16 19 enum test_flags { 17 20 /* No special request. */ ··· 127 124 void setup_numa_memblock(const unsigned int node_fracs[]); 128 125 void dummy_physical_memory_init(void); 129 126 void dummy_physical_memory_cleanup(void); 127 + phys_addr_t dummy_physical_memory_base(void); 130 128 void parse_args(int argc, char **argv); 131 129 132 130 void test_fail(void);