Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 *
4 * A test for the patch "Allow compaction of unevictable pages".
5 * With this patch we should be able to allocate at least 1/4
6 * of RAM in huge pages. Without the patch much less is
7 * allocated.
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <sys/mman.h>
13#include <sys/resource.h>
14#include <fcntl.h>
15#include <errno.h>
16#include <unistd.h>
17#include <string.h>
18
19#include "../kselftest.h"
20
21#define MAP_SIZE_MB 100
22#define MAP_SIZE (MAP_SIZE_MB * 1024 * 1024)
23
24struct map_list {
25 void *map;
26 struct map_list *next;
27};
28
29int read_memory_info(unsigned long *memfree, unsigned long *hugepagesize)
30{
31 char buffer[256] = {0};
32 char *cmd = "cat /proc/meminfo | grep -i memfree | grep -o '[0-9]*'";
33 FILE *cmdfile = popen(cmd, "r");
34
35 if (!(fgets(buffer, sizeof(buffer), cmdfile))) {
36 ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno));
37 return -1;
38 }
39
40 pclose(cmdfile);
41
42 *memfree = atoll(buffer);
43 cmd = "cat /proc/meminfo | grep -i hugepagesize | grep -o '[0-9]*'";
44 cmdfile = popen(cmd, "r");
45
46 if (!(fgets(buffer, sizeof(buffer), cmdfile))) {
47 ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno));
48 return -1;
49 }
50
51 pclose(cmdfile);
52 *hugepagesize = atoll(buffer);
53
54 return 0;
55}
56
57int prereq(void)
58{
59 char allowed;
60 int fd;
61
62 fd = open("/proc/sys/vm/compact_unevictable_allowed",
63 O_RDONLY | O_NONBLOCK);
64 if (fd < 0) {
65 ksft_print_msg("Failed to open /proc/sys/vm/compact_unevictable_allowed: %s\n",
66 strerror(errno));
67 return -1;
68 }
69
70 if (read(fd, &allowed, sizeof(char)) != sizeof(char)) {
71 ksft_print_msg("Failed to read from /proc/sys/vm/compact_unevictable_allowed: %s\n",
72 strerror(errno));
73 close(fd);
74 return -1;
75 }
76
77 close(fd);
78 if (allowed == '1')
79 return 0;
80
81 ksft_print_msg("Compaction isn't allowed\n");
82 return -1;
83}
84
85int check_compaction(unsigned long mem_free, unsigned long hugepage_size,
86 unsigned long initial_nr_hugepages)
87{
88 unsigned long nr_hugepages_ul;
89 int fd, ret = -1;
90 int compaction_index = 0;
91 char nr_hugepages[20] = {0};
92 char init_nr_hugepages[20] = {0};
93
94 sprintf(init_nr_hugepages, "%lu", initial_nr_hugepages);
95
96 /* We want to test with 80% of available memory. Else, OOM killer comes
97 in to play */
98 mem_free = mem_free * 0.8;
99
100 fd = open("/proc/sys/vm/nr_hugepages", O_RDWR | O_NONBLOCK);
101 if (fd < 0) {
102 ksft_print_msg("Failed to open /proc/sys/vm/nr_hugepages: %s\n",
103 strerror(errno));
104 ret = -1;
105 goto out;
106 }
107
108 /* Request a large number of huge pages. The Kernel will allocate
109 as much as it can */
110 if (write(fd, "100000", (6*sizeof(char))) != (6*sizeof(char))) {
111 ksft_print_msg("Failed to write 100000 to /proc/sys/vm/nr_hugepages: %s\n",
112 strerror(errno));
113 goto close_fd;
114 }
115
116 lseek(fd, 0, SEEK_SET);
117
118 if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) {
119 ksft_print_msg("Failed to re-read from /proc/sys/vm/nr_hugepages: %s\n",
120 strerror(errno));
121 goto close_fd;
122 }
123
124 /* We should have been able to request at least 1/3 rd of the memory in
125 huge pages */
126 nr_hugepages_ul = strtoul(nr_hugepages, NULL, 10);
127 if (!nr_hugepages_ul) {
128 ksft_print_msg("ERROR: No memory is available as huge pages\n");
129 goto close_fd;
130 }
131 compaction_index = mem_free/(nr_hugepages_ul * hugepage_size);
132
133 lseek(fd, 0, SEEK_SET);
134
135 if (write(fd, init_nr_hugepages, strlen(init_nr_hugepages))
136 != strlen(init_nr_hugepages)) {
137 ksft_print_msg("Failed to write value to /proc/sys/vm/nr_hugepages: %s\n",
138 strerror(errno));
139 goto close_fd;
140 }
141
142 ksft_print_msg("Number of huge pages allocated = %lu\n",
143 nr_hugepages_ul);
144
145 if (compaction_index > 3) {
146 ksft_print_msg("ERROR: Less than 1/%d of memory is available\n"
147 "as huge pages\n", compaction_index);
148 goto close_fd;
149 }
150
151 ret = 0;
152
153 close_fd:
154 close(fd);
155 out:
156 ksft_test_result(ret == 0, "check_compaction\n");
157 return ret;
158}
159
160int set_zero_hugepages(unsigned long *initial_nr_hugepages)
161{
162 int fd, ret = -1;
163 char nr_hugepages[20] = {0};
164
165 fd = open("/proc/sys/vm/nr_hugepages", O_RDWR | O_NONBLOCK);
166 if (fd < 0) {
167 ksft_print_msg("Failed to open /proc/sys/vm/nr_hugepages: %s\n",
168 strerror(errno));
169 goto out;
170 }
171 if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) {
172 ksft_print_msg("Failed to read from /proc/sys/vm/nr_hugepages: %s\n",
173 strerror(errno));
174 goto close_fd;
175 }
176
177 lseek(fd, 0, SEEK_SET);
178
179 /* Start with the initial condition of 0 huge pages */
180 if (write(fd, "0", sizeof(char)) != sizeof(char)) {
181 ksft_print_msg("Failed to write 0 to /proc/sys/vm/nr_hugepages: %s\n",
182 strerror(errno));
183 goto close_fd;
184 }
185
186 *initial_nr_hugepages = strtoul(nr_hugepages, NULL, 10);
187 ret = 0;
188
189 close_fd:
190 close(fd);
191
192 out:
193 return ret;
194}
195
196int main(int argc, char **argv)
197{
198 struct rlimit lim;
199 struct map_list *list = NULL, *entry;
200 size_t page_size, i;
201 void *map = NULL;
202 unsigned long mem_free = 0;
203 unsigned long hugepage_size = 0;
204 long mem_fragmentable_MB = 0;
205 unsigned long initial_nr_hugepages;
206
207 ksft_print_header();
208
209 if (prereq() || geteuid())
210 ksft_exit_skip("Prerequisites unsatisfied\n");
211
212 ksft_set_plan(1);
213
214 /* Start the test without hugepages reducing mem_free */
215 if (set_zero_hugepages(&initial_nr_hugepages))
216 ksft_exit_fail();
217
218 lim.rlim_cur = RLIM_INFINITY;
219 lim.rlim_max = RLIM_INFINITY;
220 if (setrlimit(RLIMIT_MEMLOCK, &lim))
221 ksft_exit_fail_msg("Failed to set rlimit: %s\n", strerror(errno));
222
223 page_size = getpagesize();
224
225 if (read_memory_info(&mem_free, &hugepage_size) != 0)
226 ksft_exit_fail_msg("Failed to get meminfo\n");
227
228 mem_fragmentable_MB = mem_free * 0.8 / 1024;
229
230 while (mem_fragmentable_MB > 0) {
231 map = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE,
232 MAP_ANONYMOUS | MAP_PRIVATE | MAP_LOCKED, -1, 0);
233 if (map == MAP_FAILED)
234 break;
235
236 entry = malloc(sizeof(struct map_list));
237 if (!entry) {
238 munmap(map, MAP_SIZE);
239 break;
240 }
241 entry->map = map;
242 entry->next = list;
243 list = entry;
244
245 /* Write something (in this case the address of the map) to
246 * ensure that KSM can't merge the mapped pages
247 */
248 for (i = 0; i < MAP_SIZE; i += page_size)
249 *(unsigned long *)(map + i) = (unsigned long)map + i;
250
251 mem_fragmentable_MB -= MAP_SIZE_MB;
252 }
253
254 for (entry = list; entry != NULL; entry = entry->next) {
255 munmap(entry->map, MAP_SIZE);
256 if (!entry->next)
257 break;
258 entry = entry->next;
259 }
260
261 if (check_compaction(mem_free, hugepage_size,
262 initial_nr_hugepages) == 0)
263 ksft_exit_pass();
264
265 ksft_exit_fail();
266}