1//===-- Unittests for sched_{set,get}{scheduler,param} --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "src/errno/libc_errno.h"
10#include "src/sched/sched_get_priority_max.h"
11#include "src/sched/sched_get_priority_min.h"
12#include "src/sched/sched_getparam.h"
13#include "src/sched/sched_getscheduler.h"
14#include "src/sched/sched_setparam.h"
15#include "src/sched/sched_setscheduler.h"
16#include "src/unistd/getuid.h"
17#include "test/UnitTest/Test.h"
18
19#include <sched.h>
20
21// We Test:
22// SCHED_OTHER, SCHED_FIFO, SCHED_RR
23//
24// TODO: Missing two tests.
25// 1) Missing permissions -> EPERM. Maybe doable by finding
26// another pid that exists and changing its policy, but that
27// seems risky. Maybe something with fork/clone would work.
28//
29// 2) Unkown pid -> ESRCH. Probably safe to choose a large range
30// number or scanning current pids and getting one that doesn't
31// exist, but again seems like it may risk actually changing
32// sched policy on a running task.
33//
34// Linux specific test could also include:
35// SCHED_ISO, SCHED_DEADLINE
36
37class SchedTest : public LIBC_NAMESPACE::testing::Test {
38public:
39 void testSched(int policy, bool can_set) {
40 LIBC_NAMESPACE::libc_errno = 0;
41
42 int init_policy = LIBC_NAMESPACE::sched_getscheduler(tid: 0);
43 ASSERT_GE(init_policy, 0);
44 ASSERT_ERRNO_SUCCESS();
45
46 int max_priority = LIBC_NAMESPACE::sched_get_priority_max(policy);
47 ASSERT_GE(max_priority, 0);
48 ASSERT_ERRNO_SUCCESS();
49 int min_priority = LIBC_NAMESPACE::sched_get_priority_min(policy);
50 ASSERT_GE(min_priority, 0);
51 ASSERT_ERRNO_SUCCESS();
52
53 struct sched_param param = {.sched_priority: min_priority};
54
55 // Negative pid
56 ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(-1, policy, &param), -1);
57 ASSERT_ERRNO_EQ(EINVAL);
58 LIBC_NAMESPACE::libc_errno = 0;
59
60 ASSERT_EQ(LIBC_NAMESPACE::sched_getscheduler(-1), -1);
61 ASSERT_ERRNO_EQ(EINVAL);
62 LIBC_NAMESPACE::libc_errno = 0;
63
64 // Invalid Policy
65 ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy | 128, &param), -1);
66 ASSERT_ERRNO_EQ(EINVAL);
67 LIBC_NAMESPACE::libc_errno = 0;
68
69 // Out of bounds priority
70 param.sched_priority = min_priority - 1;
71 ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, &param), -1);
72 ASSERT_ERRNO_EQ(EINVAL);
73 LIBC_NAMESPACE::libc_errno = 0;
74
75 param.sched_priority = max_priority + 1;
76 ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, &param), -1);
77 // A bit hard to test as depending if we are root or not we can run into
78 // different issues.
79 ASSERT_TRUE(LIBC_NAMESPACE::libc_errno == EINVAL ||
80 LIBC_NAMESPACE::libc_errno == EPERM);
81 LIBC_NAMESPACE::libc_errno = 0;
82
83 // Some sched policies require permissions, so skip
84 param.sched_priority = min_priority;
85 // Success / missing permissions.
86 ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, &param),
87 can_set ? 0 : -1);
88 ASSERT_TRUE(can_set ? (LIBC_NAMESPACE::libc_errno == 0)
89 : (LIBC_NAMESPACE::libc_errno == EINVAL ||
90 LIBC_NAMESPACE::libc_errno == EPERM));
91 LIBC_NAMESPACE::libc_errno = 0;
92
93 ASSERT_EQ(LIBC_NAMESPACE::sched_getscheduler(0),
94 can_set ? policy : init_policy);
95 ASSERT_ERRNO_SUCCESS();
96
97 // Out of bounds priority
98 param.sched_priority = -1;
99 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, &param), -1);
100 ASSERT_ERRNO_EQ(EINVAL);
101 LIBC_NAMESPACE::libc_errno = 0;
102
103 param.sched_priority = max_priority + 1;
104 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, &param), -1);
105 ASSERT_ERRNO_EQ(EINVAL);
106 LIBC_NAMESPACE::libc_errno = 0;
107
108 for (int priority = min_priority; priority <= max_priority; ++priority) {
109 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, &param), 0);
110 ASSERT_ERRNO_SUCCESS();
111 int init_priority = param.sched_priority;
112
113 param.sched_priority = priority;
114
115 // Negative pid
116 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(-1, &param), -1);
117 ASSERT_ERRNO_EQ(EINVAL);
118 LIBC_NAMESPACE::libc_errno = 0;
119
120 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(-1, &param), -1);
121 ASSERT_ERRNO_EQ(EINVAL);
122 LIBC_NAMESPACE::libc_errno = 0;
123
124 // Success / missing permissions
125 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, &param), can_set ? 0 : -1);
126 ASSERT_TRUE(can_set ? (LIBC_NAMESPACE::libc_errno == 0)
127 : (LIBC_NAMESPACE::libc_errno == EINVAL ||
128 LIBC_NAMESPACE::libc_errno == EPERM));
129 LIBC_NAMESPACE::libc_errno = 0;
130
131 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, &param), 0);
132 ASSERT_ERRNO_SUCCESS();
133
134 ASSERT_EQ(param.sched_priority, can_set ? priority : init_priority);
135 }
136
137 // Null test
138 ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, nullptr), -1);
139 ASSERT_ERRNO_EQ(EINVAL);
140 LIBC_NAMESPACE::libc_errno = 0;
141 }
142};
143
144#define LIST_SCHED_TESTS(policy, can_set) \
145 using LlvmLibcSchedTest = SchedTest; \
146 TEST_F(LlvmLibcSchedTest, Sched_##policy) { testSched(policy, can_set); }
147
148// Root is required to set these policies.
149LIST_SCHED_TESTS(SCHED_FIFO, LIBC_NAMESPACE::getuid() == 0)
150LIST_SCHED_TESTS(SCHED_RR, LIBC_NAMESPACE::getuid() == 0)
151
152// No root is required to set these policies.
153LIST_SCHED_TESTS(SCHED_OTHER, true)
154LIST_SCHED_TESTS(SCHED_BATCH, true)
155LIST_SCHED_TESTS(SCHED_IDLE, true)
156
157TEST(LlvmLibcSchedParamAndSchedulerTest, NullParamTest) {
158 LIBC_NAMESPACE::libc_errno = 0;
159
160 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, nullptr), -1);
161 ASSERT_ERRNO_EQ(EINVAL);
162 LIBC_NAMESPACE::libc_errno = 0;
163
164 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, nullptr), -1);
165 ASSERT_ERRNO_EQ(EINVAL);
166 LIBC_NAMESPACE::libc_errno = 0;
167}
168

source code of libc/test/src/sched/param_and_scheduler_test.cpp