← Documents Documentation/trace/rv/monitor_sched.rst GitHub 원문 ↗

Linux 6.18.37 · Tracing

Scheduler 모니터

scheduler의 복잡한 동작을 sco·snroc·scpd·snep·sts·nrp·sssw·opid monitor로 분해해 context, preemption, IRQ, task state 및 wakeup 규칙을 검증하는 방법을 설명합니다.

Source pathDocumentation/trace/rv/monitor_sched.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

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

1. 요약·해설

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

요약·해설

monitor_sched.rst:1-402

scheduler의 복잡한 동작을 sco·snroc·scpd·snep·sts·nrp·sssw·opid monitor로 분해해 context, preemption, IRQ, task state 및 wakeup 규칙을 검증하는 방법을 설명합니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 Scheduler monitors
2 ==================
3
4 - Name: sched
5 - Type: container for multiple monitors
6 - Author: Gabriele Monaco <[email protected]>, Daniel Bristot de Oliveira <[email protected]>
7
8 Description
9 -----------
10
11 Monitors describing complex systems, such as the scheduler, can easily grow to
12 the point where they are just hard to understand because of the many possible
13 state transitions.
14 Often it is possible to break such descriptions into smaller monitors,
15 sharing some or all events. Enabling those smaller monitors concurrently is,
16 in fact, testing the system as if we had one single larger monitor.
17 Splitting models into multiple specification is not only easier to
18 understand, but gives some more clues when we see errors.
19
20 The sched monitor is a set of specifications to describe the scheduler behaviour.
21 It includes several per-cpu and per-task monitors that work independently to verify
22 different specifications the scheduler should follow.
23
24 To make this system as straightforward as possible, sched specifications are *nested*
25 monitors, whereas sched itself is a *container*.
26 From the interface perspective, sched includes other monitors as sub-directories,
27 enabling/disabling or setting reactors to sched, propagates the change to all monitors,
28 however single monitors can be used independently as well.
29
30 It is important that future modules are built after their container (sched, in
31 this case), otherwise the linker would not respect the order and the nesting
32 wouldn't work as expected.
33 To do so, simply add them after sched in the Makefile.
34
35 Specifications
36 --------------
37
38 The specifications included in sched are currently a work in progress, adapting the ones
39 defined in by Daniel Bristot in [1].
40
41 Currently we included the following:
42
43 Monitor sco
44 ~~~~~~~~~~~
45
46 The scheduling context operations (sco) monitor ensures changes in a task state
47 happen only in thread context::
48
49
50 |
51 |
52 v
53 sched_set_state +------------------+
54 +------------------ | |
55 | | thread_context |
56 +-----------------> | | <+
57 +------------------+ |
58 | |
59 | schedule_entry | schedule_exit
60 v |
61 |
62 scheduling_context -+
63
64 Monitor snroc
65 ~~~~~~~~~~~~~
66
67 The set non runnable on its own context (snroc) monitor ensures changes in a
68 task state happens only in the respective task's context. This is a per-task
69 monitor::
70
71 |
72 |
73 v
74 +------------------+
75 | other_context | <+
76 +------------------+ |
77 | |
78 | sched_switch_in | sched_switch_out
79 v |
80 sched_set_state |
81 +------------------ |
82 | own_context |
83 +-----------------> -+
84
85 Monitor scpd
86 ~~~~~~~~~~~~
87
88 The schedule called with preemption disabled (scpd) monitor ensures schedule is
89 called with preemption disabled::
90
91 |
92 |
93 v
94 +------------------+
95 | cant_sched | <+
96 +------------------+ |
97 | |
98 | preempt_disable | preempt_enable
99 v |
100 schedule_entry |
101 schedule_exit |
102 +----------------- can_sched |
103 | |
104 +----------------> -+
105
106 Monitor snep
107 ~~~~~~~~~~~~
108
109 The schedule does not enable preempt (snep) monitor ensures a schedule call
110 does not enable preemption::
111
112 |
113 |
114 v
115 preempt_disable +------------------------+
116 preempt_enable | |
117 +------------------ | non_scheduling_context |
118 | | |
119 +-----------------> | | <+
120 +------------------------+ |
121 | |
122 | schedule_entry | schedule_exit
123 v |
124 |
125 scheduling_contex -+
126
127 Monitor sts
128 ~~~~~~~~~~~
129
130 The schedule implies task switch (sts) monitor ensures a task switch happens
131 only in scheduling context and up to once, as well as scheduling occurs with
132 interrupts enabled but no task switch can happen before interrupts are
133 disabled. When the next task picked for execution is the same as the previously
134 running one, no real task switch occurs but interrupts are disabled nonetheless::
135
136 irq_entry |
137 +----+ |
138 v | v
139 +------------+ irq_enable #===================# irq_disable
140 | | ------------> H H irq_entry
141 | cant_sched | <------------ H H irq_enable
142 | | irq_disable H can_sched H --------------+
143 +------------+ H H |
144 H H |
145 +---------------> H H <-------------+
146 | #===================#
147 | |
148 schedule_exit | schedule_entry
149 | v
150 | +-------------------+ irq_enable
151 | | scheduling | <---------------+
152 | +-------------------+ |
153 | | |
154 | | irq_disable +--------+ irq_entry
155 | v | | --------+
156 | +-------------------+ irq_entry | in_irq | |
157 | | | -----------> | | <-------+
158 | | disable_to_switch | +--------+
159 | | | --+
160 | +-------------------+ |
161 | | |
162 | | sched_switch |
163 | v |
164 | +-------------------+ |
165 | | switching | | irq_enable
166 | +-------------------+ |
167 | | |
168 | | irq_enable |
169 | v |
170 | +-------------------+ |
171 +-- | enable_to_exit | <-+
172 +-------------------+
173 ^ | irq_disable
174 | | irq_entry
175 +---------------+ irq_enable
176
177 Monitor nrp
178 -----------
179
180 The need resched preempts (nrp) monitor ensures preemption requires
181 ``need_resched``. Only kernel preemption is considered, since preemption
182 while returning to userspace, for this monitor, is indistinguishable from
183 ``sched_switch_yield`` (described in the sssw monitor).
184 A kernel preemption is whenever ``__schedule`` is called with the preemption
185 flag set to true (e.g. from preempt_enable or exiting from interrupts). This
186 type of preemption occurs after the need for ``rescheduling`` has been set.
187 This is not valid for the *lazy* variant of the flag, which causes only
188 userspace preemption.
189 A ``schedule_entry_preempt`` may involve a task switch or not, in the latter
190 case, a task goes through the scheduler from a preemption context but it is
191 picked as the next task to run. Since the scheduler runs, this clears the need
192 to reschedule. The ``any_thread_running`` state does not imply the monitored
193 task is not running as this monitor does not track the outcome of scheduling.
194
195 In theory, a preemption can only occur after the ``need_resched`` flag is set. In
196 practice, however, it is possible to see a preemption where the flag is not
197 set. This can happen in one specific condition::
198
199 need_resched
200 preempt_schedule()
201 preempt_schedule_irq()
202 __schedule()
203 !need_resched
204 __schedule()
205
206 In the situation above, standard preemption starts (e.g. from preempt_enable
207 when the flag is set), an interrupt occurs before scheduling and, on its exit
208 path, it schedules, which clears the ``need_resched`` flag.
209 When the preempted task runs again, the standard preemption started earlier
210 resumes, although the flag is no longer set. The monitor considers this a
211 ``nested_preemption``, this allows another preemption without re-setting the
212 flag. This condition relaxes the monitor constraints and may catch false
213 negatives (i.e. no real ``nested_preemptions``) but makes the monitor more
214 robust and able to validate other scenarios.
215 For simplicity, the monitor starts in ``preempt_irq``, although no interrupt
216 occurred, as the situation above is hard to pinpoint::
217
218 schedule_entry
219 irq_entry #===========================================#
220 +-------------------------- H H
221 | H H
222 +-------------------------> H any_thread_running H
223 H H
224 +-------------------------> H H
225 | #===========================================#
226 | schedule_entry | ^
227 | schedule_entry_preempt | sched_need_resched | schedule_entry
228 | | schedule_entry_preempt
229 | v |
230 | +----------------------+ |
231 | +--- | | |
232 | sched_need_resched | | rescheduling | -+
233 | +--> | |
234 | +----------------------+
235 | | irq_entry
236 | v
237 | +----------------------+
238 | | | ---+
239 | ---> | | | sched_need_resched
240 | | preempt_irq | | irq_entry
241 | | | <--+
242 | | | <--+
243 | +----------------------+ |
244 | | schedule_entry | sched_need_resched
245 | | schedule_entry_preempt |
246 | v |
247 | +-----------------------+ |
248 +-------------------------- | nested_preempt | --+
249 +-----------------------+
250 ^ irq_entry |
251 +-------------------+
252
253 Due to how the ``need_resched`` flag on the preemption count works on arm64,
254 this monitor is unstable on that architecture, as it often records preemption
255 when the flag is not set, even in presence of the workaround above.
256 For the time being, the monitor is disabled by default on arm64.
257
258 Monitor sssw
259 ------------
260
261 The set state sleep and wakeup (sssw) monitor ensures ``set_state`` to
262 sleepable leads to sleeping and sleeping tasks require wakeup. It includes the
263 following types of switch:
264
265 * ``switch_suspend``:
266 a task puts itself to sleep, this can happen only after explicitly setting
267 the task to ``sleepable``. After a task is suspended, it needs to be woken up
268 (``waking`` state) before being switched in again.
269 Setting the task's state to ``sleepable`` can be reverted before switching if it
270 is woken up or set to ``runnable``.
271 * ``switch_blocking``:
272 a special case of a ``switch_suspend`` where the task is waiting on a
273 sleeping RT lock (``PREEMPT_RT`` only), it is common to see wakeup and set
274 state events racing with each other and this leads the model to perceive this
275 type of switch when the task is not set to sleepable. This is a limitation of
276 the model in SMP system and workarounds may slow down the system.
277 * ``switch_preempt``:
278 a task switch as a result of kernel preemption (``schedule_entry_preempt`` in
279 the nrp model).
280 * ``switch_yield``:
281 a task explicitly calls the scheduler or is preempted while returning to
282 userspace. It can happen after a ``yield`` system call, from the idle task or
283 if the ``need_resched`` flag is set. By definition, a task cannot yield while
284 ``sleepable`` as that would be a suspension. A special case of a yield occurs
285 when a task in ``TASK_INTERRUPTIBLE`` calls the scheduler while a signal is
286 pending. The task doesn't go through the usual blocking/waking and is set
287 back to runnable, the resulting switch (if there) looks like a yield to the
288 ``signal_wakeup`` state and is followed by the signal delivery. From this
289 state, the monitor expects a signal even if it sees a wakeup event, although
290 not necessary, to rule out false negatives.
291
292 This monitor doesn't include a running state, ``sleepable`` and ``runnable``
293 are only referring to the task's desired state, which could be scheduled out
294 (e.g. due to preemption). However, it does include the event
295 ``sched_switch_in`` to represent when a task is allowed to become running. This
296 can be triggered also by preemption, but cannot occur after the task got to
297 ``sleeping`` before a ``wakeup`` occurs::
298
299 +--------------------------------------------------------------------------+
300 | |
301 | |
302 | switch_suspend | |
303 | switch_blocking | |
304 v v |
305 +----------+ #==========================# set_state_runnable |
306 | | H H wakeup |
307 | | H H switch_in |
308 | | H H switch_yield |
309 | sleeping | H H switch_preempt |
310 | | H H signal_deliver |
311 | | switch_ H H ------+ |
312 | | _blocking H runnable H | |
313 | | <----------- H H <-----+ |
314 +----------+ H H |
315 | wakeup H H |
316 +---------------------> H H |
317 H H |
318 +---------> H H |
319 | #==========================# |
320 | | ^ |
321 | | | set_state_runnable |
322 | | | wakeup |
323 | set_state_sleepable | +------------------------+
324 | v | |
325 | +--------------------------+ set_state_sleepable
326 | | | switch_in
327 | | | switch_preempt
328 signal_deliver | sleepable | signal_deliver
329 | | | ------+
330 | | | |
331 | | | <-----+
332 | +--------------------------+
333 | | ^
334 | switch_yield | set_state_sleepable
335 | v |
336 | +---------------+ |
337 +---------- | signal_wakeup | -+
338 +---------------+
339 ^ | switch_in
340 | | switch_preempt
341 | | switch_yield
342 +-----------+ wakeup
343
344 Monitor opid
345 ------------
346
347 The operations with preemption and irq disabled (opid) monitor ensures
348 operations like ``wakeup`` and ``need_resched`` occur with interrupts and
349 preemption disabled or during interrupt context, in such case preemption may
350 not be disabled explicitly.
351 ``need_resched`` can be set by some RCU internals functions, in which case it
352 doesn't match a task wakeup and might occur with only interrupts disabled::
353
354 | sched_need_resched
355 | sched_waking
356 | irq_entry
357 | +--------------------+
358 v v |
359 +------------------------------------------------------+
360 +----------- | disabled | <+
361 | +------------------------------------------------------+ |
362 | | ^ |
363 | | preempt_disable sched_need_resched |
364 | preempt_enable | +--------------------+ |
365 | v | v | |
366 | +------------------------------------------------------+ |
367 | | irq_disabled | |
368 | +------------------------------------------------------+ |
369 | | | ^ |
370 | irq_entry irq_entry | | |
371 | sched_need_resched v | irq_disable |
372 | sched_waking +--------------+ | | |
373 | +----- | | irq_enable | |
374 | | | in_irq | | | |
375 | +----> | | | | |
376 | +--------------+ | | irq_disable
377 | | | | |
378 | irq_enable | irq_enable | | |
379 | v v | |
380 | #======================================================# |
381 | H enabled H |
382 | #======================================================# |
383 | | ^ ^ preempt_enable | |
384 | preempt_disable preempt_enable +--------------------+ |
385 | v | |
386 | +------------------+ | |
387 +----------> | preempt_disabled | -+ |
388 +------------------+ |
389 | |
390 +-------------------------------------------------------+
391
392 This monitor is designed to work on ``PREEMPT_RT`` kernels, the special case of
393 events occurring in interrupt context is a shortcut to identify valid scenarios
394 where the preemption tracepoints might not be visible, during interrupts
395 preemption is always disabled. On non- ``PREEMPT_RT`` kernels, the interrupts
396 might invoke a softirq to set ``need_resched`` and wake up a task. This is
397 another special case that is currently not supported by the monitor.
398
399 References
400 ----------
401
402 [1] - https://bristot.me/linux-task-model
403

3. 한국어 전문 번역

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

설명

1-34

`sched`는 Gabriele Monaco와 Daniel Bristot de Oliveira가 작성한 여러 monitor의 컨테이너이다. scheduler처럼 복잡한 시스템을 하나의 monitor로 설명하면 가능한 state transition이 많아져 이해하기 어려워질 수 있다.

이런 설명은 일부 또는 모든 event를 공유하는 더 작은 monitor로 나눌 수 있다. 작은 monitor들을 동시에 활성화하면 하나의 큰 monitor로 시스템을 시험하는 것과 같은 효과를 얻는다. 여러 명세로 분리하면 이해하기 쉬울 뿐 아니라 오류가 발생했을 때 원인을 좁힐 단서도 더 많아진다.

`sched` monitor는 scheduler 동작을 기술하는 명세 모음이다. 여러 per-CPU 및 per-task monitor가 독립적으로 작동하면서 scheduler가 따라야 할 서로 다른 명세를 검증한다.

구조를 단순하게 유지하기 위해 각 sched 명세는 nested monitor이고 `sched` 자체는 container이다. 인터페이스에서 `sched` 아래에 다른 monitor가 하위 디렉터리로 나타난다. `sched`를 enable 또는 disable하거나 reactor를 설정하면 모든 하위 monitor에 변경이 전파되지만, 개별 monitor도 독립적으로 사용할 수 있다.

향후 module은 container인 `sched`보다 뒤에 build해야 한다. 그렇지 않으면 linker가 순서를 지키지 않아 nesting이 예상대로 작동하지 않는다. Makefile에서 새 module을 `sched` 뒤에 추가하면 된다.

sched container 구조
sched containerenable / disable / reactor 설정
nested per-CPU monitorsCPU별 scheduler 속성 검증
nested per-task monitorstask별 상태 전이 검증
concurrent execution하나의 큰 scheduler model과 같은 검증 범위

상위 인터페이스의 설정을 여러 독립 명세로 전파하면서 개별 사용도 허용한다.

sched 구성 원칙
항목동작
Container`sched`가 하위 monitor를 디렉터리로 묶음
Propagation상위 enable·disable·reactor 설정을 모두에게 전파
Independent use각 하위 monitor를 따로 실행 가능
Build order새 module을 Makefile에서 `sched` 뒤에 배치

복잡한 scheduler model을 분해할 때의 동작과 build 제약이다.

Scheduler monitors
==================

- Name: sched
- Type: container for multiple monitors
- Author: Gabriele Monaco <[email protected]>, Daniel Bristot de Oliveira <[email protected]>

Description
-----------

Monitors describing complex systems, such as the scheduler, can easily grow to
the point where they are just hard to understand because of the many possible
state transitions.
Often it is possible to break such descriptions into smaller monitors,
sharing some or all events. Enabling those smaller monitors concurrently is,
in fact, testing the system as if we had one single larger monitor.
Splitting models into multiple specification is not only easier to
understand, but gives some more clues when we see errors.

The sched monitor is a set of specifications to describe the scheduler behaviour.
It includes several per-cpu and per-task monitors that work independently to verify
different specifications the scheduler should follow.

To make this system as straightforward as possible, sched specifications are *nested*
monitors, whereas sched itself is a *container*.
From the interface perspective, sched includes other monitors as sub-directories,
enabling/disabling or setting reactors to sched, propagates the change to all monitors,
however single monitors can be used independently as well.

It is important that future modules are built after their container (sched, in
this case), otherwise the linker would not respect the order and the nesting
wouldn't work as expected.
To do so, simply add them after sched in the Makefile.

명세 개요

35-42

`sched`에 포함된 명세는 현재 진행 중인 작업이며, Daniel Bristot가 참고 문헌 [1]에서 정의한 모델을 적용하고 있다.

sched 하위 monitor
monitor검증 대상
`sco`task state 변경은 thread context에서만 발생
`snroc`task state 변경은 해당 task 자신의 context에서만 발생
`scpd`preemption disabled 상태에서 schedule 호출
`snep`schedule 호출이 preemption을 활성화하지 않음
`sts`scheduling context 안에서 최대 한 번 task switch
`nrp`kernel preemption에는 `need_resched`가 필요
`sssw`sleepable state와 sleep·wakeup 관계
`opid`wakeup·need_resched의 IRQ/preemption 보호

각 작은 monitor가 검증하는 scheduler 불변 조건을 요약한다.

Specifications
--------------

The specifications included in sched are currently a work in progress, adapting the ones
defined in by Daniel Bristot in [1].

Currently we included the following:

sco monitor

43-63

scheduling context operations(`sco`) monitor는 task state 변경이 thread context에서만 일어나도록 보장한다.

sco 상태 전이
startthread_context (initial/final)
sched_set_statethread_context self-loop
schedule_entryscheduling_context
schedule_exitthread_context

원문의 ASCII 상태기를 thread context와 scheduling context 사이의 전이로 다시 구성했다.

`sched_set_state`는 `thread_context`에서만 허용된다. `schedule_entry` 이후 `scheduling_context`에 있는 동안에는 task state 변경을 허용하지 않고, `schedule_exit`으로 돌아와야 다시 허용한다.

Monitor sco
~~~~~~~~~~~

The scheduling context operations (sco) monitor ensures changes in a task state
happen only in thread context::


                        |
                        |
                        v
    sched_set_state   +------------------+
  +------------------ |                  |
  |                   |  thread_context  |
  +-----------------> |                  | <+
                      +------------------+  |
                        |                   |
                        | schedule_entry    | schedule_exit
                        v                   |
                                            |
                       scheduling_context  -+

snroc monitor

64-84

set non runnable on its own context(`snroc`) monitor는 task state 변경이 해당 task 자신의 context에서만 발생하도록 보장하는 per-task monitor이다.

snroc 상태 전이
startother_context
sched_switch_inown_context
sched_set_stateown_context self-loop
sched_switch_outother_context

각 task가 CPU에서 실행되는 자신의 context에 들어오고 나가는 상태를 추적한다.

`sched_switch_in` 뒤의 `own_context`에서만 `sched_set_state`를 허용한다. task가 `sched_switch_out`으로 나가 `other_context`가 되면 다른 task의 context에서는 그 상태를 바꿀 수 없다.

Monitor snroc
~~~~~~~~~~~~~

The set non runnable on its own context (snroc) monitor ensures changes in a
task state happens only in the respective task's context. This is a per-task
monitor::

                        |
                        |
                        v
                      +------------------+
                      |  other_context   | <+
                      +------------------+  |
                        |                   |
                        | sched_switch_in   | sched_switch_out
                        v                   |
    sched_set_state                         |
  +------------------                       |
  |                       own_context       |
  +----------------->                      -+

scpd monitor

85-105

schedule called with preemption disabled(`scpd`) monitor는 `schedule`이 preemption disabled 상태에서 호출되는지 확인한다.

scpd 상태 전이
startcant_sched
preempt_disablecan_sched
schedule_entrycan_sched self-loop
schedule_exitcan_sched self-loop
preempt_enablecant_sched

preemption 상태에 따라 schedule 호출 가능 여부를 구분한다.

초기 `cant_sched`에서는 schedule event를 허용하지 않는다. `preempt_disable` 뒤 `can_sched`에서 `schedule_entry`와 `schedule_exit`을 허용하고, `preempt_enable`이면 다시 `cant_sched`로 돌아간다.

Monitor scpd
~~~~~~~~~~~~

The schedule called with preemption disabled (scpd) monitor ensures schedule is
called with preemption disabled::

                       |
                       |
                       v
                     +------------------+
                     |    cant_sched    | <+
                     +------------------+  |
                       |                   |
                       | preempt_disable   | preempt_enable
                       v                   |
    schedule_entry                         |
    schedule_exit                          |
  +-----------------      can_sched        |
  |                                        |
  +---------------->                      -+

snep monitor

106-126

schedule does not enable preempt(`snep`) monitor는 한 번의 schedule 호출이 preemption을 활성화하지 않는지 보장한다.

snep 상태 전이
startnon_scheduling_context
preempt_disable / preempt_enablenon_scheduling_context self-loop
schedule_entryscheduling_context
schedule_exitnon_scheduling_context

schedule 구간 안에서는 preemption event가 나타나지 않아야 한다.

`preempt_disable`과 `preempt_enable`은 schedule 밖의 `non_scheduling_context`에서만 관찰한다. `schedule_entry`부터 `schedule_exit`까지의 `scheduling_context`에서는 preemption을 켜는 전이가 없어야 한다.

Monitor snep
~~~~~~~~~~~~

The schedule does not enable preempt (snep) monitor ensures a schedule call
does not enable preemption::

                        |
                        |
                        v
    preempt_disable   +------------------------+
    preempt_enable    |                        |
  +------------------ | non_scheduling_context |
  |                   |                        |
  +-----------------> |                        | <+
                      +------------------------+  |
                        |                         |
                        | schedule_entry          | schedule_exit
                        v                         |
                                                  |
                          scheduling_contex      -+

sts monitor

127-176

schedule implies task switch(`sts`) monitor는 task switch가 scheduling context 안에서 최대 한 번만 일어나도록 보장한다. 또한 scheduling은 interrupt enabled 상태에서 시작하되 interrupt가 disabled되기 전에는 task switch가 일어날 수 없도록 검사한다.

scheduler가 다음 실행 task로 이전과 같은 task를 고르면 실제 task switch는 일어나지 않지만, 이 경우에도 interrupt는 비활성화된다.

sts 주 전이 경로
can_sched (initial/final)schedule_entry
schedulingirq_disable
disable_to_switchsched_switch
switchingirq_enable
enable_to_exitschedule_exit
can_sched다음 schedule 대기

schedule 진입에서 실제 switch 또는 switch 없는 종료까지의 순서를 구조화했다.

sts 상태와 역할
state의미와 허용 전이
`cant_sched`interrupt 진입 중이며 scheduling 시작 불가
`can_sched`interrupt enabled, `schedule_entry` 허용
`scheduling`schedule 진입 직후, `irq_disable` 대기
`disable_to_switch`interrupt disabled, `sched_switch` 최대 한 번 허용
`switching`task switch 뒤 `irq_enable` 대기
`enable_to_exit`switch 여부와 무관하게 `schedule_exit` 대기
`in_irq`schedule 중 interrupt의 중첩 진입과 복귀 처리

interrupt event와 task switch 순서를 검증하는 각 상태의 의미다.

`can_sched`에서는 `irq_disable` 또는 `irq_entry`로 `cant_sched`에 들어가고, `irq_enable`로 돌아온다. schedule 중 `irq_entry`가 발생하면 `in_irq`에서 중첩 interrupt를 처리한 뒤 원래 경로로 복귀한다.

`disable_to_switch`에서 `sched_switch`를 한 번 관찰하면 `switching`으로 이동하므로 두 번째 switch를 허용하지 않는다. 실제 switch가 없어도 `irq_enable` 경로를 통해 `enable_to_exit`으로 갈 수 있다.

Monitor sts
~~~~~~~~~~~

The schedule implies task switch (sts) monitor ensures a task switch happens
only in scheduling context and up to once, as well as scheduling occurs with
interrupts enabled but no task switch can happen before interrupts are
disabled. When the next task picked for execution is the same as the previously
running one, no real task switch occurs but interrupts are disabled nonetheless::

    irq_entry                      |
     +----+                        |
     v    |                        v
 +------------+ irq_enable    #===================#   irq_disable
 |            | ------------> H                   H   irq_entry
 | cant_sched | <------------ H                   H   irq_enable
 |            | irq_disable   H     can_sched     H --------------+
 +------------+               H                   H               |
                              H                   H               |
            +---------------> H                   H <-------------+
            |                 #===================#
            |                   |
      schedule_exit             | schedule_entry
            |                   v
            |   +-------------------+     irq_enable
            |   |    scheduling     | <---------------+
            |   +-------------------+                 |
            |     |                                   |
            |     | irq_disable                    +--------+  irq_entry
            |     v                                |        | --------+
            |   +-------------------+  irq_entry   | in_irq |         |
            |   |                   | -----------> |        | <-------+
            |   | disable_to_switch |              +--------+
            |   |                   | --+
            |   +-------------------+   |
            |     |                     |
            |     | sched_switch        |
            |     v                     |
            |   +-------------------+   |
            |   |     switching     |   | irq_enable
            |   +-------------------+   |
            |     |                     |
            |     | irq_enable          |
            |     v                     |
            |   +-------------------+   |
            +-- |  enable_to_exit   | <-+
                +-------------------+
                  ^               | irq_disable
                  |               | irq_entry
                  +---------------+ irq_enable

nrp monitor

177-257

need resched preempts(`nrp`) monitor는 preemption에 `need_resched`가 필요함을 보장한다. userspace로 복귀하는 동안의 preemption은 `sssw` monitor의 `sched_switch_yield`와 구분할 수 없으므로 kernel preemption만 고려한다.

kernel preemption은 `preempt_enable` 또는 interrupt 종료 같은 경로에서 preemption flag가 true인 채 `__schedule`이 호출되는 경우이다. 이런 preemption은 rescheduling 필요가 설정된 뒤 발생한다. userspace preemption만 일으키는 flag의 lazy 변형에는 이 규칙을 적용하지 않는다.

`schedule_entry_preempt`는 task switch를 일으킬 수도 있고 일으키지 않을 수도 있다. switch가 없으면 task가 preemption context에서 scheduler를 거쳤지만 다음 실행 task로 다시 선택된 것이다. scheduler가 실행되었으므로 reschedule 필요는 지워진다.

`any_thread_running` state는 monitored task가 실행 중이 아님을 뜻하지 않는다. 이 monitor는 scheduling 결과를 추적하지 않기 때문이다.

이론상 preemption은 `need_resched` flag가 설정된 뒤에만 가능하지만, 실제로는 flag가 없는 preemption을 볼 수 있는 특정 중첩 조건이 있다.

need_resched
                 preempt_schedule()
                                         preempt_schedule_irq()
                                                 __schedule()
!need_resched
                         __schedule()

flag가 설정된 상태에서 표준 preemption이 시작된 뒤 scheduling 전에 interrupt가 발생할 수 있다. interrupt 종료 경로가 먼저 schedule을 수행하면 `need_resched` flag가 지워진다. 이후 선점되었던 task가 다시 실행될 때 앞서 시작한 표준 preemption이 flag 없이 재개된다.

monitor는 이를 `nested_preemption`으로 간주해 flag를 다시 설정하지 않고도 한 번 더 preemption을 허용한다. 이 완화는 실제 `nested_preemption`이 아닌 경우를 놓치는 false negative를 만들 수 있지만, monitor를 더 견고하게 하고 다른 시나리오를 검증할 수 있게 한다.

이 상황의 정확한 시작점을 찾기 어려우므로 monitor는 실제 interrupt가 없었더라도 단순화를 위해 `preempt_irq`에서 시작한다.

nrp 기본 전이
any_thread_running (initial/final)sched_need_resched
reschedulingirq_entry
preempt_irqschedule_entry / schedule_entry_preempt
nested_preemptschedule_entry 또는 irq_entry
any_thread_runningscheduler 실행 뒤 resched 필요 해제

need_resched 설정에서 kernel preemption과 중첩 preemption까지 이어지는 상태를 나타낸다.

nrp 예외 전이
stateevent결과
`any_thread_running``sched_need_resched``rescheduling`
`rescheduling`추가 `sched_need_resched`self-loop
`rescheduling``irq_entry``preempt_irq`
`preempt_irq``sched_need_resched` / `irq_entry`self-loop
`preempt_irq``schedule_entry_preempt``nested_preempt`
`nested_preempt``irq_entry`self-loop

중첩 interrupt와 rescheduling event가 monitor 제약을 완화하는 방식이다.

arm64에서는 preemption count의 `need_resched` flag 동작 방식 때문에 이 monitor가 불안정하다. 위 우회가 있어도 flag가 설정되지 않은 preemption을 자주 기록하므로 현재 arm64에서는 기본적으로 비활성화되어 있다.

Monitor nrp
-----------

The need resched preempts (nrp) monitor ensures preemption requires
``need_resched``. Only kernel preemption is considered, since preemption
while returning to userspace, for this monitor, is indistinguishable from
``sched_switch_yield`` (described in the sssw monitor).
A kernel preemption is whenever ``__schedule`` is called with the preemption
flag set to true (e.g. from preempt_enable or exiting from interrupts). This
type of preemption occurs after the need for ``rescheduling`` has been set.
This is not valid for the *lazy* variant of the flag, which causes only
userspace preemption.
A ``schedule_entry_preempt`` may involve a task switch or not, in the latter
case, a task goes through the scheduler from a preemption context but it is
picked as the next task to run. Since the scheduler runs, this clears the need
to reschedule. The ``any_thread_running`` state does not imply the monitored
task is not running as this monitor does not track the outcome of scheduling.

In theory, a preemption can only occur after the ``need_resched`` flag is set. In
practice, however, it is possible to see a preemption where the flag is not
set. This can happen in one specific condition::

  need_resched
                   preempt_schedule()
                                           preempt_schedule_irq()
                                                   __schedule()
  !need_resched
                           __schedule()

In the situation above, standard preemption starts (e.g. from preempt_enable
when the flag is set), an interrupt occurs before scheduling and, on its exit
path, it schedules, which clears the ``need_resched`` flag.
When the preempted task runs again, the standard preemption started earlier
resumes, although the flag is no longer set. The monitor considers this a
``nested_preemption``, this allows another preemption without re-setting the
flag. This condition relaxes the monitor constraints and may catch false
negatives (i.e. no real ``nested_preemptions``) but makes the monitor more
robust and able to validate other scenarios.
For simplicity, the monitor starts in ``preempt_irq``, although no interrupt
occurred, as the situation above is hard to pinpoint::

    schedule_entry
    irq_entry                 #===========================================#
  +-------------------------- H                                           H
  |                           H                                           H
  +-------------------------> H             any_thread_running            H
                              H                                           H
  +-------------------------> H                                           H
  |                           #===========================================#
  | schedule_entry              |                       ^
  | schedule_entry_preempt      | sched_need_resched    | schedule_entry
  |                             |                      schedule_entry_preempt
  |                             v                       |
  |                           +----------------------+  |
  |                      +--- |                      |  |
  |   sched_need_resched |    |     rescheduling     | -+
  |                      +--> |                      |
  |                           +----------------------+
  |                             | irq_entry
  |                             v
  |                           +----------------------+
  |                           |                      | ---+
  |                      ---> |                      |    | sched_need_resched
  |                           |      preempt_irq     |    | irq_entry
  |                           |                      | <--+
  |                           |                      | <--+
  |                           +----------------------+    |
  |                             | schedule_entry          | sched_need_resched
  |                             | schedule_entry_preempt  |
  |                             v                         |
  |                           +-----------------------+   |
  +-------------------------- |    nested_preempt     | --+
                              +-----------------------+
                                ^ irq_entry         |
                                +-------------------+

Due to how the ``need_resched`` flag on the preemption count works on arm64,
this monitor is unstable on that architecture, as it often records preemption
when the flag is not set, even in presence of the workaround above.
For the time being, the monitor is disabled by default on arm64.

sssw monitor

258-343

set state sleep and wakeup(`sssw`) monitor는 `set_state`로 `sleepable`이 된 task가 실제로 잠들고, 잠든 task는 다시 실행되기 전에 wakeup이 필요함을 보장한다.

`switch_suspend`는 task가 명시적으로 `sleepable` state를 설정한 뒤 스스로 잠드는 전이다. suspend 뒤에는 다시 switch in되기 전에 `waking` state를 거쳐 깨워져야 한다. switch 전에 wakeup되거나 `runnable`로 설정되면 `sleepable` 설정을 되돌릴 수 있다.

`switch_blocking`은 task가 sleeping RT lock을 기다리는 `switch_suspend`의 특수 사례이며 `PREEMPT_RT`에서만 해당한다. wakeup event와 set state event의 race 때문에 실제로 task가 sleepable이 아닌데 모델이 이 switch로 인식할 수 있다. 이는 SMP model의 제한이며 우회책은 시스템을 느리게 할 수 있다.

`switch_preempt`는 kernel preemption으로 발생한 task switch이며 `nrp` model의 `schedule_entry_preempt`에 해당한다.

`switch_yield`는 task가 명시적으로 scheduler를 호출하거나 userspace 복귀 중 선점될 때 발생한다. `yield` syscall 뒤, idle task에서, 또는 `need_resched` flag가 설정되었을 때 가능하다. `sleepable` task의 양보는 정의상 suspension이므로 yield할 수 없다.

특수한 yield는 signal pending 상태에서 `TASK_INTERRUPTIBLE` task가 scheduler를 호출할 때 생긴다. task는 일반적인 blocking/waking을 거치지 않고 runnable로 돌아가며, switch가 있다면 `signal_wakeup` state로의 yield처럼 보이고 뒤이어 signal delivery가 온다.

`signal_wakeup`에서 monitor는 wakeup event를 보더라도 false negative를 배제하기 위해 signal을 기대한다. 실제로 signal이 반드시 필요한 것은 아니다.

이 monitor에는 running state가 없다. `sleepable`과 `runnable`은 task의 원하는 state만 가리키며, task는 preemption 등으로 schedule out될 수 있다. 다만 task가 running이 될 수 있는 시점을 나타내는 `sched_switch_in` event는 포함한다.

`sched_switch_in`은 preemption으로도 발생할 수 있지만, task가 `sleeping`에 도달한 뒤에는 `wakeup` 전까지 발생할 수 없다.

sssw switch 유형
event의미
`switch_suspend`명시적 `sleepable` 설정 뒤 실제 sleep
`switch_blocking`PREEMPT_RT sleeping lock 대기의 특수 suspension
`switch_preempt`kernel preemption에 의한 switch
`switch_yield`명시적 yield 또는 userspace 복귀 중 preemption

각 switch가 나타내는 task 상태 변화와 제한을 정리한다.

sssw 주 상태 전이
runnable (initial/final)set_state_sleepable
sleepableswitch_suspend / switch_blocking
sleepingwakeup
runnableswitch_in / switch_preempt / signal_deliver self-loop
sleepableswitch_yield
signal_wakeupsignal_deliver
runnable다음 상태 변경 대기

원문의 큰 ASCII 상태기를 task의 원하는 상태와 wakeup 요구 조건 중심으로 구성했다.

sssw 상태별 허용 event
state허용 event와 결과
`runnable``set_state_runnable`, `wakeup`, `switch_in`, `switch_yield`, `switch_preempt`, `signal_deliver` self-loop
`runnable``set_state_sleepable` -> `sleepable`
`sleepable``set_state_runnable` 또는 `wakeup` -> `runnable`
`sleepable``switch_suspend` / `switch_blocking` -> `sleeping`
`sleepable``switch_yield` -> `signal_wakeup`
`sleeping``wakeup` -> `runnable`
`signal_wakeup``signal_deliver` -> `runnable`; switch·wakeup은 self-loop

self-loop와 복귀 event를 포함해 상태별 핵심 허용 조건을 보인다.

Monitor sssw
------------

The set state sleep and wakeup (sssw) monitor ensures ``set_state`` to
sleepable leads to sleeping and sleeping tasks require wakeup. It includes the
following types of switch:

* ``switch_suspend``:
  a task puts itself to sleep, this can happen only after explicitly setting
  the task to ``sleepable``. After a task is suspended, it needs to be woken up
  (``waking`` state) before being switched in again.
  Setting the task's state to ``sleepable`` can be reverted before switching if it
  is woken up or set to ``runnable``.
* ``switch_blocking``:
  a special case of a ``switch_suspend`` where the task is waiting on a
  sleeping RT lock (``PREEMPT_RT`` only), it is common to see wakeup and set
  state events racing with each other and this leads the model to perceive this
  type of switch when the task is not set to sleepable. This is a limitation of
  the model in SMP system and workarounds may slow down the system.
* ``switch_preempt``:
  a task switch as a result of kernel preemption (``schedule_entry_preempt`` in
  the nrp model).
* ``switch_yield``:
  a task explicitly calls the scheduler or is preempted while returning to
  userspace. It can happen after a ``yield`` system call, from the idle task or
  if the ``need_resched`` flag is set. By definition, a task cannot yield while
  ``sleepable`` as that would be a suspension. A special case of a yield occurs
  when a task in ``TASK_INTERRUPTIBLE`` calls the scheduler while a signal is
  pending. The task doesn't go through the usual blocking/waking and is set
  back to runnable, the resulting switch (if there) looks like a yield to the
  ``signal_wakeup`` state and is followed by the signal delivery. From this
  state, the monitor expects a signal even if it sees a wakeup event, although
  not necessary, to rule out false negatives.

This monitor doesn't include a running state, ``sleepable`` and ``runnable``
are only referring to the task's desired state, which could be scheduled out
(e.g. due to preemption). However, it does include the event
``sched_switch_in`` to represent when a task is allowed to become running. This
can be triggered also by preemption, but cannot occur after the task got to
``sleeping`` before a ``wakeup`` occurs::

   +--------------------------------------------------------------------------+
   |                                                                          |
   |                                                                          |
   | switch_suspend           |                                               |
   | switch_blocking          |                                               |
   v                          v                                               |
 +----------+              #==========================#   set_state_runnable  |
 |          |              H                          H   wakeup              |
 |          |              H                          H   switch_in           |
 |          |              H                          H   switch_yield        |
 | sleeping |              H                          H   switch_preempt      |
 |          |              H                          H   signal_deliver      |
 |          |  switch_     H                          H ------+               |
 |          |  _blocking   H         runnable         H       |               |
 |          | <----------- H                          H <-----+               |
 +----------+              H                          H                       |
   |   wakeup              H                          H                       |
   +---------------------> H                          H                       |
                           H                          H                       |
               +---------> H                          H                       |
               |           #==========================#                       |
               |             |                ^                               |
               |             |                | set_state_runnable            |
               |             |                | wakeup                        |
               |    set_state_sleepable       |      +------------------------+
               |             v                |      |
               |           +--------------------------+  set_state_sleepable
               |           |                          |  switch_in
               |           |                          |  switch_preempt
   signal_deliver          |        sleepable         |  signal_deliver
               |           |                          | ------+
               |           |                          |       |
               |           |                          | <-----+
               |           +--------------------------+
               |             |                ^
               |        switch_yield          | set_state_sleepable
               |             v                |
               |           +---------------+  |
               +---------- | signal_wakeup | -+
                           +---------------+
                             ^           | switch_in
                             |           | switch_preempt
                             |           | switch_yield
                             +-----------+ wakeup

opid monitor

344-398

operations with preemption and irq disabled(`opid`) monitor는 `wakeup`과 `need_resched` 같은 동작이 interrupt와 preemption이 모두 disabled인 상태 또는 interrupt context에서 발생하도록 보장한다. interrupt context에서는 preemption이 명시적으로 disabled되지 않았을 수도 있다.

일부 RCU 내부 함수도 `need_resched`를 설정할 수 있다. 이 경우 task wakeup과 대응하지 않으며 interrupt만 disabled인 상태에서 발생할 수 있다.

opid 보호 상태
enabled (initial/final)preempt_disable
preempt_disabledirq_disable
disabledsched_need_resched / sched_waking / irq_entry self-loop
enabledirq_disable
irq_disabledpreempt_disable 또는 irq_entry
in_irqirq_enable로 이전 보호 상태 복귀

preemption과 IRQ 상태의 조합 및 interrupt context를 구조화했다.

opid 상태별 조건
statepreemptionIRQ주요 허용 event
`enabled`enabledenabled보호 event 불허, disable 전이만 허용
`preempt_disabled`disabledenabled`irq_disable`로 `disabled` 진입
`irq_disabled`enableddisabledRCU의 `sched_need_resched`, `irq_entry`
`disabled`disableddisabled`sched_need_resched`, `sched_waking`, `irq_entry`
`in_irq`암묵적으로 disabledinterrupt contextwakeup 및 need_resched 관련 event

wakeup 및 need_resched가 허용되는 보호 상태를 정리한다.

`disabled`에서는 `sched_need_resched`, `sched_waking`, `irq_entry`를 허용한다. `irq_disabled`는 RCU 내부 경로의 `sched_need_resched`를 허용하며, `in_irq`는 tracepoint가 보이지 않을 수 있는 interrupt context를 나타낸다.

이 monitor는 `PREEMPT_RT` kernel에서 작동하도록 설계되었다. interrupt 중에는 preemption이 항상 disabled이므로 interrupt context event는 preemption tracepoint가 보이지 않는 유효한 시나리오를 식별하는 지름길이다.

non-`PREEMPT_RT` kernel에서는 interrupt가 softirq를 호출해 `need_resched`를 설정하고 task를 깨울 수 있다. 이 특수 사례는 현재 monitor가 지원하지 않는다.

Monitor opid
------------

The operations with preemption and irq disabled (opid) monitor ensures
operations like ``wakeup`` and ``need_resched`` occur with interrupts and
preemption disabled or during interrupt context, in such case preemption may
not be disabled explicitly.
``need_resched`` can be set by some RCU internals functions, in which case it
doesn't match a task wakeup and might occur with only interrupts disabled::

                 |                     sched_need_resched
                 |                     sched_waking
                 |                     irq_entry
                 |                   +--------------------+
                 v                   v                    |
               +------------------------------------------------------+
  +----------- |                     disabled                         | <+
  |            +------------------------------------------------------+  |
  |              |                 ^                                     |
  |              |          preempt_disable      sched_need_resched      |
  |       preempt_enable           |           +--------------------+    |
  |              v                 |           v                    |    |
  |            +------------------------------------------------------+  |
  |            |                   irq_disabled                       |  |
  |            +------------------------------------------------------+  |
  |                              |             |        ^                |
  |     irq_entry            irq_entry         |        |                |
  |     sched_need_resched       v             |   irq_disable           |
  |     sched_waking +--------------+          |        |                |
  |           +----- |              |     irq_enable    |                |
  |           |      |    in_irq    |          |        |                |
  |           +----> |              |          |        |                |
  |                  +--------------+          |        |          irq_disable
  |                     |                      |        |                |
  | irq_enable          | irq_enable           |        |                |
  |                     v                      v        |                |
  |            #======================================================#  |
  |            H                     enabled                          H  |
  |            #======================================================#  |
  |              |                   ^         ^ preempt_enable     |    |
  |       preempt_disable     preempt_enable   +--------------------+    |
  |              v                   |                                   |
  |            +------------------+  |                                   |
  +----------> | preempt_disabled | -+                                   |
               +------------------+                                      |
                 |                                                       |
                 +-------------------------------------------------------+

This monitor is designed to work on ``PREEMPT_RT`` kernels, the special case of
events occurring in interrupt context is a shortcut to identify valid scenarios
where the preemption tracepoints might not be visible, during interrupts
preemption is always disabled. On non- ``PREEMPT_RT`` kernels, the interrupts
might invoke a softirq to set ``need_resched`` and wake up a task. This is
another special case that is currently not supported by the monitor.

참고 문헌

399-402

sched 명세가 적용 중인 Linux task model의 참고 자료는 [1] `https://bristot.me/linux-task-model`이다.

References
----------

[1] - https://bristot.me/linux-task-model