xref: /linux/tools/testing/selftests/mm/rmap.c (revision 8804d970fab45726b3c7cd7f240b31122aa94219)
1*c9615059SWei Yang // SPDX-License-Identifier: GPL-2.0
2*c9615059SWei Yang /*
3*c9615059SWei Yang  * RMAP functional tests
4*c9615059SWei Yang  *
5*c9615059SWei Yang  * Author(s): Wei Yang <richard.weiyang@gmail.com>
6*c9615059SWei Yang  */
7*c9615059SWei Yang 
8*c9615059SWei Yang #include "../kselftest_harness.h"
9*c9615059SWei Yang #include <strings.h>
10*c9615059SWei Yang #include <pthread.h>
11*c9615059SWei Yang #include <numa.h>
12*c9615059SWei Yang #include <numaif.h>
13*c9615059SWei Yang #include <sys/mman.h>
14*c9615059SWei Yang #include <sys/prctl.h>
15*c9615059SWei Yang #include <sys/types.h>
16*c9615059SWei Yang #include <signal.h>
17*c9615059SWei Yang #include <time.h>
18*c9615059SWei Yang #include <sys/sem.h>
19*c9615059SWei Yang #include <unistd.h>
20*c9615059SWei Yang #include <fcntl.h>
21*c9615059SWei Yang 
22*c9615059SWei Yang #include "vm_util.h"
23*c9615059SWei Yang 
24*c9615059SWei Yang #define TOTAL_LEVEL 5
25*c9615059SWei Yang #define MAX_CHILDREN 3
26*c9615059SWei Yang 
27*c9615059SWei Yang #define FAIL_ON_CHECK	(1 << 0)
28*c9615059SWei Yang #define FAIL_ON_WORK	(1 << 1)
29*c9615059SWei Yang 
30*c9615059SWei Yang struct sembuf sem_wait = {0, -1, 0};
31*c9615059SWei Yang struct sembuf sem_signal = {0, 1, 0};
32*c9615059SWei Yang 
33*c9615059SWei Yang enum backend_type {
34*c9615059SWei Yang 	ANON,
35*c9615059SWei Yang 	SHM,
36*c9615059SWei Yang 	NORM_FILE,
37*c9615059SWei Yang };
38*c9615059SWei Yang 
39*c9615059SWei Yang #define PREFIX "kst_rmap"
40*c9615059SWei Yang #define MAX_FILENAME_LEN 256
41*c9615059SWei Yang const char *suffixes[] = {
42*c9615059SWei Yang 	"",
43*c9615059SWei Yang 	"_shm",
44*c9615059SWei Yang 	"_file",
45*c9615059SWei Yang };
46*c9615059SWei Yang 
47*c9615059SWei Yang struct global_data;
48*c9615059SWei Yang typedef int (*work_fn)(struct global_data *data);
49*c9615059SWei Yang typedef int (*check_fn)(struct global_data *data);
50*c9615059SWei Yang typedef void (*prepare_fn)(struct global_data *data);
51*c9615059SWei Yang 
52*c9615059SWei Yang struct global_data {
53*c9615059SWei Yang 	int worker_level;
54*c9615059SWei Yang 
55*c9615059SWei Yang 	int semid;
56*c9615059SWei Yang 	int pipefd[2];
57*c9615059SWei Yang 
58*c9615059SWei Yang 	unsigned int mapsize;
59*c9615059SWei Yang 	unsigned int rand_seed;
60*c9615059SWei Yang 	char *region;
61*c9615059SWei Yang 
62*c9615059SWei Yang 	prepare_fn do_prepare;
63*c9615059SWei Yang 	work_fn do_work;
64*c9615059SWei Yang 	check_fn do_check;
65*c9615059SWei Yang 
66*c9615059SWei Yang 	enum backend_type backend;
67*c9615059SWei Yang 	char filename[MAX_FILENAME_LEN];
68*c9615059SWei Yang 
69*c9615059SWei Yang 	unsigned long *expected_pfn;
70*c9615059SWei Yang };
71*c9615059SWei Yang 
72*c9615059SWei Yang /*
73*c9615059SWei Yang  * Create a process tree with TOTAL_LEVEL height and at most MAX_CHILDREN
74*c9615059SWei Yang  * children for each.
75*c9615059SWei Yang  *
76*c9615059SWei Yang  * It will randomly select one process as 'worker' process which will
77*c9615059SWei Yang  * 'do_work' until all processes are created. And all other processes will
78*c9615059SWei Yang  * wait until 'worker' finish its work.
79*c9615059SWei Yang  */
propagate_children(struct __test_metadata * _metadata,struct global_data * data)80*c9615059SWei Yang void propagate_children(struct __test_metadata *_metadata, struct global_data *data)
81*c9615059SWei Yang {
82*c9615059SWei Yang 	pid_t root_pid, pid;
83*c9615059SWei Yang 	unsigned int num_child;
84*c9615059SWei Yang 	int status;
85*c9615059SWei Yang 	int ret = 0;
86*c9615059SWei Yang 	int curr_child, worker_child;
87*c9615059SWei Yang 	int curr_level = 1;
88*c9615059SWei Yang 	bool is_worker = true;
89*c9615059SWei Yang 
90*c9615059SWei Yang 	root_pid = getpid();
91*c9615059SWei Yang repeat:
92*c9615059SWei Yang 	num_child = rand_r(&data->rand_seed) % MAX_CHILDREN + 1;
93*c9615059SWei Yang 	worker_child = is_worker ? rand_r(&data->rand_seed) % num_child : -1;
94*c9615059SWei Yang 
95*c9615059SWei Yang 	for (curr_child = 0; curr_child < num_child; curr_child++) {
96*c9615059SWei Yang 		pid = fork();
97*c9615059SWei Yang 
98*c9615059SWei Yang 		if (pid < 0) {
99*c9615059SWei Yang 			perror("Error: fork\n");
100*c9615059SWei Yang 		} else if (pid == 0) {
101*c9615059SWei Yang 			curr_level++;
102*c9615059SWei Yang 
103*c9615059SWei Yang 			if (curr_child != worker_child)
104*c9615059SWei Yang 				is_worker = false;
105*c9615059SWei Yang 
106*c9615059SWei Yang 			if (curr_level == TOTAL_LEVEL)
107*c9615059SWei Yang 				break;
108*c9615059SWei Yang 
109*c9615059SWei Yang 			data->rand_seed += curr_child;
110*c9615059SWei Yang 			goto repeat;
111*c9615059SWei Yang 		}
112*c9615059SWei Yang 	}
113*c9615059SWei Yang 
114*c9615059SWei Yang 	if (data->do_prepare)
115*c9615059SWei Yang 		data->do_prepare(data);
116*c9615059SWei Yang 
117*c9615059SWei Yang 	close(data->pipefd[1]);
118*c9615059SWei Yang 
119*c9615059SWei Yang 	if (is_worker && curr_level == data->worker_level) {
120*c9615059SWei Yang 		/* This is the worker process, first wait last process created */
121*c9615059SWei Yang 		char buf;
122*c9615059SWei Yang 
123*c9615059SWei Yang 		while (read(data->pipefd[0], &buf, 1) > 0)
124*c9615059SWei Yang 			;
125*c9615059SWei Yang 
126*c9615059SWei Yang 		if (data->do_work)
127*c9615059SWei Yang 			ret = data->do_work(data);
128*c9615059SWei Yang 
129*c9615059SWei Yang 		/* Kick others */
130*c9615059SWei Yang 		semctl(data->semid, 0, IPC_RMID);
131*c9615059SWei Yang 	} else {
132*c9615059SWei Yang 		/* Wait worker finish */
133*c9615059SWei Yang 		semop(data->semid, &sem_wait, 1);
134*c9615059SWei Yang 		if (data->do_check)
135*c9615059SWei Yang 			ret = data->do_check(data);
136*c9615059SWei Yang 	}
137*c9615059SWei Yang 
138*c9615059SWei Yang 	/* Wait all child to quit */
139*c9615059SWei Yang 	while (wait(&status) > 0) {
140*c9615059SWei Yang 		if (WIFEXITED(status))
141*c9615059SWei Yang 			ret |= WEXITSTATUS(status);
142*c9615059SWei Yang 	}
143*c9615059SWei Yang 
144*c9615059SWei Yang 	if (getpid() == root_pid) {
145*c9615059SWei Yang 		if (ret & FAIL_ON_WORK)
146*c9615059SWei Yang 			SKIP(return, "Failed in worker");
147*c9615059SWei Yang 
148*c9615059SWei Yang 		ASSERT_EQ(ret, 0);
149*c9615059SWei Yang 	} else {
150*c9615059SWei Yang 		exit(ret);
151*c9615059SWei Yang 	}
152*c9615059SWei Yang }
153*c9615059SWei Yang 
FIXTURE(migrate)154*c9615059SWei Yang FIXTURE(migrate)
155*c9615059SWei Yang {
156*c9615059SWei Yang 	struct global_data data;
157*c9615059SWei Yang };
158*c9615059SWei Yang 
FIXTURE_SETUP(migrate)159*c9615059SWei Yang FIXTURE_SETUP(migrate)
160*c9615059SWei Yang {
161*c9615059SWei Yang 	struct global_data *data = &self->data;
162*c9615059SWei Yang 
163*c9615059SWei Yang 	if (numa_available() < 0)
164*c9615059SWei Yang 		SKIP(return, "NUMA not available");
165*c9615059SWei Yang 	if (numa_bitmask_weight(numa_all_nodes_ptr) <= 1)
166*c9615059SWei Yang 		SKIP(return, "Not enough NUMA nodes available");
167*c9615059SWei Yang 
168*c9615059SWei Yang 	data->mapsize = getpagesize();
169*c9615059SWei Yang 
170*c9615059SWei Yang 	data->expected_pfn = mmap(0, sizeof(unsigned long),
171*c9615059SWei Yang 				PROT_READ | PROT_WRITE,
172*c9615059SWei Yang 				MAP_SHARED | MAP_ANONYMOUS, -1, 0);
173*c9615059SWei Yang 	ASSERT_NE(data->expected_pfn, MAP_FAILED);
174*c9615059SWei Yang 
175*c9615059SWei Yang 	/* Prepare semaphore */
176*c9615059SWei Yang 	data->semid = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
177*c9615059SWei Yang 	ASSERT_NE(data->semid, -1);
178*c9615059SWei Yang 	ASSERT_NE(semctl(data->semid, 0, SETVAL, 0), -1);
179*c9615059SWei Yang 
180*c9615059SWei Yang 	/* Prepare pipe */
181*c9615059SWei Yang 	ASSERT_NE(pipe(data->pipefd), -1);
182*c9615059SWei Yang 
183*c9615059SWei Yang 	data->rand_seed = time(NULL);
184*c9615059SWei Yang 	srand(data->rand_seed);
185*c9615059SWei Yang 
186*c9615059SWei Yang 	data->worker_level = rand() % TOTAL_LEVEL + 1;
187*c9615059SWei Yang 
188*c9615059SWei Yang 	data->do_prepare = NULL;
189*c9615059SWei Yang 	data->do_work = NULL;
190*c9615059SWei Yang 	data->do_check = NULL;
191*c9615059SWei Yang 
192*c9615059SWei Yang 	data->backend = ANON;
193*c9615059SWei Yang };
194*c9615059SWei Yang 
FIXTURE_TEARDOWN(migrate)195*c9615059SWei Yang FIXTURE_TEARDOWN(migrate)
196*c9615059SWei Yang {
197*c9615059SWei Yang 	struct global_data *data = &self->data;
198*c9615059SWei Yang 
199*c9615059SWei Yang 	if (data->region != MAP_FAILED)
200*c9615059SWei Yang 		munmap(data->region, data->mapsize);
201*c9615059SWei Yang 	data->region = MAP_FAILED;
202*c9615059SWei Yang 	if (data->expected_pfn != MAP_FAILED)
203*c9615059SWei Yang 		munmap(data->expected_pfn, sizeof(unsigned long));
204*c9615059SWei Yang 	data->expected_pfn = MAP_FAILED;
205*c9615059SWei Yang 	semctl(data->semid, 0, IPC_RMID);
206*c9615059SWei Yang 	data->semid = -1;
207*c9615059SWei Yang 
208*c9615059SWei Yang 	close(data->pipefd[0]);
209*c9615059SWei Yang 
210*c9615059SWei Yang 	switch (data->backend) {
211*c9615059SWei Yang 	case ANON:
212*c9615059SWei Yang 		break;
213*c9615059SWei Yang 	case SHM:
214*c9615059SWei Yang 		shm_unlink(data->filename);
215*c9615059SWei Yang 		break;
216*c9615059SWei Yang 	case NORM_FILE:
217*c9615059SWei Yang 		unlink(data->filename);
218*c9615059SWei Yang 		break;
219*c9615059SWei Yang 	}
220*c9615059SWei Yang }
221*c9615059SWei Yang 
access_region(struct global_data * data)222*c9615059SWei Yang void access_region(struct global_data *data)
223*c9615059SWei Yang {
224*c9615059SWei Yang 	/*
225*c9615059SWei Yang 	 * Force read "region" to make sure page fault in.
226*c9615059SWei Yang 	 */
227*c9615059SWei Yang 	FORCE_READ(*data->region);
228*c9615059SWei Yang }
229*c9615059SWei Yang 
try_to_move_page(char * region)230*c9615059SWei Yang int try_to_move_page(char *region)
231*c9615059SWei Yang {
232*c9615059SWei Yang 	int ret;
233*c9615059SWei Yang 	int node;
234*c9615059SWei Yang 	int status = 0;
235*c9615059SWei Yang 	int failures = 0;
236*c9615059SWei Yang 
237*c9615059SWei Yang 	ret = move_pages(0, 1, (void **)&region, NULL, &status, MPOL_MF_MOVE_ALL);
238*c9615059SWei Yang 	if (ret != 0) {
239*c9615059SWei Yang 		perror("Failed to get original numa");
240*c9615059SWei Yang 		return FAIL_ON_WORK;
241*c9615059SWei Yang 	}
242*c9615059SWei Yang 
243*c9615059SWei Yang 	/* Pick up a different target node */
244*c9615059SWei Yang 	for (node = 0; node <= numa_max_node(); node++) {
245*c9615059SWei Yang 		if (numa_bitmask_isbitset(numa_all_nodes_ptr, node) && node != status)
246*c9615059SWei Yang 			break;
247*c9615059SWei Yang 	}
248*c9615059SWei Yang 
249*c9615059SWei Yang 	if (node > numa_max_node()) {
250*c9615059SWei Yang 		ksft_print_msg("Couldn't find available numa node for testing\n");
251*c9615059SWei Yang 		return FAIL_ON_WORK;
252*c9615059SWei Yang 	}
253*c9615059SWei Yang 
254*c9615059SWei Yang 	while (1) {
255*c9615059SWei Yang 		ret = move_pages(0, 1, (void **)&region, &node, &status, MPOL_MF_MOVE_ALL);
256*c9615059SWei Yang 
257*c9615059SWei Yang 		/* migrate successfully */
258*c9615059SWei Yang 		if (!ret)
259*c9615059SWei Yang 			break;
260*c9615059SWei Yang 
261*c9615059SWei Yang 		/* error happened */
262*c9615059SWei Yang 		if (ret < 0) {
263*c9615059SWei Yang 			ksft_perror("Failed to move pages");
264*c9615059SWei Yang 			return FAIL_ON_WORK;
265*c9615059SWei Yang 		}
266*c9615059SWei Yang 
267*c9615059SWei Yang 		/* migration is best effort; try again */
268*c9615059SWei Yang 		if (++failures >= 100)
269*c9615059SWei Yang 			return FAIL_ON_WORK;
270*c9615059SWei Yang 	}
271*c9615059SWei Yang 
272*c9615059SWei Yang 	return 0;
273*c9615059SWei Yang }
274*c9615059SWei Yang 
move_region(struct global_data * data)275*c9615059SWei Yang int move_region(struct global_data *data)
276*c9615059SWei Yang {
277*c9615059SWei Yang 	int ret;
278*c9615059SWei Yang 	int pagemap_fd;
279*c9615059SWei Yang 
280*c9615059SWei Yang 	ret = try_to_move_page(data->region);
281*c9615059SWei Yang 	if (ret != 0)
282*c9615059SWei Yang 		return ret;
283*c9615059SWei Yang 
284*c9615059SWei Yang 	pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
285*c9615059SWei Yang 	if (pagemap_fd == -1)
286*c9615059SWei Yang 		return FAIL_ON_WORK;
287*c9615059SWei Yang 	*data->expected_pfn = pagemap_get_pfn(pagemap_fd, data->region);
288*c9615059SWei Yang 
289*c9615059SWei Yang 	return 0;
290*c9615059SWei Yang }
291*c9615059SWei Yang 
has_same_pfn(struct global_data * data)292*c9615059SWei Yang int has_same_pfn(struct global_data *data)
293*c9615059SWei Yang {
294*c9615059SWei Yang 	unsigned long pfn;
295*c9615059SWei Yang 	int pagemap_fd;
296*c9615059SWei Yang 
297*c9615059SWei Yang 	if (data->region == MAP_FAILED)
298*c9615059SWei Yang 		return 0;
299*c9615059SWei Yang 
300*c9615059SWei Yang 	pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
301*c9615059SWei Yang 	if (pagemap_fd == -1)
302*c9615059SWei Yang 		return FAIL_ON_CHECK;
303*c9615059SWei Yang 
304*c9615059SWei Yang 	pfn = pagemap_get_pfn(pagemap_fd, data->region);
305*c9615059SWei Yang 	if (pfn != *data->expected_pfn)
306*c9615059SWei Yang 		return FAIL_ON_CHECK;
307*c9615059SWei Yang 
308*c9615059SWei Yang 	return 0;
309*c9615059SWei Yang }
310*c9615059SWei Yang 
TEST_F(migrate,anon)311*c9615059SWei Yang TEST_F(migrate, anon)
312*c9615059SWei Yang {
313*c9615059SWei Yang 	struct global_data *data = &self->data;
314*c9615059SWei Yang 
315*c9615059SWei Yang 	/* Map an area and fault in */
316*c9615059SWei Yang 	data->region = mmap(0, data->mapsize, PROT_READ | PROT_WRITE,
317*c9615059SWei Yang 				MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
318*c9615059SWei Yang 	ASSERT_NE(data->region, MAP_FAILED);
319*c9615059SWei Yang 	memset(data->region, 0xcf, data->mapsize);
320*c9615059SWei Yang 
321*c9615059SWei Yang 	data->do_prepare = access_region;
322*c9615059SWei Yang 	data->do_work = move_region;
323*c9615059SWei Yang 	data->do_check = has_same_pfn;
324*c9615059SWei Yang 
325*c9615059SWei Yang 	propagate_children(_metadata, data);
326*c9615059SWei Yang }
327*c9615059SWei Yang 
TEST_F(migrate,shm)328*c9615059SWei Yang TEST_F(migrate, shm)
329*c9615059SWei Yang {
330*c9615059SWei Yang 	int shm_fd;
331*c9615059SWei Yang 	struct global_data *data = &self->data;
332*c9615059SWei Yang 
333*c9615059SWei Yang 	snprintf(data->filename, MAX_FILENAME_LEN, "%s%s", PREFIX, suffixes[SHM]);
334*c9615059SWei Yang 	shm_fd = shm_open(data->filename, O_CREAT | O_RDWR, 0666);
335*c9615059SWei Yang 	ASSERT_NE(shm_fd, -1);
336*c9615059SWei Yang 	ftruncate(shm_fd, data->mapsize);
337*c9615059SWei Yang 	data->backend = SHM;
338*c9615059SWei Yang 
339*c9615059SWei Yang 	/* Map a shared area and fault in */
340*c9615059SWei Yang 	data->region = mmap(0, data->mapsize, PROT_READ | PROT_WRITE,
341*c9615059SWei Yang 				MAP_SHARED, shm_fd, 0);
342*c9615059SWei Yang 	ASSERT_NE(data->region, MAP_FAILED);
343*c9615059SWei Yang 	memset(data->region, 0xcf, data->mapsize);
344*c9615059SWei Yang 	close(shm_fd);
345*c9615059SWei Yang 
346*c9615059SWei Yang 	data->do_prepare = access_region;
347*c9615059SWei Yang 	data->do_work = move_region;
348*c9615059SWei Yang 	data->do_check = has_same_pfn;
349*c9615059SWei Yang 
350*c9615059SWei Yang 	propagate_children(_metadata, data);
351*c9615059SWei Yang }
352*c9615059SWei Yang 
TEST_F(migrate,file)353*c9615059SWei Yang TEST_F(migrate, file)
354*c9615059SWei Yang {
355*c9615059SWei Yang 	int fd;
356*c9615059SWei Yang 	struct global_data *data = &self->data;
357*c9615059SWei Yang 
358*c9615059SWei Yang 	snprintf(data->filename, MAX_FILENAME_LEN, "%s%s", PREFIX, suffixes[NORM_FILE]);
359*c9615059SWei Yang 	fd = open(data->filename, O_CREAT | O_RDWR | O_EXCL, 0666);
360*c9615059SWei Yang 	ASSERT_NE(fd, -1);
361*c9615059SWei Yang 	ftruncate(fd, data->mapsize);
362*c9615059SWei Yang 	data->backend = NORM_FILE;
363*c9615059SWei Yang 
364*c9615059SWei Yang 	/* Map a shared area and fault in */
365*c9615059SWei Yang 	data->region = mmap(0, data->mapsize, PROT_READ | PROT_WRITE,
366*c9615059SWei Yang 				MAP_SHARED, fd, 0);
367*c9615059SWei Yang 	ASSERT_NE(data->region, MAP_FAILED);
368*c9615059SWei Yang 	memset(data->region, 0xcf, data->mapsize);
369*c9615059SWei Yang 	close(fd);
370*c9615059SWei Yang 
371*c9615059SWei Yang 	data->do_prepare = access_region;
372*c9615059SWei Yang 	data->do_work = move_region;
373*c9615059SWei Yang 	data->do_check = has_same_pfn;
374*c9615059SWei Yang 
375*c9615059SWei Yang 	propagate_children(_metadata, data);
376*c9615059SWei Yang }
377*c9615059SWei Yang 
prepare_local_region(struct global_data * data)378*c9615059SWei Yang void prepare_local_region(struct global_data *data)
379*c9615059SWei Yang {
380*c9615059SWei Yang 	/* Allocate range and set the same data */
381*c9615059SWei Yang 	data->region = mmap(NULL, data->mapsize, PROT_READ|PROT_WRITE,
382*c9615059SWei Yang 			   MAP_PRIVATE|MAP_ANON, -1, 0);
383*c9615059SWei Yang 	if (data->region == MAP_FAILED)
384*c9615059SWei Yang 		return;
385*c9615059SWei Yang 
386*c9615059SWei Yang 	memset(data->region, 0xcf, data->mapsize);
387*c9615059SWei Yang }
388*c9615059SWei Yang 
merge_and_migrate(struct global_data * data)389*c9615059SWei Yang int merge_and_migrate(struct global_data *data)
390*c9615059SWei Yang {
391*c9615059SWei Yang 	int pagemap_fd;
392*c9615059SWei Yang 	int ret = 0;
393*c9615059SWei Yang 
394*c9615059SWei Yang 	if (data->region == MAP_FAILED)
395*c9615059SWei Yang 		return FAIL_ON_WORK;
396*c9615059SWei Yang 
397*c9615059SWei Yang 	if (ksm_start() < 0)
398*c9615059SWei Yang 		return FAIL_ON_WORK;
399*c9615059SWei Yang 
400*c9615059SWei Yang 	ret = try_to_move_page(data->region);
401*c9615059SWei Yang 
402*c9615059SWei Yang 	pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
403*c9615059SWei Yang 	if (pagemap_fd == -1)
404*c9615059SWei Yang 		return FAIL_ON_WORK;
405*c9615059SWei Yang 	*data->expected_pfn = pagemap_get_pfn(pagemap_fd, data->region);
406*c9615059SWei Yang 
407*c9615059SWei Yang 	return ret;
408*c9615059SWei Yang }
409*c9615059SWei Yang 
TEST_F(migrate,ksm)410*c9615059SWei Yang TEST_F(migrate, ksm)
411*c9615059SWei Yang {
412*c9615059SWei Yang 	int ret;
413*c9615059SWei Yang 	struct global_data *data = &self->data;
414*c9615059SWei Yang 
415*c9615059SWei Yang 	if (ksm_stop() < 0)
416*c9615059SWei Yang 		SKIP(return, "accessing \"/sys/kernel/mm/ksm/run\") failed");
417*c9615059SWei Yang 	if (ksm_get_full_scans() < 0)
418*c9615059SWei Yang 		SKIP(return, "accessing \"/sys/kernel/mm/ksm/full_scan\") failed");
419*c9615059SWei Yang 
420*c9615059SWei Yang 	ret = prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0);
421*c9615059SWei Yang 	if (ret < 0 && errno == EINVAL)
422*c9615059SWei Yang 		SKIP(return, "PR_SET_MEMORY_MERGE not supported");
423*c9615059SWei Yang 	else if (ret)
424*c9615059SWei Yang 		ksft_exit_fail_perror("PR_SET_MEMORY_MERGE=1 failed");
425*c9615059SWei Yang 
426*c9615059SWei Yang 	data->do_prepare = prepare_local_region;
427*c9615059SWei Yang 	data->do_work = merge_and_migrate;
428*c9615059SWei Yang 	data->do_check = has_same_pfn;
429*c9615059SWei Yang 
430*c9615059SWei Yang 	propagate_children(_metadata, data);
431*c9615059SWei Yang }
432*c9615059SWei Yang 
433*c9615059SWei Yang TEST_HARNESS_MAIN
434