12966Swollman// SPDX-License-Identifier: GPL-2.0 250477Speter/* 31590Srgrimes * 4274837Sbapt * A test for the patch "Allow compaction of unevictable pages". 5156813Sru * With this patch we should be able to allocate at least 1/4 64699Sjkh * of RAM in huge pages. Without the patch much less is 734706Sbde * allocated. 8213391Sgordon */ 91930Swollman 101930Swollman#include <stdio.h> 11106717Smarcel#include <stdlib.h> 12286592Semaste#include <sys/mman.h> 13100200Swollman#include <sys/resource.h> 1496630Stjr#include <fcntl.h> 1591706Sobrien#include <errno.h> 1638653Sgpalmer#include <unistd.h> 1738653Sgpalmer#include <string.h> 1857013Sobrien 19299529Smm#include "../kselftest.h" 20148771Scperciva 2178562Sobrien#define MAP_SIZE_MB 100 22108667Sobrien#define MAP_SIZE (MAP_SIZE_MB * 1024 * 1024) 2338653Sgpalmer 2438653Sgpalmerstruct map_list { 2538653Sgpalmer void *map; 2638653Sgpalmer struct map_list *next; 2738653Sgpalmer}; 2838653Sgpalmer 29291115Sbaptint read_memory_info(unsigned long *memfree, unsigned long *hugepagesize) 3038653Sgpalmer{ 3138653Sgpalmer char buffer[256] = {0}; 3238653Sgpalmer char *cmd = "cat /proc/meminfo | grep -i memfree | grep -o '[0-9]*'"; 3338653Sgpalmer FILE *cmdfile = popen(cmd, "r"); 34176733Sjeff 3595926Stjr if (!(fgets(buffer, sizeof(buffer), cmdfile))) { 36229997Sken ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno)); 3738653Sgpalmer return -1; 3838653Sgpalmer } 3938653Sgpalmer 40108439Sobrien pclose(cmdfile); 41109314Sobrien 4240826Sjoerg *memfree = atoll(buffer); 4338653Sgpalmer cmd = "cat /proc/meminfo | grep -i hugepagesize | grep -o '[0-9]*'"; 44332946Sbenno cmdfile = popen(cmd, "r"); 4538653Sgpalmer 4638653Sgpalmer if (!(fgets(buffer, sizeof(buffer), cmdfile))) { 4738653Sgpalmer ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno)); 4838653Sgpalmer return -1; 49203711Sdelphij } 5038653Sgpalmer 5141036Sdima pclose(cmdfile); 5263499Sps *hugepagesize = atoll(buffer); 53103303Speter 5438653Sgpalmer return 0; 55316098Svangyzen} 56101629Sjake 57158287Smaximint prereq(void) 5838653Sgpalmer{ 59222273Sobrien char allowed; 60166255Sdelphij int fd; 6138653Sgpalmer 6238653Sgpalmer fd = open("/proc/sys/vm/compact_unevictable_allowed", 6338653Sgpalmer O_RDONLY | O_NONBLOCK); 64285890Sbapt if (fd < 0) { 6538653Sgpalmer ksft_print_msg("Failed to open /proc/sys/vm/compact_unevictable_allowed: %s\n", 6638653Sgpalmer strerror(errno)); 6738653Sgpalmer return -1; 6838653Sgpalmer } 6938653Sgpalmer 7038653Sgpalmer if (read(fd, &allowed, sizeof(char)) != sizeof(char)) { 7138653Sgpalmer ksft_print_msg("Failed to read from /proc/sys/vm/compact_unevictable_allowed: %s\n", 7238653Sgpalmer strerror(errno)); 7393619Sjake close(fd); 7438653Sgpalmer return -1; 7538653Sgpalmer } 7639614Sbde 7738653Sgpalmer close(fd); 7860789Sps if (allowed == '1') 7960789Sps return 0; 8060789Sps 8138653Sgpalmer ksft_print_msg("Compaction isn't allowed\n"); 82116677Sphantom return -1; 83290494Sbapt} 8438653Sgpalmer 8538653Sgpalmerint check_compaction(unsigned long mem_free, unsigned int hugepage_size) 8638653Sgpalmer{ 8738653Sgpalmer int fd, ret = -1; 88126701Sdes int compaction_index = 0; 8938653Sgpalmer char initial_nr_hugepages[10] = {0}; 9038653Sgpalmer char nr_hugepages[10] = {0}; 9138653Sgpalmer 9238653Sgpalmer /* We want to test with 80% of available memory. Else, OOM killer comes 93207842Smm in to play */ 9438653Sgpalmer mem_free = mem_free * 0.8; 95241774Suqs 9638653Sgpalmer fd = open("/proc/sys/vm/nr_hugepages", O_RDWR | O_NONBLOCK); 97174814Sru if (fd < 0) { 98174810Sphk ksft_print_msg("Failed to open /proc/sys/vm/nr_hugepages: %s\n", 9938653Sgpalmer strerror(errno)); 10038653Sgpalmer ret = -1; 101263919Smarcel goto out; 102291115Sbapt } 10338653Sgpalmer 104146107Sfjoe if (read(fd, initial_nr_hugepages, sizeof(initial_nr_hugepages)) <= 0) { 10539914Sdfr ksft_print_msg("Failed to read from /proc/sys/vm/nr_hugepages: %s\n", 10638653Sgpalmer strerror(errno)); 10738653Sgpalmer goto close_fd; 10897365Stjr } 10938653Sgpalmer 11038653Sgpalmer /* Start with the initial condition of 0 huge pages*/ 11176273Sbrian if (write(fd, "0", sizeof(char)) != sizeof(char)) { 112285387Sadrian ksft_print_msg("Failed to write 0 to /proc/sys/vm/nr_hugepages: %s\n", 11338653Sgpalmer strerror(errno)); 11438653Sgpalmer goto close_fd; 11538653Sgpalmer } 11638653Sgpalmer 11738653Sgpalmer lseek(fd, 0, SEEK_SET); 11838653Sgpalmer 11938653Sgpalmer /* Request a large number of huge pages. The Kernel will allocate 120246074Sgabor as much as it can */ 12197096Stjr if (write(fd, "100000", (6*sizeof(char))) != (6*sizeof(char))) { 122192398Sgnn ksft_print_msg("Failed to write 100000 to /proc/sys/vm/nr_hugepages: %s\n", 12338653Sgpalmer strerror(errno)); 12438653Sgpalmer goto close_fd; 12538653Sgpalmer } 126306487Skib 127174200Srwatson lseek(fd, 0, SEEK_SET); 128275043Sbapt 129220166Strasz if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) { 13038653Sgpalmer ksft_print_msg("Failed to re-read from /proc/sys/vm/nr_hugepages: %s\n", 131297897Scem strerror(errno)); 13238653Sgpalmer goto close_fd; 133194267Sed } 13438653Sgpalmer 13538653Sgpalmer /* We should have been able to request at least 1/3 rd of the memory in 13638653Sgpalmer huge pages */ 137330769Semaste compaction_index = mem_free/(atoi(nr_hugepages) * hugepage_size); 13838653Sgpalmer 13938653Sgpalmer lseek(fd, 0, SEEK_SET); 140330769Semaste 14138653Sgpalmer if (write(fd, initial_nr_hugepages, strlen(initial_nr_hugepages)) 142298823Sbapt != strlen(initial_nr_hugepages)) { 14338653Sgpalmer ksft_print_msg("Failed to write value to /proc/sys/vm/nr_hugepages: %s\n", 144267577Sgavin strerror(errno)); 145204103Sdelphij goto close_fd; 14638653Sgpalmer } 14738653Sgpalmer 14845701Sdes ksft_print_msg("Number of huge pages allocated = %d\n", 149282318Sbapt atoi(nr_hugepages)); 150235268Sgabor 15138653Sgpalmer if (compaction_index > 3) { 15297955Sdougb ksft_print_msg("ERROR: Less that 1/%d of memory is available\n" 153234772Sjlh "as huge pages\n", compaction_index); 154327010Semaste goto close_fd; 15538653Sgpalmer } 15641035Sdima 15797034Stjr ret = 0; 15838653Sgpalmer 159127947Skientzle close_fd: 16038653Sgpalmer close(fd); 16138653Sgpalmer out: 16238653Sgpalmer ksft_test_result(ret == 0, "check_compaction\n"); 163274837Sbapt return ret; 16488277Smarkm} 16538653Sgpalmer 16638653Sgpalmer 16738653Sgpalmerint main(int argc, char **argv) 16838653Sgpalmer{ 16938653Sgpalmer struct rlimit lim; 17063437Ssheldonh struct map_list *list = NULL, *entry; 17138653Sgpalmer size_t page_size, i; 17238653Sgpalmer void *map = NULL; 17338653Sgpalmer unsigned long mem_free = 0; 17438653Sgpalmer unsigned long hugepage_size = 0; 17538653Sgpalmer long mem_fragmentable_MB = 0; 17638653Sgpalmer 177200068Srdivacky ksft_print_header(); 17838653Sgpalmer 17938653Sgpalmer if (prereq() || geteuid()) 18038653Sgpalmer return ksft_exit_skip("Prerequisites unsatisfied\n"); 18138653Sgpalmer 18238653Sgpalmer ksft_set_plan(1); 18341062Sbde 18438653Sgpalmer lim.rlim_cur = RLIM_INFINITY; 18538653Sgpalmer lim.rlim_max = RLIM_INFINITY; 18638653Sgpalmer if (setrlimit(RLIMIT_MEMLOCK, &lim)) 18738653Sgpalmer ksft_exit_fail_msg("Failed to set rlimit: %s\n", strerror(errno)); 18899379Sjohan 18938653Sgpalmer page_size = getpagesize(); 19038653Sgpalmer 19138653Sgpalmer if (read_memory_info(&mem_free, &hugepage_size) != 0) 19238653Sgpalmer ksft_exit_fail_msg("Failed to get meminfo\n"); 19338653Sgpalmer 194273568Smarcel mem_fragmentable_MB = mem_free * 0.8 / 1024; 195207842Smm 196207842Smm while (mem_fragmentable_MB > 0) { 197286590Semaste map = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, 1981590Srgrimes MAP_ANONYMOUS | MAP_PRIVATE | MAP_LOCKED, -1, 0); 199183242Ssam if (map == MAP_FAILED) 200183242Ssam break; 201288904Simp 202288904Simp entry = malloc(sizeof(struct map_list)); 203288904Simp if (!entry) { 204288904Simp munmap(map, MAP_SIZE); 205288904Simp break; 206288904Simp } 207319192Sngie entry->map = map; 208288904Simp entry->next = list; 209288904Simp list = entry; 210288904Simp 211288904Simp /* Write something (in this case the address of the map) to 212288904Simp * ensure that KSM can't merge the mapped pages 213288904Simp */ 214288904Simp for (i = 0; i < MAP_SIZE; i += page_size) 215288904Simp *(unsigned long *)(map + i) = (unsigned long)map + i; 216288904Simp 217288904Simp mem_fragmentable_MB -= MAP_SIZE_MB; 218288904Simp } 219288904Simp 220288904Simp for (entry = list; entry != NULL; entry = entry->next) { 221331465Sian munmap(entry->map, MAP_SIZE); 222331465Sian if (!entry->next) 223331465Sian break; 224289839Sbdrewery entry = entry->next; 225289935Stheraven } 226289839Sbdrewery 227289839Sbdrewery if (check_compaction(mem_free, hugepage_size) == 0) 228289935Stheraven return ksft_exit_pass(); 229288904Simp 230288904Simp return ksft_exit_fail(); 231288904Simp} 232288904Simp