Implement simple ktest that looks up the physical address via /proc/self/pagemap and migrates the page based on that information. Signed-off-by: Gregory Price <gregory.price@xxxxxxxxxxxx> --- tools/testing/selftests/mm/migration.c | 99 ++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c index 6908569ef406..c005c98dbdc1 100644 --- a/tools/testing/selftests/mm/migration.c +++ b/tools/testing/selftests/mm/migration.c @@ -5,6 +5,8 @@ */ #include "../kselftest_harness.h" +#include <stdint.h> +#include <stdio.h> #include <strings.h> #include <pthread.h> #include <numa.h> @@ -14,11 +16,17 @@ #include <sys/types.h> #include <signal.h> #include <time.h> +#include <unistd.h> #define TWOMEG (2<<20) #define RUNTIME (20) +#define GET_BIT(X, Y) ((X & ((uint64_t)1<<Y)) >> Y) +#define GET_PFN(X) (X & 0x7FFFFFFFFFFFFFull) #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1))) +#define PAGEMAP_ENTRY 8 +const int __endian_bit = 1; +#define is_bigendian() ((*(char *)&__endian_bit) == 0) FIXTURE(migration) { @@ -94,6 +102,45 @@ int migrate(uint64_t *ptr, int n1, int n2) return 0; } +int migrate_phys(uint64_t paddr, int n1, int n2) +{ + int ret, tmp; + int status = 0; + struct timespec ts1, ts2; + + if (clock_gettime(CLOCK_MONOTONIC, &ts1)) + return -1; + + while (1) { + if (clock_gettime(CLOCK_MONOTONIC, &ts2)) + return -1; + + if (ts2.tv_sec - ts1.tv_sec >= RUNTIME) + return 0; + + /* + * FIXME: move_phys_pages was syscall 462 during RFC. + * Update this when an official syscall number is adopted + * and the libnuma interface is implemented. + */ + ret = syscall(462, 1, (void **) &paddr, &n2, &status, + MPOL_MF_MOVE_ALL); + if (ret) { + if (ret > 0) + printf("Didn't migrate %d pages\n", ret); + else + perror("Couldn't migrate pages"); + return -2; + } + + tmp = n2; + n2 = n1; + n1 = tmp; + } + + return 0; +} + void *access_mem(void *ptr) { volatile uint64_t y = 0; @@ -199,4 +246,56 @@ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME) ASSERT_EQ(pthread_cancel(self->threads[i]), 0); } +/* + * Same as the basic migration, but test move_phys_pages. + */ +TEST_F_TIMEOUT(migration, phys_addr, 2*RUNTIME) +{ + uint64_t *ptr; + uint64_t pagemap_val, paddr, file_offset; + unsigned char c_buf[PAGEMAP_ENTRY]; + int i, c, status; + FILE *f; + + if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) + SKIP(return, "Not enough threads or NUMA nodes available"); + + ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(ptr, MAP_FAILED); + + memset(ptr, 0xde, TWOMEG); + + /* PFN of ptr from /proc/self/pagemap */ + f = fopen("/proc/self/pagemap", "rb"); + file_offset = ((uint64_t)ptr) / getpagesize() * PAGEMAP_ENTRY; + status = fseek(f, file_offset, SEEK_SET); + ASSERT_EQ(status, 0); + for (i = 0; i < PAGEMAP_ENTRY; i++) { + c = getc(f); + ASSERT_NE(c, EOF); + /* handle endiand differences */ + if (is_bigendian()) + c_buf[i] = c; + else + c_buf[PAGEMAP_ENTRY - i - 1] = c; + } + fclose(f); + + for (i = 0; i < PAGEMAP_ENTRY; i++) + pagemap_val = (pagemap_val << 8) + c_buf[i]; + + ASSERT_TRUE(GET_BIT(pagemap_val, 63)); + /* This reports a pfn, we need to shift this by page size */ + paddr = GET_PFN(pagemap_val) << __builtin_ctz(getpagesize()); + + for (i = 0; i < self->nthreads - 1; i++) + if (pthread_create(&self->threads[i], NULL, access_mem, ptr)) + perror("Couldn't create thread"); + + ASSERT_EQ(migrate_phys(paddr, self->n1, self->n2), 0); + for (i = 0; i < self->nthreads - 1; i++) + ASSERT_EQ(pthread_cancel(self->threads[i]), 0); +} + TEST_HARNESS_MAIN -- 2.39.1