xref: /linux/tools/testing/selftests/mm/migration.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 #define MAX_RETRIES	100
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 	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 			else
90 				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 
102 void *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 forth
120  * between nodes whilst other threads try and access them triggering the
121  * migration entry wait paths in the kernel.
122  */
123 TEST_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  */
148 TEST_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  */
183 TEST_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 
207 TEST_HARNESS_MAIN
208