migration.c (4176B)
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