blob: e056cfc487e082aff28e589cc208bf903646114e [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001// 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 1048576
22
23struct map_list {
24 void *map;
25 struct map_list *next;
26};
27
28int read_memory_info(unsigned long *memfree, unsigned long *hugepagesize)
29{
30 char buffer[256] = {0};
31 char *cmd = "cat /proc/meminfo | grep -i memfree | grep -o '[0-9]*'";
32 FILE *cmdfile = popen(cmd, "r");
33
34 if (!(fgets(buffer, sizeof(buffer), cmdfile))) {
35 ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno));
36 return -1;
37 }
38
39 pclose(cmdfile);
40
41 *memfree = atoll(buffer);
42 cmd = "cat /proc/meminfo | grep -i hugepagesize | grep -o '[0-9]*'";
43 cmdfile = popen(cmd, "r");
44
45 if (!(fgets(buffer, sizeof(buffer), cmdfile))) {
46 ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno));
47 return -1;
48 }
49
50 pclose(cmdfile);
51 *hugepagesize = atoll(buffer);
52
53 return 0;
54}
55
56int prereq(void)
57{
58 char allowed;
59 int fd;
60
61 fd = open("/proc/sys/vm/compact_unevictable_allowed",
62 O_RDONLY | O_NONBLOCK);
63 if (fd < 0) {
64 ksft_print_msg("Failed to open /proc/sys/vm/compact_unevictable_allowed: %s\n",
65 strerror(errno));
66 return -1;
67 }
68
69 if (read(fd, &allowed, sizeof(char)) != sizeof(char)) {
70 ksft_print_msg("Failed to read from /proc/sys/vm/compact_unevictable_allowed: %s\n",
71 strerror(errno));
72 close(fd);
73 return -1;
74 }
75
76 close(fd);
77 if (allowed == '1')
78 return 0;
79
80 ksft_print_msg("Compaction isn't allowed\n");
81 return -1;
82}
83
84int check_compaction(unsigned long mem_free, unsigned long hugepage_size)
85{
86 unsigned long nr_hugepages_ul;
87 int fd, ret = -1;
88 int compaction_index = 0;
89 char initial_nr_hugepages[20] = {0};
90 char nr_hugepages[20] = {0};
91
92 /* We want to test with 80% of available memory. Else, OOM killer comes
93 in to play */
94 mem_free = mem_free * 0.8;
95
96 fd = open("/proc/sys/vm/nr_hugepages", O_RDWR | O_NONBLOCK);
97 if (fd < 0) {
98 ksft_test_result_fail("Failed to open /proc/sys/vm/nr_hugepages: %s\n",
99 strerror(errno));
100 return -1;
101 }
102
103 if (read(fd, initial_nr_hugepages, sizeof(initial_nr_hugepages)) <= 0) {
104 ksft_test_result_fail("Failed to read from /proc/sys/vm/nr_hugepages: %s\n",
105 strerror(errno));
106 goto close_fd;
107 }
108
109 lseek(fd, 0, SEEK_SET);
110
111 /* Start with the initial condition of 0 huge pages*/
112 if (write(fd, "0", sizeof(char)) != sizeof(char)) {
113 ksft_test_result_fail("Failed to write 0 to /proc/sys/vm/nr_hugepages: %s\n",
114 strerror(errno));
115 goto close_fd;
116 }
117
118 lseek(fd, 0, SEEK_SET);
119
120 /* Request a large number of huge pages. The Kernel will allocate
121 as much as it can */
122 if (write(fd, "100000", (6*sizeof(char))) != (6*sizeof(char))) {
123 ksft_test_result_fail("Failed to write 100000 to /proc/sys/vm/nr_hugepages: %s\n",
124 strerror(errno));
125 goto close_fd;
126 }
127
128 lseek(fd, 0, SEEK_SET);
129
130 if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) {
131 ksft_test_result_fail("Failed to re-read from /proc/sys/vm/nr_hugepages: %s\n",
132 strerror(errno));
133 goto close_fd;
134 }
135
136 /* We should have been able to request at least 1/3 rd of the memory in
137 huge pages */
138 nr_hugepages_ul = strtoul(nr_hugepages, NULL, 10);
139 if (!nr_hugepages_ul) {
140 ksft_print_msg("ERROR: No memory is available as huge pages\n");
141 goto close_fd;
142 }
143 compaction_index = mem_free/(nr_hugepages_ul * hugepage_size);
144
145 lseek(fd, 0, SEEK_SET);
146
147 if (write(fd, initial_nr_hugepages, strlen(initial_nr_hugepages))
148 != strlen(initial_nr_hugepages)) {
149 ksft_test_result_fail("Failed to write value to /proc/sys/vm/nr_hugepages: %s\n",
150 strerror(errno));
151 goto close_fd;
152 }
153
154 if (compaction_index > 3) {
155 ksft_print_msg("ERROR: Less than 1/%d of memory is available\n"
156 "as huge pages\n", compaction_index);
157 ksft_test_result_fail("No of huge pages allocated = %d\n", (atoi(nr_hugepages)));
158 goto close_fd;
159 }
160
161 ksft_test_result_pass("Memory compaction succeeded. No of huge pages allocated = %d\n",
162 (atoi(nr_hugepages)));
163 ret = 0;
164
165 close_fd:
166 close(fd);
167 return ret;
168}
169
170
171int main(int argc, char **argv)
172{
173 struct rlimit lim;
174 struct map_list *list = NULL, *entry;
175 size_t page_size, i;
176 void *map = NULL;
177 unsigned long mem_free = 0;
178 unsigned long hugepage_size = 0;
179 unsigned long mem_fragmentable = 0;
180
181 ksft_print_header();
182
183 if (prereq() != 0)
184 return ksft_exit_pass();
185
186 ksft_set_plan(1);
187
188 lim.rlim_cur = RLIM_INFINITY;
189 lim.rlim_max = RLIM_INFINITY;
190 if (setrlimit(RLIMIT_MEMLOCK, &lim))
191 ksft_exit_fail_msg("Failed to set rlimit: %s\n", strerror(errno));
192
193 page_size = getpagesize();
194
195 if (read_memory_info(&mem_free, &hugepage_size) != 0)
196 ksft_exit_fail_msg("Failed to get meminfo\n");
197
198 mem_fragmentable = mem_free * 0.8 / 1024;
199
200 while (mem_fragmentable > 0) {
201 map = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE,
202 MAP_ANONYMOUS | MAP_PRIVATE | MAP_LOCKED, -1, 0);
203 if (map == MAP_FAILED)
204 break;
205
206 entry = malloc(sizeof(struct map_list));
207 if (!entry) {
208 munmap(map, MAP_SIZE);
209 break;
210 }
211 entry->map = map;
212 entry->next = list;
213 list = entry;
214
215 /* Write something (in this case the address of the map) to
216 * ensure that KSM can't merge the mapped pages
217 */
218 for (i = 0; i < MAP_SIZE; i += page_size)
219 *(unsigned long *)(map + i) = (unsigned long)map + i;
220
221 mem_fragmentable--;
222 }
223
224 for (entry = list; entry != NULL; entry = entry->next) {
225 munmap(entry->map, MAP_SIZE);
226 if (!entry->next)
227 break;
228 entry = entry->next;
229 }
230
231 if (check_compaction(mem_free, hugepage_size) == 0)
232 return ksft_exit_pass();
233
234 return ksft_exit_fail();
235}