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/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 21 #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1))) 22 23 FIXTURE(migration) 24 { 25 pthread_t *threads; 26 pid_t *pids; 27 int nthreads; 28 int n1; 29 int n2; 30 }; 31 32 FIXTURE_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 57 FIXTURE_TEARDOWN(migration) 58 { 59 free(self->threads); 60 free(self->pids); 61 } 62 63 int migrate(uint64_t *ptr, int n1, int n2) 64 { 65 int ret, tmp; 66 int status = 0; 67 struct timespec ts1, ts2; 68 69 if (clock_gettime(CLOCK_MONOTONIC, &ts1)) 70 return -1; 71 72 while (1) { 73 if (clock_gettime(CLOCK_MONOTONIC, &ts2)) 74 return -1; 75 76 if (ts2.tv_sec - ts1.tv_sec >= RUNTIME) 77 return 0; 78 79 ret = move_pages(0, 1, (void **) &ptr, &n2, &status, 80 MPOL_MF_MOVE_ALL); 81 if (ret) { 82 if (ret > 0) 83 printf("Didn't migrate %d pages\n", ret); 84 else 85 perror("Couldn't migrate pages"); 86 return -2; 87 } 88 89 tmp = n2; 90 n2 = n1; 91 n1 = tmp; 92 } 93 94 return 0; 95 } 96 97 void *access_mem(void *ptr) 98 { 99 volatile uint64_t y = 0; 100 volatile uint64_t *x = ptr; 101 102 while (1) { 103 pthread_testcancel(); 104 y += *x; 105 106 /* Prevent the compiler from optimizing out the writes to y: */ 107 asm volatile("" : "+r" (y)); 108 } 109 110 return NULL; 111 } 112 113 /* 114 * Basic migration entry testing. One thread will move pages back and forth 115 * between nodes whilst other threads try and access them triggering the 116 * migration entry wait paths in the kernel. 117 */ 118 TEST_F_TIMEOUT(migration, private_anon, 2*RUNTIME) 119 { 120 uint64_t *ptr; 121 int i; 122 123 if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) 124 SKIP(return, "Not enough threads or NUMA nodes available"); 125 126 ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE, 127 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 128 ASSERT_NE(ptr, MAP_FAILED); 129 130 memset(ptr, 0xde, TWOMEG); 131 for (i = 0; i < self->nthreads - 1; i++) 132 if (pthread_create(&self->threads[i], NULL, access_mem, ptr)) 133 perror("Couldn't create thread"); 134 135 ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); 136 for (i = 0; i < self->nthreads - 1; i++) 137 ASSERT_EQ(pthread_cancel(self->threads[i]), 0); 138 } 139 140 /* 141 * Same as the previous test but with shared memory. 142 */ 143 TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME) 144 { 145 pid_t pid; 146 uint64_t *ptr; 147 int i; 148 149 if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) 150 SKIP(return, "Not enough threads or NUMA nodes available"); 151 152 ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE, 153 MAP_SHARED | MAP_ANONYMOUS, -1, 0); 154 ASSERT_NE(ptr, MAP_FAILED); 155 156 memset(ptr, 0xde, TWOMEG); 157 for (i = 0; i < self->nthreads - 1; i++) { 158 pid = fork(); 159 if (!pid) { 160 prctl(PR_SET_PDEATHSIG, SIGHUP); 161 /* Parent may have died before prctl so check now. */ 162 if (getppid() == 1) 163 kill(getpid(), SIGHUP); 164 access_mem(ptr); 165 } else { 166 self->pids[i] = pid; 167 } 168 } 169 170 ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); 171 for (i = 0; i < self->nthreads - 1; i++) 172 ASSERT_EQ(kill(self->pids[i], SIGTERM), 0); 173 } 174 175 /* 176 * Tests the pmd migration entry paths. 177 */ 178 TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME) 179 { 180 uint64_t *ptr; 181 int i; 182 183 if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) 184 SKIP(return, "Not enough threads or NUMA nodes available"); 185 186 ptr = mmap(NULL, 2*TWOMEG, PROT_READ | PROT_WRITE, 187 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 188 ASSERT_NE(ptr, MAP_FAILED); 189 190 ptr = (uint64_t *) ALIGN((uintptr_t) ptr, TWOMEG); 191 ASSERT_EQ(madvise(ptr, TWOMEG, MADV_HUGEPAGE), 0); 192 memset(ptr, 0xde, TWOMEG); 193 for (i = 0; i < self->nthreads - 1; i++) 194 if (pthread_create(&self->threads[i], NULL, access_mem, ptr)) 195 perror("Couldn't create thread"); 196 197 ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); 198 for (i = 0; i < self->nthreads - 1; i++) 199 ASSERT_EQ(pthread_cancel(self->threads[i]), 0); 200 } 201 202 TEST_HARNESS_MAIN 203