162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci#include "tests/common.h" 362306a36Sopenharmony_ci#include <string.h> 462306a36Sopenharmony_ci#include <getopt.h> 562306a36Sopenharmony_ci#include <linux/memory_hotplug.h> 662306a36Sopenharmony_ci#include <linux/build_bug.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define PREFIXES_MAX 15 962306a36Sopenharmony_ci#define DELIM ": " 1062306a36Sopenharmony_ci#define BASIS 10000 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic struct test_memory memory_block; 1362306a36Sopenharmony_cistatic const char __maybe_unused *prefixes[PREFIXES_MAX]; 1462306a36Sopenharmony_cistatic int __maybe_unused nr_prefixes; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic const char *short_opts = "hmv"; 1762306a36Sopenharmony_cistatic const struct option long_opts[] = { 1862306a36Sopenharmony_ci {"help", 0, NULL, 'h'}, 1962306a36Sopenharmony_ci {"movable-node", 0, NULL, 'm'}, 2062306a36Sopenharmony_ci {"verbose", 0, NULL, 'v'}, 2162306a36Sopenharmony_ci {NULL, 0, NULL, 0} 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic const char * const help_opts[] = { 2562306a36Sopenharmony_ci "display this help message and exit", 2662306a36Sopenharmony_ci "disallow allocations from regions marked as hotplugged\n\t\t\t" 2762306a36Sopenharmony_ci "by simulating enabling the \"movable_node\" kernel\n\t\t\t" 2862306a36Sopenharmony_ci "parameter", 2962306a36Sopenharmony_ci "enable verbose output, which includes the name of the\n\t\t\t" 3062306a36Sopenharmony_ci "memblock function being tested, the name of the test,\n\t\t\t" 3162306a36Sopenharmony_ci "and whether the test passed or failed." 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int verbose; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* sets global variable returned by movable_node_is_enabled() stub */ 3762306a36Sopenharmony_cibool movable_node_enabled; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_civoid reset_memblock_regions(void) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci memset(memblock.memory.regions, 0, 4262306a36Sopenharmony_ci memblock.memory.cnt * sizeof(struct memblock_region)); 4362306a36Sopenharmony_ci memblock.memory.cnt = 1; 4462306a36Sopenharmony_ci memblock.memory.max = INIT_MEMBLOCK_REGIONS; 4562306a36Sopenharmony_ci memblock.memory.total_size = 0; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci memset(memblock.reserved.regions, 0, 4862306a36Sopenharmony_ci memblock.reserved.cnt * sizeof(struct memblock_region)); 4962306a36Sopenharmony_ci memblock.reserved.cnt = 1; 5062306a36Sopenharmony_ci memblock.reserved.max = INIT_MEMBLOCK_RESERVED_REGIONS; 5162306a36Sopenharmony_ci memblock.reserved.total_size = 0; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_civoid reset_memblock_attributes(void) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci memblock.memory.name = "memory"; 5762306a36Sopenharmony_ci memblock.reserved.name = "reserved"; 5862306a36Sopenharmony_ci memblock.bottom_up = false; 5962306a36Sopenharmony_ci memblock.current_limit = MEMBLOCK_ALLOC_ANYWHERE; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic inline void fill_memblock(void) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci memset(memory_block.base, 1, MEM_SIZE); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_civoid setup_memblock(void) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci reset_memblock_regions(); 7062306a36Sopenharmony_ci memblock_add((phys_addr_t)memory_block.base, MEM_SIZE); 7162306a36Sopenharmony_ci fill_memblock(); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/** 7562306a36Sopenharmony_ci * setup_numa_memblock: 7662306a36Sopenharmony_ci * Set up a memory layout with multiple NUMA nodes in a previously allocated 7762306a36Sopenharmony_ci * dummy physical memory. 7862306a36Sopenharmony_ci * @node_fracs: an array representing the fraction of MEM_SIZE contained in 7962306a36Sopenharmony_ci * each node in basis point units (one hundredth of 1% or 1/10000). 8062306a36Sopenharmony_ci * For example, if node 0 should contain 1/8 of MEM_SIZE, 8162306a36Sopenharmony_ci * node_fracs[0] = 1250. 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * The nids will be set to 0 through NUMA_NODES - 1. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_civoid setup_numa_memblock(const unsigned int node_fracs[]) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci phys_addr_t base; 8862306a36Sopenharmony_ci int flags; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci reset_memblock_regions(); 9162306a36Sopenharmony_ci base = (phys_addr_t)memory_block.base; 9262306a36Sopenharmony_ci flags = (movable_node_is_enabled()) ? MEMBLOCK_NONE : MEMBLOCK_HOTPLUG; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci for (int i = 0; i < NUMA_NODES; i++) { 9562306a36Sopenharmony_ci assert(node_fracs[i] <= BASIS); 9662306a36Sopenharmony_ci phys_addr_t size = MEM_SIZE * node_fracs[i] / BASIS; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci memblock_add_node(base, size, i, flags); 9962306a36Sopenharmony_ci base += size; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci fill_memblock(); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_civoid dummy_physical_memory_init(void) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci memory_block.base = malloc(MEM_SIZE); 10762306a36Sopenharmony_ci assert(memory_block.base); 10862306a36Sopenharmony_ci fill_memblock(); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_civoid dummy_physical_memory_cleanup(void) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci free(memory_block.base); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ciphys_addr_t dummy_physical_memory_base(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci return (phys_addr_t)memory_block.base; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void usage(const char *prog) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(help_opts) != ARRAY_SIZE(long_opts) - 1); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci printf("Usage: %s [-%s]\n", prog, short_opts); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci for (int i = 0; long_opts[i].name; i++) { 12862306a36Sopenharmony_ci printf(" -%c, --%-12s\t%s\n", long_opts[i].val, 12962306a36Sopenharmony_ci long_opts[i].name, help_opts[i]); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci exit(1); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid parse_args(int argc, char **argv) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int c; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci while ((c = getopt_long_only(argc, argv, short_opts, long_opts, 14062306a36Sopenharmony_ci NULL)) != -1) { 14162306a36Sopenharmony_ci switch (c) { 14262306a36Sopenharmony_ci case 'm': 14362306a36Sopenharmony_ci movable_node_enabled = true; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci case 'v': 14662306a36Sopenharmony_ci verbose = 1; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci default: 14962306a36Sopenharmony_ci usage(argv[0]); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_civoid print_prefixes(const char *postfix) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci for (int i = 0; i < nr_prefixes; i++) 15762306a36Sopenharmony_ci test_print("%s%s", prefixes[i], DELIM); 15862306a36Sopenharmony_ci test_print(postfix); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_civoid test_fail(void) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci if (verbose) { 16462306a36Sopenharmony_ci ksft_test_result_fail(": "); 16562306a36Sopenharmony_ci print_prefixes("failed\n"); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_civoid test_pass(void) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci if (verbose) { 17262306a36Sopenharmony_ci ksft_test_result_pass(": "); 17362306a36Sopenharmony_ci print_prefixes("passed\n"); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_civoid test_print(const char *fmt, ...) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci if (verbose) { 18062306a36Sopenharmony_ci int saved_errno = errno; 18162306a36Sopenharmony_ci va_list args; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci va_start(args, fmt); 18462306a36Sopenharmony_ci errno = saved_errno; 18562306a36Sopenharmony_ci vprintf(fmt, args); 18662306a36Sopenharmony_ci va_end(args); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_civoid prefix_reset(void) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci memset(prefixes, 0, PREFIXES_MAX * sizeof(char *)); 19362306a36Sopenharmony_ci nr_prefixes = 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_civoid prefix_push(const char *prefix) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci assert(nr_prefixes < PREFIXES_MAX); 19962306a36Sopenharmony_ci prefixes[nr_prefixes] = prefix; 20062306a36Sopenharmony_ci nr_prefixes++; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_civoid prefix_pop(void) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci if (nr_prefixes > 0) { 20662306a36Sopenharmony_ci prefixes[nr_prefixes - 1] = 0; 20762306a36Sopenharmony_ci nr_prefixes--; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci} 210