From dbe5a4abd9da807cb0b2ec80db80b30059188d31 Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Tue, 12 Jul 2022 15:03:57 -0700 Subject: [PATCH] Implement MPOL_PREFERRED as MPOL_LOCAL when nodeset is empty. After 7858d7bca7fb ("mm/mempolicy: don't handle MPOL_LOCAL like a fake MPOL_PREFERRED policy"), MPOL_PREFERRED is implemented as MPOL_LOCAL when nodeset is empty. Earlier, MPOL_LOCAL was implemented as MPOL_PREFERRED and kernel checked that nodeset was empty. Fixed the syscall test to avoid this conversion process. We don't know which machine our native tests will run on, so it is hard to test for either of these conversions of MPOL modes. In this state the syscall test passes on newer and older kernels. PiperOrigin-RevId: 460560999 --- pkg/sentry/syscalls/linux/sys_mempolicy.go | 14 +++++++++----- test/syscalls/linux/mempolicy.cc | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/pkg/sentry/syscalls/linux/sys_mempolicy.go b/pkg/sentry/syscalls/linux/sys_mempolicy.go index 6e7bcb868..841383c47 100644 --- a/pkg/sentry/syscalls/linux/sys_mempolicy.go +++ b/pkg/sentry/syscalls/linux/sys_mempolicy.go @@ -294,16 +294,20 @@ func copyInMempolicyNodemask(t *kernel.Task, modeWithFlags linux.NumaPolicy, nod } case linux.MPOL_PREFERRED: // This permits an empty nodemask, as long as no flags are set. - if nodemaskVal == 0 && flags != 0 { - return 0, 0, linuxerr.EINVAL + if nodemaskVal == 0 { + if flags != 0 { + return 0, 0, linuxerr.EINVAL + } + // On newer Linux versions, MPOL_PREFERRED is implemented as MPOL_LOCAL + // when node set is empty. See 7858d7bca7fb ("mm/mempolicy: don't handle + // MPOL_LOCAL like a fake MPOL_PREFERRED policy"). + mode = linux.MPOL_LOCAL } case linux.MPOL_LOCAL: - // This requires an empty nodemask and no flags set ... + // This requires an empty nodemask and no flags set. if nodemaskVal != 0 || flags != 0 { return 0, 0, linuxerr.EINVAL } - // ... and is implemented as MPOL_PREFERRED. - mode = linux.MPOL_PREFERRED default: // Unknown mode, which we should have rejected above. panic(fmt.Sprintf("unknown mode: %v", mode)) diff --git a/test/syscalls/linux/mempolicy.cc b/test/syscalls/linux/mempolicy.cc index 059fad598..fa8e5fcda 100644 --- a/test/syscalls/linux/mempolicy.cc +++ b/test/syscalls/linux/mempolicy.cc @@ -260,9 +260,11 @@ TEST(MempolicyTest, GetMempolicyNextInterleaveNode) { } TEST(MempolicyTest, Mbind) { + uint64_t nodemask = 0x1; // Temporarily set the thread policy to MPOL_PREFERRED. const auto cleanup_thread_policy = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy(MPOL_PREFERRED, nullptr, 0)); + ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy( + MPOL_PREFERRED, &nodemask, sizeof(nodemask) * BITS_PER_BYTE)); const auto mapping = ASSERT_NO_ERRNO_AND_VALUE( MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS)); @@ -274,10 +276,12 @@ TEST(MempolicyTest, Mbind) { 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()); + // Set MPOL_PREFERRED for the vma and read it back. Note that setting + // MPOL_PREFERRED with an empty node set will set mode to MPOL_LOCAL on newer + // Linux releases. + ASSERT_THAT(mbind(mapping.ptr(), mapping.len(), MPOL_PREFERRED, &nodemask, + sizeof(nodemask) * BITS_PER_BYTE, 0), + SyscallSucceeds()); ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, mapping.ptr(), MPOL_F_ADDR), SyscallSucceeds()); EXPECT_EQ(mode, MPOL_PREFERRED);