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