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