[번역] scheduler/sched-arch.txt
스케줄러의 아키텍쳐 코드 구현을 위한 힌트(CPU Scheduler implementation hints for architecture specific code)
Nick Piggin, 2005
컨텍스트 스위칭(Context switch)
1. 런큐 잠금(Runqueue locking)
보통 아키텍쳐마다 구현된 함수 switch_to()는 런큐가 잠긴 상태에서 호출된다. switch_to()가 런큐의 락을 잠그려 하지 않는다면 문제는 발생하지 않는다. 보통 컨텍스트 스위칭중에 wake-up 동작이 발생할 경우에 문제가 된다. 한 예로 arch/ia64/include/asm/switch_to.h를 보자.
By default, the switch_to arch function is called with the runqueue locked. This is usually not a problem unless switch_to may need to take the runqueue lock. This is usually due to a wake up operation in the context switch. See arch/ia64/include/asm/switch_to.h for an example.
스케쥴러에게 런큐의 락을 잡지 않은 상태에서 switch_to()를 호출하도록 요청하기 위해서는 헤더파일에 __ARCH_WAIT_UNLOCKED_CTXSW를 정의해야 한다.(보통 switch_to()가 정의된 곳에 정의하면 된다)
To request the scheduler call switch_to with the runqueue unlocked, you must `#define __ARCH_WANT_UNLOCKED_CTXSW` in a header file (typically the one where switch_to is defined).
역주) 커널 버전 4.16 기준으로 __ARCH_WAIT_UNLOCKED_CTXSW 정의는 더 이상 사용되지 않는다.
CONFIG_SMP를 사용하는 경우에 런큐를 잠그지 않은 상태에서 컨텍스트 스위칭을 하면 core 스케쥴러에 작은 성능상의 패널티를 얻게 된다.
Unlocked context switches introduce only a very minor performance penalty to the core scheduler implementation in the CONFIG_SMP case.
CPU idle
직접 구현한 cpu_idle() 루틴은 아래의 룰을 따를 필요가 있다.
Your cpu_idle routines need to obey the following rules:
1. idle 루틴에서는 선점이 비활성화되어야 한다. schedule()을 호출할 때만 잠시 활성화하고 호출한 후에는 다시 비활성화해야 한다.
1. Preempt should now disabled over idle routines. Should only be enabled to call schedule() then disabled again.
2. TIF_NEED_RESCHED는 실행 중인 태스크가 schedule()를 호출 할 때까지 설정되고 결코 클리어되지 않는다. idle 스레드는 need_resched()를 query하면 되며 설정하거나 클리어할 일이 없다.
2. need_resched/TIF_NEED_RESCHED is only ever set, and will never be cleared until the running task has called schedule(). Idle threads need only ever query need_resched, and may never set or clear it.
3. cpu_idle()은 need_resched() == true인 상황일때 schedule()을 호출해야 한다. cpu_idle()은 schedule() 외에는 호출하면 안된다.
3. When cpu_idle finds (need_resched() == 'true'), it should call schedule(). It should not call schedule() otherwise.
4. need_resched를 검사 할 때 인터럽트를 비활성화해야 하는 경우는 다음 인터럽트가 발생할 때까지 프로세서를 sleep 상태로 만들 때이다.(need_resched에 대해 어떤 보호도 제공하지 않는다. 이것은 인터럽트를 놓치는 것을 방지한다)
4. The only time interrupts need to be disabled when checking need_resched is if we are about to sleep the processor until the next interrupt (this doesn't provide any protection of need_resched, it prevents losing an interrupt).
4a. 이런 타입의 sleep의 공통적인 문제는 아래와 같이 나타난다.
4a. Common problem with this type of sleep appears to be:
local_irq_disable();
if (!need_resched()) {
local_irq_enable();
*** resched interrupt arrives here ***
__asm__("sleep until next interrupt");
}
TIF_POLLING_NRFLAG는 need_resched()의 리턴값이 참이 될 때 인터럽트를 필요로하지 않는 idle 루틴에 의해 설정 될 수 있다. 즉, 일부 백그라운드 작업을 수행하거나 low CPU priority에 진입하는 것이 합리적 일지라도 주기적으로 need_resched를 폴링해야 한다.
5. TIF_POLLING_NRFLAG can be set by idle routines that do not need an interrupt to wake them up when need_resched goes high. In other words, they must be periodically polling need_resched, although it may be reasonable to do some background work or enter a low CPU priority.
5a. 만약 TIF_POLLING_NRFLAG가 설정되고 interrupt sleep에 진입할지 여부를 결정한다면, 메모리 베리어를 발행하기 위해 TIF_POLLING_NRFLAG를 클리어해야 한다.(3에서 설명했던것처럼 인터럽트비활성화 + need_resched() 체크가 뒤 따라온다)
5a. If TIF_POLLING_NRFLAG is set, and we do decide to enter an interrupt sleep, it needs to be cleared then a memory barrier issued (followed by a test of need_resched with interrupts disabled, as explained in 3).
polling, sleeping idle함수에 대한 예제가 arch/x86/kernel/process.c에 있다.
arch/x86/kernel/process.c has examples of both polling and sleeping idle functions.
아키텍쳐별 가능성있는 문제들(Possible arch/ problems)
찾아낸 아키텍쳐별 문제점(그리고 수정을 시도했거나 하지 않은 문제)
Possible arch problems I found (and either tried to fix or didn't):
ia64 - safe_halt()를 호출하는게 인터럽트와 race condtion을 유발하지 않을까?
is safe_halt call racy vs interrupts? (does it sleep?) (See #4a)
sh64 - sleep하는게 인터럽트와 race condition을 유발하지 않을까?
Is sleeping racy vs interrupts? (See #4a)
sparc - 여기서 IRQ가 활성화되어야 함? local_irq_save() 대신 local_irq-disable()을 사용하도록 바꾸기
IRQs on at this point(?), change local_irq_save to _disable.
- TODO: secondary CPU들의 선점을 비활성화하는게 필요함(#1을 보자)
needs secondary CPUs to disable preempt (See #1)