1 // SPDX-License-Identifier: GPL-2.0-or-later
2 #include "tests/common.h"
3 #include <string.h>
4 #include <getopt.h>
5 #include <linux/memory_hotplug.h>
6 #include <linux/build_bug.h>
7 
8 #define INIT_MEMBLOCK_REGIONS			128
9 #define INIT_MEMBLOCK_RESERVED_REGIONS		INIT_MEMBLOCK_REGIONS
10 #define PREFIXES_MAX				15
11 #define DELIM					": "
12 #define BASIS					10000
13 
14 static struct test_memory memory_block;
15 static const char __maybe_unused *prefixes[PREFIXES_MAX];
16 static int __maybe_unused nr_prefixes;
17 
18 static const char *short_opts = "hmv";
19 static const struct option long_opts[] = {
20 	{"help", 0, NULL, 'h'},
21 	{"movable-node", 0, NULL, 'm'},
22 	{"verbose", 0, NULL, 'v'},
23 	{NULL, 0, NULL, 0}
24 };
25 
26 static const char * const help_opts[] = {
27 	"display this help message and exit",
28 	"disallow allocations from regions marked as hotplugged\n\t\t\t"
29 		"by simulating enabling the \"movable_node\" kernel\n\t\t\t"
30 		"parameter",
31 	"enable verbose output, which includes the name of the\n\t\t\t"
32 		"memblock function being tested, the name of the test,\n\t\t\t"
33 		"and whether the test passed or failed."
34 };
35 
36 static int verbose;
37 
38 /* sets global variable returned by movable_node_is_enabled() stub */
39 bool movable_node_enabled;
40 
reset_memblock_regions(void)41 void reset_memblock_regions(void)
42 {
43 	memset(memblock.memory.regions, 0,
44 	       memblock.memory.cnt * sizeof(struct memblock_region));
45 	memblock.memory.cnt	= 1;
46 	memblock.memory.max	= INIT_MEMBLOCK_REGIONS;
47 	memblock.memory.total_size = 0;
48 
49 	memset(memblock.reserved.regions, 0,
50 	       memblock.reserved.cnt * sizeof(struct memblock_region));
51 	memblock.reserved.cnt	= 1;
52 	memblock.reserved.max	= INIT_MEMBLOCK_RESERVED_REGIONS;
53 	memblock.reserved.total_size = 0;
54 }
55 
reset_memblock_attributes(void)56 void reset_memblock_attributes(void)
57 {
58 	memblock.memory.name	= "memory";
59 	memblock.reserved.name	= "reserved";
60 	memblock.bottom_up	= false;
61 	memblock.current_limit	= MEMBLOCK_ALLOC_ANYWHERE;
62 }
63 
fill_memblock(void)64 static inline void fill_memblock(void)
65 {
66 	memset(memory_block.base, 1, MEM_SIZE);
67 }
68 
setup_memblock(void)69 void setup_memblock(void)
70 {
71 	reset_memblock_regions();
72 	memblock_add((phys_addr_t)memory_block.base, MEM_SIZE);
73 	fill_memblock();
74 }
75 
76 /**
77  * setup_numa_memblock:
78  * Set up a memory layout with multiple NUMA nodes in a previously allocated
79  * dummy physical memory.
80  * @node_fracs: an array representing the fraction of MEM_SIZE contained in
81  *              each node in basis point units (one hundredth of 1% or 1/10000).
82  *              For example, if node 0 should contain 1/8 of MEM_SIZE,
83  *              node_fracs[0] = 1250.
84  *
85  * The nids will be set to 0 through NUMA_NODES - 1.
86  */
setup_numa_memblock(const unsigned int node_fracs[])87 void setup_numa_memblock(const unsigned int node_fracs[])
88 {
89 	phys_addr_t base;
90 	int flags;
91 
92 	reset_memblock_regions();
93 	base = (phys_addr_t)memory_block.base;
94 	flags = (movable_node_is_enabled()) ? MEMBLOCK_NONE : MEMBLOCK_HOTPLUG;
95 
96 	for (int i = 0; i < NUMA_NODES; i++) {
97 		assert(node_fracs[i] <= BASIS);
98 		phys_addr_t size = MEM_SIZE * node_fracs[i] / BASIS;
99 
100 		memblock_add_node(base, size, i, flags);
101 		base += size;
102 	}
103 	fill_memblock();
104 }
105 
dummy_physical_memory_init(void)106 void dummy_physical_memory_init(void)
107 {
108 	memory_block.base = malloc(MEM_SIZE);
109 	assert(memory_block.base);
110 	fill_memblock();
111 }
112 
dummy_physical_memory_cleanup(void)113 void dummy_physical_memory_cleanup(void)
114 {
115 	free(memory_block.base);
116 }
117 
usage(const char * prog)118 static void usage(const char *prog)
119 {
120 	BUILD_BUG_ON(ARRAY_SIZE(help_opts) != ARRAY_SIZE(long_opts) - 1);
121 
122 	printf("Usage: %s [-%s]\n", prog, short_opts);
123 
124 	for (int i = 0; long_opts[i].name; i++) {
125 		printf("  -%c, --%-12s\t%s\n", long_opts[i].val,
126 		       long_opts[i].name, help_opts[i]);
127 	}
128 
129 	exit(1);
130 }
131 
parse_args(int argc,char ** argv)132 void parse_args(int argc, char **argv)
133 {
134 	int c;
135 
136 	while ((c = getopt_long_only(argc, argv, short_opts, long_opts,
137 				     NULL)) != -1) {
138 		switch (c) {
139 		case 'm':
140 			movable_node_enabled = true;
141 			break;
142 		case 'v':
143 			verbose = 1;
144 			break;
145 		default:
146 			usage(argv[0]);
147 		}
148 	}
149 }
150 
print_prefixes(const char * postfix)151 void print_prefixes(const char *postfix)
152 {
153 	for (int i = 0; i < nr_prefixes; i++)
154 		test_print("%s%s", prefixes[i], DELIM);
155 	test_print(postfix);
156 }
157 
test_fail(void)158 void test_fail(void)
159 {
160 	if (verbose) {
161 		ksft_test_result_fail(": ");
162 		print_prefixes("failed\n");
163 	}
164 }
165 
test_pass(void)166 void test_pass(void)
167 {
168 	if (verbose) {
169 		ksft_test_result_pass(": ");
170 		print_prefixes("passed\n");
171 	}
172 }
173 
test_print(const char * fmt,...)174 void test_print(const char *fmt, ...)
175 {
176 	if (verbose) {
177 		int saved_errno = errno;
178 		va_list args;
179 
180 		va_start(args, fmt);
181 		errno = saved_errno;
182 		vprintf(fmt, args);
183 		va_end(args);
184 	}
185 }
186 
prefix_reset(void)187 void prefix_reset(void)
188 {
189 	memset(prefixes, 0, PREFIXES_MAX * sizeof(char *));
190 	nr_prefixes = 0;
191 }
192 
prefix_push(const char * prefix)193 void prefix_push(const char *prefix)
194 {
195 	assert(nr_prefixes < PREFIXES_MAX);
196 	prefixes[nr_prefixes] = prefix;
197 	nr_prefixes++;
198 }
199 
prefix_pop(void)200 void prefix_pop(void)
201 {
202 	if (nr_prefixes > 0) {
203 		prefixes[nr_prefixes - 1] = 0;
204 		nr_prefixes--;
205 	}
206 }
207