162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#define _GNU_SOURCE 362306a36Sopenharmony_ci#include <sys/mman.h> 462306a36Sopenharmony_ci#include <stdint.h> 562306a36Sopenharmony_ci#include <unistd.h> 662306a36Sopenharmony_ci#include <string.h> 762306a36Sopenharmony_ci#include <sys/time.h> 862306a36Sopenharmony_ci#include <sys/resource.h> 962306a36Sopenharmony_ci#include <stdbool.h> 1062306a36Sopenharmony_ci#include "mlock2.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "../kselftest.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistruct vm_boundaries { 1562306a36Sopenharmony_ci unsigned long start; 1662306a36Sopenharmony_ci unsigned long end; 1762306a36Sopenharmony_ci}; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int get_vm_area(unsigned long addr, struct vm_boundaries *area) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci FILE *file; 2262306a36Sopenharmony_ci int ret = 1; 2362306a36Sopenharmony_ci char line[1024] = {0}; 2462306a36Sopenharmony_ci char *end_addr; 2562306a36Sopenharmony_ci char *stop; 2662306a36Sopenharmony_ci unsigned long start; 2762306a36Sopenharmony_ci unsigned long end; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (!area) 3062306a36Sopenharmony_ci return ret; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci file = fopen("/proc/self/maps", "r"); 3362306a36Sopenharmony_ci if (!file) { 3462306a36Sopenharmony_ci perror("fopen"); 3562306a36Sopenharmony_ci return ret; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci memset(area, 0, sizeof(struct vm_boundaries)); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci while(fgets(line, 1024, file)) { 4162306a36Sopenharmony_ci end_addr = strchr(line, '-'); 4262306a36Sopenharmony_ci if (!end_addr) { 4362306a36Sopenharmony_ci printf("cannot parse /proc/self/maps\n"); 4462306a36Sopenharmony_ci goto out; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci *end_addr = '\0'; 4762306a36Sopenharmony_ci end_addr++; 4862306a36Sopenharmony_ci stop = strchr(end_addr, ' '); 4962306a36Sopenharmony_ci if (!stop) { 5062306a36Sopenharmony_ci printf("cannot parse /proc/self/maps\n"); 5162306a36Sopenharmony_ci goto out; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci sscanf(line, "%lx", &start); 5562306a36Sopenharmony_ci sscanf(end_addr, "%lx", &end); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (start <= addr && end > addr) { 5862306a36Sopenharmony_ci area->start = start; 5962306a36Sopenharmony_ci area->end = end; 6062306a36Sopenharmony_ci ret = 0; 6162306a36Sopenharmony_ci goto out; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ciout: 6562306a36Sopenharmony_ci fclose(file); 6662306a36Sopenharmony_ci return ret; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define VMFLAGS "VmFlags:" 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic bool is_vmflag_set(unsigned long addr, const char *vmflag) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci char *line = NULL; 7462306a36Sopenharmony_ci char *flags; 7562306a36Sopenharmony_ci size_t size = 0; 7662306a36Sopenharmony_ci bool ret = false; 7762306a36Sopenharmony_ci FILE *smaps; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci smaps = seek_to_smaps_entry(addr); 8062306a36Sopenharmony_ci if (!smaps) { 8162306a36Sopenharmony_ci printf("Unable to parse /proc/self/smaps\n"); 8262306a36Sopenharmony_ci goto out; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci while (getline(&line, &size, smaps) > 0) { 8662306a36Sopenharmony_ci if (!strstr(line, VMFLAGS)) { 8762306a36Sopenharmony_ci free(line); 8862306a36Sopenharmony_ci line = NULL; 8962306a36Sopenharmony_ci size = 0; 9062306a36Sopenharmony_ci continue; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci flags = line + strlen(VMFLAGS); 9462306a36Sopenharmony_ci ret = (strstr(flags, vmflag) != NULL); 9562306a36Sopenharmony_ci goto out; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciout: 9962306a36Sopenharmony_ci free(line); 10062306a36Sopenharmony_ci fclose(smaps); 10162306a36Sopenharmony_ci return ret; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define SIZE "Size:" 10562306a36Sopenharmony_ci#define RSS "Rss:" 10662306a36Sopenharmony_ci#define LOCKED "lo" 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic unsigned long get_value_for_name(unsigned long addr, const char *name) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci char *line = NULL; 11162306a36Sopenharmony_ci size_t size = 0; 11262306a36Sopenharmony_ci char *value_ptr; 11362306a36Sopenharmony_ci FILE *smaps = NULL; 11462306a36Sopenharmony_ci unsigned long value = -1UL; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci smaps = seek_to_smaps_entry(addr); 11762306a36Sopenharmony_ci if (!smaps) { 11862306a36Sopenharmony_ci printf("Unable to parse /proc/self/smaps\n"); 11962306a36Sopenharmony_ci goto out; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci while (getline(&line, &size, smaps) > 0) { 12362306a36Sopenharmony_ci if (!strstr(line, name)) { 12462306a36Sopenharmony_ci free(line); 12562306a36Sopenharmony_ci line = NULL; 12662306a36Sopenharmony_ci size = 0; 12762306a36Sopenharmony_ci continue; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci value_ptr = line + strlen(name); 13162306a36Sopenharmony_ci if (sscanf(value_ptr, "%lu kB", &value) < 1) { 13262306a36Sopenharmony_ci printf("Unable to parse smaps entry for Size\n"); 13362306a36Sopenharmony_ci goto out; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciout: 13962306a36Sopenharmony_ci if (smaps) 14062306a36Sopenharmony_ci fclose(smaps); 14162306a36Sopenharmony_ci free(line); 14262306a36Sopenharmony_ci return value; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic bool is_vma_lock_on_fault(unsigned long addr) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci bool locked; 14862306a36Sopenharmony_ci unsigned long vma_size, vma_rss; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci locked = is_vmflag_set(addr, LOCKED); 15162306a36Sopenharmony_ci if (!locked) 15262306a36Sopenharmony_ci return false; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci vma_size = get_value_for_name(addr, SIZE); 15562306a36Sopenharmony_ci vma_rss = get_value_for_name(addr, RSS); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* only one page is faulted in */ 15862306a36Sopenharmony_ci return (vma_rss < vma_size); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci#define PRESENT_BIT 0x8000000000000000ULL 16262306a36Sopenharmony_ci#define PFN_MASK 0x007FFFFFFFFFFFFFULL 16362306a36Sopenharmony_ci#define UNEVICTABLE_BIT (1UL << 18) 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int lock_check(unsigned long addr) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci bool locked; 16862306a36Sopenharmony_ci unsigned long vma_size, vma_rss; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci locked = is_vmflag_set(addr, LOCKED); 17162306a36Sopenharmony_ci if (!locked) 17262306a36Sopenharmony_ci return false; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci vma_size = get_value_for_name(addr, SIZE); 17562306a36Sopenharmony_ci vma_rss = get_value_for_name(addr, RSS); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return (vma_rss == vma_size); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int unlock_lock_check(char *map) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci if (is_vmflag_set((unsigned long)map, LOCKED)) { 18362306a36Sopenharmony_ci printf("VMA flag %s is present on page 1 after unlock\n", LOCKED); 18462306a36Sopenharmony_ci return 1; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int test_mlock_lock() 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci char *map; 19362306a36Sopenharmony_ci int ret = 1; 19462306a36Sopenharmony_ci unsigned long page_size = getpagesize(); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 19762306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 19862306a36Sopenharmony_ci if (map == MAP_FAILED) { 19962306a36Sopenharmony_ci perror("test_mlock_locked mmap"); 20062306a36Sopenharmony_ci goto out; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (mlock2_(map, 2 * page_size, 0)) { 20462306a36Sopenharmony_ci if (errno == ENOSYS) { 20562306a36Sopenharmony_ci printf("Cannot call new mlock family, skipping test\n"); 20662306a36Sopenharmony_ci _exit(KSFT_SKIP); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci perror("mlock2(0)"); 20962306a36Sopenharmony_ci goto unmap; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!lock_check((unsigned long)map)) 21362306a36Sopenharmony_ci goto unmap; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Now unlock and recheck attributes */ 21662306a36Sopenharmony_ci if (munlock(map, 2 * page_size)) { 21762306a36Sopenharmony_ci perror("munlock()"); 21862306a36Sopenharmony_ci goto unmap; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ret = unlock_lock_check(map); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciunmap: 22462306a36Sopenharmony_ci munmap(map, 2 * page_size); 22562306a36Sopenharmony_ciout: 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int onfault_check(char *map) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci *map = 'a'; 23262306a36Sopenharmony_ci if (!is_vma_lock_on_fault((unsigned long)map)) { 23362306a36Sopenharmony_ci printf("VMA is not marked for lock on fault\n"); 23462306a36Sopenharmony_ci return 1; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int unlock_onfault_check(char *map) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci unsigned long page_size = getpagesize(); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (is_vma_lock_on_fault((unsigned long)map) || 24562306a36Sopenharmony_ci is_vma_lock_on_fault((unsigned long)map + page_size)) { 24662306a36Sopenharmony_ci printf("VMA is still lock on fault after unlock\n"); 24762306a36Sopenharmony_ci return 1; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int test_mlock_onfault() 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci char *map; 25662306a36Sopenharmony_ci int ret = 1; 25762306a36Sopenharmony_ci unsigned long page_size = getpagesize(); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 26062306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 26162306a36Sopenharmony_ci if (map == MAP_FAILED) { 26262306a36Sopenharmony_ci perror("test_mlock_locked mmap"); 26362306a36Sopenharmony_ci goto out; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) { 26762306a36Sopenharmony_ci if (errno == ENOSYS) { 26862306a36Sopenharmony_ci printf("Cannot call new mlock family, skipping test\n"); 26962306a36Sopenharmony_ci _exit(KSFT_SKIP); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci perror("mlock2(MLOCK_ONFAULT)"); 27262306a36Sopenharmony_ci goto unmap; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (onfault_check(map)) 27662306a36Sopenharmony_ci goto unmap; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Now unlock and recheck attributes */ 27962306a36Sopenharmony_ci if (munlock(map, 2 * page_size)) { 28062306a36Sopenharmony_ci if (errno == ENOSYS) { 28162306a36Sopenharmony_ci printf("Cannot call new mlock family, skipping test\n"); 28262306a36Sopenharmony_ci _exit(KSFT_SKIP); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci perror("munlock()"); 28562306a36Sopenharmony_ci goto unmap; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = unlock_onfault_check(map); 28962306a36Sopenharmony_ciunmap: 29062306a36Sopenharmony_ci munmap(map, 2 * page_size); 29162306a36Sopenharmony_ciout: 29262306a36Sopenharmony_ci return ret; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int test_lock_onfault_of_present() 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci char *map; 29862306a36Sopenharmony_ci int ret = 1; 29962306a36Sopenharmony_ci unsigned long page_size = getpagesize(); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 30262306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 30362306a36Sopenharmony_ci if (map == MAP_FAILED) { 30462306a36Sopenharmony_ci perror("test_mlock_locked mmap"); 30562306a36Sopenharmony_ci goto out; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci *map = 'a'; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) { 31162306a36Sopenharmony_ci if (errno == ENOSYS) { 31262306a36Sopenharmony_ci printf("Cannot call new mlock family, skipping test\n"); 31362306a36Sopenharmony_ci _exit(KSFT_SKIP); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci perror("mlock2(MLOCK_ONFAULT)"); 31662306a36Sopenharmony_ci goto unmap; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (!is_vma_lock_on_fault((unsigned long)map) || 32062306a36Sopenharmony_ci !is_vma_lock_on_fault((unsigned long)map + page_size)) { 32162306a36Sopenharmony_ci printf("VMA with present pages is not marked lock on fault\n"); 32262306a36Sopenharmony_ci goto unmap; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci ret = 0; 32562306a36Sopenharmony_ciunmap: 32662306a36Sopenharmony_ci munmap(map, 2 * page_size); 32762306a36Sopenharmony_ciout: 32862306a36Sopenharmony_ci return ret; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int test_munlockall() 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci char *map; 33462306a36Sopenharmony_ci int ret = 1; 33562306a36Sopenharmony_ci unsigned long page_size = getpagesize(); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 33862306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (map == MAP_FAILED) { 34162306a36Sopenharmony_ci perror("test_munlockall mmap"); 34262306a36Sopenharmony_ci goto out; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (mlockall(MCL_CURRENT)) { 34662306a36Sopenharmony_ci perror("mlockall(MCL_CURRENT)"); 34762306a36Sopenharmony_ci goto out; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (!lock_check((unsigned long)map)) 35162306a36Sopenharmony_ci goto unmap; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (munlockall()) { 35462306a36Sopenharmony_ci perror("munlockall()"); 35562306a36Sopenharmony_ci goto unmap; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (unlock_lock_check(map)) 35962306a36Sopenharmony_ci goto unmap; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci munmap(map, 2 * page_size); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 36462306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (map == MAP_FAILED) { 36762306a36Sopenharmony_ci perror("test_munlockall second mmap"); 36862306a36Sopenharmony_ci goto out; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (mlockall(MCL_CURRENT | MCL_ONFAULT)) { 37262306a36Sopenharmony_ci perror("mlockall(MCL_CURRENT | MCL_ONFAULT)"); 37362306a36Sopenharmony_ci goto unmap; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (onfault_check(map)) 37762306a36Sopenharmony_ci goto unmap; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (munlockall()) { 38062306a36Sopenharmony_ci perror("munlockall()"); 38162306a36Sopenharmony_ci goto unmap; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (unlock_onfault_check(map)) 38562306a36Sopenharmony_ci goto unmap; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (mlockall(MCL_CURRENT | MCL_FUTURE)) { 38862306a36Sopenharmony_ci perror("mlockall(MCL_CURRENT | MCL_FUTURE)"); 38962306a36Sopenharmony_ci goto out; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (!lock_check((unsigned long)map)) 39362306a36Sopenharmony_ci goto unmap; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (munlockall()) { 39662306a36Sopenharmony_ci perror("munlockall()"); 39762306a36Sopenharmony_ci goto unmap; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret = unlock_lock_check(map); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciunmap: 40362306a36Sopenharmony_ci munmap(map, 2 * page_size); 40462306a36Sopenharmony_ciout: 40562306a36Sopenharmony_ci munlockall(); 40662306a36Sopenharmony_ci return ret; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int test_vma_management(bool call_mlock) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci int ret = 1; 41262306a36Sopenharmony_ci void *map; 41362306a36Sopenharmony_ci unsigned long page_size = getpagesize(); 41462306a36Sopenharmony_ci struct vm_boundaries page1; 41562306a36Sopenharmony_ci struct vm_boundaries page2; 41662306a36Sopenharmony_ci struct vm_boundaries page3; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, 41962306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 42062306a36Sopenharmony_ci if (map == MAP_FAILED) { 42162306a36Sopenharmony_ci perror("mmap()"); 42262306a36Sopenharmony_ci return ret; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) { 42662306a36Sopenharmony_ci if (errno == ENOSYS) { 42762306a36Sopenharmony_ci printf("Cannot call new mlock family, skipping test\n"); 42862306a36Sopenharmony_ci _exit(KSFT_SKIP); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci perror("mlock(ONFAULT)\n"); 43162306a36Sopenharmony_ci goto out; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (get_vm_area((unsigned long)map, &page1) || 43562306a36Sopenharmony_ci get_vm_area((unsigned long)map + page_size, &page2) || 43662306a36Sopenharmony_ci get_vm_area((unsigned long)map + page_size * 2, &page3)) { 43762306a36Sopenharmony_ci printf("couldn't find mapping in /proc/self/maps\n"); 43862306a36Sopenharmony_ci goto out; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* 44262306a36Sopenharmony_ci * Before we unlock a portion, we need to that all three pages are in 44362306a36Sopenharmony_ci * the same VMA. If they are not we abort this test (Note that this is 44462306a36Sopenharmony_ci * not a failure) 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci if (page1.start != page2.start || page2.start != page3.start) { 44762306a36Sopenharmony_ci printf("VMAs are not merged to start, aborting test\n"); 44862306a36Sopenharmony_ci ret = 0; 44962306a36Sopenharmony_ci goto out; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (munlock(map + page_size, page_size)) { 45362306a36Sopenharmony_ci perror("munlock()"); 45462306a36Sopenharmony_ci goto out; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (get_vm_area((unsigned long)map, &page1) || 45862306a36Sopenharmony_ci get_vm_area((unsigned long)map + page_size, &page2) || 45962306a36Sopenharmony_ci get_vm_area((unsigned long)map + page_size * 2, &page3)) { 46062306a36Sopenharmony_ci printf("couldn't find mapping in /proc/self/maps\n"); 46162306a36Sopenharmony_ci goto out; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* All three VMAs should be different */ 46562306a36Sopenharmony_ci if (page1.start == page2.start || page2.start == page3.start) { 46662306a36Sopenharmony_ci printf("failed to split VMA for munlock\n"); 46762306a36Sopenharmony_ci goto out; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Now unlock the first and third page and check the VMAs again */ 47162306a36Sopenharmony_ci if (munlock(map, page_size * 3)) { 47262306a36Sopenharmony_ci perror("munlock()"); 47362306a36Sopenharmony_ci goto out; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (get_vm_area((unsigned long)map, &page1) || 47762306a36Sopenharmony_ci get_vm_area((unsigned long)map + page_size, &page2) || 47862306a36Sopenharmony_ci get_vm_area((unsigned long)map + page_size * 2, &page3)) { 47962306a36Sopenharmony_ci printf("couldn't find mapping in /proc/self/maps\n"); 48062306a36Sopenharmony_ci goto out; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* Now all three VMAs should be the same */ 48462306a36Sopenharmony_ci if (page1.start != page2.start || page2.start != page3.start) { 48562306a36Sopenharmony_ci printf("failed to merge VMAs after munlock\n"); 48662306a36Sopenharmony_ci goto out; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ret = 0; 49062306a36Sopenharmony_ciout: 49162306a36Sopenharmony_ci munmap(map, 3 * page_size); 49262306a36Sopenharmony_ci return ret; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int test_mlockall(int (test_function)(bool call_mlock)) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci int ret = 1; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) { 50062306a36Sopenharmony_ci perror("mlockall"); 50162306a36Sopenharmony_ci return ret; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci ret = test_function(false); 50562306a36Sopenharmony_ci munlockall(); 50662306a36Sopenharmony_ci return ret; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciint main(int argc, char **argv) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci int ret = 0; 51262306a36Sopenharmony_ci ret += test_mlock_lock(); 51362306a36Sopenharmony_ci ret += test_mlock_onfault(); 51462306a36Sopenharmony_ci ret += test_munlockall(); 51562306a36Sopenharmony_ci ret += test_lock_onfault_of_present(); 51662306a36Sopenharmony_ci ret += test_vma_management(true); 51762306a36Sopenharmony_ci ret += test_mlockall(test_vma_management); 51862306a36Sopenharmony_ci return ret; 51962306a36Sopenharmony_ci} 520