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/__support/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 is_mandatory) {
40 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 = {.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_errno = 0;
59
60 ASSERT_EQ(LIBC_NAMESPACE::sched_getscheduler(-1), -1);
61 ASSERT_ERRNO_EQ(EINVAL);
62 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_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_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 on user privileges we can run into
78 // different issues.
79 ASSERT_TRUE(libc_errno == EINVAL || libc_errno == EPERM);
80 libc_errno = 0;
81
82 param.sched_priority = min_priority;
83 // Success/unsupported policy/missing permissions.
84 int setscheduler_result =
85 LIBC_NAMESPACE::sched_setscheduler(0, policy, &param);
86 ASSERT_TRUE(setscheduler_result == 0 || setscheduler_result == -1);
87 ASSERT_TRUE(
88 setscheduler_result != -1
89 ? (libc_errno == 0)
90 : ((!is_mandatory && libc_errno == EINVAL) || libc_errno == EPERM));
91 libc_errno = 0;
92
93 ASSERT_EQ(LIBC_NAMESPACE::sched_getscheduler(0),
94 setscheduler_result != -1 ? 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_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_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_errno = 0;
119
120 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(-1, &param), -1);
121 ASSERT_ERRNO_EQ(EINVAL);
122 libc_errno = 0;
123
124 // Success/unsupported policy/missing permissions
125 int setparam_result = LIBC_NAMESPACE::sched_setparam(0, &param);
126 ASSERT_TRUE(setparam_result == 0 || setparam_result == -1);
127 ASSERT_TRUE(setparam_result != -1
128 ? (libc_errno == 0)
129 : ((setscheduler_result == -1 && libc_errno == EINVAL) ||
130 libc_errno == EPERM));
131 libc_errno = 0;
132
133 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, &param), 0);
134 ASSERT_ERRNO_SUCCESS();
135
136 ASSERT_EQ(param.sched_priority,
137 setparam_result != -1 ? priority : init_priority);
138 }
139
140 // Null test
141 ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, nullptr), -1);
142 ASSERT_ERRNO_EQ(EINVAL);
143 libc_errno = 0;
144 }
145};
146
147#define LIST_SCHED_TESTS(policy, can_set) \
148 using LlvmLibcSchedTest = SchedTest; \
149 TEST_F(LlvmLibcSchedTest, Sched_##policy) { testSched(policy, can_set); }
150
151// Mandated by POSIX.
152LIST_SCHED_TESTS(SCHED_OTHER, true)
153LIST_SCHED_TESTS(SCHED_FIFO, true)
154LIST_SCHED_TESTS(SCHED_RR, true)
155
156// Linux extensions.
157LIST_SCHED_TESTS(SCHED_BATCH, true)
158LIST_SCHED_TESTS(SCHED_IDLE, true)
159
160TEST(LlvmLibcSchedParamAndSchedulerTest, NullParamTest) {
161 libc_errno = 0;
162
163 ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, nullptr), -1);
164 ASSERT_ERRNO_EQ(EINVAL);
165 libc_errno = 0;
166
167 ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, nullptr), -1);
168 ASSERT_ERRNO_EQ(EINVAL);
169 libc_errno = 0;
170}
171

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