xref: /linux/tools/testing/selftests/mm/migration.c (revision a06c3fad49a50d5d5eb078f93e70f4d3eca5d5a5)
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