blob: d53860323b68b8d4332ce793bd605ad9f6076ecd [file] [log] [blame]
// Copyright 2025 The Fuchsia Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <sys/syscall.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include <linux/membarrier.h>
#include "src/starnix/tests/syscalls/cpp/test_helper.h"
namespace {
// glibc does not provide a wrapper for this system call.
long membarrier(int cmd, unsigned int flags, int cpu_id) {
return syscall(SYS_membarrier, cmd, flags, cpu_id);
}
TEST(Membarrier, Global) {
// The (legacy) global barrier command does not require registration.
SAFE_SYSCALL(membarrier(MEMBARRIER_CMD_GLOBAL, 0, 0));
}
TEST(Membarrier, Query) {
// Check that the set of supported commands includes what Starnix supports.
long cmds = SAFE_SYSCALL(membarrier(MEMBARRIER_CMD_QUERY, 0, 0));
EXPECT_NE(cmds & MEMBARRIER_CMD_GLOBAL, 0);
EXPECT_NE(cmds & MEMBARRIER_CMD_GLOBAL_EXPEDITED, 0);
EXPECT_NE(cmds & MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED, 0);
EXPECT_NE(cmds & MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0);
EXPECT_NE(cmds & MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0);
EXPECT_NE(cmds & MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE, 0);
EXPECT_NE(cmds & MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE, 0);
}
TEST(Membarrier, PrivateExpedited) {
test_helper::ForkHelper helper;
helper.RunInForkedProcess([]() {
// Expect this to find no registered processes and fail.
EXPECT_EQ(membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0), -1);
EXPECT_EQ(errno, EPERM) << strerror(errno);
// Register.
SAFE_SYSCALL(membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0));
// Now a barrier should succeed.
SAFE_SYSCALL(membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0));
});
ASSERT_TRUE(helper.WaitForChildren());
}
TEST(Membarrier, PrivateExpeditedRegisterThroughFork) {
// Run this test inside a child process to avoid polluting this process' state for other test
// cases.
test_helper::ForkHelper helper;
helper.RunInForkedProcess([]() {
// Register.
SAFE_SYSCALL(membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0));
// Fork a child process within this child..
test_helper::ForkHelper child_helper;
child_helper.RunInForkedProcess([]() {
// This succeeds as the registration carries through fork.
EXPECT_EQ(membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0), 0);
});
ASSERT_TRUE(child_helper.WaitForChildren());
});
ASSERT_TRUE(helper.WaitForChildren());
}
TEST(Membarrier, PrivateRegisterMemorySendSyncCore) {
test_helper::ForkHelper helper;
helper.RunInForkedProcess([]() {
// Register for private, expedited (memory) barriers.
SAFE_SYSCALL(membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0));
// SYNC_CORE barriers fail since nothing is registered for this type.
EXPECT_EQ(membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE, 0, 0), -1);
EXPECT_EQ(errno, EPERM);
});
ASSERT_TRUE(helper.WaitForChildren());
}
TEST(Membarrier, PrivateRegisterSyncCoreSendMemory) {
test_helper::ForkHelper helper;
helper.RunInForkedProcess([]() {
// Register for private, expedited sync_core barriers.
SAFE_SYSCALL(membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE, 0, 0));
// SYNC_CORE barriers fail since nothing is registered for this type.
EXPECT_EQ(membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0), -1);
EXPECT_EQ(errno, EPERM);
});
ASSERT_TRUE(helper.WaitForChildren());
}
} // namespace