Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  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