//===-- Unittests for sched_{set,get}{scheduler,param} --------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "src/errno/libc_errno.h" #include "src/sched/sched_get_priority_max.h" #include "src/sched/sched_get_priority_min.h" #include "src/sched/sched_getparam.h" #include "src/sched/sched_getscheduler.h" #include "src/sched/sched_setparam.h" #include "src/sched/sched_setscheduler.h" #include "src/unistd/getuid.h" #include "test/UnitTest/Test.h" #include // We Test: // SCHED_OTHER, SCHED_FIFO, SCHED_RR // // TODO: Missing two tests. // 1) Missing permissions -> EPERM. Maybe doable by finding // another pid that exists and changing its policy, but that // seems risky. Maybe something with fork/clone would work. // // 2) Unkown pid -> ESRCH. Probably safe to choose a large range // number or scanning current pids and getting one that doesn't // exist, but again seems like it may risk actually changing // sched policy on a running task. // // Linux specific test could also include: // SCHED_ISO, SCHED_DEADLINE class SchedTest : public LIBC_NAMESPACE::testing::Test { public: void testSched(int policy, bool can_set) { LIBC_NAMESPACE::libc_errno = 0; int init_policy = LIBC_NAMESPACE::sched_getscheduler(0); ASSERT_GE(init_policy, 0); ASSERT_ERRNO_SUCCESS(); int max_priority = LIBC_NAMESPACE::sched_get_priority_max(policy); ASSERT_GE(max_priority, 0); ASSERT_ERRNO_SUCCESS(); int min_priority = LIBC_NAMESPACE::sched_get_priority_min(policy); ASSERT_GE(min_priority, 0); ASSERT_ERRNO_SUCCESS(); struct sched_param param = {min_priority}; // Negative pid ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(-1, policy, ¶m), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; ASSERT_EQ(LIBC_NAMESPACE::sched_getscheduler(-1), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; // Invalid Policy ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy | 128, ¶m), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; // Out of bounds priority param.sched_priority = min_priority - 1; ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, ¶m), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; param.sched_priority = max_priority + 1; ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, ¶m), -1); // A bit hard to test as depending if we are root or not we can run into // different issues. ASSERT_TRUE(LIBC_NAMESPACE::libc_errno == EINVAL || LIBC_NAMESPACE::libc_errno == EPERM); LIBC_NAMESPACE::libc_errno = 0; // Some sched policies require permissions, so skip param.sched_priority = min_priority; // Success / missing permissions. ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, ¶m), can_set ? 0 : -1); ASSERT_TRUE(can_set ? (LIBC_NAMESPACE::libc_errno == 0) : (LIBC_NAMESPACE::libc_errno == EINVAL || LIBC_NAMESPACE::libc_errno == EPERM)); LIBC_NAMESPACE::libc_errno = 0; ASSERT_EQ(LIBC_NAMESPACE::sched_getscheduler(0), can_set ? policy : init_policy); ASSERT_ERRNO_SUCCESS(); // Out of bounds priority param.sched_priority = -1; ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, ¶m), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; param.sched_priority = max_priority + 1; ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, ¶m), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; for (int priority = min_priority; priority <= max_priority; ++priority) { ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, ¶m), 0); ASSERT_ERRNO_SUCCESS(); int init_priority = param.sched_priority; param.sched_priority = priority; // Negative pid ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(-1, ¶m), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(-1, ¶m), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; // Success / missing permissions ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, ¶m), can_set ? 0 : -1); ASSERT_TRUE(can_set ? (LIBC_NAMESPACE::libc_errno == 0) : (LIBC_NAMESPACE::libc_errno == EINVAL || LIBC_NAMESPACE::libc_errno == EPERM)); LIBC_NAMESPACE::libc_errno = 0; ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, ¶m), 0); ASSERT_ERRNO_SUCCESS(); ASSERT_EQ(param.sched_priority, can_set ? priority : init_priority); } // Null test ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, nullptr), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; } }; #define LIST_SCHED_TESTS(policy, can_set) \ using LlvmLibcSchedTest = SchedTest; \ TEST_F(LlvmLibcSchedTest, Sched_##policy) { testSched(policy, can_set); } // Root is required to set these policies. LIST_SCHED_TESTS(SCHED_FIFO, LIBC_NAMESPACE::getuid() == 0) LIST_SCHED_TESTS(SCHED_RR, LIBC_NAMESPACE::getuid() == 0) // No root is required to set these policies. LIST_SCHED_TESTS(SCHED_OTHER, true) LIST_SCHED_TESTS(SCHED_BATCH, true) LIST_SCHED_TESTS(SCHED_IDLE, true) TEST(LlvmLibcSchedParamAndSchedulerTest, NullParamTest) { LIBC_NAMESPACE::libc_errno = 0; ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, nullptr), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, nullptr), -1); ASSERT_ERRNO_EQ(EINVAL); LIBC_NAMESPACE::libc_errno = 0; }