Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * The main purpose of the tests here is to exercise the migration entry code
0004  * paths in the kernel.
0005  */
0006 
0007 #include "../kselftest_harness.h"
0008 #include <strings.h>
0009 #include <pthread.h>
0010 #include <numa.h>
0011 #include <numaif.h>
0012 #include <sys/mman.h>
0013 #include <sys/types.h>
0014 #include <signal.h>
0015 #include <time.h>
0016 
0017 #define TWOMEG (2<<20)
0018 #define RUNTIME (60)
0019 
0020 #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
0021 
0022 FIXTURE(migration)
0023 {
0024     pthread_t *threads;
0025     pid_t *pids;
0026     int nthreads;
0027     int n1;
0028     int n2;
0029 };
0030 
0031 FIXTURE_SETUP(migration)
0032 {
0033     int n;
0034 
0035     ASSERT_EQ(numa_available(), 0);
0036     self->nthreads = numa_num_task_cpus() - 1;
0037     self->n1 = -1;
0038     self->n2 = -1;
0039 
0040     for (n = 0; n < numa_max_possible_node(); n++)
0041         if (numa_bitmask_isbitset(numa_all_nodes_ptr, n)) {
0042             if (self->n1 == -1) {
0043                 self->n1 = n;
0044             } else {
0045                 self->n2 = n;
0046                 break;
0047             }
0048         }
0049 
0050     self->threads = malloc(self->nthreads * sizeof(*self->threads));
0051     ASSERT_NE(self->threads, NULL);
0052     self->pids = malloc(self->nthreads * sizeof(*self->pids));
0053     ASSERT_NE(self->pids, NULL);
0054 };
0055 
0056 FIXTURE_TEARDOWN(migration)
0057 {
0058     free(self->threads);
0059     free(self->pids);
0060 }
0061 
0062 int migrate(uint64_t *ptr, int n1, int n2)
0063 {
0064     int ret, tmp;
0065     int status = 0;
0066     struct timespec ts1, ts2;
0067 
0068     if (clock_gettime(CLOCK_MONOTONIC, &ts1))
0069         return -1;
0070 
0071     while (1) {
0072         if (clock_gettime(CLOCK_MONOTONIC, &ts2))
0073             return -1;
0074 
0075         if (ts2.tv_sec - ts1.tv_sec >= RUNTIME)
0076             return 0;
0077 
0078         ret = move_pages(0, 1, (void **) &ptr, &n2, &status,
0079                 MPOL_MF_MOVE_ALL);
0080         if (ret) {
0081             if (ret > 0)
0082                 printf("Didn't migrate %d pages\n", ret);
0083             else
0084                 perror("Couldn't migrate pages");
0085             return -2;
0086         }
0087 
0088         tmp = n2;
0089         n2 = n1;
0090         n1 = tmp;
0091     }
0092 
0093     return 0;
0094 }
0095 
0096 void *access_mem(void *ptr)
0097 {
0098     uint64_t y = 0;
0099     volatile uint64_t *x = ptr;
0100 
0101     while (1) {
0102         pthread_testcancel();
0103         y += *x;
0104     }
0105 
0106     return NULL;
0107 }
0108 
0109 /*
0110  * Basic migration entry testing. One thread will move pages back and forth
0111  * between nodes whilst other threads try and access them triggering the
0112  * migration entry wait paths in the kernel.
0113  */
0114 TEST_F_TIMEOUT(migration, private_anon, 2*RUNTIME)
0115 {
0116     uint64_t *ptr;
0117     int i;
0118 
0119     if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
0120         SKIP(return, "Not enough threads or NUMA nodes available");
0121 
0122     ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
0123         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
0124     ASSERT_NE(ptr, MAP_FAILED);
0125 
0126     memset(ptr, 0xde, TWOMEG);
0127     for (i = 0; i < self->nthreads - 1; i++)
0128         if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
0129             perror("Couldn't create thread");
0130 
0131     ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
0132     for (i = 0; i < self->nthreads - 1; i++)
0133         ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
0134 }
0135 
0136 /*
0137  * Same as the previous test but with shared memory.
0138  */
0139 TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME)
0140 {
0141     pid_t pid;
0142     uint64_t *ptr;
0143     int i;
0144 
0145     if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
0146         SKIP(return, "Not enough threads or NUMA nodes available");
0147 
0148     ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
0149         MAP_SHARED | MAP_ANONYMOUS, -1, 0);
0150     ASSERT_NE(ptr, MAP_FAILED);
0151 
0152     memset(ptr, 0xde, TWOMEG);
0153     for (i = 0; i < self->nthreads - 1; i++) {
0154         pid = fork();
0155         if (!pid)
0156             access_mem(ptr);
0157         else
0158             self->pids[i] = pid;
0159     }
0160 
0161     ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
0162     for (i = 0; i < self->nthreads - 1; i++)
0163         ASSERT_EQ(kill(self->pids[i], SIGTERM), 0);
0164 }
0165 
0166 /*
0167  * Tests the pmd migration entry paths.
0168  */
0169 TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME)
0170 {
0171     uint64_t *ptr;
0172     int i;
0173 
0174     if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
0175         SKIP(return, "Not enough threads or NUMA nodes available");
0176 
0177     ptr = mmap(NULL, 2*TWOMEG, PROT_READ | PROT_WRITE,
0178         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
0179     ASSERT_NE(ptr, MAP_FAILED);
0180 
0181     ptr = (uint64_t *) ALIGN((uintptr_t) ptr, TWOMEG);
0182     ASSERT_EQ(madvise(ptr, TWOMEG, MADV_HUGEPAGE), 0);
0183     memset(ptr, 0xde, TWOMEG);
0184     for (i = 0; i < self->nthreads - 1; i++)
0185         if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
0186             perror("Couldn't create thread");
0187 
0188     ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
0189     for (i = 0; i < self->nthreads - 1; i++)
0190         ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
0191 }
0192 
0193 TEST_HARNESS_MAIN