1 // SPDX-License-Identifier: GPL-2.0
8 #include <sys/resource.h>
10 #include "../kselftest.h"
13 struct vm_boundaries {
18 static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
22 char line[1024] = {0};
29 file = fopen("/proc/self/maps", "r");
35 memset(area, 0, sizeof(struct vm_boundaries));
37 while(fgets(line, 1024, file)) {
38 if (sscanf(line, "%lx-%lx", &start, &end) != 2) {
39 ksft_print_msg("cannot parse /proc/self/maps\n");
43 if (start <= addr && end > addr) {
55 #define VMFLAGS "VmFlags:"
57 static bool is_vmflag_set(unsigned long addr, const char *vmflag)
65 smaps = seek_to_smaps_entry(addr);
67 ksft_print_msg("Unable to parse /proc/self/smaps\n");
71 while (getline(&line, &size, smaps) > 0) {
72 if (!strstr(line, VMFLAGS)) {
79 flags = line + strlen(VMFLAGS);
80 ret = (strstr(flags, vmflag) != NULL);
94 static unsigned long get_value_for_name(unsigned long addr, const char *name)
100 unsigned long value = -1UL;
102 smaps = seek_to_smaps_entry(addr);
104 ksft_print_msg("Unable to parse /proc/self/smaps\n");
108 while (getline(&line, &size, smaps) > 0) {
109 if (!strstr(line, name)) {
116 value_ptr = line + strlen(name);
117 if (sscanf(value_ptr, "%lu kB", &value) < 1) {
118 ksft_print_msg("Unable to parse smaps entry for Size\n");
131 static bool is_vma_lock_on_fault(unsigned long addr)
134 unsigned long vma_size, vma_rss;
136 locked = is_vmflag_set(addr, LOCKED);
140 vma_size = get_value_for_name(addr, SIZE);
141 vma_rss = get_value_for_name(addr, RSS);
143 /* only one page is faulted in */
144 return (vma_rss < vma_size);
147 #define PRESENT_BIT 0x8000000000000000ULL
148 #define PFN_MASK 0x007FFFFFFFFFFFFFULL
149 #define UNEVICTABLE_BIT (1UL << 18)
151 static int lock_check(unsigned long addr)
154 unsigned long vma_size, vma_rss;
156 locked = is_vmflag_set(addr, LOCKED);
160 vma_size = get_value_for_name(addr, SIZE);
161 vma_rss = get_value_for_name(addr, RSS);
163 return (vma_rss == vma_size);
166 static int unlock_lock_check(char *map)
168 if (is_vmflag_set((unsigned long)map, LOCKED)) {
169 ksft_print_msg("VMA flag %s is present on page 1 after unlock\n", LOCKED);
176 static void test_mlock_lock(void)
179 unsigned long page_size = getpagesize();
181 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
182 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
183 if (map == MAP_FAILED)
184 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
186 if (mlock2_(map, 2 * page_size, 0)) {
187 munmap(map, 2 * page_size);
188 ksft_exit_fail_msg("mlock2(0): %s\n", strerror(errno));
191 ksft_test_result(lock_check((unsigned long)map), "%s: Locked\n", __func__);
193 /* Now unlock and recheck attributes */
194 if (munlock(map, 2 * page_size)) {
195 munmap(map, 2 * page_size);
196 ksft_exit_fail_msg("munlock(): %s\n", strerror(errno));
199 ksft_test_result(!unlock_lock_check(map), "%s: Locked\n", __func__);
200 munmap(map, 2 * page_size);
203 static int onfault_check(char *map)
206 if (!is_vma_lock_on_fault((unsigned long)map)) {
207 ksft_print_msg("VMA is not marked for lock on fault\n");
214 static int unlock_onfault_check(char *map)
216 unsigned long page_size = getpagesize();
218 if (is_vma_lock_on_fault((unsigned long)map) ||
219 is_vma_lock_on_fault((unsigned long)map + page_size)) {
220 ksft_print_msg("VMA is still lock on fault after unlock\n");
227 static void test_mlock_onfault(void)
230 unsigned long page_size = getpagesize();
232 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
233 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
234 if (map == MAP_FAILED)
235 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
237 if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
238 munmap(map, 2 * page_size);
239 ksft_exit_fail_msg("mlock2(MLOCK_ONFAULT): %s\n", strerror(errno));
242 ksft_test_result(!onfault_check(map), "%s: VMA marked for lock on fault\n", __func__);
244 /* Now unlock and recheck attributes */
245 if (munlock(map, 2 * page_size)) {
246 munmap(map, 2 * page_size);
247 ksft_exit_fail_msg("munlock(): %s\n", strerror(errno));
250 ksft_test_result(!unlock_onfault_check(map), "VMA open lock after fault\n");
251 munmap(map, 2 * page_size);
254 static void test_lock_onfault_of_present(void)
257 unsigned long page_size = getpagesize();
259 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
260 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
261 if (map == MAP_FAILED)
262 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
266 if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
267 munmap(map, 2 * page_size);
268 ksft_test_result_fail("mlock2(MLOCK_ONFAULT) error: %s", strerror(errno));
271 ksft_test_result(is_vma_lock_on_fault((unsigned long)map) ||
272 is_vma_lock_on_fault((unsigned long)map + page_size),
273 "VMA with present pages is not marked lock on fault\n");
274 munmap(map, 2 * page_size);
277 static void test_munlockall0(void)
280 unsigned long page_size = getpagesize();
282 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
283 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
284 if (map == MAP_FAILED)
285 ksft_exit_fail_msg("mmap error: %s\n", strerror(errno));
287 if (mlockall(MCL_CURRENT)) {
288 munmap(map, 2 * page_size);
289 ksft_exit_fail_msg("mlockall(MCL_CURRENT): %s\n", strerror(errno));
292 ksft_test_result(lock_check((unsigned long)map), "%s: Locked memory area\n", __func__);
295 munmap(map, 2 * page_size);
296 ksft_exit_fail_msg("munlockall(): %s\n", strerror(errno));
299 ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__);
300 munmap(map, 2 * page_size);
303 static void test_munlockall1(void)
306 unsigned long page_size = getpagesize();
308 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
309 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
310 if (map == MAP_FAILED)
311 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
313 if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
314 munmap(map, 2 * page_size);
315 ksft_exit_fail_msg("mlockall(MCL_CURRENT | MCL_ONFAULT): %s\n", strerror(errno));
318 ksft_test_result(!onfault_check(map), "%s: VMA marked for lock on fault\n", __func__);
321 munmap(map, 2 * page_size);
322 ksft_exit_fail_msg("munlockall(): %s\n", strerror(errno));
325 ksft_test_result(!unlock_onfault_check(map), "%s: Unlocked\n", __func__);
327 if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
328 munmap(map, 2 * page_size);
329 ksft_exit_fail_msg("mlockall(MCL_CURRENT | MCL_FUTURE): %s\n", strerror(errno));
332 ksft_test_result(lock_check((unsigned long)map), "%s: Locked\n", __func__);
335 munmap(map, 2 * page_size);
336 ksft_exit_fail_msg("munlockall() %s\n", strerror(errno));
339 ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__);
340 munmap(map, 2 * page_size);
343 static void test_vma_management(bool call_mlock)
346 unsigned long page_size = getpagesize();
347 struct vm_boundaries page1;
348 struct vm_boundaries page2;
349 struct vm_boundaries page3;
351 map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
352 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
353 if (map == MAP_FAILED)
354 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
356 if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
357 munmap(map, 3 * page_size);
358 ksft_test_result_fail("mlock error: %s", strerror(errno));
361 if (get_vm_area((unsigned long)map, &page1) ||
362 get_vm_area((unsigned long)map + page_size, &page2) ||
363 get_vm_area((unsigned long)map + page_size * 2, &page3)) {
364 munmap(map, 3 * page_size);
365 ksft_test_result_fail("couldn't find mapping in /proc/self/maps");
369 * Before we unlock a portion, we need to that all three pages are in
370 * the same VMA. If they are not we abort this test (Note that this is
373 if (page1.start != page2.start || page2.start != page3.start) {
374 munmap(map, 3 * page_size);
375 ksft_test_result_fail("VMAs are not merged to start, aborting test");
378 if (munlock(map + page_size, page_size)) {
379 munmap(map, 3 * page_size);
380 ksft_test_result_fail("munlock(): %s", strerror(errno));
383 if (get_vm_area((unsigned long)map, &page1) ||
384 get_vm_area((unsigned long)map + page_size, &page2) ||
385 get_vm_area((unsigned long)map + page_size * 2, &page3)) {
386 munmap(map, 3 * page_size);
387 ksft_test_result_fail("couldn't find mapping in /proc/self/maps");
390 /* All three VMAs should be different */
391 if (page1.start == page2.start || page2.start == page3.start) {
392 munmap(map, 3 * page_size);
393 ksft_test_result_fail("failed to split VMA for munlock");
396 /* Now unlock the first and third page and check the VMAs again */
397 if (munlock(map, page_size * 3)) {
398 munmap(map, 3 * page_size);
399 ksft_test_result_fail("munlock(): %s", strerror(errno));
402 if (get_vm_area((unsigned long)map, &page1) ||
403 get_vm_area((unsigned long)map + page_size, &page2) ||
404 get_vm_area((unsigned long)map + page_size * 2, &page3)) {
405 munmap(map, 3 * page_size);
406 ksft_test_result_fail("couldn't find mapping in /proc/self/maps");
409 /* Now all three VMAs should be the same */
410 if (page1.start != page2.start || page2.start != page3.start) {
411 munmap(map, 3 * page_size);
412 ksft_test_result_fail("failed to merge VMAs after munlock");
415 ksft_test_result_pass("%s call_mlock %d\n", __func__, call_mlock);
416 munmap(map, 3 * page_size);
419 static void test_mlockall(void)
421 if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE))
422 ksft_exit_fail_msg("mlockall failed: %s\n", strerror(errno));
424 test_vma_management(false);
428 int main(int argc, char **argv)
430 int ret, size = 3 * getpagesize();
435 map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
436 if (map == MAP_FAILED)
437 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
439 ret = mlock2_(map, size, MLOCK_ONFAULT);
440 if (ret && errno == ENOSYS)
448 test_mlock_onfault();
451 test_lock_onfault_of_present();
452 test_vma_management(true);