linux/tools/testing/selftests/vm/migration.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * The main purpose of the tests here is to exercise the migration entry code
   4 * paths in the kernel.
   5 */
   6
   7#include "../kselftest_harness.h"
   8#include <strings.h>
   9#include <pthread.h>
  10#include <numa.h>
  11#include <numaif.h>
  12#include <sys/mman.h>
  13#include <sys/types.h>
  14#include <signal.h>
  15#include <time.h>
  16
  17#define TWOMEG (2<<20)
  18#define RUNTIME (60)
  19
  20#define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
  21
  22FIXTURE(migration)
  23{
  24        pthread_t *threads;
  25        pid_t *pids;
  26        int nthreads;
  27        int n1;
  28        int n2;
  29};
  30
  31FIXTURE_SETUP(migration)
  32{
  33        int n;
  34
  35        ASSERT_EQ(numa_available(), 0);
  36        self->nthreads = numa_num_task_cpus() - 1;
  37        self->n1 = -1;
  38        self->n2 = -1;
  39
  40        for (n = 0; n < numa_max_possible_node(); n++)
  41                if (numa_bitmask_isbitset(numa_all_nodes_ptr, n)) {
  42                        if (self->n1 == -1) {
  43                                self->n1 = n;
  44                        } else {
  45                                self->n2 = n;
  46                                break;
  47                        }
  48                }
  49
  50        self->threads = malloc(self->nthreads * sizeof(*self->threads));
  51        ASSERT_NE(self->threads, NULL);
  52        self->pids = malloc(self->nthreads * sizeof(*self->pids));
  53        ASSERT_NE(self->pids, NULL);
  54};
  55
  56FIXTURE_TEARDOWN(migration)
  57{
  58        free(self->threads);
  59        free(self->pids);
  60}
  61
  62int migrate(uint64_t *ptr, int n1, int n2)
  63{
  64        int ret, tmp;
  65        int status = 0;
  66        struct timespec ts1, ts2;
  67
  68        if (clock_gettime(CLOCK_MONOTONIC, &ts1))
  69                return -1;
  70
  71        while (1) {
  72                if (clock_gettime(CLOCK_MONOTONIC, &ts2))
  73                        return -1;
  74
  75                if (ts2.tv_sec - ts1.tv_sec >= RUNTIME)
  76                        return 0;
  77
  78                ret = move_pages(0, 1, (void **) &ptr, &n2, &status,
  79                                MPOL_MF_MOVE_ALL);
  80                if (ret) {
  81                        if (ret > 0)
  82                                printf("Didn't migrate %d pages\n", ret);
  83                        else
  84                                perror("Couldn't migrate pages");
  85                        return -2;
  86                }
  87
  88                tmp = n2;
  89                n2 = n1;
  90                n1 = tmp;
  91        }
  92
  93        return 0;
  94}
  95
  96void *access_mem(void *ptr)
  97{
  98        uint64_t y = 0;
  99        volatile uint64_t *x = ptr;
 100
 101        while (1) {
 102                pthread_testcancel();
 103                y += *x;
 104        }
 105
 106        return NULL;
 107}
 108
 109/*
 110 * Basic migration entry testing. One thread will move pages back and forth
 111 * between nodes whilst other threads try and access them triggering the
 112 * migration entry wait paths in the kernel.
 113 */
 114TEST_F_TIMEOUT(migration, private_anon, 2*RUNTIME)
 115{
 116        uint64_t *ptr;
 117        int i;
 118
 119        if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
 120                SKIP(return, "Not enough threads or NUMA nodes available");
 121
 122        ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
 123                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 124        ASSERT_NE(ptr, MAP_FAILED);
 125
 126        memset(ptr, 0xde, TWOMEG);
 127        for (i = 0; i < self->nthreads - 1; i++)
 128                if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
 129                        perror("Couldn't create thread");
 130
 131        ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
 132        for (i = 0; i < self->nthreads - 1; i++)
 133                ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
 134}
 135
 136/*
 137 * Same as the previous test but with shared memory.
 138 */
 139TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME)
 140{
 141        pid_t pid;
 142        uint64_t *ptr;
 143        int i;
 144
 145        if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
 146                SKIP(return, "Not enough threads or NUMA nodes available");
 147
 148        ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
 149                MAP_SHARED | MAP_ANONYMOUS, -1, 0);
 150        ASSERT_NE(ptr, MAP_FAILED);
 151
 152        memset(ptr, 0xde, TWOMEG);
 153        for (i = 0; i < self->nthreads - 1; i++) {
 154                pid = fork();
 155                if (!pid)
 156                        access_mem(ptr);
 157                else
 158                        self->pids[i] = pid;
 159        }
 160
 161        ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
 162        for (i = 0; i < self->nthreads - 1; i++)
 163                ASSERT_EQ(kill(self->pids[i], SIGTERM), 0);
 164}
 165
 166/*
 167 * Tests the pmd migration entry paths.
 168 */
 169TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME)
 170{
 171        uint64_t *ptr;
 172        int i;
 173
 174        if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
 175                SKIP(return, "Not enough threads or NUMA nodes available");
 176
 177        ptr = mmap(NULL, 2*TWOMEG, PROT_READ | PROT_WRITE,
 178                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 179        ASSERT_NE(ptr, MAP_FAILED);
 180
 181        ptr = (uint64_t *) ALIGN((uintptr_t) ptr, TWOMEG);
 182        ASSERT_EQ(madvise(ptr, TWOMEG, MADV_HUGEPAGE), 0);
 183        memset(ptr, 0xde, TWOMEG);
 184        for (i = 0; i < self->nthreads - 1; i++)
 185                if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
 186                        perror("Couldn't create thread");
 187
 188        ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
 189        for (i = 0; i < self->nthreads - 1; i++)
 190                ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
 191}
 192
 193TEST_HARNESS_MAIN
 194