]> Git Repo - J-linux.git/blob - tools/testing/selftests/mm/migration.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / mm / migration.c
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
This page took 0.037973 seconds and 4 git commands to generate.