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