Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *
0004  * A test for the patch "Allow compaction of unevictable pages".
0005  * With this patch we should be able to allocate at least 1/4
0006  * of RAM in huge pages. Without the patch much less is
0007  * allocated.
0008  */
0009 
0010 #include <stdio.h>
0011 #include <stdlib.h>
0012 #include <sys/mman.h>
0013 #include <sys/resource.h>
0014 #include <fcntl.h>
0015 #include <errno.h>
0016 #include <unistd.h>
0017 #include <string.h>
0018 
0019 #include "../kselftest.h"
0020 
0021 #define MAP_SIZE_MB 100
0022 #define MAP_SIZE    (MAP_SIZE_MB * 1024 * 1024)
0023 
0024 struct map_list {
0025     void *map;
0026     struct map_list *next;
0027 };
0028 
0029 int read_memory_info(unsigned long *memfree, unsigned long *hugepagesize)
0030 {
0031     char  buffer[256] = {0};
0032     char *cmd = "cat /proc/meminfo | grep -i memfree | grep -o '[0-9]*'";
0033     FILE *cmdfile = popen(cmd, "r");
0034 
0035     if (!(fgets(buffer, sizeof(buffer), cmdfile))) {
0036         perror("Failed to read meminfo\n");
0037         return -1;
0038     }
0039 
0040     pclose(cmdfile);
0041 
0042     *memfree = atoll(buffer);
0043     cmd = "cat /proc/meminfo | grep -i hugepagesize | grep -o '[0-9]*'";
0044     cmdfile = popen(cmd, "r");
0045 
0046     if (!(fgets(buffer, sizeof(buffer), cmdfile))) {
0047         perror("Failed to read meminfo\n");
0048         return -1;
0049     }
0050 
0051     pclose(cmdfile);
0052     *hugepagesize = atoll(buffer);
0053 
0054     return 0;
0055 }
0056 
0057 int prereq(void)
0058 {
0059     char allowed;
0060     int fd;
0061 
0062     fd = open("/proc/sys/vm/compact_unevictable_allowed",
0063           O_RDONLY | O_NONBLOCK);
0064     if (fd < 0) {
0065         perror("Failed to open\n"
0066                "/proc/sys/vm/compact_unevictable_allowed\n");
0067         return -1;
0068     }
0069 
0070     if (read(fd, &allowed, sizeof(char)) != sizeof(char)) {
0071         perror("Failed to read from\n"
0072                "/proc/sys/vm/compact_unevictable_allowed\n");
0073         close(fd);
0074         return -1;
0075     }
0076 
0077     close(fd);
0078     if (allowed == '1')
0079         return 0;
0080 
0081     return -1;
0082 }
0083 
0084 int check_compaction(unsigned long mem_free, unsigned int hugepage_size)
0085 {
0086     int fd;
0087     int compaction_index = 0;
0088     char initial_nr_hugepages[10] = {0};
0089     char nr_hugepages[10] = {0};
0090 
0091     /* We want to test with 80% of available memory. Else, OOM killer comes
0092        in to play */
0093     mem_free = mem_free * 0.8;
0094 
0095     fd = open("/proc/sys/vm/nr_hugepages", O_RDWR | O_NONBLOCK);
0096     if (fd < 0) {
0097         perror("Failed to open /proc/sys/vm/nr_hugepages");
0098         return -1;
0099     }
0100 
0101     if (read(fd, initial_nr_hugepages, sizeof(initial_nr_hugepages)) <= 0) {
0102         perror("Failed to read from /proc/sys/vm/nr_hugepages");
0103         goto close_fd;
0104     }
0105 
0106     /* Start with the initial condition of 0 huge pages*/
0107     if (write(fd, "0", sizeof(char)) != sizeof(char)) {
0108         perror("Failed to write 0 to /proc/sys/vm/nr_hugepages\n");
0109         goto close_fd;
0110     }
0111 
0112     lseek(fd, 0, SEEK_SET);
0113 
0114     /* Request a large number of huge pages. The Kernel will allocate
0115        as much as it can */
0116     if (write(fd, "100000", (6*sizeof(char))) != (6*sizeof(char))) {
0117         perror("Failed to write 100000 to /proc/sys/vm/nr_hugepages\n");
0118         goto close_fd;
0119     }
0120 
0121     lseek(fd, 0, SEEK_SET);
0122 
0123     if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) {
0124         perror("Failed to re-read from /proc/sys/vm/nr_hugepages\n");
0125         goto close_fd;
0126     }
0127 
0128     /* We should have been able to request at least 1/3 rd of the memory in
0129        huge pages */
0130     compaction_index = mem_free/(atoi(nr_hugepages) * hugepage_size);
0131 
0132     if (compaction_index > 3) {
0133         printf("No of huge pages allocated = %d\n",
0134                (atoi(nr_hugepages)));
0135         fprintf(stderr, "ERROR: Less that 1/%d of memory is available\n"
0136             "as huge pages\n", compaction_index);
0137         goto close_fd;
0138     }
0139 
0140     printf("No of huge pages allocated = %d\n",
0141            (atoi(nr_hugepages)));
0142 
0143     lseek(fd, 0, SEEK_SET);
0144 
0145     if (write(fd, initial_nr_hugepages, strlen(initial_nr_hugepages))
0146         != strlen(initial_nr_hugepages)) {
0147         perror("Failed to write value to /proc/sys/vm/nr_hugepages\n");
0148         goto close_fd;
0149     }
0150 
0151     close(fd);
0152     return 0;
0153 
0154  close_fd:
0155     close(fd);
0156     printf("Not OK. Compaction test failed.");
0157     return -1;
0158 }
0159 
0160 
0161 int main(int argc, char **argv)
0162 {
0163     struct rlimit lim;
0164     struct map_list *list, *entry;
0165     size_t page_size, i;
0166     void *map = NULL;
0167     unsigned long mem_free = 0;
0168     unsigned long hugepage_size = 0;
0169     long mem_fragmentable_MB = 0;
0170 
0171     if (prereq() != 0) {
0172         printf("Either the sysctl compact_unevictable_allowed is not\n"
0173                "set to 1 or couldn't read the proc file.\n"
0174                "Skipping the test\n");
0175         return KSFT_SKIP;
0176     }
0177 
0178     lim.rlim_cur = RLIM_INFINITY;
0179     lim.rlim_max = RLIM_INFINITY;
0180     if (setrlimit(RLIMIT_MEMLOCK, &lim)) {
0181         perror("Failed to set rlimit:\n");
0182         return -1;
0183     }
0184 
0185     page_size = getpagesize();
0186 
0187     list = NULL;
0188 
0189     if (read_memory_info(&mem_free, &hugepage_size) != 0) {
0190         printf("ERROR: Cannot read meminfo\n");
0191         return -1;
0192     }
0193 
0194     mem_fragmentable_MB = mem_free * 0.8 / 1024;
0195 
0196     while (mem_fragmentable_MB > 0) {
0197         map = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE,
0198                MAP_ANONYMOUS | MAP_PRIVATE | MAP_LOCKED, -1, 0);
0199         if (map == MAP_FAILED)
0200             break;
0201 
0202         entry = malloc(sizeof(struct map_list));
0203         if (!entry) {
0204             munmap(map, MAP_SIZE);
0205             break;
0206         }
0207         entry->map = map;
0208         entry->next = list;
0209         list = entry;
0210 
0211         /* Write something (in this case the address of the map) to
0212          * ensure that KSM can't merge the mapped pages
0213          */
0214         for (i = 0; i < MAP_SIZE; i += page_size)
0215             *(unsigned long *)(map + i) = (unsigned long)map + i;
0216 
0217         mem_fragmentable_MB -= MAP_SIZE_MB;
0218     }
0219 
0220     for (entry = list; entry != NULL; entry = entry->next) {
0221         munmap(entry->map, MAP_SIZE);
0222         if (!entry->next)
0223             break;
0224         entry = entry->next;
0225     }
0226 
0227     if (check_compaction(mem_free, hugepage_size) == 0)
0228         return 0;
0229 
0230     return -1;
0231 }