cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

migration.c (4176B)


      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/types.h>
     14#include <signal.h>
     15#include <time.h>
     16
     17#define TWOMEG (2<<20)
     18#define RUNTIME (60)
     19
     20#define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
     21
     22FIXTURE(migration)
     23{
     24	pthread_t *threads;
     25	pid_t *pids;
     26	int nthreads;
     27	int n1;
     28	int n2;
     29};
     30
     31FIXTURE_SETUP(migration)
     32{
     33	int n;
     34
     35	ASSERT_EQ(numa_available(), 0);
     36	self->nthreads = numa_num_task_cpus() - 1;
     37	self->n1 = -1;
     38	self->n2 = -1;
     39
     40	for (n = 0; n < numa_max_possible_node(); n++)
     41		if (numa_bitmask_isbitset(numa_all_nodes_ptr, n)) {
     42			if (self->n1 == -1) {
     43				self->n1 = n;
     44			} else {
     45				self->n2 = n;
     46				break;
     47			}
     48		}
     49
     50	self->threads = malloc(self->nthreads * sizeof(*self->threads));
     51	ASSERT_NE(self->threads, NULL);
     52	self->pids = malloc(self->nthreads * sizeof(*self->pids));
     53	ASSERT_NE(self->pids, NULL);
     54};
     55
     56FIXTURE_TEARDOWN(migration)
     57{
     58	free(self->threads);
     59	free(self->pids);
     60}
     61
     62int migrate(uint64_t *ptr, int n1, int n2)
     63{
     64	int ret, tmp;
     65	int status = 0;
     66	struct timespec ts1, ts2;
     67
     68	if (clock_gettime(CLOCK_MONOTONIC, &ts1))
     69		return -1;
     70
     71	while (1) {
     72		if (clock_gettime(CLOCK_MONOTONIC, &ts2))
     73			return -1;
     74
     75		if (ts2.tv_sec - ts1.tv_sec >= RUNTIME)
     76			return 0;
     77
     78		ret = move_pages(0, 1, (void **) &ptr, &n2, &status,
     79				MPOL_MF_MOVE_ALL);
     80		if (ret) {
     81			if (ret > 0)
     82				printf("Didn't migrate %d pages\n", ret);
     83			else
     84				perror("Couldn't migrate pages");
     85			return -2;
     86		}
     87
     88		tmp = n2;
     89		n2 = n1;
     90		n1 = tmp;
     91	}
     92
     93	return 0;
     94}
     95
     96void *access_mem(void *ptr)
     97{
     98	uint64_t y = 0;
     99	volatile uint64_t *x = ptr;
    100
    101	while (1) {
    102		pthread_testcancel();
    103		y += *x;
    104	}
    105
    106	return NULL;
    107}
    108
    109/*
    110 * Basic migration entry testing. One thread will move pages back and forth
    111 * between nodes whilst other threads try and access them triggering the
    112 * migration entry wait paths in the kernel.
    113 */
    114TEST_F_TIMEOUT(migration, private_anon, 2*RUNTIME)
    115{
    116	uint64_t *ptr;
    117	int i;
    118
    119	if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
    120		SKIP(return, "Not enough threads or NUMA nodes available");
    121
    122	ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
    123		MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    124	ASSERT_NE(ptr, MAP_FAILED);
    125
    126	memset(ptr, 0xde, TWOMEG);
    127	for (i = 0; i < self->nthreads - 1; i++)
    128		if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
    129			perror("Couldn't create thread");
    130
    131	ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
    132	for (i = 0; i < self->nthreads - 1; i++)
    133		ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
    134}
    135
    136/*
    137 * Same as the previous test but with shared memory.
    138 */
    139TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME)
    140{
    141	pid_t pid;
    142	uint64_t *ptr;
    143	int i;
    144
    145	if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
    146		SKIP(return, "Not enough threads or NUMA nodes available");
    147
    148	ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
    149		MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    150	ASSERT_NE(ptr, MAP_FAILED);
    151
    152	memset(ptr, 0xde, TWOMEG);
    153	for (i = 0; i < self->nthreads - 1; i++) {
    154		pid = fork();
    155		if (!pid)
    156			access_mem(ptr);
    157		else
    158			self->pids[i] = pid;
    159	}
    160
    161	ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
    162	for (i = 0; i < self->nthreads - 1; i++)
    163		ASSERT_EQ(kill(self->pids[i], SIGTERM), 0);
    164}
    165
    166/*
    167 * Tests the pmd migration entry paths.
    168 */
    169TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME)
    170{
    171	uint64_t *ptr;
    172	int i;
    173
    174	if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
    175		SKIP(return, "Not enough threads or NUMA nodes available");
    176
    177	ptr = mmap(NULL, 2*TWOMEG, PROT_READ | PROT_WRITE,
    178		MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    179	ASSERT_NE(ptr, MAP_FAILED);
    180
    181	ptr = (uint64_t *) ALIGN((uintptr_t) ptr, TWOMEG);
    182	ASSERT_EQ(madvise(ptr, TWOMEG, MADV_HUGEPAGE), 0);
    183	memset(ptr, 0xde, TWOMEG);
    184	for (i = 0; i < self->nthreads - 1; i++)
    185		if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
    186			perror("Couldn't create thread");
    187
    188	ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
    189	for (i = 0; i < self->nthreads - 1; i++)
    190		ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
    191}
    192
    193TEST_HARNESS_MAIN