gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/priority.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 <sys/resource.h> 16 #include <sys/time.h> 17 #include <sys/types.h> 18 #include <unistd.h> 19 20 #include <string> 21 #include <vector> 22 23 #include "gtest/gtest.h" 24 #include "absl/strings/numbers.h" 25 #include "absl/strings/str_split.h" 26 #include "test/util/capability_util.h" 27 #include "test/util/fs_util.h" 28 #include "test/util/test_util.h" 29 #include "test/util/thread_util.h" 30 31 namespace gvisor { 32 namespace testing { 33 34 namespace { 35 36 // These tests are for both the getpriority(2) and setpriority(2) syscalls 37 // These tests are very rudimentary because getpriority and setpriority 38 // have not yet been fully implemented. 39 40 // Getpriority does something 41 TEST(GetpriorityTest, Implemented) { 42 // "getpriority() can legitimately return the value -1, it is necessary to 43 // clear the external variable errno prior to the call" 44 errno = 0; 45 EXPECT_THAT(getpriority(PRIO_PROCESS, /*who=*/0), SyscallSucceeds()); 46 } 47 48 // Invalid which 49 TEST(GetpriorityTest, InvalidWhich) { 50 errno = 0; 51 EXPECT_THAT(getpriority(/*which=*/3, /*who=*/0), 52 SyscallFailsWithErrno(EINVAL)); 53 } 54 55 // Process is found when which=PRIO_PROCESS 56 TEST(GetpriorityTest, ValidWho) { 57 errno = 0; 58 EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), SyscallSucceeds()); 59 } 60 61 // Process is not found when which=PRIO_PROCESS 62 TEST(GetpriorityTest, InvalidWho) { 63 errno = 0; 64 // Flaky, but it's tough to avoid a race condition when finding an unused pid 65 EXPECT_THAT(getpriority(PRIO_PROCESS, /*who=*/INT_MAX - 1), 66 SyscallFailsWithErrno(ESRCH)); 67 } 68 69 // Setpriority does something 70 TEST(SetpriorityTest, Implemented) { 71 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); 72 73 // No need to clear errno for setpriority(): 74 // "The setpriority() call returns 0 if there is no error, or -1 if there is" 75 EXPECT_THAT(setpriority(PRIO_PROCESS, /*who=*/0, 76 /*nice=*/16), // NOLINT(bugprone-argument-comment) 77 SyscallSucceeds()); 78 } 79 80 // Invalid which 81 TEST(Setpriority, InvalidWhich) { 82 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); 83 84 EXPECT_THAT(setpriority(/*which=*/3, /*who=*/0, 85 /*nice=*/16), // NOLINT(bugprone-argument-comment) 86 SyscallFailsWithErrno(EINVAL)); 87 } 88 89 // Process is found when which=PRIO_PROCESS 90 TEST(SetpriorityTest, ValidWho) { 91 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); 92 93 EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), 94 /*nice=*/16), // NOLINT(bugprone-argument-comment) 95 SyscallSucceeds()); 96 } 97 98 // niceval is within the range [-20, 19] 99 TEST(SetpriorityTest, InsideRange) { 100 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); 101 102 // Set 0 < niceval < 19 103 int nice = 12; 104 EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), nice), SyscallSucceeds()); 105 106 errno = 0; 107 EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), 108 SyscallSucceedsWithValue(nice)); 109 110 // Set -20 < niceval < 0 111 nice = -12; 112 EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), nice), SyscallSucceeds()); 113 114 errno = 0; 115 EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), 116 SyscallSucceedsWithValue(nice)); 117 } 118 119 // Verify that priority/niceness are exposed via /proc/PID/stat. 120 TEST(SetpriorityTest, NicenessExposedViaProcfs) { 121 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); 122 123 constexpr int kNiceVal = 12; 124 ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), kNiceVal), SyscallSucceeds()); 125 126 errno = 0; 127 ASSERT_THAT(getpriority(PRIO_PROCESS, getpid()), 128 SyscallSucceedsWithValue(kNiceVal)); 129 130 // Now verify we can read that same value via /proc/self/stat. 131 std::string proc_stat; 132 ASSERT_NO_ERRNO(GetContents("/proc/self/stat", &proc_stat)); 133 std::vector<std::string> pieces = absl::StrSplit(proc_stat, ' '); 134 ASSERT_GT(pieces.size(), 20); 135 136 int niceness_procfs = 0; 137 ASSERT_TRUE(absl::SimpleAtoi(pieces[18], &niceness_procfs)); 138 EXPECT_EQ(niceness_procfs, kNiceVal); 139 } 140 141 // In the kernel's implementation, values outside the range of [-20, 19] are 142 // truncated to these minimum and maximum values. See 143 // https://elixir.bootlin.com/linux/v4.4/source/kernel/sys.c#L190 144 TEST(SetpriorityTest, OutsideRange) { 145 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); 146 147 // Set niceval > 19 148 EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), 149 /*nice=*/100), // NOLINT(bugprone-argument-comment) 150 SyscallSucceeds()); 151 152 errno = 0; 153 // Test niceval truncated to 19 154 EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), 155 SyscallSucceedsWithValue( 156 /*maxnice=*/19)); // NOLINT(bugprone-argument-comment) 157 158 // Set niceval < -20 159 EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), 160 /*nice=*/-100), // NOLINT(bugprone-argument-comment) 161 SyscallSucceeds()); 162 163 errno = 0; 164 // Test niceval truncated to -20 165 EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), 166 SyscallSucceedsWithValue( 167 /*minnice=*/-20)); // NOLINT(bugprone-argument-comment) 168 } 169 170 // Process is not found when which=PRIO_PROCESS 171 TEST(SetpriorityTest, InvalidWho) { 172 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); 173 174 // Flaky, but it's tough to avoid a race condition when finding an unused pid 175 EXPECT_THAT(setpriority(PRIO_PROCESS, 176 /*who=*/INT_MAX - 1, 177 /*nice=*/16), // NOLINT(bugprone-argument-comment) 178 SyscallFailsWithErrno(ESRCH)); 179 } 180 181 // Nice succeeds, correctly modifies (or in this case does not 182 // modify priority of process 183 TEST(SetpriorityTest, NiceSucceeds) { 184 errno = 0; 185 const int priority_before = getpriority(PRIO_PROCESS, /*who=*/0); 186 ASSERT_THAT(nice(/*inc=*/0), SyscallSucceeds()); 187 188 // nice(0) should not change priority 189 EXPECT_EQ(priority_before, getpriority(PRIO_PROCESS, /*who=*/0)); 190 } 191 192 // Threads resulting from clone() maintain parent's priority 193 // Changes to child priority do not affect parent's priority 194 TEST(GetpriorityTest, CloneMaintainsPriority) { 195 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); 196 197 constexpr int kParentPriority = 16; 198 constexpr int kChildPriority = 14; 199 ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), kParentPriority), 200 SyscallSucceeds()); 201 202 ScopedThread th([]() { 203 // Check that priority equals that of parent thread 204 pid_t my_tid; 205 EXPECT_THAT(my_tid = syscall(__NR_gettid), SyscallSucceeds()); 206 EXPECT_THAT(getpriority(PRIO_PROCESS, my_tid), 207 SyscallSucceedsWithValue(kParentPriority)); 208 209 // Change the child thread's priority 210 EXPECT_THAT(setpriority(PRIO_PROCESS, my_tid, kChildPriority), 211 SyscallSucceeds()); 212 }); 213 th.Join(); 214 215 // Check that parent's priority reemained the same even though 216 // the child's priority was altered 217 EXPECT_EQ(kParentPriority, getpriority(PRIO_PROCESS, syscall(__NR_gettid))); 218 } 219 220 } // namespace 221 222 } // namespace testing 223 } // namespace gvisor