← Documents Documentation/scheduler/sched-deadline.rst GitHub 원문 ↗

Linux 6.18.37 · Scheduler

SCHED_DEADLINE과 CBS/GRUB

EDF 선택, CBS 대역폭 격리, GRUB 회수, admission control과 CPU affinity까지 SCHED_DEADLINE의 동작을 연결해 설명합니다.

Source pathDocumentation/scheduler/sched-deadline.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

설정 전에 알아야 할 점

sched-deadline.rst:1-46

SCHED_DEADLINE은 일반 우선순위 조정 기능이 아니라 CPU 시간을 예약하는 실시간 스케줄링 클래스입니다. runtime, deadline, period를 잘못 조합하거나 시스템 전체 대역폭 한계를 과도하게 높이면 일반 태스크가 실행할 시간을 잃거나 실시간 보장이 무너질 수 있습니다.

구현은 두 역할로 나뉩니다. EDF는 절대 deadline이 가장 이른 runnable task를 고르고, CBS는 각 task가 period마다 runtime 이상을 소비하지 못하게 막습니다. EDF만 사용하면 과도하게 실행한 task가 다른 예약을 침범하지만 CBS를 함께 사용하면 task별 CPU 대역폭을 격리할 수 있습니다.

CBS의 deadline과 runtime 갱신

sched-deadline.rst:47-125
의미갱신 시점
runtime한 period에서 사용할 수 있는 CPU 시간사용자가 sched_attr로 설정
deadlineperiod 시작점부터 완료해야 하는 상대 시간wake-up 때 절대 deadline 계산에 사용
period예약 대역폭을 다시 공급하는 주기replenishment 때 다음 주기로 이동
remaining runtime현재 예약에서 남은 실행 시간실행한 시간만큼 감소
scheduling deadlineEDF 비교에 쓰는 절대 deadlinewake-up 또는 replenishment 때 갱신

wake-up 시점에는 남은 runtime을 기존 deadline까지의 남은 시간으로 나눈 값과 runtime/period를 비교합니다. 기존 예약을 그대로 유지했을 때 순간 대역폭이 예약 비율보다 커지거나 deadline이 이미 지났다면 새 deadline을 current time + deadline으로 잡고 runtime을 다시 채웁니다.

remaining runtime이 0 이하가 되면 task는 throttled 상태가 됩니다. 이 task는 replenishment 시각 전에는 runnable이어도 선택되지 않습니다. replenishment에서는 scheduling deadline에 period를 더하고 remaining runtime에 runtime을 더해 다음 예약을 준비합니다.

SCHED_FLAG_DL_OVERRUN을 설정하면 runtime 초과 사실을 SIGXCPU로 통지받을 수 있습니다. 신호의 수신 주체와 실제 초과를 일으킨 thread를 연결할 때는 process-directed signal이라는 제한을 함께 고려해야 합니다.

GRUB 상태와 0-lag 시각

sched-deadline.rst:126-224
GRUB task 상태 변화
Active Contending(a) blockActive Non-Contending(c) 0-lag timerInactive
Inactive(d) wake-upActive Contending
Active Non-Contending(b) early wake-upActive Contending

block 직후에는 예약 대역폭을 곧바로 회수하지 않습니다. 0-lag 시각까지 Active Non-Contending으로 남겨 실시간 보장을 유지하고, timer가 만료된 뒤에만 Inactive 대역폭으로 회수합니다.

Active Contending은 실행 중이거나 실행 가능한 상태입니다. task가 block되면 즉시 Inactive가 되지 않고 Active Non-Contending으로 이동합니다. 남은 runtime이 있는데 대역폭을 즉시 회수하면 task가 다시 깨어났을 때 원래 보장을 지킬 수 없기 때문입니다.

0-lag 시각은 deadline - (remaining runtime × dl_period / dl_runtime)으로 계산합니다. 그 시각까지 task가 깨어나지 않으면 inactive timer가 running_bw에서 해당 utilization을 제거합니다. 먼저 깨어나면 timer를 취소하고 다시 Active Contending으로 돌아갑니다.

runqueue 값포함 범위용도
running_bwActive Contending + Active Non-Contending현재 보장해야 하는 활성 대역폭
this_bwInactive를 포함해 runqueue에 속한 모든 deadline task전체 예약량
max_bwdeadline class가 쓸 수 있는 최대 대역폭RT throttling 한계 반영

두 task가 대역폭을 회수하는 시간축

sched-deadline.rst:225-289
runtime 4, period 8인 두 deadline task
Task T1
runrun
t=2에 block, t=4의 0-lag 이후 Inactive
Task T2
runrunreclaimreclaimreclaimreclaim
t=4부터 T1의 미사용 대역폭을 회수하므로 runtime 감소 속도가 완만해짐
running_bw
1.01.01.01.0
T1의 0-lag timer가 만료되면 1.0에서 0.5로 감소
time
dq = -(max(Ui, Umax - Uinact - Uextra) / Umax) dt

t=0에는 두 예약이 모두 활성입니다. T1이 t=2에 block되어도 t=4까지 대역폭은 유지됩니다. t=4 이후 T1이 Inactive가 되면 T2가 그 대역폭을 회수합니다.

회수는 실행 task의 runtime 차감 속도를 조정하는 방식입니다. inactive utilization이 커질수록 실행 task의 예산은 실제 시간보다 느리게 줄어듭니다. 따라서 새로운 CPU 시간을 만드는 것이 아니라 이미 예약되었으나 쓰이지 않는 시간을 다른 deadline task가 빌려 쓰는 구조입니다.

실시간 task 모델과 schedulability

sched-deadline.rst:303-551

주기 또는 sporadic task의 각 job은 arrival time r, computation time c, absolute deadline d로 기술합니다. WCET는 모든 job의 c 중 최댓값이며 상대 deadline D는 d = r + D 관계로 연결됩니다. 커널의 runtime은 보장하려는 실행 예산, period는 최소 도착 간격, deadline은 상대 deadline에 대응합니다.

단일 CPU에서 deadline과 period가 같은 implicit-deadline task는 utilization 합이 1 이하이면 EDF로 스케줄 가능합니다. deadline이 period보다 짧은 constrained-deadline task는 단순 utilization만으로 충분하지 않으며 demand bound function으로 모든 구간의 요구량을 검사해야 합니다.

다중 CPU에서는 global EDF의 migration 비용과 partitioned EDF의 bin-packing 문제가 추가됩니다. Linux는 root domain과 CPU affinity 범위 안에서 deadline bandwidth admission을 수행하므로 cpuset 또는 affinity 변경도 예약 가능성 판정에 영향을 줍니다.

이론 값SCHED_DEADLINE 값주의점
WCETruntime측정 평균이 아니라 보장할 최악 실행량을 기준으로 잡아야 함
relative deadline Ddeadline일반적으로 runtime ≤ deadline
minimum inter-arrival Pperiod일반적으로 deadline ≤ period
utilization C/Pruntime/periodadmission control이 합계를 제한

대역폭 관리와 사용자 인터페이스

sched-deadline.rst:552-671

시스템 전체 deadline 대역폭은 /proc/sys/kernel/sched_rt_period_us와 sched_rt_runtime_us의 영향을 받습니다. runtime을 -1로 두면 RT throttling을 끄지만 일반 task starvation을 막는 보호막도 사라집니다.

task별 속성은 sched_setattr()와 sched_getattr()로 설정하고 읽습니다. sched_policy를 SCHED_DEADLINE으로 지정한 뒤 sched_runtime, sched_deadline, sched_period를 나노초 단위로 제공합니다. 커널은 기본 관계와 admission 조건을 검증하고 불가능한 예약은 EINVAL 또는 EBUSY로 거부합니다.

sched_yield()는 남은 runtime을 단순히 다른 task에게 양도하는 일반 yield와 의미가 다릅니다. deadline task가 호출하면 현재 job을 완료한 것으로 취급하여 다음 period까지 실행되지 않을 수 있으므로 반복문 안의 관습적인 yield 용도로 사용하면 안 됩니다.

CPU affinity, cpuset과 시험 방법

sched-deadline.rst:672-920

deadline task를 특정 CPU 집합에 묶으려면 그 집합의 root domain에서 admission이 성립해야 합니다. cgroup v1 cpuset과 cgroup v2 cpuset partition은 root domain을 나누는 방법과 활성화 순서가 다르므로 문서의 절차대로 CPU와 memory node를 배치해야 합니다.

시험은 kernel selftests의 sched deadline 항목과 cyclictest 같은 latency 도구를 함께 사용합니다. 단순히 deadline miss 개수만 보지 말고 runtime depletion, throttling, migration, cpufreq 전환 지연과 IRQ 간섭을 tracepoint로 확인해야 원인을 분리할 수 있습니다.

부록의 최소 C 프로그램은 sched_setattr() syscall wrapper와 주기 실행 loop를 보여 주는 출발점입니다. 실제 제품 코드에서는 mlockall(), page fault 사전 제거, clock source 선택, error path와 권한 설정까지 추가해야 합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 ========================
2 Deadline Task Scheduling
3 ========================
4
5 .. CONTENTS
6
7 0. WARNING
8 1. Overview
9 2. Scheduling algorithm
10 2.1 Main algorithm
11 2.2 Bandwidth reclaiming
12 3. Scheduling Real-Time Tasks
13 3.1 Definitions
14 3.2 Schedulability Analysis for Uniprocessor Systems
15 3.3 Schedulability Analysis for Multiprocessor Systems
16 3.4 Relationship with SCHED_DEADLINE Parameters
17 4. Bandwidth management
18 4.1 System-wide settings
19 4.2 Task interface
20 4.3 Default behavior
21 4.4 Behavior of sched_yield()
22 5. Tasks CPU affinity
23 5.1 Using cgroup v1 cpuset controller
24 5.2 Using cgroup v2 cpuset controller
25 6. Future plans
26 A. Test suite
27 B. Minimal main()
28
29
30 0. WARNING
31 ==========
32
33 Fiddling with these settings can result in an unpredictable or even unstable
34 system behavior. As for -rt (group) scheduling, it is assumed that root users
35 know what they're doing.
36
37
38 1. Overview
39 ===========
40
41 The SCHED_DEADLINE policy contained inside the sched_dl scheduling class is
42 basically an implementation of the Earliest Deadline First (EDF) scheduling
43 algorithm, augmented with a mechanism (called Constant Bandwidth Server, CBS)
44 that makes it possible to isolate the behavior of tasks between each other.
45
46
47 2. Scheduling algorithm
48 =======================
49
50 2.1 Main algorithm
51 ------------------
52
53 SCHED_DEADLINE [18] uses three parameters, named "runtime", "period", and
54 "deadline", to schedule tasks. A SCHED_DEADLINE task should receive
55 "runtime" microseconds of execution time every "period" microseconds, and
56 these "runtime" microseconds are available within "deadline" microseconds
57 from the beginning of the period. In order to implement this behavior,
58 every time the task wakes up, the scheduler computes a "scheduling deadline"
59 consistent with the guarantee (using the CBS[2,3] algorithm). Tasks are then
60 scheduled using EDF[1] on these scheduling deadlines (the task with the
61 earliest scheduling deadline is selected for execution). Notice that the
62 task actually receives "runtime" time units within "deadline" if a proper
63 "admission control" strategy (see Section "4. Bandwidth management") is used
64 (clearly, if the system is overloaded this guarantee cannot be respected).
65
66 Summing up, the CBS[2,3] algorithm assigns scheduling deadlines to tasks so
67 that each task runs for at most its runtime every period, avoiding any
68 interference between different tasks (bandwidth isolation), while the EDF[1]
69 algorithm selects the task with the earliest scheduling deadline as the one
70 to be executed next. Thanks to this feature, tasks that do not strictly comply
71 with the "traditional" real-time task model (see Section 3) can effectively
72 use the new policy.
73
74 In more details, the CBS algorithm assigns scheduling deadlines to
75 tasks in the following way:
76
77 - Each SCHED_DEADLINE task is characterized by the "runtime",
78 "deadline", and "period" parameters;
79
80 - The state of the task is described by a "scheduling deadline", and
81 a "remaining runtime". These two parameters are initially set to 0;
82
83 - When a SCHED_DEADLINE task wakes up (becomes ready for execution),
84 the scheduler checks if::
85
86 remaining runtime runtime
87 ---------------------------------- > ---------
88 scheduling deadline - current time period
89
90 then, if the scheduling deadline is smaller than the current time, or
91 this condition is verified, the scheduling deadline and the
92 remaining runtime are re-initialized as
93
94 scheduling deadline = current time + deadline
95 remaining runtime = runtime
96
97 otherwise, the scheduling deadline and the remaining runtime are
98 left unchanged;
99
100 - When a SCHED_DEADLINE task executes for an amount of time t, its
101 remaining runtime is decreased as::
102
103 remaining runtime = remaining runtime - t
104
105 (technically, the runtime is decreased at every tick, or when the
106 task is descheduled / preempted);
107
108 - When the remaining runtime becomes less or equal than 0, the task is
109 said to be "throttled" (also known as "depleted" in real-time literature)
110 and cannot be scheduled until its scheduling deadline. The "replenishment
111 time" for this task (see next item) is set to be equal to the current
112 value of the scheduling deadline;
113
114 - When the current time is equal to the replenishment time of a
115 throttled task, the scheduling deadline and the remaining runtime are
116 updated as::
117
118 scheduling deadline = scheduling deadline + period
119 remaining runtime = remaining runtime + runtime
120
121 The SCHED_FLAG_DL_OVERRUN flag in sched_attr's sched_flags field allows a task
122 to get informed about runtime overruns through the delivery of SIGXCPU
123 signals.
124
125
126 2.2 Bandwidth reclaiming
127 ------------------------
128
129 Bandwidth reclaiming for deadline tasks is based on the GRUB (Greedy
130 Reclamation of Unused Bandwidth) algorithm [15, 16, 17] and it is enabled
131 when flag SCHED_FLAG_RECLAIM is set.
132
133 The following diagram illustrates the state names for tasks handled by GRUB::
134
135 ------------
136 (d) | Active |
137 ------------->| |
138 | | Contending |
139 | ------------
140 | A |
141 ---------- | |
142 | | | |
143 | Inactive | |(b) | (a)
144 | | | |
145 ---------- | |
146 A | V
147 | ------------
148 | | Active |
149 --------------| Non |
150 (c) | Contending |
151 ------------
152
153 A task can be in one of the following states:
154
155 - ActiveContending: if it is ready for execution (or executing);
156
157 - ActiveNonContending: if it just blocked and has not yet surpassed the 0-lag
158 time;
159
160 - Inactive: if it is blocked and has surpassed the 0-lag time.
161
162 State transitions:
163
164 (a) When a task blocks, it does not become immediately inactive since its
165 bandwidth cannot be immediately reclaimed without breaking the
166 real-time guarantees. It therefore enters a transitional state called
167 ActiveNonContending. The scheduler arms the "inactive timer" to fire at
168 the 0-lag time, when the task's bandwidth can be reclaimed without
169 breaking the real-time guarantees.
170
171 The 0-lag time for a task entering the ActiveNonContending state is
172 computed as::
173
174 (runtime * dl_period)
175 deadline - ---------------------
176 dl_runtime
177
178 where runtime is the remaining runtime, while dl_runtime and dl_period
179 are the reservation parameters.
180
181 (b) If the task wakes up before the inactive timer fires, the task re-enters
182 the ActiveContending state and the "inactive timer" is canceled.
183 In addition, if the task wakes up on a different runqueue, then
184 the task's utilization must be removed from the previous runqueue's active
185 utilization and must be added to the new runqueue's active utilization.
186 In order to avoid races between a task waking up on a runqueue while the
187 "inactive timer" is running on a different CPU, the "dl_non_contending"
188 flag is used to indicate that a task is not on a runqueue but is active
189 (so, the flag is set when the task blocks and is cleared when the
190 "inactive timer" fires or when the task wakes up).
191
192 (c) When the "inactive timer" fires, the task enters the Inactive state and
193 its utilization is removed from the runqueue's active utilization.
194
195 (d) When an inactive task wakes up, it enters the ActiveContending state and
196 its utilization is added to the active utilization of the runqueue where
197 it has been enqueued.
198
199 For each runqueue, the algorithm GRUB keeps track of two different bandwidths:
200
201 - Active bandwidth (running_bw): this is the sum of the bandwidths of all
202 tasks in active state (i.e., ActiveContending or ActiveNonContending);
203
204 - Total bandwidth (this_bw): this is the sum of all tasks "belonging" to the
205 runqueue, including the tasks in Inactive state.
206
207 - Maximum usable bandwidth (max_bw): This is the maximum bandwidth usable by
208 deadline tasks and is currently set to the RT capacity.
209
210
211 The algorithm reclaims the bandwidth of the tasks in Inactive state.
212 It does so by decrementing the runtime of the executing task Ti at a pace equal
213 to
214
215 dq = -(max{ Ui, (Umax - Uinact - Uextra) } / Umax) dt
216
217 where:
218
219 - Ui is the bandwidth of task Ti;
220 - Umax is the maximum reclaimable utilization (subjected to RT throttling
221 limits);
222 - Uinact is the (per runqueue) inactive utilization, computed as
223 (this_bq - running_bw);
224 - Uextra is the (per runqueue) extra reclaimable utilization
225 (subjected to RT throttling limits).
226
227
228 Let's now see a trivial example of two deadline tasks with runtime equal
229 to 4 and period equal to 8 (i.e., bandwidth equal to 0.5)::
230
231 A Task T1
232 |
233 | |
234 | |
235 |-------- |----
236 | | V
237 |---|---|---|---|---|---|---|---|--------->t
238 0 1 2 3 4 5 6 7 8
239
240
241 A Task T2
242 |
243 | |
244 | |
245 | ------------------------|
246 | | V
247 |---|---|---|---|---|---|---|---|--------->t
248 0 1 2 3 4 5 6 7 8
249
250
251 A running_bw
252 |
253 1 ----------------- ------
254 | | |
255 0.5- -----------------
256 | |
257 |---|---|---|---|---|---|---|---|--------->t
258 0 1 2 3 4 5 6 7 8
259
260
261 - Time t = 0:
262
263 Both tasks are ready for execution and therefore in ActiveContending state.
264 Suppose Task T1 is the first task to start execution.
265 Since there are no inactive tasks, its runtime is decreased as dq = -1 dt.
266
267 - Time t = 2:
268
269 Suppose that task T1 blocks
270 Task T1 therefore enters the ActiveNonContending state. Since its remaining
271 runtime is equal to 2, its 0-lag time is equal to t = 4.
272 Task T2 start execution, with runtime still decreased as dq = -1 dt since
273 there are no inactive tasks.
274
275 - Time t = 4:
276
277 This is the 0-lag time for Task T1. Since it didn't woken up in the
278 meantime, it enters the Inactive state. Its bandwidth is removed from
279 running_bw.
280 Task T2 continues its execution. However, its runtime is now decreased as
281 dq = - 0.5 dt because Uinact = 0.5.
282 Task T2 therefore reclaims the bandwidth unused by Task T1.
283
284 - Time t = 8:
285
286 Task T1 wakes up. It enters the ActiveContending state again, and the
287 running_bw is incremented.
288
289
290 2.3 Energy-aware scheduling
291 ---------------------------
292
293 When cpufreq's schedutil governor is selected, SCHED_DEADLINE implements the
294 GRUB-PA [19] algorithm, reducing the CPU operating frequency to the minimum
295 value that still allows to meet the deadlines. This behavior is currently
296 implemented only for ARM architectures.
297
298 A particular care must be taken in case the time needed for changing frequency
299 is of the same order of magnitude of the reservation period. In such cases,
300 setting a fixed CPU frequency results in a lower amount of deadline misses.
301
302
303 3. Scheduling Real-Time Tasks
304 =============================
305
306
307
308 .. BIG FAT WARNING ******************************************************
309
310 .. warning::
311
312 This section contains a (not-thorough) summary on classical deadline
313 scheduling theory, and how it applies to SCHED_DEADLINE.
314 The reader can "safely" skip to Section 4 if only interested in seeing
315 how the scheduling policy can be used. Anyway, we strongly recommend
316 to come back here and continue reading (once the urge for testing is
317 satisfied :P) to be sure of fully understanding all technical details.
318
319 .. ************************************************************************
320
321 There are no limitations on what kind of task can exploit this new
322 scheduling discipline, even if it must be said that it is particularly
323 suited for periodic or sporadic real-time tasks that need guarantees on their
324 timing behavior, e.g., multimedia, streaming, control applications, etc.
325
326 3.1 Definitions
327 ------------------------
328
329 A typical real-time task is composed of a repetition of computation phases
330 (task instances, or jobs) which are activated on a periodic or sporadic
331 fashion.
332 Each job J_j (where J_j is the j^th job of the task) is characterized by an
333 arrival time r_j (the time when the job starts), an amount of computation
334 time c_j needed to finish the job, and a job absolute deadline d_j, which
335 is the time within which the job should be finished. The maximum execution
336 time max{c_j} is called "Worst Case Execution Time" (WCET) for the task.
337 A real-time task can be periodic with period P if r_{j+1} = r_j + P, or
338 sporadic with minimum inter-arrival time P is r_{j+1} >= r_j + P. Finally,
339 d_j = r_j + D, where D is the task's relative deadline.
340 Summing up, a real-time task can be described as
341
342 Task = (WCET, D, P)
343
344 The utilization of a real-time task is defined as the ratio between its
345 WCET and its period (or minimum inter-arrival time), and represents
346 the fraction of CPU time needed to execute the task.
347
348 If the total utilization U=sum(WCET_i/P_i) is larger than M (with M equal
349 to the number of CPUs), then the scheduler is unable to respect all the
350 deadlines.
351 Note that total utilization is defined as the sum of the utilizations
352 WCET_i/P_i over all the real-time tasks in the system. When considering
353 multiple real-time tasks, the parameters of the i-th task are indicated
354 with the "_i" suffix.
355 Moreover, if the total utilization is larger than M, then we risk starving
356 non- real-time tasks by real-time tasks.
357 If, instead, the total utilization is smaller than M, then non real-time
358 tasks will not be starved and the system might be able to respect all the
359 deadlines.
360 As a matter of fact, in this case it is possible to provide an upper bound
361 for tardiness (defined as the maximum between 0 and the difference
362 between the finishing time of a job and its absolute deadline).
363 More precisely, it can be proven that using a global EDF scheduler the
364 maximum tardiness of each task is smaller or equal than
365
366 ((M − 1) · WCET_max − WCET_min)/(M − (M − 2) · U_max) + WCET_max
367
368 where WCET_max = max{WCET_i} is the maximum WCET, WCET_min=min{WCET_i}
369 is the minimum WCET, and U_max = max{WCET_i/P_i} is the maximum
370 utilization[12].
371
372 3.2 Schedulability Analysis for Uniprocessor Systems
373 ----------------------------------------------------
374
375 If M=1 (uniprocessor system), or in case of partitioned scheduling (each
376 real-time task is statically assigned to one and only one CPU), it is
377 possible to formally check if all the deadlines are respected.
378 If D_i = P_i for all tasks, then EDF is able to respect all the deadlines
379 of all the tasks executing on a CPU if and only if the total utilization
380 of the tasks running on such a CPU is smaller or equal than 1.
381 If D_i != P_i for some task, then it is possible to define the density of
382 a task as WCET_i/min{D_i,P_i}, and EDF is able to respect all the deadlines
383 of all the tasks running on a CPU if the sum of the densities of the tasks
384 running on such a CPU is smaller or equal than 1:
385
386 sum(WCET_i / min{D_i, P_i}) <= 1
387
388 It is important to notice that this condition is only sufficient, and not
389 necessary: there are task sets that are schedulable, but do not respect the
390 condition. For example, consider the task set {Task_1,Task_2} composed by
391 Task_1=(50ms,50ms,100ms) and Task_2=(10ms,100ms,100ms).
392 EDF is clearly able to schedule the two tasks without missing any deadline
393 (Task_1 is scheduled as soon as it is released, and finishes just in time
394 to respect its deadline; Task_2 is scheduled immediately after Task_1, hence
395 its response time cannot be larger than 50ms + 10ms = 60ms) even if
396
397 50 / min{50,100} + 10 / min{100, 100} = 50 / 50 + 10 / 100 = 1.1
398
399 Of course it is possible to test the exact schedulability of tasks with
400 D_i != P_i (checking a condition that is both sufficient and necessary),
401 but this cannot be done by comparing the total utilization or density with
402 a constant. Instead, the so called "processor demand" approach can be used,
403 computing the total amount of CPU time h(t) needed by all the tasks to
404 respect all of their deadlines in a time interval of size t, and comparing
405 such a time with the interval size t. If h(t) is smaller than t (that is,
406 the amount of time needed by the tasks in a time interval of size t is
407 smaller than the size of the interval) for all the possible values of t, then
408 EDF is able to schedule the tasks respecting all of their deadlines. Since
409 performing this check for all possible values of t is impossible, it has been
410 proven[4,5,6] that it is sufficient to perform the test for values of t
411 between 0 and a maximum value L. The cited papers contain all of the
412 mathematical details and explain how to compute h(t) and L.
413 In any case, this kind of analysis is too complex as well as too
414 time-consuming to be performed on-line. Hence, as explained in Section
415 4 Linux uses an admission test based on the tasks' utilizations.
416
417 3.3 Schedulability Analysis for Multiprocessor Systems
418 ------------------------------------------------------
419
420 On multiprocessor systems with global EDF scheduling (non partitioned
421 systems), a sufficient test for schedulability can not be based on the
422 utilizations or densities: it can be shown that even if D_i = P_i task
423 sets with utilizations slightly larger than 1 can miss deadlines regardless
424 of the number of CPUs.
425
426 Consider a set {Task_1,...Task_{M+1}} of M+1 tasks on a system with M
427 CPUs, with the first task Task_1=(P,P,P) having period, relative deadline
428 and WCET equal to P. The remaining M tasks Task_i=(e,P-1,P-1) have an
429 arbitrarily small worst case execution time (indicated as "e" here) and a
430 period smaller than the one of the first task. Hence, if all the tasks
431 activate at the same time t, global EDF schedules these M tasks first
432 (because their absolute deadlines are equal to t + P - 1, hence they are
433 smaller than the absolute deadline of Task_1, which is t + P). As a
434 result, Task_1 can be scheduled only at time t + e, and will finish at
435 time t + e + P, after its absolute deadline. The total utilization of the
436 task set is U = M · e / (P - 1) + P / P = M · e / (P - 1) + 1, and for small
437 values of e this can become very close to 1. This is known as "Dhall's
438 effect"[7]. Note: the example in the original paper by Dhall has been
439 slightly simplified here (for example, Dhall more correctly computed
440 lim_{e->0}U).
441
442 More complex schedulability tests for global EDF have been developed in
443 real-time literature[8,9], but they are not based on a simple comparison
444 between total utilization (or density) and a fixed constant. If all tasks
445 have D_i = P_i, a sufficient schedulability condition can be expressed in
446 a simple way:
447
448 sum(WCET_i / P_i) <= M - (M - 1) · U_max
449
450 where U_max = max{WCET_i / P_i}[10]. Notice that for U_max = 1,
451 M - (M - 1) · U_max becomes M - M + 1 = 1 and this schedulability condition
452 just confirms the Dhall's effect. A more complete survey of the literature
453 about schedulability tests for multi-processor real-time scheduling can be
454 found in [11].
455
456 As seen, enforcing that the total utilization is smaller than M does not
457 guarantee that global EDF schedules the tasks without missing any deadline
458 (in other words, global EDF is not an optimal scheduling algorithm). However,
459 a total utilization smaller than M is enough to guarantee that non real-time
460 tasks are not starved and that the tardiness of real-time tasks has an upper
461 bound[12] (as previously noted). Different bounds on the maximum tardiness
462 experienced by real-time tasks have been developed in various papers[13,14],
463 but the theoretical result that is important for SCHED_DEADLINE is that if
464 the total utilization is smaller or equal than M then the response times of
465 the tasks are limited.
466
467 3.4 Relationship with SCHED_DEADLINE Parameters
468 -----------------------------------------------
469
470 Finally, it is important to understand the relationship between the
471 SCHED_DEADLINE scheduling parameters described in Section 2 (runtime,
472 deadline and period) and the real-time task parameters (WCET, D, P)
473 described in this section. Note that the tasks' temporal constraints are
474 represented by its absolute deadlines d_j = r_j + D described above, while
475 SCHED_DEADLINE schedules the tasks according to scheduling deadlines (see
476 Section 2).
477 If an admission test is used to guarantee that the scheduling deadlines
478 are respected, then SCHED_DEADLINE can be used to schedule real-time tasks
479 guaranteeing that all the jobs' deadlines of a task are respected.
480 In order to do this, a task must be scheduled by setting:
481
482 - runtime >= WCET
483 - deadline = D
484 - period <= P
485
486 IOW, if runtime >= WCET and if period is <= P, then the scheduling deadlines
487 and the absolute deadlines (d_j) coincide, so a proper admission control
488 allows to respect the jobs' absolute deadlines for this task (this is what is
489 called "hard schedulability property" and is an extension of Lemma 1 of [2]).
490 Notice that if runtime > deadline the admission control will surely reject
491 this task, as it is not possible to respect its temporal constraints.
492
493 References:
494
495 1 - C. L. Liu and J. W. Layland. Scheduling algorithms for multiprogram-
496 ming in a hard-real-time environment. Journal of the Association for
497 Computing Machinery, 20(1), 1973.
498 2 - L. Abeni , G. Buttazzo. Integrating Multimedia Applications in Hard
499 Real-Time Systems. Proceedings of the 19th IEEE Real-time Systems
500 Symposium, 1998. http://retis.sssup.it/~giorgio/paps/1998/rtss98-cbs.pdf
501 3 - L. Abeni. Server Mechanisms for Multimedia Applications. ReTiS Lab
502 Technical Report. http://disi.unitn.it/~abeni/tr-98-01.pdf
503 4 - J. Y. Leung and M.L. Merril. A Note on Preemptive Scheduling of
504 Periodic, Real-Time Tasks. Information Processing Letters, vol. 11,
505 no. 3, pp. 115-118, 1980.
506 5 - S. K. Baruah, A. K. Mok and L. E. Rosier. Preemptively Scheduling
507 Hard-Real-Time Sporadic Tasks on One Processor. Proceedings of the
508 11th IEEE Real-time Systems Symposium, 1990.
509 6 - S. K. Baruah, L. E. Rosier and R. R. Howell. Algorithms and Complexity
510 Concerning the Preemptive Scheduling of Periodic Real-Time tasks on
511 One Processor. Real-Time Systems Journal, vol. 4, no. 2, pp 301-324,
512 1990.
513 7 - S. J. Dhall and C. L. Liu. On a real-time scheduling problem. Operations
514 research, vol. 26, no. 1, pp 127-140, 1978.
515 8 - T. Baker. Multiprocessor EDF and Deadline Monotonic Schedulability
516 Analysis. Proceedings of the 24th IEEE Real-Time Systems Symposium, 2003.
517 9 - T. Baker. An Analysis of EDF Schedulability on a Multiprocessor.
518 IEEE Transactions on Parallel and Distributed Systems, vol. 16, no. 8,
519 pp 760-768, 2005.
520 10 - J. Goossens, S. Funk and S. Baruah, Priority-Driven Scheduling of
521 Periodic Task Systems on Multiprocessors. Real-Time Systems Journal,
522 vol. 25, no. 2–3, pp. 187–205, 2003.
523 11 - R. Davis and A. Burns. A Survey of Hard Real-Time Scheduling for
524 Multiprocessor Systems. ACM Computing Surveys, vol. 43, no. 4, 2011.
525 http://www-users.cs.york.ac.uk/~robdavis/papers/MPSurveyv5.0.pdf
526 12 - U. C. Devi and J. H. Anderson. Tardiness Bounds under Global EDF
527 Scheduling on a Multiprocessor. Real-Time Systems Journal, vol. 32,
528 no. 2, pp 133-189, 2008.
529 13 - P. Valente and G. Lipari. An Upper Bound to the Lateness of Soft
530 Real-Time Tasks Scheduled by EDF on Multiprocessors. Proceedings of
531 the 26th IEEE Real-Time Systems Symposium, 2005.
532 14 - J. Erickson, U. Devi and S. Baruah. Improved tardiness bounds for
533 Global EDF. Proceedings of the 22nd Euromicro Conference on
534 Real-Time Systems, 2010.
535 15 - G. Lipari, S. Baruah, Greedy reclamation of unused bandwidth in
536 constant-bandwidth servers, 12th IEEE Euromicro Conference on Real-Time
537 Systems, 2000.
538 16 - L. Abeni, J. Lelli, C. Scordino, L. Palopoli, Greedy CPU reclaiming for
539 SCHED DEADLINE. In Proceedings of the Real-Time Linux Workshop (RTLWS),
540 Dusseldorf, Germany, 2014.
541 17 - L. Abeni, G. Lipari, A. Parri, Y. Sun, Multicore CPU reclaiming: parallel
542 or sequential?. In Proceedings of the 31st Annual ACM Symposium on Applied
543 Computing, 2016.
544 18 - J. Lelli, C. Scordino, L. Abeni, D. Faggioli, Deadline scheduling in the
545 Linux kernel, Software: Practice and Experience, 46(6): 821-839, June
546 2016.
547 19 - C. Scordino, L. Abeni, J. Lelli, Energy-Aware Real-Time Scheduling in
548 the Linux Kernel, 33rd ACM/SIGAPP Symposium On Applied Computing (SAC
549 2018), Pau, France, April 2018.
550
551
552 4. Bandwidth management
553 =======================
554
555 As previously mentioned, in order for -deadline scheduling to be
556 effective and useful (that is, to be able to provide "runtime" time units
557 within "deadline"), it is important to have some method to keep the allocation
558 of the available fractions of CPU time to the various tasks under control.
559 This is usually called "admission control" and if it is not performed, then
560 no guarantee can be given on the actual scheduling of the -deadline tasks.
561
562 As already stated in Section 3, a necessary condition to be respected to
563 correctly schedule a set of real-time tasks is that the total utilization
564 is smaller than M. When talking about -deadline tasks, this requires that
565 the sum of the ratio between runtime and period for all tasks is smaller
566 than M. Notice that the ratio runtime/period is equivalent to the utilization
567 of a "traditional" real-time task, and is also often referred to as
568 "bandwidth".
569 The interface used to control the CPU bandwidth that can be allocated
570 to -deadline tasks is similar to the one already used for -rt
571 tasks with real-time group scheduling (a.k.a. RT-throttling - see
572 Documentation/scheduler/sched-rt-group.rst), and is based on readable/
573 writable control files located in procfs (for system wide settings).
574 Notice that per-group settings (controlled through cgroupfs) are still not
575 defined for -deadline tasks, because more discussion is needed in order to
576 figure out how we want to manage SCHED_DEADLINE bandwidth at the task group
577 level.
578
579 A main difference between deadline bandwidth management and RT-throttling
580 is that -deadline tasks have bandwidth on their own (while -rt ones don't!),
581 and thus we don't need a higher level throttling mechanism to enforce the
582 desired bandwidth. In other words, this means that interface parameters are
583 only used at admission control time (i.e., when the user calls
584 sched_setattr()). Scheduling is then performed considering actual tasks'
585 parameters, so that CPU bandwidth is allocated to SCHED_DEADLINE tasks
586 respecting their needs in terms of granularity. Therefore, using this simple
587 interface we can put a cap on total utilization of -deadline tasks (i.e.,
588 \Sum (runtime_i / period_i) < global_dl_utilization_cap).
589
590 4.1 System wide settings
591 ------------------------
592
593 The system wide settings are configured under the /proc virtual file system.
594
595 For now the -rt knobs are used for -deadline admission control and with
596 CONFIG_RT_GROUP_SCHED the -deadline runtime is accounted against the (root)
597 -rt runtime. With !CONFIG_RT_GROUP_SCHED the knob only serves for the -dl
598 admission control. We realize that this isn't entirely desirable; however, it
599 is better to have a small interface for now, and be able to change it easily
600 later. The ideal situation (see 5.) is to run -rt tasks from a -deadline
601 server; in which case the -rt bandwidth is a direct subset of dl_bw.
602
603 This means that, for a root_domain comprising M CPUs, -deadline tasks
604 can be created while the sum of their bandwidths stays below:
605
606 M * (sched_rt_runtime_us / sched_rt_period_us)
607
608 It is also possible to disable this bandwidth management logic, and
609 be thus free of oversubscribing the system up to any arbitrary level.
610 This is done by writing -1 in /proc/sys/kernel/sched_rt_runtime_us.
611
612
613 4.2 Task interface
614 ------------------
615
616 Specifying a periodic/sporadic task that executes for a given amount of
617 runtime at each instance, and that is scheduled according to the urgency of
618 its own timing constraints needs, in general, a way of declaring:
619
620 - a (maximum/typical) instance execution time,
621 - a minimum interval between consecutive instances,
622 - a time constraint by which each instance must be completed.
623
624 Therefore:
625
626 * a new struct sched_attr, containing all the necessary fields is
627 provided;
628 * the new scheduling related syscalls that manipulate it, i.e.,
629 sched_setattr() and sched_getattr() are implemented.
630
631 For debugging purposes, the leftover runtime and absolute deadline of a
632 SCHED_DEADLINE task can be retrieved through /proc/<pid>/sched (entries
633 dl.runtime and dl.deadline, both values in ns). A programmatic way to
634 retrieve these values from production code is under discussion.
635
636
637 4.3 Default behavior
638 ---------------------
639
640 The default value for SCHED_DEADLINE bandwidth is to have rt_runtime equal to
641 950000. With rt_period equal to 1000000, by default, it means that -deadline
642 tasks can use at most 95%, multiplied by the number of CPUs that compose the
643 root_domain, for each root_domain.
644 This means that non -deadline tasks will receive at least 5% of the CPU time,
645 and that -deadline tasks will receive their runtime with a guaranteed
646 worst-case delay respect to the "deadline" parameter. If "deadline" = "period"
647 and the cpuset mechanism is used to implement partitioned scheduling (see
648 Section 5), then this simple setting of the bandwidth management is able to
649 deterministically guarantee that -deadline tasks will receive their runtime
650 in a period.
651
652 Finally, notice that in order not to jeopardize the admission control a
653 -deadline task cannot fork.
654
655
656 4.4 Behavior of sched_yield()
657 -----------------------------
658
659 When a SCHED_DEADLINE task calls sched_yield(), it gives up its
660 remaining runtime and is immediately throttled, until the next
661 period, when its runtime will be replenished (a special flag
662 dl_yielded is set and used to handle correctly throttling and runtime
663 replenishment after a call to sched_yield()).
664
665 This behavior of sched_yield() allows the task to wake-up exactly at
666 the beginning of the next period. Also, this may be useful in the
667 future with bandwidth reclaiming mechanisms, where sched_yield() will
668 make the leftoever runtime available for reclamation by other
669 SCHED_DEADLINE tasks.
670
671
672 5. Tasks CPU affinity
673 =====================
674
675 Deadline tasks cannot have a cpu affinity mask smaller than the root domain they
676 are created on. So, using ``sched_setaffinity(2)`` won't work. Instead, the
677 the deadline task should be created in a restricted root domain. This can be
678 done using the cpuset controller of either cgroup v1 (deprecated) or cgroup v2.
679 See :ref:`Documentation/admin-guide/cgroup-v1/cpusets.rst <cpusets>` and
680 :ref:`Documentation/admin-guide/cgroup-v2.rst <cgroup-v2>` for more information.
681
682 5.1 Using cgroup v1 cpuset controller
683 -------------------------------------
684
685 An example of a simple configuration (pin a -deadline task to CPU0) follows::
686
687 mkdir /dev/cpuset
688 mount -t cgroup -o cpuset cpuset /dev/cpuset
689 cd /dev/cpuset
690 mkdir cpu0
691 echo 0 > cpu0/cpuset.cpus
692 echo 0 > cpu0/cpuset.mems
693 echo 1 > cpuset.cpu_exclusive
694 echo 0 > cpuset.sched_load_balance
695 echo 1 > cpu0/cpuset.cpu_exclusive
696 echo 1 > cpu0/cpuset.mem_exclusive
697 echo $$ > cpu0/tasks
698 chrt --sched-runtime 100000 --sched-period 200000 --deadline 0 yes > /dev/null
699
700 5.2 Using cgroup v2 cpuset controller
701 -------------------------------------
702
703 Assuming the cgroup v2 root is mounted at ``/sys/fs/cgroup``.
704
705 cd /sys/fs/cgroup
706 echo '+cpuset' > cgroup.subtree_control
707 mkdir deadline_group
708 echo 0 > deadline_group/cpuset.cpus
709 echo 'root' > deadline_group/cpuset.cpus.partition
710 echo $$ > deadline_group/cgroup.procs
711 chrt --sched-runtime 100000 --sched-period 200000 --deadline 0 yes > /dev/null
712
713 6. Future plans
714 ===============
715
716 Still missing:
717
718 - programmatic way to retrieve current runtime and absolute deadline
719 - refinements to deadline inheritance, especially regarding the possibility
720 of retaining bandwidth isolation among non-interacting tasks. This is
721 being studied from both theoretical and practical points of view, and
722 hopefully we should be able to produce some demonstrative code soon;
723 - (c)group based bandwidth management, and maybe scheduling;
724 - access control for non-root users (and related security concerns to
725 address), which is the best way to allow unprivileged use of the mechanisms
726 and how to prevent non-root users "cheat" the system?
727
728 As already discussed, we are planning also to merge this work with the EDF
729 throttling patches [https://lore.kernel.org/r/[email protected]] but we still are in
730 the preliminary phases of the merge and we really seek feedback that would
731 help us decide on the direction it should take.
732
733 Appendix A. Test suite
734 ======================
735
736 The SCHED_DEADLINE policy can be easily tested using two applications that
737 are part of a wider Linux Scheduler validation suite. The suite is
738 available as a GitHub repository: https://github.com/scheduler-tools.
739
740 The first testing application is called rt-app and can be used to
741 start multiple threads with specific parameters. rt-app supports
742 SCHED_{OTHER,FIFO,RR,DEADLINE} scheduling policies and their related
743 parameters (e.g., niceness, priority, runtime/deadline/period). rt-app
744 is a valuable tool, as it can be used to synthetically recreate certain
745 workloads (maybe mimicking real use-cases) and evaluate how the scheduler
746 behaves under such workloads. In this way, results are easily reproducible.
747 rt-app is available at: https://github.com/scheduler-tools/rt-app.
748
749 rt-app does not accept command line arguments, and instead reads from a JSON
750 configuration file. Here is an example ``config.json``:
751
752 .. code-block:: json
753
754 {
755 "tasks": {
756 "dl_task": {
757 "policy": "SCHED_DEADLINE",
758 "priority": 0,
759 "dl-runtime": 10000,
760 "dl-period": 100000,
761 "dl-deadline": 100000
762 },
763 "fifo_task": {
764 "policy": "SCHED_FIFO",
765 "priority": 10,
766 "runtime": 20000,
767 "sleep": 130000
768 }
769 },
770 "global": {
771 "duration": 5
772 }
773 }
774
775 On running ``rt-app config.json``, it creates 2 threads. The first one,
776 scheduled by SCHED_DEADLINE, executes for 10ms every 100ms. The second one,
777 scheduled at SCHED_FIFO priority 10, executes for 20ms every 150ms. The test
778 will run for a total of 5 seconds.
779
780 Please refer to the rt-app documentation for the JSON schema and more examples.
781
782 The second testing application is done using chrt which has support
783 for SCHED_DEADLINE.
784
785 The usage is straightforward::
786
787 # chrt -d -T 10000000 -D 100000000 0 ./my_cpuhog_app
788
789 With this, my_cpuhog_app is put to run inside a SCHED_DEADLINE reservation
790 of 10ms every 100ms (note that parameters are expressed in nanoseconds).
791 You can also use chrt to create a reservation for an already running
792 application, given that you know its pid::
793
794 # chrt -d -T 10000000 -D 100000000 -p 0 my_app_pid
795
796 Appendix B. Minimal main()
797 ==========================
798
799 We provide in what follows a simple (ugly) self-contained code snippet
800 showing how SCHED_DEADLINE reservations can be created by a real-time
801 application developer::
802
803 #define _GNU_SOURCE
804 #include <unistd.h>
805 #include <stdio.h>
806 #include <stdlib.h>
807 #include <string.h>
808 #include <time.h>
809 #include <linux/unistd.h>
810 #include <linux/kernel.h>
811 #include <linux/types.h>
812 #include <sys/syscall.h>
813 #include <pthread.h>
814
815 #define gettid() syscall(__NR_gettid)
816
817 #define SCHED_DEADLINE 6
818
819 /* XXX use the proper syscall numbers */
820 #ifdef __x86_64__
821 #define __NR_sched_setattr 314
822 #define __NR_sched_getattr 315
823 #endif
824
825 #ifdef __i386__
826 #define __NR_sched_setattr 351
827 #define __NR_sched_getattr 352
828 #endif
829
830 #ifdef __arm__
831 #define __NR_sched_setattr 380
832 #define __NR_sched_getattr 381
833 #endif
834
835 static volatile int done;
836
837 struct sched_attr {
838 __u32 size;
839
840 __u32 sched_policy;
841 __u64 sched_flags;
842
843 /* SCHED_NORMAL, SCHED_BATCH */
844 __s32 sched_nice;
845
846 /* SCHED_FIFO, SCHED_RR */
847 __u32 sched_priority;
848
849 /* SCHED_DEADLINE (nsec) */
850 __u64 sched_runtime;
851 __u64 sched_deadline;
852 __u64 sched_period;
853 };
854
855 int sched_setattr(pid_t pid,
856 const struct sched_attr *attr,
857 unsigned int flags)
858 {
859 return syscall(__NR_sched_setattr, pid, attr, flags);
860 }
861
862 int sched_getattr(pid_t pid,
863 struct sched_attr *attr,
864 unsigned int size,
865 unsigned int flags)
866 {
867 return syscall(__NR_sched_getattr, pid, attr, size, flags);
868 }
869
870 void *run_deadline(void *data)
871 {
872 struct sched_attr attr;
873 int x = 0;
874 int ret;
875 unsigned int flags = 0;
876
877 printf("deadline thread started [%ld]\n", gettid());
878
879 attr.size = sizeof(attr);
880 attr.sched_flags = 0;
881 attr.sched_nice = 0;
882 attr.sched_priority = 0;
883
884 /* This creates a 10ms/30ms reservation */
885 attr.sched_policy = SCHED_DEADLINE;
886 attr.sched_runtime = 10 * 1000 * 1000;
887 attr.sched_period = attr.sched_deadline = 30 * 1000 * 1000;
888
889 ret = sched_setattr(0, &attr, flags);
890 if (ret < 0) {
891 done = 0;
892 perror("sched_setattr");
893 exit(-1);
894 }
895
896 while (!done) {
897 x++;
898 }
899
900 printf("deadline thread dies [%ld]\n", gettid());
901 return NULL;
902 }
903
904 int main (int argc, char **argv)
905 {
906 pthread_t thread;
907
908 printf("main thread [%ld]\n", gettid());
909
910 pthread_create(&thread, NULL, run_deadline, NULL);
911
912 sleep(10);
913
914 done = 1;
915 pthread_join(thread, NULL);
916
917 printf("main dies [%ld]\n", gettid());
918 return 0;
919 }
920

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

주의 사항과 SCHED_DEADLINE 개요

1-46

SCHED_DEADLINE 및 RT bandwidth 설정을 잘못 바꾸면 system 동작이 예측 불가능해지거나 불안정해질 수 있다. 이 interface를 조정하는 root 사용자는 task model과 admission control을 이해해야 한다.

sched_dl scheduling class의 SCHED_DEADLINE policy는 Earliest Deadline First(EDF)를 기본 선택 알고리즘으로 사용하고 Constant Bandwidth Server(CBS)를 결합한다. EDF는 가장 이른 scheduling deadline을 가진 task를 고르고, CBS는 각 task가 예약한 bandwidth를 넘지 못하게 해 task 사이 간섭을 격리한다.

CBS와 EDF의 기본 동작

47-125

SCHED_DEADLINE task는 runtime, deadline, period 세 parameter를 가진다. 각 period마다 runtime만큼 CPU 실행 시간을 받고, 그 runtime은 period 시작에서 deadline 이내에 제공되어야 한다. task가 wakeup할 때 CBS가 이 보장과 일치하는 scheduling deadline을 계산하고, EDF가 가장 이른 scheduling deadline의 task를 실행한다.

admission control이 system overload를 막아야만 runtime을 deadline 안에 제공한다는 보장이 성립한다. CBS는 각 period에 task가 runtime보다 오래 실행하지 못하게 하여 bandwidth isolation을 제공하고, 전통적인 엄격한 periodic task model을 따르지 않는 workload도 reservation 안에 격리한다.

의미
runtime각 period에 예약된 최대 실행 시간
deadlineperiod 시작 뒤 runtime 제공을 완료해야 하는 상대 시간
periodreservation이 반복되는 간격
scheduling deadlineCBS가 EDF 선택에 사용하는 현재 절대 deadline
remaining runtime현재 reservation에서 남은 실행 budget

wakeup 시 scheduler는 아래 비율을 비교한다. scheduling deadline이 이미 현재 시각보다 작거나, 남은 시간에 비해 remaining runtime의 밀도가 원래 reservation bandwidth보다 크면 새로운 server instance로 재설정한다.

remaining_runtime / (scheduling_deadline - current_time) > runtime / period

scheduling_deadline = current_time + deadline
remaining_runtime = runtime

조건이 거짓이면 이전 scheduling deadline과 remaining runtime을 유지한다. task가 t만큼 실행할 때 remaining_runtime -= t로 budget을 차감하며, 실제 accounting은 tick 또는 deschedule/preemption 지점에서 수행한다.

remaining runtime이 0 이하가 되면 task는 throttled 또는 depleted 상태가 되어 현재 scheduling deadline까지 실행할 수 없다. 그 deadline을 replenishment time으로 삼고 시각이 도달하면 scheduling_deadline += period, remaining_runtime += runtime으로 다음 budget을 공급한다.

sched_attr.sched_flags에 SCHED_FLAG_DL_OVERRUN을 지정하면 task가 runtime을 초과했을 때 SIGXCPU signal을 받을 수 있다.

CBS reservation 한 주기
task wakeupdeadline 만료 또는 density 조건 검사필요하면 deadline/runtime 재설정EDF runqueue에서 가장 이른 deadline 선택실행 시간만큼 remaining runtime 차감0 이하이면 throttlereplenishment 시 period/runtime 가산

wakeup에서 budget을 검사하고 실행·throttle·replenishment로 이어지는 상태 흐름이다.

GRUB bandwidth reclaim

126-289

SCHED_FLAG_RECLAIM을 켜면 deadline task의 사용하지 않은 bandwidth를 GRUB(Greedy Reclamation of Unused Bandwidth) 알고리즘으로 회수한다.

상태조건
ActiveContending실행 중이거나 runqueue에서 실행 가능
ActiveNonContending방금 block했지만 아직 0-lag time을 지나지 않음
Inactiveblock 상태이며 0-lag time을 지남

task가 block하자마자 inactive가 되면 아직 보장에 필요한 bandwidth까지 다른 task가 가져가 real-time guarantee를 깨뜨릴 수 있다. 그래서 먼저 ActiveNonContending으로 이동하고 0-lag time에 inactive timer를 설정한다.

zero_lag_time = deadline - (remaining_runtime * dl_period) / dl_runtime

timer 전에 wakeup하면 timer를 취소하고 ActiveContending으로 돌아간다. 다른 runqueue에서 깨면 이전 runqueue의 active utilization에서 제거하고 새 runqueue에 더한다. 다른 CPU의 inactive timer와 wakeup race를 조정하기 위해 dl_non_contending flag가 runqueue 밖이지만 아직 active bandwidth에 포함된 상태를 표시한다.

inactive timer가 실행되면 Inactive가 되고 runqueue의 active utilization에서 제거된다. Inactive task가 다시 wakeup하면 새 runqueue의 active utilization에 bandwidth를 더하고 ActiveContending이 된다.

GRUB task 상태
ActiveContendingblockActiveNonContending0-lag timerInactivewakeupActiveContending
ActiveNonContending0-lag 전 wakeupActiveContending

block 직후에는 예약 보장을 위해 bandwidth를 바로 회수하지 않고 0-lag까지 유예한다.

runqueue 값정의
running_bwActiveContending과 ActiveNonContending task bandwidth의 합
this_bwInactive를 포함해 해당 runqueue에 속한 모든 task bandwidth의 합
max_bwdeadline task가 쓸 수 있는 최대 bandwidth이며 현재 RT capacity로 설정

Inactive task의 bandwidth는 실행 task Ti의 budget 차감 속도를 늦추는 방식으로 회수한다. Ui는 Ti bandwidth, Umax는 RT throttling limit을 반영한 최대 회수 utilization, Uinact=this_bw-running_bw, Uextra는 추가 회수 가능한 runqueue utilization이다.

dq = -(max{Ui, Umax - Uinact - Uextra} / Umax) * dt

runtime=4, period=8인 bandwidth 0.5 task T1과 T2를 생각한다. t=0에는 둘 다 ActiveContending이고 T1이 실행하며 inactive bandwidth가 없어 dq=-1*dt다. t=2에 T1이 remaining runtime 2를 남기고 block하면 0-lag는 t=4이고 T2가 정상 속도로 budget을 쓴다.

t=4까지 T1이 깨지 않으면 Inactive가 되어 running_bw가 1에서 0.5로 감소한다. T2는 dq=-0.5*dt로 budget이 절반 속도로 차감되어 T1의 미사용 bandwidth를 회수한다. t=8에 T1이 wakeup하면 ActiveContending으로 돌아가 running_bw가 증가한다.

두 reservation의 GRUB reclaim
T1
runrunwake
0-lag=t4
T2
runrunreclaimreclaimreclaimreclaim
t4부터 dq=-0.5dt
running_bw
11111
time
T1,T2 bandwidth=0.5; t=4 이후 Uinact=0.5

T1이 t=2에 block하고 t=4에 inactive가 되면 T2의 budget 차감 속도가 절반으로 감소한다.

GRUB-PA energy-aware scheduling

290-301

cpufreq의 schedutil governor를 선택하면 SCHED_DEADLINE은 deadline을 만족할 수 있는 최소 CPU frequency로 낮추는 GRUB-PA를 사용한다. 이 동작은 현재 ARM architecture에서만 구현되어 있다.

frequency 전환 시간이 reservation period와 비슷한 크기라면 governor의 반응 지연이 timing guarantee를 해칠 수 있다. 이런 조건에서는 고정 CPU frequency가 deadline miss를 더 줄일 수 있다.

real-time task model과 utilization

303-371

SCHED_DEADLINE은 특정 task 종류에 제한되지 않지만, timing guarantee가 필요한 periodic 또는 sporadic multimedia, streaming, control workload에 특히 적합하다.

real-time task는 반복되는 computation phase, 즉 job으로 구성된다. j번째 job J_j는 arrival time r_j, 완료에 필요한 computation time c_j, 완료되어야 하는 absolute deadline d_j를 가진다. max{c_j}가 task의 Worst Case Execution Time(WCET)다.

periodic task는 r_(j+1)=r_j+P이고, sporadic task는 최소 inter-arrival P를 가져 r_(j+1)>=r_j+P다. relative deadline D에 대해 d_j=r_j+D다. 따라서 task model은 Task=(WCET,D,P)로 표현한다.

Task = (WCET, D, P)
U_i = WCET_i / P_i
U_total = sum(WCET_i / P_i)

utilization은 task가 요구하는 CPU 시간 비율이다. CPU가 M개일 때 total utilization이 M보다 크면 모든 deadline을 지킬 수 없고 non-real-time task가 굶을 위험도 있다. U<M이면 non-real-time starvation을 막고 global EDF task tardiness에 상한을 둘 수 있다.

tardiness는 max(0, job finish time - absolute deadline)이다. 원문은 global EDF에서 task별 최대 tardiness가 WCET_max, WCET_min, U_max와 CPU 수 M으로 제한되는 이론식을 제시하며, 핵심은 U<=M이면 response time이 무한히 늘어나지 않는다는 점이다.

단일 CPU schedulability

372-416

M=1이거나 각 real-time task를 CPU 하나에 고정하는 partitioned scheduling에서는 deadline 충족 여부를 형식적으로 검사할 수 있다. 모든 task에서 D_i=P_i이면 해당 CPU의 utilization 합이 1 이하일 때에만 EDF가 모든 deadline을 만족한다.

D_i와 P_i가 다른 task가 있으면 density를 WCET_i/min(D_i,P_i)로 정의한다. density 합이 1 이하라는 조건은 schedulable을 보장하는 충분조건이지만 필요조건은 아니다.

sum(WCET_i / min{D_i, P_i}) <= 1

Task_1=(50ms,50ms,100ms), Task_2=(10ms,100ms,100ms)의 density 합은 1.1이지만 EDF는 Task_1을 release 즉시 50ms 실행하고 이어 Task_2를 10ms 실행해 response time 60ms로 두 deadline을 지킨다.

50/min(50,100) + 10/min(100,100) = 1 + 0.1 = 1.1

D_i!=P_i에서 필요충분 검사는 interval t마다 deadline을 지키는 데 필요한 총 processor demand h(t)를 계산해 h(t)<=t인지 확인한다. 모든 t를 검사할 수 없지만 이론적으로 유한 상한 L까지 확인하면 된다. 이 계산은 online admission에 너무 복잡하고 오래 걸려 Linux는 utilization 기반 검사를 사용한다.

다중 CPU global EDF 분석

417-466

partition하지 않은 M-CPU global EDF에서는 단순히 utilization이나 density를 고정 상수와 비교하는 것으로 완전한 schedulability를 판정할 수 없다. 모든 D_i=P_i이고 total utilization이 1보다 아주 조금 큰 task set도 CPU 수와 무관하게 deadline을 놓칠 수 있다.

Dhall effect 예에서 Task_1=(P,P,P)이고 나머지 M개 task는 (e,P-1,P-1)이다. 모두 시각 t에 release되면 짧은 deadline t+P-1을 가진 M개 task가 먼저 모든 CPU에서 실행된다. Task_1은 t+e에 시작해 t+e+P에 끝나므로 deadline t+P를 놓친다. e를 0에 가깝게 만들면 total utilization은 1에 임의로 가까워진다.

모든 D_i=P_i일 때 알려진 충분조건 가운데 하나는 아래 식이다. U_max=1이면 오른쪽이 1이 되어 Dhall effect를 반영한다.

sum(WCET_i / P_i) <= M - (M - 1) * U_max
U_max = max(WCET_i / P_i)

따라서 U<M은 global EDF가 모든 deadline을 지킨다는 보장은 아니다. 다만 non-real-time task starvation을 막고 real-time task tardiness와 response time에 유한 상한을 제공한다.

task model과 SCHED_DEADLINE parameter 대응

467-550

real-time task의 absolute deadline d_j=r_j+D와 CBS가 EDF 선택에 사용하는 scheduling deadline은 구별해야 한다. admission control이 scheduling deadline 충족을 보장할 때 두 parameter 집합을 올바르게 대응시키면 job의 실제 absolute deadline도 보장할 수 있다.

runtime >= WCET
deadline = D
period <= P

runtime이 worst-case 실행 시간을 덮고 reservation period가 실제 최소 inter-arrival보다 길지 않으면 scheduling deadline과 job absolute deadline을 일치시킬 수 있다. runtime>deadline인 요청은 주어진 시간 안에 budget 자체를 실행할 수 없어 admission control이 거부한다.

원문 참고문헌은 Liu와 Layland의 EDF 기반 논문, Abeni와 Buttazzo의 CBS, 단일 CPU processor demand, Dhall effect와 global EDF, tardiness bound, GRUB·multicore reclaim, Linux SCHED_DEADLINE 구현과 GRUB-PA 논문을 포함한다.

admission control과 system-wide bandwidth

552-612

runtime을 deadline 안에 보장하려면 사용 가능한 CPU fraction을 task에 배분하는 admission control이 필요하다. deadline task에서는 runtime/period가 전통적인 real-time task utilization에 대응하며 이를 bandwidth라고 부른다.

system-wide 제어는 RT group scheduling의 RT-throttling과 비슷한 procfs knob를 공유한다. deadline task의 per-cgroup bandwidth interface는 아직 정의되지 않았다. SCHED_DEADLINE task 자체가 CBS bandwidth를 가지므로 RT task처럼 상위 계층의 주기적 throttling으로 강제할 필요는 없고, knob는 sched_setattr() admission 시 total utilization cap으로만 쓰인다.

CONFIG_RT_GROUP_SCHED에서는 deadline runtime이 root RT runtime에 accounting되고, 설정이 없으면 같은 knob가 deadline admission에만 쓰인다. M CPU root_domain에서 허용되는 reservation bandwidth 합은 다음 값보다 작아야 한다.

M * (sched_rt_runtime_us / sched_rt_period_us)

sum(runtime_i / period_i) < global_dl_utilization_cap

/proc/sys/kernel/sched_rt_runtime_us에 -1을 쓰면 bandwidth management를 끄고 임의 수준으로 oversubscribe할 수 있지만 timing guarantee도 잃는다.

task interface, 기본값과 sched_yield

613-670

periodic/sporadic task를 표현하려면 최대 또는 대표 instance 실행 시간, 연속 instance 사이 최소 간격, 각 instance 완료 시간 제약이 필요하다. Linux는 이를 담는 struct sched_attr와 sched_setattr()/sched_getattr() syscall을 제공한다.

debugging에서는 /proc/<pid>/sched의 dl.runtime과 dl.deadline에서 남은 runtime과 현재 absolute scheduling deadline을 ns 단위로 읽을 수 있다. production code가 이를 programmatically 읽는 정식 interface는 논의 중이다.

기본 sched_rt_runtime_us=950000, sched_rt_period_us=1000000이므로 각 root_domain에서 CPU 수의 95%까지 deadline bandwidth로 예약할 수 있다. non-deadline task에는 최소 5%를 남긴다. deadline=period이고 cpuset으로 partitioned scheduling을 구성하면 이 cap 아래에서 각 task가 period 안에 runtime을 받는 것을 결정적으로 보장할 수 있다.

admission accounting을 깨뜨리지 않도록 SCHED_DEADLINE task는 fork할 수 없다. sched_yield()를 호출하면 현재 remaining runtime 전체를 포기하고 즉시 throttle되어 다음 period의 replenishment까지 기다린다. dl_yielded flag가 이 경로를 표시한다.

이 semantics는 task가 다음 period 시작에 맞춰 다시 wakeup하게 한다. bandwidth reclaim과 결합하면 포기한 남은 budget을 다른 deadline task가 회수할 수 있다.

CPU affinity와 cpuset root domain

672-712

deadline task의 CPU affinity mask는 task가 생성된 root domain보다 작을 수 없으므로 sched_setaffinity()로 단순 pinning할 수 없다. 먼저 cgroup cpuset controller로 제한된 root domain을 만들고 그 안에서 task를 SCHED_DEADLINE으로 전환해야 한다.

deprecated cgroup v1에서 CPU0 전용 cpuset을 만드는 원문 예는 다음과 같다.

mkdir /dev/cpuset
mount -t cgroup -o cpuset cpuset /dev/cpuset
cd /dev/cpuset
mkdir cpu0
echo 0 > cpu0/cpuset.cpus
echo 0 > cpu0/cpuset.mems
echo 1 > cpuset.cpu_exclusive
echo 0 > cpuset.sched_load_balance
echo 1 > cpu0/cpuset.cpu_exclusive
echo 1 > cpu0/cpuset.mem_exclusive
echo $$ > cpu0/tasks
chrt --sched-runtime 100000 --sched-period 200000 --deadline 0 yes > /dev/null

cgroup v2 root가 /sys/fs/cgroup에 mount된 경우에는 cpuset controller를 subtree에 활성화하고 CPU0 partition root를 만든다.

cd /sys/fs/cgroup
echo '+cpuset' > cgroup.subtree_control
mkdir deadline_group
echo 0 > deadline_group/cpuset.cpus
echo 'root' > deadline_group/cpuset.cpus.partition
echo $$ > deadline_group/cgroup.procs
chrt --sched-runtime 100000 --sched-period 200000 --deadline 0 yes > /dev/null

남은 과제와 test suite

713-795

남은 과제에는 현재 runtime과 absolute deadline을 읽는 programmatic API, 비상호작용 task의 bandwidth isolation을 유지하는 deadline inheritance 개선, cgroup 기반 bandwidth management와 scheduling, non-root access control과 악용 방지가 있다.

scheduler-tools의 rt-app은 SCHED_OTHER, FIFO, RR, DEADLINE thread를 JSON으로 구성해 재현 가능한 synthetic workload를 만든다. 아래 예는 10ms/100ms deadline task와 20ms 실행 후 130ms sleep하는 FIFO priority 10 task를 5초 실행한다.

{
  "tasks": {
    "dl_task": {
      "policy": "SCHED_DEADLINE",
      "priority": 0,
      "dl-runtime": 10000,
      "dl-period": 100000,
      "dl-deadline": 100000
    },
    "fifo_task": {
      "policy": "SCHED_FIFO",
      "priority": 10,
      "runtime": 20000,
      "sleep": 130000
    }
  },
  "global": { "duration": 5 }
}

chrt도 SCHED_DEADLINE을 지원한다. -T runtime과 -D deadline은 nanosecond다. 첫 명령은 새 process를 10ms/100ms reservation으로 실행하고, 두 번째는 기존 PID에 같은 reservation을 적용한다.

# chrt -d -T 10000000 -D 100000000 0 ./my_cpuhog_app
# chrt -d -T 10000000 -D 100000000 -p 0 my_app_pid

sched_setattr 최소 실행 예제

796-919

원문 예제는 sched_attr를 직접 채워 현재 thread에 10ms/30ms reservation을 설정한다. syscall 번호를 source에 직접 고정한 부분은 demonstration용이며 실제 application은 대상 system의 올바른 UAPI와 libc 지원을 사용해야 한다.

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <linux/unistd.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <sys/syscall.h>
#include <pthread.h>

#define gettid() syscall(__NR_gettid)
#define SCHED_DEADLINE 6

/* XXX use the proper syscall numbers */
#ifdef __x86_64__
#define __NR_sched_setattr 314
#define __NR_sched_getattr 315
#endif
#ifdef __i386__
#define __NR_sched_setattr 351
#define __NR_sched_getattr 352
#endif
#ifdef __arm__
#define __NR_sched_setattr 380
#define __NR_sched_getattr 381
#endif

static volatile int done;

struct sched_attr {
	__u32 size;
	__u32 sched_policy;
	__u64 sched_flags;
	__s32 sched_nice;
	__u32 sched_priority;
	__u64 sched_runtime;
	__u64 sched_deadline;
	__u64 sched_period;
};

int sched_setattr(pid_t pid, const struct sched_attr *attr,
		  unsigned int flags)
{
	return syscall(__NR_sched_setattr, pid, attr, flags);
}

int sched_getattr(pid_t pid, struct sched_attr *attr,
		  unsigned int size, unsigned int flags)
{
	return syscall(__NR_sched_getattr, pid, attr, size, flags);
}

void *run_deadline(void *data)
{
	struct sched_attr attr;
	int x = 0;
	int ret;
	unsigned int flags = 0;

	printf("deadline thread started [%ld]\n", gettid());
	attr.size = sizeof(attr);
	attr.sched_flags = 0;
	attr.sched_nice = 0;
	attr.sched_priority = 0;
	attr.sched_policy = SCHED_DEADLINE;
	attr.sched_runtime = 10 * 1000 * 1000;
	attr.sched_period = attr.sched_deadline = 30 * 1000 * 1000;

	ret = sched_setattr(0, &attr, flags);
	if (ret < 0) {
		done = 0;
		perror("sched_setattr");
		exit(-1);
	}
	while (!done)
		x++;
	printf("deadline thread dies [%ld]\n", gettid());
	return NULL;
}

int main(int argc, char **argv)
{
	pthread_t thread;

	printf("main thread [%ld]\n", gettid());
	pthread_create(&thread, NULL, run_deadline, NULL);
	sleep(10);
	done = 1;
	pthread_join(thread, NULL);
	printf("main dies [%ld]\n", gettid());
	return 0;
}

worker thread는 sched_setattr(0, ...)로 자신을 SCHED_DEADLINE으로 바꾸고 10초 동안 busy loop한다. runtime 10ms를 소진할 때마다 CBS가 throttle하고 30ms period마다 replenish한다. main thread는 done을 설정하고 join한다.