1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2018 Linaro Limited. All rights reserved.
4 * Author: Rafael David Tinoco <[email protected]>
5 */
6 /*
7 * Basic tests for membarrier(2) syscall. Tests below are responsible for
8 * testing the membarrier(2) interface only, without checking if the barrier
9 * was successful or not. Check test_case structure for each test description.
10 */
11
12 #include "config.h"
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/wait.h>
16 #include <syscall.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <signal.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "tst_test.h"
25 #include "lapi/syscalls.h"
26 #include "lapi/membarrier.h"
27
28 struct test_case {
29 char testname[80];
30 int command; /* membarrier cmd */
31 int needregister; /* membarrier cmd needs register cmd */
32 int flags; /* flags for given membarrier cmd */
33 long exp_ret; /* expected return code for given cmd */
34 int exp_errno; /* expected errno for given cmd failure */
35 int enabled; /* enabled, despite results from CMD_QUERY */
36 int always; /* CMD_QUERY should always enable this test */
37 int force; /* force if CMD_QUERY reports not enabled */
38 int force_exp_errno; /* expected errno after forced cmd */
39 int change_exp_errno; /* previous kernels forced errno result */
40 int change_kernver[3]; /* kernel version having diff expected errno */
41 };
42
43 struct test_case tc[] = {
44 {
45 /*
46 * case 00) invalid cmd
47 * - enabled by default
48 * - should always fail with EINVAL
49 */
50 .testname = "cmd_fail",
51 .command = -1,
52 .exp_ret = -1,
53 .exp_errno = EINVAL,
54 .enabled = 1,
55 },
56 {
57 /*
58 * case 01) invalid flags
59 * - enabled by default
60 * - should always fail with EINVAL
61 */
62 .testname = "cmd_flags_fail",
63 .command = MEMBARRIER_CMD_QUERY,
64 .flags = 1,
65 .exp_ret = -1,
66 .exp_errno = EINVAL,
67 .enabled = 1,
68 },
69 {
70 /*
71 * case 02) global barrier
72 * - should ALWAYS be enabled by CMD_QUERY
73 * - should always succeed
74 */
75 .testname = "cmd_global_success",
76 .command = MEMBARRIER_CMD_GLOBAL,
77 .flags = 0,
78 .exp_ret = 0,
79 .always = 1,
80 },
81 /*
82 * commit 22e4ebb975 (v4.14-rc1) added cases 03, 04 and 05 features:
83 */
84 {
85 /*
86 * case 03) private expedited barrier with no registrations
87 * - should fail with errno=EPERM due to no registrations
88 * - or be skipped if unsupported by running kernel
89 */
90 .testname = "cmd_private_expedited_fail",
91 .command = MEMBARRIER_CMD_PRIVATE_EXPEDITED,
92 .flags = 0,
93 .exp_ret = -1,
94 .exp_errno = EPERM,
95 },
96 {
97 /*
98 * case 04) register private expedited
99 * - should succeed when supported by running kernel
100 * - or fail with errno=EINVAL if unsupported and forced
101 */
102 .testname = "cmd_private_expedited_register_success",
103 .command = MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED,
104 .flags = 0,
105 .exp_ret = 0,
106 .force = 1,
107 .force_exp_errno = EINVAL,
108 },
109 {
110 /*
111 * case 05) private expedited barrier with registration
112 * - should succeed due to existing registration
113 * - or fail with errno=EINVAL if unsupported and forced
114 * - NOTE: commit 70216e18e5 (v4.16-rc1) changed behavior:
115 * - (a) if unsupported, and forced, < 4.16 , errno is EINVAL
116 * - (b) if unsupported, and forced, >= 4.16, errno is EPERM
117 */
118 .testname = "cmd_private_expedited_success",
119 .needregister = MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED,
120 .command = MEMBARRIER_CMD_PRIVATE_EXPEDITED,
121 .flags = 0,
122 .exp_ret = 0,
123 .force = 1,
124 .force_exp_errno = EPERM,
125 .change_exp_errno = EINVAL,
126 .change_kernver = { 4, 16, 0 },
127 },
128 /*
129 * commit 70216e18e5 (v4.16-rc1) added cases 06, 07 and 08 features:
130 */
131 {
132 /*
133 * case 06) private expedited sync core barrier with no registrations
134 * - should fail with errno=EPERM due to no registrations
135 * - or be skipped if unsupported by running kernel
136 */
137 .testname = "cmd_private_expedited_sync_core_fail",
138 .command = MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE,
139 .flags = 0,
140 .exp_ret = -1,
141 .exp_errno = EPERM,
142 },
143 {
144 /*
145 * case 07) register private expedited sync core
146 * - should succeed when supported by running kernel
147 * - or fail with errno=EINVAL if unsupported and forced
148 */
149 .testname = "cmd_private_expedited_sync_core_register_success",
150 .command = MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE,
151 .flags = 0,
152 .exp_ret = 0,
153 .force = 1,
154 .force_exp_errno = EINVAL,
155 },
156 {
157 /*
158 * case 08) private expedited sync core barrier with registration
159 * - should succeed due to existing registration
160 * - or fail with errno=EINVAL if unsupported and forced
161 */
162 .testname = "cmd_private_expedited_sync_core_success",
163 .needregister = MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE,
164 .command = MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE,
165 .flags = 0,
166 .exp_ret = 0,
167 .force = 1,
168 .force_exp_errno = EINVAL,
169 },
170 /*
171 * commit c5f58bd58f4 (v4.16-rc1) added cases 09, 10 and 11 features:
172 */
173 {
174 /*
175 * case 09) global expedited barrier with no registrations
176 * - should never fail due to no registrations
177 * - or be skipped if unsupported by running kernel
178 */
179 .testname = "cmd_global_expedited_success",
180 .command = MEMBARRIER_CMD_GLOBAL_EXPEDITED,
181 .flags = 0,
182 .exp_ret = 0,
183 },
184 {
185 /*
186 * case 10) register global expedited
187 * - should succeed when supported by running kernel
188 * - or fail with errno=EINVAL if unsupported and forced
189 */
190 .testname = "cmd_global_expedited_register_success",
191 .command = MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED,
192 .flags = 0,
193 .exp_ret = 0,
194 .force = 1,
195 .force_exp_errno = EINVAL,
196 },
197 {
198 /*
199 * case 11) global expedited barrier with registration
200 * - should also succeed with registrations
201 * - or fail with errno=EINVAL if unsupported and forced
202 */
203 .testname = "cmd_global_expedited_success",
204 .needregister = MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED,
205 .command = MEMBARRIER_CMD_GLOBAL_EXPEDITED,
206 .flags = 0,
207 .exp_ret = 0,
208 .force = 1,
209 .force_exp_errno = EINVAL,
210 },
211 };
212
213 #define passed_ok(_test) \
214 do { \
215 tst_res(TPASS, "membarrier(2): %s passed", _test.testname); \
216 return; \
217 } while (0)
218
219 #define passed_unexpec(_test) \
220 do { \
221 tst_res(TFAIL, "membarrier(2): %s passed unexpectedly. " \
222 "ret = %ld with errno %d were expected. (force: %d)", \
223 _test.testname, _test.exp_ret, _test.exp_errno, \
224 _test.force); \
225 return; \
226 } while (0)
227
228 #define failed_ok(_test) \
229 do { \
230 tst_res(TPASS, "membarrier(2): %s failed as expected", \
231 _test.testname); \
232 return; \
233 } while (0)
234
235 #define failed_ok_unsupported(_test) \
236 do { \
237 tst_res(TPASS, "membarrier(2): %s failed as expected " \
238 "(unsupported)", _test.testname); \
239 return; \
240 } while (0)
241
242 #define failed_not_ok(_test, _gotret, _goterr) \
243 do { \
244 tst_res(TFAIL, "membarrier(2): %s failed. " \
245 "ret = %ld when expected was %ld. " \
246 "errno = %d when expected was %d. (force: %d)", \
247 _test.testname, _gotret, _test.exp_ret, _goterr, \
248 _test.exp_errno, _test.force); \
249 return; \
250 } while (0)
251
252 #define failed_unexpec(_test, _gotret, _goterr) \
253 do { \
254 tst_res(TFAIL, "membarrier(2): %s failed unexpectedly. " \
255 "Got ret = %ld with errno %d. (force: %d)", \
256 _test.testname, _gotret, _goterr, _test.force); \
257 return; \
258 } while (0)
259
260 #define skipped(_test) \
261 do { \
262 tst_res(TPASS, "membarrier(2): %s skipped (unsupported)", \
263 _test.testname); \
264 return; \
265 } while (0)
266
267 #define skipped_fail(_test) \
268 do { \
269 tst_res(TFAIL, "membarrier(2): %s reported as not supported", \
270 _test.testname); \
271 return; \
272 } while (0)
273
sys_membarrier(int cmd,int flags)274 static int sys_membarrier(int cmd, int flags)
275 {
276 return tst_syscall(__NR_membarrier, cmd, flags);
277 }
278
verify_membarrier(unsigned int i)279 static void verify_membarrier(unsigned int i)
280 {
281 int ret;
282
283 /* not enabled and not enforced: test is skipped */
284
285 if (!tc[i].enabled && !tc[i].force) {
286
287 if (tc[i].always == 0)
288 skipped(tc[i]);
289
290 skipped_fail(tc[i]);
291 }
292
293 /* iterations: registration needed for some cases */
294
295 if (tc[i].needregister && tc[i].enabled) {
296 ret = sys_membarrier(tc[i].needregister, 0);
297 if (ret < 0) {
298 tst_brk(TBROK, "membarrier(2): %s could not register",
299 tc[i].testname);
300 }
301 }
302
303 TEST(sys_membarrier(tc[i].command, tc[i].flags));
304
305 /* enabled and not enforced: regular expected results only */
306
307 if (tc[i].enabled && !tc[i].force) {
308
309 if (TST_RET >= 0 && tc[i].exp_ret == TST_RET)
310 passed_ok(tc[i]);
311
312 if (TST_RET < 0) {
313 if (tc[i].exp_ret == TST_RET)
314 failed_ok(tc[i]);
315 else
316 failed_not_ok(tc[i], TST_RET, TST_ERR);
317 }
318 }
319
320 /* not enabled and enforced: failure and expected errors */
321
322 if (!tc[i].enabled && tc[i].force) {
323
324 if (TST_RET >= 0)
325 passed_unexpec(tc[i]);
326
327 if (TST_RET < 0) {
328 if (tc[i].force_exp_errno == TST_ERR)
329 failed_ok_unsupported(tc[i]);
330 else
331 failed_unexpec(tc[i], TST_RET, TST_ERR);
332 }
333 }
334
335 /* enabled and enforced: tricky */
336
337 if (tc[i].enabled && tc[i].force) {
338
339 if (TST_RET >= 0) {
340 if (tc[i].exp_ret == TST_RET)
341 passed_ok(tc[i]);
342 else
343 passed_unexpec(tc[i]);
344 }
345
346 if (TST_RET < 0) {
347
348 if (tc[i].exp_ret == TST_RET) {
349
350 if (tc[i].exp_errno == TST_ERR)
351 failed_ok(tc[i]);
352 else
353 failed_unexpec(tc[i], TST_RET, TST_ERR);
354 }
355
356 /* unknown on force failure if enabled and forced */
357 failed_unexpec(tc[i], TST_RET, TST_ERR);
358 }
359 }
360 }
361
wrap_verify_membarrier(unsigned int i)362 static void wrap_verify_membarrier(unsigned int i)
363 {
364 pid_t pid;
365
366 /*
367 * The Linux kernel does not provide a way to unregister the process
368 * (mm->membarrier_state) intent of being affected by the membarrier(2)
369 * system call, thus the need of having a wrapper to fork() a child.
370 */
371
372 pid = SAFE_FORK();
373
374 if (pid)
375 SAFE_WAITPID(pid, NULL, 0);
376 else
377 verify_membarrier(i);
378 }
379
setup(void)380 static void setup(void)
381 {
382 size_t i;
383 int ret;
384
385 ret = sys_membarrier(MEMBARRIER_CMD_QUERY, 0);
386 if (ret < 0) {
387 if (errno == ENOSYS)
388 tst_brk(TBROK, "membarrier(2): not supported");
389 }
390
391 for (i = 0; i < ARRAY_SIZE(tc); i++) {
392 if ((tc[i].command > 0) && (ret & tc[i].command))
393 tc[i].enabled = 1;
394
395 /* forcing unsupported command might have different errno */
396
397 if (tc[i].change_exp_errno && tc[i].enabled == 0) {
398 if (tst_kvercmp(tc[i].change_kernver[0],
399 tc[i].change_kernver[1],
400 tc[i].change_kernver[2]) < 0)
401 tc[i].force_exp_errno = tc[i].change_exp_errno;
402 }
403 }
404 }
405
406 static struct tst_test test = {
407 .setup = setup,
408 .test = wrap_verify_membarrier,
409 .tcnt = ARRAY_SIZE(tc),
410 .min_kver = "4.3.0", /* commit: 5b25b13ab0 (sys_membarrier(): ...) */
411 .forks_child = 1,
412 };
413