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