diff options
Diffstat (limited to 'test/syscalls/linux/mempolicy.cc')
-rw-r--r-- | test/syscalls/linux/mempolicy.cc | 289 |
1 files changed, 0 insertions, 289 deletions
diff --git a/test/syscalls/linux/mempolicy.cc b/test/syscalls/linux/mempolicy.cc deleted file mode 100644 index 059fad598..000000000 --- a/test/syscalls/linux/mempolicy.cc +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2018 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 <sys/syscall.h> - -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "test/util/cleanup.h" -#include "test/util/memory_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -#define BITS_PER_BYTE 8 - -#define MPOL_F_STATIC_NODES (1 << 15) -#define MPOL_F_RELATIVE_NODES (1 << 14) -#define MPOL_DEFAULT 0 -#define MPOL_PREFERRED 1 -#define MPOL_BIND 2 -#define MPOL_INTERLEAVE 3 -#define MPOL_LOCAL 4 -#define MPOL_F_NODE (1 << 0) -#define MPOL_F_ADDR (1 << 1) -#define MPOL_F_MEMS_ALLOWED (1 << 2) -#define MPOL_MF_STRICT (1 << 0) -#define MPOL_MF_MOVE (1 << 1) -#define MPOL_MF_MOVE_ALL (1 << 2) - -int get_mempolicy(int* policy, uint64_t* nmask, uint64_t maxnode, void* addr, - int flags) { - return syscall(SYS_get_mempolicy, policy, nmask, maxnode, addr, flags); -} - -int set_mempolicy(int mode, uint64_t* nmask, uint64_t maxnode) { - return syscall(SYS_set_mempolicy, mode, nmask, maxnode); -} - -int mbind(void* addr, unsigned long len, int mode, - const unsigned long* nodemask, unsigned long maxnode, - unsigned flags) { - return syscall(SYS_mbind, addr, len, mode, nodemask, maxnode, flags); -} - -// Creates a cleanup object that resets the calling thread's mempolicy to the -// system default when the calling scope ends. -Cleanup ScopedMempolicy() { - return Cleanup([] { - EXPECT_THAT(set_mempolicy(MPOL_DEFAULT, nullptr, 0), SyscallSucceeds()); - }); -} - -// Temporarily change the memory policy for the calling thread within the -// caller's scope. -PosixErrorOr<Cleanup> ScopedSetMempolicy(int mode, uint64_t* nmask, - uint64_t maxnode) { - if (set_mempolicy(mode, nmask, maxnode)) { - return PosixError(errno, "set_mempolicy"); - } - return ScopedMempolicy(); -} - -TEST(MempolicyTest, CheckDefaultPolicy) { - int mode = 0; - uint64_t nodemask = 0; - ASSERT_THAT(get_mempolicy(&mode, &nodemask, sizeof(nodemask) * BITS_PER_BYTE, - nullptr, 0), - SyscallSucceeds()); - - EXPECT_EQ(MPOL_DEFAULT, mode); - EXPECT_EQ(0x0, nodemask); -} - -TEST(MempolicyTest, PolicyPreservedAfterSetMempolicy) { - uint64_t nodemask = 0x1; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy( - MPOL_BIND, &nodemask, sizeof(nodemask) * BITS_PER_BYTE)); - - int mode = 0; - uint64_t nodemask_after = 0x0; - ASSERT_THAT(get_mempolicy(&mode, &nodemask_after, - sizeof(nodemask_after) * BITS_PER_BYTE, nullptr, 0), - SyscallSucceeds()); - EXPECT_EQ(MPOL_BIND, mode); - EXPECT_EQ(0x1, nodemask_after); - - // Try throw in some mode flags. - for (auto mode_flag : {MPOL_F_STATIC_NODES, MPOL_F_RELATIVE_NODES}) { - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( - ScopedSetMempolicy(MPOL_INTERLEAVE | mode_flag, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE)); - mode = 0; - nodemask_after = 0x0; - ASSERT_THAT( - get_mempolicy(&mode, &nodemask_after, - sizeof(nodemask_after) * BITS_PER_BYTE, nullptr, 0), - SyscallSucceeds()); - EXPECT_EQ(MPOL_INTERLEAVE | mode_flag, mode); - EXPECT_EQ(0x1, nodemask_after); - } -} - -TEST(MempolicyTest, SetMempolicyRejectsInvalidInputs) { - auto cleanup = ScopedMempolicy(); - uint64_t nodemask; - - if (IsRunningOnGvisor()) { - // Invalid nodemask, we only support a single node on gvisor. - nodemask = 0x4; - ASSERT_THAT(set_mempolicy(MPOL_DEFAULT, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE), - SyscallFailsWithErrno(EINVAL)); - } - - nodemask = 0x1; - - // Invalid mode. - ASSERT_THAT(set_mempolicy(7439, &nodemask, sizeof(nodemask) * BITS_PER_BYTE), - SyscallFailsWithErrno(EINVAL)); - - // Invalid nodemask size. - ASSERT_THAT(set_mempolicy(MPOL_DEFAULT, &nodemask, 0), - SyscallFailsWithErrno(EINVAL)); - - // Invalid mode flag. - ASSERT_THAT( - set_mempolicy(MPOL_DEFAULT | MPOL_F_STATIC_NODES | MPOL_F_RELATIVE_NODES, - &nodemask, sizeof(nodemask) * BITS_PER_BYTE), - SyscallFailsWithErrno(EINVAL)); - - // MPOL_INTERLEAVE with empty nodemask. - nodemask = 0x0; - ASSERT_THAT(set_mempolicy(MPOL_INTERLEAVE, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE), - SyscallFailsWithErrno(EINVAL)); -} - -// The manpages specify that the nodemask provided to set_mempolicy are -// considered empty if the nodemask pointer is null, or if the nodemask size is -// 0. We use a policy which accepts both empty and non-empty nodemasks -// (MPOL_PREFERRED), a policy which requires a non-empty nodemask (MPOL_BIND), -// and a policy which completely ignores the nodemask (MPOL_DEFAULT) to verify -// argument checking around nodemasks. -TEST(MempolicyTest, EmptyNodemaskOnSet) { - auto cleanup = ScopedMempolicy(); - - EXPECT_THAT(set_mempolicy(MPOL_DEFAULT, nullptr, 1), SyscallSucceeds()); - EXPECT_THAT(set_mempolicy(MPOL_BIND, nullptr, 1), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(set_mempolicy(MPOL_PREFERRED, nullptr, 1), SyscallSucceeds()); - - uint64_t nodemask = 0x1; - EXPECT_THAT(set_mempolicy(MPOL_DEFAULT, &nodemask, 0), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(set_mempolicy(MPOL_BIND, &nodemask, 0), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(set_mempolicy(MPOL_PREFERRED, &nodemask, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(MempolicyTest, QueryAvailableNodes) { - uint64_t nodemask = 0; - ASSERT_THAT( - get_mempolicy(nullptr, &nodemask, sizeof(nodemask) * BITS_PER_BYTE, - nullptr, MPOL_F_MEMS_ALLOWED), - SyscallSucceeds()); - // We can only be sure there is a single node if running on gvisor. - if (IsRunningOnGvisor()) { - EXPECT_EQ(0x1, nodemask); - } - - // MPOL_F_ADDR and MPOL_F_NODE flags may not be combined with - // MPOL_F_MEMS_ALLLOWED. - for (auto flags : - {MPOL_F_MEMS_ALLOWED | MPOL_F_ADDR, MPOL_F_MEMS_ALLOWED | MPOL_F_NODE, - MPOL_F_MEMS_ALLOWED | MPOL_F_ADDR | MPOL_F_NODE}) { - ASSERT_THAT(get_mempolicy(nullptr, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE, nullptr, flags), - SyscallFailsWithErrno(EINVAL)); - } -} - -TEST(MempolicyTest, GetMempolicyQueryNodeForAddress) { - uint64_t dummy_stack_address; - auto dummy_heap_address = absl::make_unique<uint64_t>(); - int mode; - - for (auto ptr : {&dummy_stack_address, dummy_heap_address.get()}) { - mode = -1; - ASSERT_THAT( - get_mempolicy(&mode, nullptr, 0, ptr, MPOL_F_ADDR | MPOL_F_NODE), - SyscallSucceeds()); - // If we're not running on gvisor, the address may be allocated on a - // different numa node. - if (IsRunningOnGvisor()) { - EXPECT_EQ(0, mode); - } - } - - void* invalid_address = reinterpret_cast<void*>(-1); - - // Invalid address. - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, invalid_address, - MPOL_F_ADDR | MPOL_F_NODE), - SyscallFailsWithErrno(EFAULT)); - - // Invalid mode pointer. - ASSERT_THAT(get_mempolicy(reinterpret_cast<int*>(invalid_address), nullptr, 0, - &dummy_stack_address, MPOL_F_ADDR | MPOL_F_NODE), - SyscallFailsWithErrno(EFAULT)); -} - -TEST(MempolicyTest, GetMempolicyCanOmitPointers) { - int mode; - uint64_t nodemask; - - // Omit nodemask pointer. - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, nullptr, 0), SyscallSucceeds()); - // Omit mode pointer. - ASSERT_THAT(get_mempolicy(nullptr, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE, nullptr, 0), - SyscallSucceeds()); - // Omit both pointers. - ASSERT_THAT(get_mempolicy(nullptr, nullptr, 0, nullptr, 0), - SyscallSucceeds()); -} - -TEST(MempolicyTest, GetMempolicyNextInterleaveNode) { - int mode; - // Policy for thread not yet set to MPOL_INTERLEAVE, can't query for - // the next node which will be used for allocation. - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, nullptr, MPOL_F_NODE), - SyscallFailsWithErrno(EINVAL)); - - // Set default policy for thread to MPOL_INTERLEAVE. - uint64_t nodemask = 0x1; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy( - MPOL_INTERLEAVE, &nodemask, sizeof(nodemask) * BITS_PER_BYTE)); - - mode = -1; - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, nullptr, MPOL_F_NODE), - SyscallSucceeds()); - EXPECT_EQ(0, mode); -} - -TEST(MempolicyTest, Mbind) { - // Temporarily set the thread policy to MPOL_PREFERRED. - const auto cleanup_thread_policy = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy(MPOL_PREFERRED, nullptr, 0)); - - const auto mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS)); - - // vmas default to MPOL_DEFAULT irrespective of the thread policy (currently - // MPOL_PREFERRED). - int mode; - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, mapping.ptr(), MPOL_F_ADDR), - SyscallSucceeds()); - EXPECT_EQ(mode, MPOL_DEFAULT); - - // Set MPOL_PREFERRED for the vma and read it back. - ASSERT_THAT( - mbind(mapping.ptr(), mapping.len(), MPOL_PREFERRED, nullptr, 0, 0), - SyscallSucceeds()); - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, mapping.ptr(), MPOL_F_ADDR), - SyscallSucceeds()); - EXPECT_EQ(mode, MPOL_PREFERRED); -} - -} // namespace - -} // namespace testing -} // namespace gvisor |