brintos

brintos / linux-shallow public Read only

0
0
Text · 4.5 KiB · 64bcbb7 Raw
208 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * The main purpose of the tests here is to exercise the migration entry code4 * 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	10021#define ALIGN(x, a)	(((x) + (a - 1)) & (~((a) - 1)))22 23FIXTURE(migration)24{25	pthread_t *threads;26	pid_t *pids;27	int nthreads;28	int n1;29	int n2;30};31 32FIXTURE_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 57FIXTURE_TEARDOWN(migration)58{59	free(self->threads);60	free(self->pids);61}62 63int 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			else90				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 102void *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 forth120 * between nodes whilst other threads try and access them triggering the121 * migration entry wait paths in the kernel.122 */123TEST_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 */148TEST_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 */183TEST_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 207TEST_HARNESS_MAIN208