1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2018 Linaro Limited. All rights reserved.
4 * Author: Rafael David Tinoco <rafael.tinoco@linaro.org>
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
28struct 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
43struct 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
274static int sys_membarrier(int cmd, int flags)
275{
276	return tst_syscall(__NR_membarrier, cmd, flags);
277}
278
279static 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
362static 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
380static 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
406static 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