summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/membarrier.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/membarrier.cc')
-rw-r--r--test/syscalls/linux/membarrier.cc268
1 files changed, 0 insertions, 268 deletions
diff --git a/test/syscalls/linux/membarrier.cc b/test/syscalls/linux/membarrier.cc
deleted file mode 100644
index 516956a25..000000000
--- a/test/syscalls/linux/membarrier.cc
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <errno.h>
-#include <signal.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <atomic>
-
-#include "absl/time/clock.h"
-#include "absl/time/time.h"
-#include "test/util/cleanup.h"
-#include "test/util/logging.h"
-#include "test/util/memory_util.h"
-#include "test/util/posix_error.h"
-#include "test/util/test_util.h"
-#include "test/util/thread_util.h"
-
-namespace gvisor {
-namespace testing {
-
-namespace {
-
-// This is the classic test case for memory fences on architectures with total
-// store ordering; see e.g. Intel SDM Vol. 3A Sec. 8.2.3.4 "Loads May Be
-// Reordered with Earlier Stores to Different Locations". In each iteration of
-// the test, given two variables X and Y initially set to 0
-// (MembarrierTestSharedState::local_var and remote_var in the code), two
-// threads execute as follows:
-//
-// T1 T2
-// -- --
-//
-// X = 1 Y = 1
-// T1fence() T2fence()
-// read Y read X
-//
-// On architectures where memory writes may be locally buffered by each CPU
-// (essentially all architectures), if T1fence() and T2fence() are omitted or
-// ineffective, it is possible for both T1 and T2 to read 0 because the memory
-// write from the other CPU is not yet visible outside that CPU. T1fence() and
-// T2fence() are expected to perform the necessary synchronization to restore
-// sequential consistency: both threads agree on a order of memory accesses that
-// is consistent with program order in each thread, such that at least one
-// thread reads 1.
-//
-// In the NoMembarrier test, T1fence() and T2fence() are both ordinary memory
-// fences establishing ordering between memory accesses before and after the
-// fence (std::atomic_thread_fence). In all other test cases, T1fence() is not a
-// memory fence at all, but only prevents compiler reordering of memory accesses
-// (std::atomic_signal_fence); T2fence() is an invocation of the membarrier()
-// syscall, which establishes ordering of memory accesses before and after the
-// syscall on both threads.
-
-template <typename F>
-int DoMembarrierTestSide(std::atomic<int>* our_var,
- std::atomic<int> const& their_var,
- F const& test_fence) {
- our_var->store(1, std::memory_order_relaxed);
- test_fence();
- return their_var.load(std::memory_order_relaxed);
-}
-
-struct MembarrierTestSharedState {
- std::atomic<int64_t> remote_iter_cur;
- std::atomic<int64_t> remote_iter_done;
- std::atomic<int> local_var;
- std::atomic<int> remote_var;
- int remote_obs_of_local_var;
-
- void Init() {
- remote_iter_cur.store(-1, std::memory_order_relaxed);
- remote_iter_done.store(-1, std::memory_order_relaxed);
- }
-};
-
-// Special value for MembarrierTestSharedState::remote_iter_cur indicating that
-// the remote thread should terminate.
-constexpr int64_t kRemoteIterStop = -2;
-
-// Must be async-signal-safe.
-template <typename F>
-void RunMembarrierTestRemoteSide(MembarrierTestSharedState* state,
- F const& test_fence) {
- int64_t i = 0;
- int64_t cur;
- while (true) {
- while ((cur = state->remote_iter_cur.load(std::memory_order_acquire)) < i) {
- if (cur == kRemoteIterStop) {
- return;
- }
- // spin
- }
- state->remote_obs_of_local_var =
- DoMembarrierTestSide(&state->remote_var, state->local_var, test_fence);
- state->remote_iter_done.store(i, std::memory_order_release);
- i++;
- }
-}
-
-template <typename F>
-void RunMembarrierTestLocalSide(MembarrierTestSharedState* state,
- F const& test_fence) {
- // On test completion, instruct the remote thread to terminate.
- Cleanup cleanup_remote([&] {
- state->remote_iter_cur.store(kRemoteIterStop, std::memory_order_relaxed);
- });
-
- int64_t i = 0;
- absl::Time end = absl::Now() + absl::Seconds(5); // arbitrary test duration
- while (absl::Now() < end) {
- // Reset both vars to 0.
- state->local_var.store(0, std::memory_order_relaxed);
- state->remote_var.store(0, std::memory_order_relaxed);
- // Instruct the remote thread to begin this iteration.
- state->remote_iter_cur.store(i, std::memory_order_release);
- // Perform our side of the test.
- auto local_obs_of_remote_var =
- DoMembarrierTestSide(&state->local_var, state->remote_var, test_fence);
- // Wait for the remote thread to finish this iteration.
- while (state->remote_iter_done.load(std::memory_order_acquire) < i) {
- // spin
- }
- ASSERT_TRUE(local_obs_of_remote_var != 0 ||
- state->remote_obs_of_local_var != 0);
- i++;
- }
-}
-
-TEST(MembarrierTest, NoMembarrier) {
- MembarrierTestSharedState state;
- state.Init();
-
- ScopedThread remote_thread([&] {
- RunMembarrierTestRemoteSide(
- &state, [] { std::atomic_thread_fence(std::memory_order_seq_cst); });
- });
- RunMembarrierTestLocalSide(
- &state, [] { std::atomic_thread_fence(std::memory_order_seq_cst); });
-}
-
-enum membarrier_cmd {
- MEMBARRIER_CMD_QUERY = 0,
- MEMBARRIER_CMD_GLOBAL = (1 << 0),
- MEMBARRIER_CMD_GLOBAL_EXPEDITED = (1 << 1),
- MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED = (1 << 2),
- MEMBARRIER_CMD_PRIVATE_EXPEDITED = (1 << 3),
- MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED = (1 << 4),
-};
-
-int membarrier(membarrier_cmd cmd, int flags) {
- return syscall(SYS_membarrier, cmd, flags);
-}
-
-PosixErrorOr<int> SupportedMembarrierCommands() {
- int cmds = membarrier(MEMBARRIER_CMD_QUERY, 0);
- if (cmds < 0) {
- if (errno == ENOSYS) {
- // No commands are supported.
- return 0;
- }
- return PosixError(errno, "membarrier(MEMBARRIER_CMD_QUERY) failed");
- }
- return cmds;
-}
-
-TEST(MembarrierTest, Global) {
- SKIP_IF((ASSERT_NO_ERRNO_AND_VALUE(SupportedMembarrierCommands()) &
- MEMBARRIER_CMD_GLOBAL) == 0);
-
- Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
- MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED));
- auto state = static_cast<MembarrierTestSharedState*>(m.ptr());
- state->Init();
-
- pid_t const child_pid = fork();
- if (child_pid == 0) {
- // In child process.
- RunMembarrierTestRemoteSide(
- state, [] { TEST_PCHECK(membarrier(MEMBARRIER_CMD_GLOBAL, 0) == 0); });
- _exit(0);
- }
- // In parent process.
- ASSERT_THAT(child_pid, SyscallSucceeds());
- Cleanup cleanup_child([&] {
- int status;
- ASSERT_THAT(waitpid(child_pid, &status, 0),
- SyscallSucceedsWithValue(child_pid));
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << " status " << status;
- });
- RunMembarrierTestLocalSide(
- state, [] { std::atomic_signal_fence(std::memory_order_seq_cst); });
-}
-
-TEST(MembarrierTest, GlobalExpedited) {
- constexpr int kRequiredCommands = MEMBARRIER_CMD_GLOBAL_EXPEDITED |
- MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED;
- SKIP_IF((ASSERT_NO_ERRNO_AND_VALUE(SupportedMembarrierCommands()) &
- kRequiredCommands) != kRequiredCommands);
-
- ASSERT_THAT(membarrier(MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED, 0),
- SyscallSucceeds());
-
- Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
- MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED));
- auto state = static_cast<MembarrierTestSharedState*>(m.ptr());
- state->Init();
-
- pid_t const child_pid = fork();
- if (child_pid == 0) {
- // In child process.
- RunMembarrierTestRemoteSide(state, [] {
- TEST_PCHECK(membarrier(MEMBARRIER_CMD_GLOBAL_EXPEDITED, 0) == 0);
- });
- _exit(0);
- }
- // In parent process.
- ASSERT_THAT(child_pid, SyscallSucceeds());
- Cleanup cleanup_child([&] {
- int status;
- ASSERT_THAT(waitpid(child_pid, &status, 0),
- SyscallSucceedsWithValue(child_pid));
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << " status " << status;
- });
- RunMembarrierTestLocalSide(
- state, [] { std::atomic_signal_fence(std::memory_order_seq_cst); });
-}
-
-TEST(MembarrierTest, PrivateExpedited) {
- constexpr int kRequiredCommands = MEMBARRIER_CMD_PRIVATE_EXPEDITED |
- MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED;
- SKIP_IF((ASSERT_NO_ERRNO_AND_VALUE(SupportedMembarrierCommands()) &
- kRequiredCommands) != kRequiredCommands);
-
- ASSERT_THAT(membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0),
- SyscallSucceeds());
-
- MembarrierTestSharedState state;
- state.Init();
-
- ScopedThread remote_thread([&] {
- RunMembarrierTestRemoteSide(&state, [] {
- TEST_PCHECK(membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0) == 0);
- });
- });
- RunMembarrierTestLocalSide(
- &state, [] { std::atomic_signal_fence(std::memory_order_seq_cst); });
-}
-
-} // namespace
-
-} // namespace testing
-} // namespace gvisor