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 <[email protected]>
5 *
6 * Authors:
7 * Takahiro Yasui <[email protected]>,
8 * Yumiko Sugita <[email protected]>,
9 * Satoshi Fujiwara <[email protected]>
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
32 static struct bitmask *nodemask, *getnodemask, *empty_nodemask;
33
34 static void test_default(unsigned int i, char *p);
35 static void test_none(unsigned int i, char *p);
36 static void test_invalid_nodemask(unsigned int i, char *p);
37 static void check_policy_pref_or_local(int);
38
39 struct 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
50 static 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
check_policy_pref_or_local(int policy)139 static 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
test_default(unsigned int i,char * p)148 static 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
test_none(unsigned int i,char * p)156 static 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
test_invalid_nodemask(unsigned int i,char * p)163 static 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
setup(void)172 static 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
setup_node(void)179 static 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
do_test(unsigned int i)191 static 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
256 static 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