413 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			413 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * Copyright (c) 2018 Linaro Limited. All rights reserved.
 | |
|  * Author: Rafael David Tinoco <rafael.tinoco@linaro.org>
 | |
|  */
 | |
| /*
 | |
|  * Basic tests for membarrier(2) syscall. Tests below are responsible for
 | |
|  * testing the membarrier(2) interface only, without checking if the barrier
 | |
|  * was successful or not. Check test_case structure for each test description.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/wait.h>
 | |
| #include <syscall.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <unistd.h>
 | |
| #include <signal.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include "tst_test.h"
 | |
| #include "lapi/syscalls.h"
 | |
| #include "lapi/membarrier.h"
 | |
| 
 | |
| struct test_case {
 | |
| 	char testname[80];
 | |
| 	int command;		/* membarrier cmd                            */
 | |
| 	int needregister;	/* membarrier cmd needs register cmd	     */
 | |
| 	int flags;		/* flags for given membarrier cmd	     */
 | |
| 	long exp_ret;		/* expected return code for given cmd        */
 | |
| 	int exp_errno;		/* expected errno for given cmd failure      */
 | |
| 	int enabled;		/* enabled, despite results from CMD_QUERY   */
 | |
| 	int always;		/* CMD_QUERY should always enable this test  */
 | |
| 	int force;		/* force if CMD_QUERY reports not enabled    */
 | |
| 	int force_exp_errno;	/* expected errno after forced cmd           */
 | |
| 	int change_exp_errno;	/* previous kernels forced errno result      */
 | |
| 	int change_kernver[3];	/* kernel version having diff expected errno */
 | |
| };
 | |
| 
 | |
| struct test_case tc[] = {
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 00) invalid cmd
 | |
| 	  *     - enabled by default
 | |
| 	  *     - should always fail with EINVAL
 | |
| 	  */
 | |
| 	 .testname = "cmd_fail",
 | |
| 	 .command = -1,
 | |
| 	 .exp_ret = -1,
 | |
| 	 .exp_errno = EINVAL,
 | |
| 	 .enabled = 1,
 | |
| 	 },
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 01) invalid flags
 | |
| 	  *     - enabled by default
 | |
| 	  *     - should always fail with EINVAL
 | |
| 	  */
 | |
| 	 .testname = "cmd_flags_fail",
 | |
| 	 .command = MEMBARRIER_CMD_QUERY,
 | |
| 	 .flags = 1,
 | |
| 	 .exp_ret = -1,
 | |
| 	 .exp_errno = EINVAL,
 | |
| 	 .enabled = 1,
 | |
| 	 },
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 02) global barrier
 | |
| 	  *     - should ALWAYS be enabled by CMD_QUERY
 | |
| 	  *     - should always succeed
 | |
| 	  */
 | |
| 	 .testname = "cmd_global_success",
 | |
| 	 .command = MEMBARRIER_CMD_GLOBAL,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = 0,
 | |
| 	 .always = 1,
 | |
| 	 },
 | |
| 	 /*
 | |
| 	  * commit 22e4ebb975 (v4.14-rc1) added cases 03, 04 and 05 features:
 | |
| 	  */
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 03) private expedited barrier with no registrations
 | |
| 	  *     - should fail with errno=EPERM due to no registrations
 | |
| 	  *     - or be skipped if unsupported by running kernel
 | |
| 	  */
 | |
| 	 .testname = "cmd_private_expedited_fail",
 | |
| 	 .command = MEMBARRIER_CMD_PRIVATE_EXPEDITED,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = -1,
 | |
| 	 .exp_errno = EPERM,
 | |
| 	 },
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 04) register private expedited
 | |
| 	  *     - should succeed when supported by running kernel
 | |
| 	  *     - or fail with errno=EINVAL if unsupported and forced
 | |
| 	  */
 | |
| 	 .testname = "cmd_private_expedited_register_success",
 | |
| 	 .command = MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = 0,
 | |
| 	 .force = 1,
 | |
| 	 .force_exp_errno = EINVAL,
 | |
| 	 },
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 05) private expedited barrier with registration
 | |
| 	  *     - should succeed due to existing registration
 | |
| 	  *     - or fail with errno=EINVAL if unsupported and forced
 | |
| 	  *     - NOTE: commit 70216e18e5 (v4.16-rc1) changed behavior:
 | |
| 	  *     -       (a) if unsupported, and forced, < 4.16 , errno is EINVAL
 | |
| 	  *     -       (b) if unsupported, and forced, >= 4.16, errno is EPERM
 | |
| 	  */
 | |
| 	 .testname = "cmd_private_expedited_success",
 | |
| 	 .needregister = MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED,
 | |
| 	 .command = MEMBARRIER_CMD_PRIVATE_EXPEDITED,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = 0,
 | |
| 	 .force = 1,
 | |
| 	 .force_exp_errno = EPERM,
 | |
| 	 .change_exp_errno = EINVAL,
 | |
| 	 .change_kernver = { 4, 16, 0 },
 | |
| 	 },
 | |
| 	 /*
 | |
| 	  * commit 70216e18e5 (v4.16-rc1) added cases 06, 07 and 08 features:
 | |
| 	  */
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 06) private expedited sync core barrier with no registrations
 | |
| 	  *     - should fail with errno=EPERM due to no registrations
 | |
| 	  *     - or be skipped if unsupported by running kernel
 | |
| 	  */
 | |
| 	 .testname = "cmd_private_expedited_sync_core_fail",
 | |
| 	 .command = MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = -1,
 | |
| 	 .exp_errno = EPERM,
 | |
| 	 },
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 07) register private expedited sync core
 | |
| 	  *     - should succeed when supported by running kernel
 | |
| 	  *     - or fail with errno=EINVAL if unsupported and forced
 | |
| 	  */
 | |
| 	 .testname = "cmd_private_expedited_sync_core_register_success",
 | |
| 	 .command = MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = 0,
 | |
| 	 .force = 1,
 | |
| 	 .force_exp_errno = EINVAL,
 | |
| 	 },
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 08) private expedited sync core barrier with registration
 | |
| 	  *     - should succeed due to existing registration
 | |
| 	  *     - or fail with errno=EINVAL if unsupported and forced
 | |
| 	  */
 | |
| 	 .testname = "cmd_private_expedited_sync_core_success",
 | |
| 	 .needregister = MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE,
 | |
| 	 .command = MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = 0,
 | |
| 	 .force = 1,
 | |
| 	 .force_exp_errno = EINVAL,
 | |
| 	 },
 | |
| 	 /*
 | |
| 	  * commit c5f58bd58f4 (v4.16-rc1) added cases 09, 10 and 11 features:
 | |
| 	  */
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 09) global expedited barrier with no registrations
 | |
| 	  *     - should never fail due to no registrations
 | |
| 	  *     - or be skipped if unsupported by running kernel
 | |
| 	  */
 | |
| 	 .testname = "cmd_global_expedited_success",
 | |
| 	 .command = MEMBARRIER_CMD_GLOBAL_EXPEDITED,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = 0,
 | |
| 	 },
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 10) register global expedited
 | |
| 	  *     - should succeed when supported by running kernel
 | |
| 	  *     - or fail with errno=EINVAL if unsupported and forced
 | |
| 	  */
 | |
| 	 .testname = "cmd_global_expedited_register_success",
 | |
| 	 .command = MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = 0,
 | |
| 	 .force = 1,
 | |
| 	 .force_exp_errno = EINVAL,
 | |
| 	 },
 | |
| 	{
 | |
| 	 /*
 | |
| 	  * case 11) global expedited barrier with registration
 | |
| 	  *     - should also succeed with registrations
 | |
| 	  *     - or fail with errno=EINVAL if unsupported and forced
 | |
| 	  */
 | |
| 	 .testname = "cmd_global_expedited_success",
 | |
| 	 .needregister = MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED,
 | |
| 	 .command = MEMBARRIER_CMD_GLOBAL_EXPEDITED,
 | |
| 	 .flags = 0,
 | |
| 	 .exp_ret = 0,
 | |
| 	 .force = 1,
 | |
| 	 .force_exp_errno = EINVAL,
 | |
| 	 },
 | |
| };
 | |
| 
 | |
| #define passed_ok(_test)						       \
 | |
| 	do {								       \
 | |
| 		tst_res(TPASS, "membarrier(2): %s passed", _test.testname);    \
 | |
| 		return;							       \
 | |
| 	} while (0)
 | |
| 
 | |
| #define passed_unexpec(_test)						       \
 | |
| 	do {								       \
 | |
| 		tst_res(TFAIL, "membarrier(2): %s passed unexpectedly. "       \
 | |
| 			"ret = %ld with errno %d were expected. (force: %d)",  \
 | |
| 			_test.testname, _test.exp_ret, _test.exp_errno,        \
 | |
| 			_test.force);					       \
 | |
| 		return;							       \
 | |
| 	} while (0)
 | |
| 
 | |
| #define failed_ok(_test)						       \
 | |
| 	do {								       \
 | |
| 		tst_res(TPASS, "membarrier(2): %s failed as expected",	       \
 | |
| 			_test.testname);				       \
 | |
| 		return;							       \
 | |
| 	} while (0)
 | |
| 
 | |
| #define failed_ok_unsupported(_test)					       \
 | |
| 	do {								       \
 | |
| 		tst_res(TPASS, "membarrier(2): %s failed as expected "	       \
 | |
| 			"(unsupported)", _test.testname);		       \
 | |
| 		return;							       \
 | |
| 	} while (0)
 | |
| 
 | |
| #define failed_not_ok(_test, _gotret, _goterr)				       \
 | |
| 	do {								       \
 | |
| 		tst_res(TFAIL, "membarrier(2): %s failed. "		       \
 | |
| 			"ret = %ld when expected was %ld. "		       \
 | |
| 			"errno = %d when expected was %d. (force: %d)",        \
 | |
| 			_test.testname, _gotret, _test.exp_ret, _goterr,       \
 | |
| 			_test.exp_errno, _test.force);			       \
 | |
| 		return;							       \
 | |
| 	} while (0)
 | |
| 
 | |
| #define failed_unexpec(_test, _gotret, _goterr) 			       \
 | |
| 	do {								       \
 | |
| 		tst_res(TFAIL, "membarrier(2): %s failed unexpectedly. "       \
 | |
| 			"Got ret = %ld with errno %d. (force: %d)",	       \
 | |
| 			_test.testname, _gotret, _goterr, _test.force);	       \
 | |
| 		return;							       \
 | |
| 	} while (0)
 | |
| 
 | |
| #define skipped(_test)							       \
 | |
| 	do {								       \
 | |
| 		tst_res(TPASS, "membarrier(2): %s skipped (unsupported)",      \
 | |
| 			_test.testname);				       \
 | |
| 		return;							       \
 | |
| 	} while (0)
 | |
| 
 | |
| #define skipped_fail(_test)						       \
 | |
| 	do {								       \
 | |
| 		tst_res(TFAIL, "membarrier(2): %s reported as not supported",  \
 | |
| 			_test.testname);				       \
 | |
| 		return;							       \
 | |
| 	} while (0)
 | |
| 
 | |
| static int sys_membarrier(int cmd, int flags)
 | |
| {
 | |
| 	return tst_syscall(__NR_membarrier, cmd, flags);
 | |
| }
 | |
| 
 | |
| static void verify_membarrier(unsigned int i)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	/* not enabled and not enforced: test is skipped */
 | |
| 
 | |
| 	if (!tc[i].enabled && !tc[i].force) {
 | |
| 
 | |
| 		if (tc[i].always == 0)
 | |
| 			skipped(tc[i]);
 | |
| 
 | |
| 		skipped_fail(tc[i]);
 | |
| 	}
 | |
| 
 | |
| 	/* iterations: registration needed for some cases */
 | |
| 
 | |
| 	if (tc[i].needregister && tc[i].enabled) {
 | |
| 		ret = sys_membarrier(tc[i].needregister, 0);
 | |
| 		if (ret < 0) {
 | |
| 			tst_brk(TBROK, "membarrier(2): %s could not register",
 | |
| 					tc[i].testname);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	TEST(sys_membarrier(tc[i].command, tc[i].flags));
 | |
| 
 | |
| 	/* enabled and not enforced: regular expected results only */
 | |
| 
 | |
| 	if (tc[i].enabled && !tc[i].force) {
 | |
| 
 | |
| 		if (TST_RET >= 0 && tc[i].exp_ret == TST_RET)
 | |
| 			passed_ok(tc[i]);
 | |
| 
 | |
| 		if (TST_RET < 0) {
 | |
| 			if (tc[i].exp_ret == TST_RET)
 | |
| 				failed_ok(tc[i]);
 | |
| 			else
 | |
| 				failed_not_ok(tc[i], TST_RET, TST_ERR);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* not enabled and enforced: failure and expected errors */
 | |
| 
 | |
| 	if (!tc[i].enabled && tc[i].force) {
 | |
| 
 | |
| 		if (TST_RET >= 0)
 | |
| 			passed_unexpec(tc[i]);
 | |
| 
 | |
| 		if (TST_RET < 0) {
 | |
| 			if (tc[i].force_exp_errno == TST_ERR)
 | |
| 				failed_ok_unsupported(tc[i]);
 | |
| 			else
 | |
| 				failed_unexpec(tc[i], TST_RET, TST_ERR);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* enabled and enforced: tricky */
 | |
| 
 | |
| 	if (tc[i].enabled && tc[i].force) {
 | |
| 
 | |
| 		if (TST_RET >= 0) {
 | |
| 			if (tc[i].exp_ret == TST_RET)
 | |
| 				passed_ok(tc[i]);
 | |
| 			else
 | |
| 				passed_unexpec(tc[i]);
 | |
| 		}
 | |
| 
 | |
| 		if (TST_RET < 0) {
 | |
| 
 | |
| 			if (tc[i].exp_ret == TST_RET) {
 | |
| 
 | |
| 				if (tc[i].exp_errno == TST_ERR)
 | |
| 					failed_ok(tc[i]);
 | |
| 				else
 | |
| 					failed_unexpec(tc[i], TST_RET, TST_ERR);
 | |
| 			}
 | |
| 
 | |
| 			/* unknown on force failure if enabled and forced */
 | |
| 			failed_unexpec(tc[i], TST_RET, TST_ERR);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void wrap_verify_membarrier(unsigned int i)
 | |
| {
 | |
| 	pid_t pid;
 | |
| 
 | |
| 	/*
 | |
| 	 * The Linux kernel does not provide a way to unregister the process
 | |
| 	 * (mm->membarrier_state) intent of being affected by the membarrier(2)
 | |
| 	 * system call, thus the need of having a wrapper to fork() a child.
 | |
| 	 */
 | |
| 
 | |
| 	pid = SAFE_FORK();
 | |
| 
 | |
| 	if (pid)
 | |
| 		SAFE_WAITPID(pid, NULL, 0);
 | |
| 	else
 | |
| 		verify_membarrier(i);
 | |
| }
 | |
| 
 | |
| static void setup(void)
 | |
| {
 | |
| 	size_t i;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = sys_membarrier(MEMBARRIER_CMD_QUERY, 0);
 | |
| 	if (ret < 0) {
 | |
| 		if (errno == ENOSYS)
 | |
| 			tst_brk(TBROK, "membarrier(2): not supported");
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(tc); i++) {
 | |
| 		if ((tc[i].command > 0) && (ret & tc[i].command))
 | |
| 			tc[i].enabled = 1;
 | |
| 
 | |
| 		/* forcing unsupported command might have different errno */
 | |
| 
 | |
| 		if (tc[i].change_exp_errno && tc[i].enabled == 0) {
 | |
| 			if (tst_kvercmp(tc[i].change_kernver[0],
 | |
| 					tc[i].change_kernver[1],
 | |
| 					tc[i].change_kernver[2]) < 0)
 | |
| 				tc[i].force_exp_errno = tc[i].change_exp_errno;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static struct tst_test test = {
 | |
| 	.setup = setup,
 | |
| 	.test = wrap_verify_membarrier,
 | |
| 	.tcnt = ARRAY_SIZE(tc),
 | |
| 	.min_kver = "4.3.0",	/* commit: 5b25b13ab0 (sys_membarrier(): ...) */
 | |
| 	.forks_child = 1,
 | |
| };
 |