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 #include "hugepage_settings.h" 21 22 #define MAP_SIZE_MB 100 23 #define MAP_SIZE (MAP_SIZE_MB * 1024 * 1024) 24 25 struct map_list { 26 void *map; 27 struct map_list *next; 28 }; 29 30 int read_memory_info(unsigned long *memfree, unsigned long *hugepagesize) 31 { 32 char buffer[256] = {0}; 33 char *cmd = "cat /proc/meminfo | grep -i memfree | grep -o '[0-9]*'"; 34 FILE *cmdfile = popen(cmd, "r"); 35 36 if (!(fgets(buffer, sizeof(buffer), cmdfile))) { 37 ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno)); 38 return -1; 39 } 40 41 pclose(cmdfile); 42 43 *memfree = atoll(buffer); 44 cmd = "cat /proc/meminfo | grep -i hugepagesize | grep -o '[0-9]*'"; 45 cmdfile = popen(cmd, "r"); 46 47 if (!(fgets(buffer, sizeof(buffer), cmdfile))) { 48 ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno)); 49 return -1; 50 } 51 52 pclose(cmdfile); 53 *hugepagesize = atoll(buffer); 54 55 return 0; 56 } 57 58 int prereq(void) 59 { 60 char allowed; 61 int fd; 62 63 fd = open("/proc/sys/vm/compact_unevictable_allowed", 64 O_RDONLY | O_NONBLOCK); 65 if (fd < 0) { 66 ksft_print_msg("Failed to open /proc/sys/vm/compact_unevictable_allowed: %s\n", 67 strerror(errno)); 68 return -1; 69 } 70 71 if (read(fd, &allowed, sizeof(char)) != sizeof(char)) { 72 ksft_print_msg("Failed to read from /proc/sys/vm/compact_unevictable_allowed: %s\n", 73 strerror(errno)); 74 close(fd); 75 return -1; 76 } 77 78 close(fd); 79 if (allowed == '1') 80 return 0; 81 82 ksft_print_msg("Compaction isn't allowed\n"); 83 return -1; 84 } 85 86 int check_compaction(unsigned long mem_free, unsigned long hugepage_size) 87 { 88 unsigned long nr_hugepages; 89 int compaction_index = 0; 90 int ret = -1; 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 /* 97 * Request huge pages for about half of the free memory. The Kernel 98 * will allocate as much as it can, and we expect it will get at least 1/3 99 */ 100 nr_hugepages = mem_free / hugepage_size / 2; 101 hugetlb_set_nr_default_pages(nr_hugepages); 102 103 /* We should have been able to request at least 1/3 rd of the memory in 104 huge pages */ 105 nr_hugepages = hugetlb_nr_default_pages(); 106 if (!nr_hugepages) { 107 ksft_print_msg("ERROR: No memory is available as huge pages\n"); 108 goto out; 109 } 110 compaction_index = mem_free/(nr_hugepages * hugepage_size); 111 112 ksft_print_msg("Number of huge pages allocated = %lu\n", nr_hugepages); 113 114 if (compaction_index > 3) { 115 ksft_print_msg("ERROR: Less than 1/%d of memory is available\n" 116 "as huge pages\n", compaction_index); 117 goto out; 118 } 119 120 ret = 0; 121 122 out: 123 ksft_test_result(ret == 0, "check_compaction\n"); 124 return ret; 125 } 126 127 int main(int argc, char **argv) 128 { 129 struct rlimit lim; 130 struct map_list *list = NULL, *entry; 131 size_t page_size, i; 132 void *map = NULL; 133 unsigned long mem_free = 0; 134 unsigned long hugepage_size = 0; 135 long mem_fragmentable_MB = 0; 136 137 ksft_print_header(); 138 139 if (prereq() || geteuid()) 140 ksft_exit_skip("Prerequisites unsatisfied\n"); 141 142 /* Start the test without hugepages reducing mem_free */ 143 if (!hugetlb_setup_default_exact(0)) 144 ksft_exit_skip("Could not reset nr_hugepages\n"); 145 146 ksft_set_plan(1); 147 148 lim.rlim_cur = RLIM_INFINITY; 149 lim.rlim_max = RLIM_INFINITY; 150 if (setrlimit(RLIMIT_MEMLOCK, &lim)) 151 ksft_exit_fail_msg("Failed to set rlimit: %s\n", strerror(errno)); 152 153 page_size = getpagesize(); 154 155 if (read_memory_info(&mem_free, &hugepage_size) != 0) 156 ksft_exit_fail_msg("Failed to get meminfo\n"); 157 158 mem_fragmentable_MB = mem_free * 0.8 / 1024; 159 160 while (mem_fragmentable_MB > 0) { 161 map = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, 162 MAP_ANONYMOUS | MAP_PRIVATE | MAP_LOCKED, -1, 0); 163 if (map == MAP_FAILED) 164 break; 165 166 entry = malloc(sizeof(struct map_list)); 167 if (!entry) { 168 munmap(map, MAP_SIZE); 169 break; 170 } 171 entry->map = map; 172 entry->next = list; 173 list = entry; 174 175 /* Write something (in this case the address of the map) to 176 * ensure that KSM can't merge the mapped pages 177 */ 178 for (i = 0; i < MAP_SIZE; i += page_size) 179 *(unsigned long *)(map + i) = (unsigned long)map + i; 180 181 mem_fragmentable_MB -= MAP_SIZE_MB; 182 } 183 184 /* Unmap every other entry in the list to create fragmentation with 185 * locked pages before invoking check_compaction(). 186 */ 187 for (entry = list; entry != NULL; entry = entry->next) { 188 munmap(entry->map, MAP_SIZE); 189 if (!entry->next) 190 break; 191 entry = entry->next; 192 } 193 194 if (check_compaction(mem_free, hugepage_size) == 0) 195 ksft_exit_pass(); 196 197 ksft_exit_fail(); 198 } 199