gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/affinity.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <sched.h>
    16  #include <sys/syscall.h>
    17  #include <sys/types.h>
    18  #include <unistd.h>
    19  
    20  #include "gtest/gtest.h"
    21  #include "absl/strings/str_split.h"
    22  #include "test/util/cleanup.h"
    23  #include "test/util/fs_util.h"
    24  #include "test/util/posix_error.h"
    25  #include "test/util/test_util.h"
    26  #include "test/util/thread_util.h"
    27  
    28  namespace gvisor {
    29  namespace testing {
    30  namespace {
    31  
    32  // These tests are for both the sched_getaffinity(2) and sched_setaffinity(2)
    33  // syscalls.
    34  class AffinityTest : public ::testing::Test {
    35   protected:
    36    void SetUp() override {
    37      EXPECT_THAT(
    38          // Needs use the raw syscall to get the actual size.
    39          cpuset_size_ = syscall(SYS_sched_getaffinity, /*pid=*/0,
    40                                 sizeof(cpu_set_t), &mask_),
    41          SyscallSucceeds());
    42      // Lots of tests rely on having more than 1 logical processor available.
    43      EXPECT_GT(CPU_COUNT(&mask_), 1);
    44      EXPECT_GT(cpuset_size_, 0);
    45      EXPECT_LE(cpuset_size_, sizeof(cpu_set_t));
    46    }
    47  
    48    static PosixError ClearLowestBit(cpu_set_t* mask, size_t cpus) {
    49      const size_t mask_size = CPU_ALLOC_SIZE(cpus);
    50      for (size_t n = 0; n < cpus; ++n) {
    51        if (CPU_ISSET_S(n, mask_size, mask)) {
    52          CPU_CLR_S(n, mask_size, mask);
    53          return NoError();
    54        }
    55      }
    56      return PosixError(EINVAL, "No bit to clear, mask is empty");
    57    }
    58  
    59    PosixError ClearLowestBit() { return ClearLowestBit(&mask_, CPU_SETSIZE); }
    60  
    61    // Stores the initial cpu mask for this process.
    62    cpu_set_t mask_ = {};
    63    int cpuset_size_ = 0;
    64  };
    65  
    66  // sched_getaffinity(2) is implemented.
    67  TEST_F(AffinityTest, SchedGetAffinityImplemented) {
    68    EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_),
    69                SyscallSucceeds());
    70  }
    71  
    72  // PID is not found.
    73  TEST_F(AffinityTest, SchedGetAffinityInvalidPID) {
    74    // Flaky, but it's tough to avoid a race condition when finding an unused pid
    75    EXPECT_THAT(sched_getaffinity(/*pid=*/INT_MAX - 1, sizeof(cpu_set_t), &mask_),
    76                SyscallFailsWithErrno(ESRCH));
    77  }
    78  
    79  // PID is not found.
    80  TEST_F(AffinityTest, SchedSetAffinityInvalidPID) {
    81    // Flaky, but it's tough to avoid a race condition when finding an unused pid
    82    EXPECT_THAT(sched_setaffinity(/*pid=*/INT_MAX - 1, sizeof(cpu_set_t), &mask_),
    83                SyscallFailsWithErrno(ESRCH));
    84  }
    85  
    86  TEST_F(AffinityTest, SchedSetAffinityZeroMask) {
    87    CPU_ZERO(&mask_);
    88    EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_),
    89                SyscallFailsWithErrno(EINVAL));
    90  }
    91  
    92  // N.B. This test case relies on cpuset_size_ larger than the actual number of
    93  // of all existing CPUs. Check your machine if the test fails.
    94  TEST_F(AffinityTest, SchedSetAffinityNonexistentCPUDropped) {
    95    cpu_set_t mask = mask_;
    96    // Add a nonexistent CPU.
    97    //
    98    // The number needs to be larger than the possible number of CPU available,
    99    // but smaller than the number of the CPU that the kernel claims to support --
   100    // it's implicitly returned by raw sched_getaffinity syscall.
   101    CPU_SET(cpuset_size_ * 8 - 1, &mask);
   102    EXPECT_THAT(
   103        // Use raw syscall because it will be rejected by the libc wrapper
   104        // otherwise.
   105        syscall(SYS_sched_setaffinity, /*pid=*/0, sizeof(cpu_set_t), &mask),
   106        SyscallSucceeds())
   107        << "failed with cpumask : " << CPUSetToString(mask)
   108        << ", cpuset_size_ : " << cpuset_size_;
   109    cpu_set_t newmask;
   110    EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &newmask),
   111                SyscallSucceeds());
   112    EXPECT_TRUE(CPU_EQUAL(&mask_, &newmask))
   113        << "got: " << CPUSetToString(newmask)
   114        << " != expected: " << CPUSetToString(mask_);
   115  }
   116  
   117  TEST_F(AffinityTest, SchedSetAffinityOnlyNonexistentCPUFails) {
   118    // Make an empty cpu set.
   119    CPU_ZERO(&mask_);
   120    // Add a nonexistent CPU.
   121    //
   122    // The number needs to be larger than the possible number of CPU available,
   123    // but smaller than the number of the CPU that the kernel claims to support --
   124    // it's implicitly returned by raw sched_getaffinity syscall.
   125    int cpu = cpuset_size_ * 8 - 1;
   126    if (cpu <= NumCPUs()) {
   127      GTEST_SKIP() << "Skipping test: cpu " << cpu << " exists";
   128    }
   129    CPU_SET(cpu, &mask_);
   130    EXPECT_THAT(
   131        // Use raw syscall because it will be rejected by the libc wrapper
   132        // otherwise.
   133        syscall(SYS_sched_setaffinity, /*pid=*/0, sizeof(cpu_set_t), &mask_),
   134        SyscallFailsWithErrno(EINVAL));
   135  }
   136  
   137  TEST_F(AffinityTest, SchedSetAffinityInvalidSize) {
   138    EXPECT_GT(cpuset_size_, 0);
   139    // Not big enough.
   140    EXPECT_THAT(sched_getaffinity(/*pid=*/0, cpuset_size_ - 1, &mask_),
   141                SyscallFailsWithErrno(EINVAL));
   142    // Not a multiple of word size.
   143    EXPECT_THAT(sched_getaffinity(/*pid=*/0, cpuset_size_ + 1, &mask_),
   144                SyscallFailsWithErrno(EINVAL));
   145  }
   146  
   147  TEST_F(AffinityTest, Sanity) {
   148    ASSERT_NO_ERRNO(ClearLowestBit());
   149    EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_),
   150                SyscallSucceeds());
   151    cpu_set_t newmask;
   152    EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &newmask),
   153                SyscallSucceeds());
   154    EXPECT_TRUE(CPU_EQUAL(&mask_, &newmask))
   155        << "got: " << CPUSetToString(newmask)
   156        << " != expected: " << CPUSetToString(mask_);
   157  }
   158  
   159  TEST_F(AffinityTest, NewThread) {
   160    SKIP_IF(CPU_COUNT(&mask_) < 3);
   161    ASSERT_NO_ERRNO(ClearLowestBit());
   162    ASSERT_NO_ERRNO(ClearLowestBit());
   163    EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_),
   164                SyscallSucceeds());
   165    ScopedThread([this]() {
   166      cpu_set_t child_mask;
   167      ASSERT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &child_mask),
   168                  SyscallSucceeds());
   169      ASSERT_TRUE(CPU_EQUAL(&child_mask, &mask_))
   170          << "child cpu mask: " << CPUSetToString(child_mask)
   171          << " != parent cpu mask: " << CPUSetToString(mask_);
   172    });
   173  }
   174  
   175  TEST_F(AffinityTest, ConsistentWithProcCpuInfo) {
   176    // Count how many cpus are shown in /proc/cpuinfo.
   177    std::string cpuinfo = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/cpuinfo"));
   178    int count = 0;
   179    for (auto const& line : absl::StrSplit(cpuinfo, '\n')) {
   180      if (absl::StartsWith(line, "processor")) {
   181        count++;
   182      }
   183    }
   184    EXPECT_GE(count, CPU_COUNT(&mask_));
   185  }
   186  
   187  TEST_F(AffinityTest, ConsistentWithProcStat) {
   188    // Count how many cpus are shown in /proc/stat.
   189    std::string stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat"));
   190    int count = 0;
   191    for (auto const& line : absl::StrSplit(stat, '\n')) {
   192      if (absl::StartsWith(line, "cpu") && !absl::StartsWith(line, "cpu ")) {
   193        count++;
   194      }
   195    }
   196    EXPECT_GE(count, CPU_COUNT(&mask_));
   197  }
   198  
   199  TEST_F(AffinityTest, SmallCpuMask) {
   200    const int num_cpus = NumCPUs();
   201    const size_t mask_size = CPU_ALLOC_SIZE(num_cpus);
   202    cpu_set_t* mask = CPU_ALLOC(num_cpus);
   203    ASSERT_NE(mask, nullptr);
   204    const auto free_mask = Cleanup([&] { CPU_FREE(mask); });
   205  
   206    CPU_ZERO_S(mask_size, mask);
   207    ASSERT_THAT(sched_getaffinity(0, mask_size, mask), SyscallSucceeds());
   208  }
   209  
   210  TEST_F(AffinityTest, LargeCpuMask) {
   211    // Allocate mask bigger than cpu_set_t normally allocates.
   212    const size_t cpus = CPU_SETSIZE * 8;
   213    const size_t mask_size = CPU_ALLOC_SIZE(cpus);
   214  
   215    cpu_set_t* large_mask = CPU_ALLOC(cpus);
   216    auto free_mask = Cleanup([large_mask] { CPU_FREE(large_mask); });
   217    CPU_ZERO_S(mask_size, large_mask);
   218  
   219    // Check that get affinity with large mask works as expected.
   220    ASSERT_THAT(sched_getaffinity(/*pid=*/0, mask_size, large_mask),
   221                SyscallSucceeds());
   222    EXPECT_TRUE(CPU_EQUAL(&mask_, large_mask))
   223        << "got: " << CPUSetToString(*large_mask, cpus)
   224        << " != expected: " << CPUSetToString(mask_);
   225  
   226    // Check that set affinity with large mask works as expected.
   227    ASSERT_NO_ERRNO(ClearLowestBit(large_mask, cpus));
   228    EXPECT_THAT(sched_setaffinity(/*pid=*/0, mask_size, large_mask),
   229                SyscallSucceeds());
   230  
   231    cpu_set_t* new_mask = CPU_ALLOC(cpus);
   232    auto free_new_mask = Cleanup([new_mask] { CPU_FREE(new_mask); });
   233    CPU_ZERO_S(mask_size, new_mask);
   234    EXPECT_THAT(sched_getaffinity(/*pid=*/0, mask_size, new_mask),
   235                SyscallSucceeds());
   236  
   237    EXPECT_TRUE(CPU_EQUAL_S(mask_size, large_mask, new_mask))
   238        << "got: " << CPUSetToString(*new_mask, cpus)
   239        << " != expected: " << CPUSetToString(*large_mask, cpus);
   240  }
   241  
   242  }  // namespace
   243  }  // namespace testing
   244  }  // namespace gvisor