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
37 class SchedTest : public LIBC_NAMESPACE::testing::Test {
38 public:
testSched(int policy,bool is_mandatory)39 void testSched(int policy, bool is_mandatory) {
40 LIBC_NAMESPACE::libc_errno = 0;
41
42 int init_policy = LIBC_NAMESPACE::sched_getscheduler(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 = {min_priority};
54
55 // Negative pid
56 ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(-1, policy, ¶m), -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, ¶m), -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, ¶m), -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, ¶m), -1);
77 // A bit hard to test as depending on user privileges 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 param.sched_priority = min_priority;
84 // Success/unsupported policy/missing permissions.
85 int setscheduler_result =
86 LIBC_NAMESPACE::sched_setscheduler(0, policy, ¶m);
87 ASSERT_TRUE(setscheduler_result == 0 || setscheduler_result == -1);
88 ASSERT_TRUE(
89 setscheduler_result != -1
90 ? (LIBC_NAMESPACE::libc_errno == 0)
91 : ((!is_mandatory && LIBC_NAMESPACE::libc_errno == EINVAL) ||
92 LIBC_NAMESPACE::libc_errno == EPERM));
93 LIBC_NAMESPACE::libc_errno = 0;
94
95 ASSERT_EQ(LIBC_NAMESPACE::sched_getscheduler(0),
96 setscheduler_result != -1 ? policy : init_policy);
97 ASSERT_ERRNO_SUCCESS();
98
99 // Out of bounds priority
100 param.sched_priority = -1;
101 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, ¶m), -1);
102 ASSERT_ERRNO_EQ(EINVAL);
103 LIBC_NAMESPACE::libc_errno = 0;
104
105 param.sched_priority = max_priority + 1;
106 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, ¶m), -1);
107 ASSERT_ERRNO_EQ(EINVAL);
108 LIBC_NAMESPACE::libc_errno = 0;
109
110 for (int priority = min_priority; priority <= max_priority; ++priority) {
111 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, ¶m), 0);
112 ASSERT_ERRNO_SUCCESS();
113 int init_priority = param.sched_priority;
114
115 param.sched_priority = priority;
116
117 // Negative pid
118 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(-1, ¶m), -1);
119 ASSERT_ERRNO_EQ(EINVAL);
120 LIBC_NAMESPACE::libc_errno = 0;
121
122 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(-1, ¶m), -1);
123 ASSERT_ERRNO_EQ(EINVAL);
124 LIBC_NAMESPACE::libc_errno = 0;
125
126 // Success/unsupported policy/missing permissions
127 int setparam_result = LIBC_NAMESPACE::sched_setparam(0, ¶m);
128 ASSERT_TRUE(setparam_result == 0 || setparam_result == -1);
129 ASSERT_TRUE(setparam_result != -1
130 ? (LIBC_NAMESPACE::libc_errno == 0)
131 : ((setscheduler_result == -1 &&
132 LIBC_NAMESPACE::libc_errno == EINVAL) ||
133 LIBC_NAMESPACE::libc_errno == EPERM));
134 LIBC_NAMESPACE::libc_errno = 0;
135
136 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, ¶m), 0);
137 ASSERT_ERRNO_SUCCESS();
138
139 ASSERT_EQ(param.sched_priority,
140 setparam_result != -1 ? priority : init_priority);
141 }
142
143 // Null test
144 ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, nullptr), -1);
145 ASSERT_ERRNO_EQ(EINVAL);
146 LIBC_NAMESPACE::libc_errno = 0;
147 }
148 };
149
150 #define LIST_SCHED_TESTS(policy, can_set) \
151 using LlvmLibcSchedTest = SchedTest; \
152 TEST_F(LlvmLibcSchedTest, Sched_##policy) { testSched(policy, can_set); }
153
154 // Mandated by POSIX.
LIST_SCHED_TESTS(SCHED_OTHER,true)155 LIST_SCHED_TESTS(SCHED_OTHER, true)
156 LIST_SCHED_TESTS(SCHED_FIFO, true)
157 LIST_SCHED_TESTS(SCHED_RR, true)
158
159 // Linux extensions.
160 LIST_SCHED_TESTS(SCHED_BATCH, true)
161 LIST_SCHED_TESTS(SCHED_IDLE, true)
162
163 TEST(LlvmLibcSchedParamAndSchedulerTest, NullParamTest) {
164 LIBC_NAMESPACE::libc_errno = 0;
165
166 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, nullptr), -1);
167 ASSERT_ERRNO_EQ(EINVAL);
168 LIBC_NAMESPACE::libc_errno = 0;
169
170 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, nullptr), -1);
171 ASSERT_ERRNO_EQ(EINVAL);
172 LIBC_NAMESPACE::libc_errno = 0;
173 }
174