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