]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
bd67d5c1 SJ |
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 | ||
6bfe4d40 SKSO |
19 | #include "../kselftest.h" |
20 | ||
bd67d5c1 SJ |
21 | #define MAP_SIZE 1048576 |
22 | ||
23 | struct map_list { | |
24 | void *map; | |
25 | struct map_list *next; | |
26 | }; | |
27 | ||
28 | int 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 | perror("Failed to read meminfo\n"); | |
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 | perror("Failed to read meminfo\n"); | |
47 | return -1; | |
48 | } | |
49 | ||
50 | pclose(cmdfile); | |
51 | *hugepagesize = atoll(buffer); | |
52 | ||
53 | return 0; | |
54 | } | |
55 | ||
56 | int 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 | perror("Failed to open\n" | |
65 | "/proc/sys/vm/compact_unevictable_allowed\n"); | |
66 | return -1; | |
67 | } | |
68 | ||
69 | if (read(fd, &allowed, sizeof(char)) != sizeof(char)) { | |
70 | perror("Failed to read from\n" | |
71 | "/proc/sys/vm/compact_unevictable_allowed\n"); | |
72 | close(fd); | |
73 | return -1; | |
74 | } | |
75 | ||
76 | close(fd); | |
77 | if (allowed == '1') | |
78 | return 0; | |
79 | ||
80 | return -1; | |
81 | } | |
82 | ||
83 | int check_compaction(unsigned long mem_free, unsigned int hugepage_size) | |
84 | { | |
85 | int fd; | |
86 | int compaction_index = 0; | |
87 | char initial_nr_hugepages[10] = {0}; | |
88 | char nr_hugepages[10] = {0}; | |
89 | ||
90 | /* We want to test with 80% of available memory. Else, OOM killer comes | |
91 | in to play */ | |
92 | mem_free = mem_free * 0.8; | |
93 | ||
94 | fd = open("/proc/sys/vm/nr_hugepages", O_RDWR | O_NONBLOCK); | |
95 | if (fd < 0) { | |
96 | perror("Failed to open /proc/sys/vm/nr_hugepages"); | |
97 | return -1; | |
98 | } | |
99 | ||
100 | if (read(fd, initial_nr_hugepages, sizeof(initial_nr_hugepages)) <= 0) { | |
101 | perror("Failed to read from /proc/sys/vm/nr_hugepages"); | |
102 | goto close_fd; | |
103 | } | |
104 | ||
105 | /* Start with the initial condition of 0 huge pages*/ | |
106 | if (write(fd, "0", sizeof(char)) != sizeof(char)) { | |
ee65735d | 107 | perror("Failed to write 0 to /proc/sys/vm/nr_hugepages\n"); |
bd67d5c1 SJ |
108 | goto close_fd; |
109 | } | |
110 | ||
111 | lseek(fd, 0, SEEK_SET); | |
112 | ||
113 | /* Request a large number of huge pages. The Kernel will allocate | |
114 | as much as it can */ | |
115 | if (write(fd, "100000", (6*sizeof(char))) != (6*sizeof(char))) { | |
ee65735d | 116 | perror("Failed to write 100000 to /proc/sys/vm/nr_hugepages\n"); |
bd67d5c1 SJ |
117 | goto close_fd; |
118 | } | |
119 | ||
120 | lseek(fd, 0, SEEK_SET); | |
121 | ||
122 | if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) { | |
ee65735d | 123 | perror("Failed to re-read from /proc/sys/vm/nr_hugepages\n"); |
bd67d5c1 SJ |
124 | goto close_fd; |
125 | } | |
126 | ||
127 | /* We should have been able to request at least 1/3 rd of the memory in | |
128 | huge pages */ | |
129 | compaction_index = mem_free/(atoi(nr_hugepages) * hugepage_size); | |
130 | ||
131 | if (compaction_index > 3) { | |
132 | printf("No of huge pages allocated = %d\n", | |
133 | (atoi(nr_hugepages))); | |
134 | fprintf(stderr, "ERROR: Less that 1/%d of memory is available\n" | |
135 | "as huge pages\n", compaction_index); | |
136 | goto close_fd; | |
137 | } | |
138 | ||
139 | printf("No of huge pages allocated = %d\n", | |
140 | (atoi(nr_hugepages))); | |
141 | ||
35ab37f3 AB |
142 | lseek(fd, 0, SEEK_SET); |
143 | ||
ee65735d | 144 | if (write(fd, initial_nr_hugepages, strlen(initial_nr_hugepages)) |
bd67d5c1 | 145 | != strlen(initial_nr_hugepages)) { |
ee65735d | 146 | perror("Failed to write value to /proc/sys/vm/nr_hugepages\n"); |
bd67d5c1 SJ |
147 | goto close_fd; |
148 | } | |
149 | ||
150 | close(fd); | |
151 | return 0; | |
152 | ||
153 | close_fd: | |
154 | close(fd); | |
155 | printf("Not OK. Compaction test failed."); | |
156 | return -1; | |
157 | } | |
158 | ||
159 | ||
160 | int main(int argc, char **argv) | |
161 | { | |
162 | struct rlimit lim; | |
163 | struct map_list *list, *entry; | |
164 | size_t page_size, i; | |
165 | void *map = NULL; | |
166 | unsigned long mem_free = 0; | |
167 | unsigned long hugepage_size = 0; | |
168 | unsigned long mem_fragmentable = 0; | |
169 | ||
170 | if (prereq() != 0) { | |
171 | printf("Either the sysctl compact_unevictable_allowed is not\n" | |
172 | "set to 1 or couldn't read the proc file.\n" | |
173 | "Skipping the test\n"); | |
6bfe4d40 | 174 | return KSFT_SKIP; |
bd67d5c1 SJ |
175 | } |
176 | ||
177 | lim.rlim_cur = RLIM_INFINITY; | |
178 | lim.rlim_max = RLIM_INFINITY; | |
179 | if (setrlimit(RLIMIT_MEMLOCK, &lim)) { | |
180 | perror("Failed to set rlimit:\n"); | |
181 | return -1; | |
182 | } | |
183 | ||
184 | page_size = getpagesize(); | |
185 | ||
186 | list = NULL; | |
187 | ||
188 | if (read_memory_info(&mem_free, &hugepage_size) != 0) { | |
189 | printf("ERROR: Cannot read meminfo\n"); | |
190 | return -1; | |
191 | } | |
192 | ||
193 | mem_fragmentable = mem_free * 0.8 / 1024; | |
194 | ||
195 | while (mem_fragmentable > 0) { | |
196 | map = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, | |
197 | MAP_ANONYMOUS | MAP_PRIVATE | MAP_LOCKED, -1, 0); | |
198 | if (map == MAP_FAILED) | |
199 | break; | |
200 | ||
201 | entry = malloc(sizeof(struct map_list)); | |
202 | if (!entry) { | |
203 | munmap(map, MAP_SIZE); | |
204 | break; | |
205 | } | |
206 | entry->map = map; | |
207 | entry->next = list; | |
208 | list = entry; | |
209 | ||
210 | /* Write something (in this case the address of the map) to | |
211 | * ensure that KSM can't merge the mapped pages | |
212 | */ | |
213 | for (i = 0; i < MAP_SIZE; i += page_size) | |
214 | *(unsigned long *)(map + i) = (unsigned long)map + i; | |
215 | ||
216 | mem_fragmentable--; | |
217 | } | |
218 | ||
219 | for (entry = list; entry != NULL; entry = entry->next) { | |
220 | munmap(entry->map, MAP_SIZE); | |
221 | if (!entry->next) | |
222 | break; | |
223 | entry = entry->next; | |
224 | } | |
225 | ||
226 | if (check_compaction(mem_free, hugepage_size) == 0) | |
227 | return 0; | |
228 | ||
229 | return -1; | |
230 | } |