1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) Crackerjack Project., 2007-2008, Hitachi, Ltd
4 * Copyright (c) 2017 Petr Vorel <pvorel@suse.cz>
5 *
6 * Authors:
7 * Takahiro Yasui <takahiro.yasui.mp@hitachi.com>,
8 * Yumiko Sugita <yumiko.sugita.yf@hitachi.com>,
9 * Satoshi Fujiwara <sa-fuji@sdl.hitachi.co.jp>
10 */
11
12#include <errno.h>
13#if HAVE_NUMA_H
14#include <numa.h>
15#endif
16
17#include "config.h"
18#include "numa_helper.h"
19#include "tst_test.h"
20#include "tst_numa.h"
21#include "lapi/numaif.h"
22
23#ifdef HAVE_NUMA_V2
24
25#define MEM_LENGTH (4 * 1024 * 1024)
26
27#define UNKNOWN_POLICY -1
28
29#define POLICY_DESC(x) .policy = x, .desc = #x
30#define POLICY_DESC_TEXT(x, y) .policy = x, .desc = #x" ("y")"
31
32static struct bitmask *nodemask, *getnodemask, *empty_nodemask;
33
34static void test_default(unsigned int i, char *p);
35static void test_none(unsigned int i, char *p);
36static void test_invalid_nodemask(unsigned int i, char *p);
37static void check_policy_pref_or_local(int);
38
39struct test_case {
40	int policy;
41	const char *desc;
42	unsigned flags;
43	int ret;
44	int err;
45	void (*check_policy)(int);
46	void (*test)(unsigned int, char *);
47	struct bitmask **exp_nodemask;
48};
49
50static struct test_case tcase[] = {
51	{
52		POLICY_DESC(MPOL_DEFAULT),
53		.ret = 0,
54		.err = 0,
55		.test = test_none,
56		.exp_nodemask = &empty_nodemask,
57	},
58	{
59		POLICY_DESC_TEXT(MPOL_DEFAULT, "target exists"),
60		.ret = -1,
61		.err = EINVAL,
62		.test = test_default,
63	},
64	{
65		POLICY_DESC_TEXT(MPOL_BIND, "no target"),
66		.ret = -1,
67		.err = EINVAL,
68		.test = test_none,
69	},
70	{
71		POLICY_DESC(MPOL_BIND),
72		.ret = 0,
73		.err = 0,
74		.test = test_default,
75		.exp_nodemask = &nodemask,
76	},
77	{
78		POLICY_DESC_TEXT(MPOL_INTERLEAVE, "no target"),
79		.ret = -1,
80		.err = EINVAL,
81		.test = test_none,
82	},
83	{
84		POLICY_DESC(MPOL_INTERLEAVE),
85		.ret = 0,
86		.err = 0,
87		.test = test_default,
88		.exp_nodemask = &nodemask,
89	},
90	{
91		POLICY_DESC_TEXT(MPOL_PREFERRED, "no target"),
92		.ret = 0,
93		.err = 0,
94		.test = test_none,
95		.check_policy = check_policy_pref_or_local,
96	},
97	{
98		POLICY_DESC(MPOL_PREFERRED),
99		.ret = 0,
100		.err = 0,
101		.test = test_default,
102		.exp_nodemask = &nodemask,
103	},
104	{
105		POLICY_DESC(MPOL_LOCAL),
106		.ret = 0,
107		.err = 0,
108		.test = test_none,
109		.exp_nodemask = &empty_nodemask,
110		.check_policy = check_policy_pref_or_local,
111	},
112	{
113		POLICY_DESC_TEXT(MPOL_LOCAL, "target exists"),
114		.ret = -1,
115		.err = EINVAL,
116		.test = test_default,
117	},
118	{
119		POLICY_DESC(UNKNOWN_POLICY),
120		.ret = -1,
121		.err = EINVAL,
122		.test = test_none,
123	},
124	{
125		POLICY_DESC_TEXT(MPOL_DEFAULT, "invalid flags"),
126		.flags = -1,
127		.ret = -1,
128		.err = EINVAL,
129		.test = test_none,
130	},
131	{
132		POLICY_DESC_TEXT(MPOL_PREFERRED, "invalid nodemask"),
133		.ret = -1,
134		.err = EFAULT,
135		.test = test_invalid_nodemask,
136	},
137};
138
139static void check_policy_pref_or_local(int policy)
140{
141	if (policy != MPOL_PREFERRED && policy != MPOL_LOCAL) {
142		tst_res(TFAIL, "Wrong policy: %s(%d), "
143			"expected MPOL_PREFERRED or MPOL_LOCAL",
144			tst_mempolicy_mode_name(policy), policy);
145	}
146}
147
148static void test_default(unsigned int i, char *p)
149{
150	struct test_case *tc = &tcase[i];
151
152	TEST(mbind(p, MEM_LENGTH, tc->policy, nodemask->maskp,
153		   nodemask->size, tc->flags));
154}
155
156static void test_none(unsigned int i, char *p)
157{
158	struct test_case *tc = &tcase[i];
159
160	TEST(mbind(p, MEM_LENGTH, tc->policy, NULL, 0, tc->flags));
161}
162
163static void test_invalid_nodemask(unsigned int i, char *p)
164{
165	struct test_case *tc = &tcase[i];
166
167	/* use invalid nodemask (64 MiB after heap) */
168	TEST(mbind(p, MEM_LENGTH, tc->policy, sbrk(0) + 64*1024*1024,
169		   NUMA_NUM_NODES, tc->flags));
170}
171
172static void setup(void)
173{
174	if (!is_numa(NULL, NH_MEMS, 1))
175		tst_brk(TCONF, "requires NUMA with at least 1 node");
176	empty_nodemask = numa_allocate_nodemask();
177}
178
179static void setup_node(void)
180{
181	int test_node = -1;
182
183	if (get_allowed_nodes(NH_MEMS, 1, &test_node) < 0)
184		tst_brk(TBROK | TERRNO, "get_allowed_nodes failed");
185
186	nodemask = numa_allocate_nodemask();
187	getnodemask = numa_allocate_nodemask();
188	numa_bitmask_setbit(nodemask, test_node);
189}
190
191static void do_test(unsigned int i)
192{
193	struct test_case *tc = &tcase[i];
194	int policy, fail = 0;
195	char *p = NULL;
196
197	tst_res(TINFO, "case %s", tc->desc);
198
199	if (tc->policy == MPOL_LOCAL) {
200		if ((tst_kvercmp(5, 14, 0)) >= 0)
201			tc->check_policy = NULL;
202	}
203
204	setup_node();
205
206	p = SAFE_MMAP(NULL, MEM_LENGTH, PROT_READ | PROT_WRITE, MAP_PRIVATE |
207			 MAP_ANONYMOUS, 0, 0);
208
209	tc->test(i, p);
210
211	if (TST_RET >= 0) {
212		/* Check policy of the allocated memory */
213		TEST(get_mempolicy(&policy, getnodemask->maskp,
214				   getnodemask->size, p, MPOL_F_ADDR));
215		if (TST_RET < 0) {
216			tst_res(TFAIL | TTERRNO, "get_mempolicy failed");
217			return;
218		}
219
220		if (tc->check_policy)
221			tc->check_policy(policy);
222		else if (tc->policy != policy) {
223			tst_res(TFAIL, "Wrong policy: %s(%d), expected: %s(%d)",
224				tst_mempolicy_mode_name(policy), policy,
225				tst_mempolicy_mode_name(tc->policy), tc->policy);
226			fail = 1;
227		}
228		if (tc->exp_nodemask) {
229			struct bitmask *exp_mask = *(tc->exp_nodemask);
230
231			if (!numa_bitmask_equal(exp_mask, getnodemask)) {
232				tst_res(TFAIL, "masks are not equal");
233				tst_res_hexd(TINFO, exp_mask->maskp,
234					exp_mask->size / 8, "exp_mask: ");
235				tst_res_hexd(TINFO, getnodemask->maskp,
236					getnodemask->size / 8, "returned: ");
237				fail = 1;
238			}
239		}
240	}
241
242	if (TST_RET != tc->ret) {
243		tst_res(TFAIL, "wrong return code: %ld, expected: %d",
244			TST_RET, tc->ret);
245		fail = 1;
246	}
247	if (TST_RET == -1 && TST_ERR != tc->err) {
248		tst_res(TFAIL | TTERRNO, "expected errno: %s, got",
249			tst_strerrno(tc->err));
250		fail = 1;
251	}
252	if (!fail)
253		tst_res(TPASS, "Test passed");
254}
255
256static struct tst_test test = {
257	.tcnt = ARRAY_SIZE(tcase),
258	.test = do_test,
259	.setup = setup,
260};
261
262#else
263	TST_TEST_TCONF(NUMA_ERROR_MSG);
264#endif
265