1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2017 FUJITSU LIMITED. All rights reserved.
4 * Author(s): Xiao Yang <yangx.jy@cn.fujitsu.com>
5 *            Jie Fei <feij.fnst@cn.fujitsu.com>
6 */
7
8/*
9 * Description:
10 * This is a regression test for ksm page migration which is miscalculated.
11 *
12 * The kernel bug has been fixed by:
13 *
14 * commit 4b0ece6fa0167b22c004ff69e137dc94ee2e469e
15 * Author: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
16 * Date:   Fri Mar 31 15:11:44 2017 -0700
17 *
18 *     mm: migrate: fix remove_migration_pte() for ksm pages
19 */
20
21#include <errno.h>
22#include <unistd.h>
23#include <stdlib.h>
24#include <pwd.h>
25
26#include "tst_test.h"
27#include "lapi/syscalls.h"
28#include "lapi/mmap.h"
29#include "ksm_helper.h"
30#include "numa_helper.h"
31#include "migrate_pages_common.h"
32
33#ifdef HAVE_NUMA_V2
34#define N_PAGES 20
35#define N_LOOPS 600
36#define TEST_NODES 2
37
38static int orig_ksm_run = -1;
39static unsigned int page_size;
40static void *test_pages[N_PAGES];
41static int num_nodes, max_node;
42static int *nodes;
43static unsigned long *new_nodes[2];
44static const char nobody_uid[] = "nobody";
45static struct passwd *ltpuser;
46
47static void setup(void)
48{
49	int n;
50	unsigned long nodemask_size;
51
52	if (access(PATH_KSM, F_OK))
53		tst_brk(TCONF, "KSM configuration was not enabled");
54
55	if (get_allowed_nodes_arr(NH_MEMS, &num_nodes, &nodes) < 0)
56		tst_brk(TBROK | TERRNO, "get_allowed_nodes() failed");
57
58	if (num_nodes < TEST_NODES) {
59		tst_brk(TCONF, "requires NUMA with at least %d node",
60			TEST_NODES);
61	}
62
63	ltpuser = SAFE_GETPWNAM(nobody_uid);
64
65	max_node = LTP_ALIGN(get_max_node(), sizeof(unsigned long) * 8);
66	nodemask_size = max_node / 8;
67	new_nodes[0] = SAFE_MALLOC(nodemask_size);
68	new_nodes[1] = SAFE_MALLOC(nodemask_size);
69	memset(new_nodes[0], 0, nodemask_size);
70	memset(new_nodes[1], 0, nodemask_size);
71	set_bit(new_nodes[0], nodes[0], 1);
72	set_bit(new_nodes[1], nodes[1], 1);
73
74	page_size = getpagesize();
75
76	for (n = 0; n < N_PAGES; n++) {
77		test_pages[n] = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE | PROT_EXEC,
78					  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
79		if (madvise(test_pages[n], page_size, MADV_MERGEABLE)) {
80			if (errno == EINVAL) {
81				tst_brk(TCONF | TERRNO, "madvise() didn't "
82					"support MADV_MERGEABLE");
83			}
84
85			tst_brk(TBROK | TERRNO,
86				"madvise(MADV_MERGEABLE) failed");
87		}
88
89		if (mbind(test_pages[n], page_size, MPOL_BIND, new_nodes[0],
90			  max_node, 0))
91			tst_brk(TBROK | TERRNO, "mbind(MPOL_BIND) failed");
92
93		memset(test_pages[n], 0, page_size);
94	}
95
96	SAFE_FILE_SCANF(PATH_KSM "run", "%d", &orig_ksm_run);
97	SAFE_FILE_PRINTF(PATH_KSM "run", "%d", 1);
98	wait_ksmd_full_scan();
99}
100
101static void cleanup(void)
102{
103	int n;
104
105	for (n = 0; n < N_PAGES; n++) {
106		if (test_pages[n])
107			SAFE_MUNMAP(test_pages[n], page_size);
108	}
109
110	free(new_nodes[0]);
111	free(new_nodes[1]);
112
113	if (orig_ksm_run != -1)
114		SAFE_FILE_PRINTF(PATH_KSM "run", "%d", orig_ksm_run);
115}
116
117static void migrate_test(void)
118{
119	int loop, i, ret;
120
121	SAFE_SETEUID(ltpuser->pw_uid);
122	for (loop = 0; loop < N_LOOPS; loop++) {
123		i = loop % 2;
124		ret = tst_syscall(__NR_migrate_pages, 0, max_node,
125				   new_nodes[i], new_nodes[i ? 0 : 1]);
126		if (ret < 0) {
127			tst_res(TFAIL | TERRNO, "migrate_pages() failed");
128			return;
129		}
130
131		if (!tst_remaining_runtime()) {
132			tst_res(TINFO, "Out of runtime, exiting...");
133			break;
134		}
135	}
136	SAFE_SETEUID(0);
137
138	tst_res(TPASS, "migrate_pages() passed");
139}
140
141static struct tst_test test = {
142	.max_runtime = 300,
143	.needs_root = 1,
144	.setup = setup,
145	.cleanup = cleanup,
146	.test_all = migrate_test,
147	.tags = (const struct tst_tag[]) {
148		{"linux-git", "4b0ece6fa016"},
149		{}
150	}
151};
152
153#else
154	TST_TEST_TCONF("require libnuma >= 2 and it's development packages");
155#endif
156