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