요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
=============================================
Porting an architecture to support PREEMPT_RT
=============================================
:Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
This list outlines the architecture specific requirements that must be
implemented in order to enable PREEMPT_RT. Once all required features are
implemented, ARCH_SUPPORTS_RT can be selected in architecture’s Kconfig to make
PREEMPT_RT selectable.
Many prerequisites (genirq support for example) are enforced by the common code
and are omitted here.
The optional features are not strictly required but it is worth to consider
them.
Requirements
------------
Forced threaded interrupts
CONFIG_IRQ_FORCED_THREADING must be selected. Any interrupts that must
remain in hard-IRQ context must be marked with IRQF_NO_THREAD. This
requirement applies for instance to clocksource event interrupts,
perf interrupts and cascading interrupt-controller handlers.
PREEMPTION support
Kernel preemption must be supported and requires that
CONFIG_ARCH_NO_PREEMPT remain unselected. Scheduling requests, such as those
issued from an interrupt or other exception handler, must be processed
immediately.
POSIX CPU timers and KVM
POSIX CPU timers must expire from thread context rather than directly within
the timer interrupt. This behavior is enabled by setting the configuration
option CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK.
When KVM is enabled, CONFIG_KVM_XFER_TO_GUEST_WORK must also be set to ensure
that any pending work, such as POSIX timer expiration, is handled before
transitioning into guest mode.
Hard-IRQ and Soft-IRQ stacks
Soft interrupts are handled in the thread context in which they are raised. If
a soft interrupt is triggered from hard-IRQ context, its execution is deferred
to the ksoftirqd thread. Preemption is never disabled during soft interrupt
handling, which makes soft interrupts preemptible.
If an architecture provides a custom __do_softirq() implementation that uses a
separate stack, it must select CONFIG_HAVE_SOFTIRQ_ON_OWN_STACK. The
functionality should only be enabled when CONFIG_SOFTIRQ_ON_OWN_STACK is set.
FPU and SIMD access in kernel mode
FPU and SIMD registers are typically not used in kernel mode and are therefore
not saved during kernel preemption. As a result, any kernel code that uses
these registers must be enclosed within a kernel_fpu_begin() and
kernel_fpu_end() section.
The kernel_fpu_begin() function usually invokes local_bh_disable() to prevent
interruptions from softirqs and to disable regular preemption. This allows the
protected code to run safely in both thread and softirq contexts.
On PREEMPT_RT kernels, however, kernel_fpu_begin() must not call
local_bh_disable(). Instead, it should use preempt_disable(), since softirqs
are always handled in thread context under PREEMPT_RT. In this case, disabling
preemption alone is sufficient.
The crypto subsystem operates on memory pages and requires users to "walk and
map" these pages while processing a request. This operation must occur outside
the kernel_fpu_begin()/ kernel_fpu_end() section because it requires preemption
to be enabled. These preemption points are generally sufficient to avoid
excessive scheduling latency.
Exception handlers
Exception handlers, such as the page fault handler, typically enable interrupts
early, before invoking any generic code to process the exception. This is
necessary because handling a page fault may involve operations that can sleep.
Enabling interrupts is especially important on PREEMPT_RT, where certain
locks, such as spinlock_t, become sleepable. For example, handling an
invalid opcode may result in sending a SIGILL signal to the user task. A
debug excpetion will send a SIGTRAP signal.
In both cases, if the exception occurred in user space, it is safe to enable
interrupts early. Sending a signal requires both interrupts and kernel
preemption to be enabled.
Optional features
-----------------
Timer and clocksource
A high-resolution clocksource and clockevents device are recommended. The
clockevents device should support the CLOCK_EVT_FEAT_ONESHOT feature for
optimal timer behavior. In most cases, microsecond-level accuracy is
sufficient
Lazy preemption
This mechanism allows an in-kernel scheduling request for non-real-time tasks
to be delayed until the task is about to return to user space. It helps avoid
preempting a task that holds a sleeping lock at the time of the scheduling
request.
With CONFIG_GENERIC_IRQ_ENTRY enabled, supporting this feature requires
defining a bit for TIF_NEED_RESCHED_LAZY, preferably near TIF_NEED_RESCHED.
Serial console with NBCON
With PREEMPT_RT enabled, all console output is handled by a dedicated thread
rather than directly from the context in which printk() is invoked. This design
allows printk() to be safely used in atomic contexts.
However, this also means that if the kernel crashes and cannot switch to the
printing thread, no output will be visible preventing the system from printing
its final messages.
There are exceptions for immediate output, such as during panic() handling. To
support this, the console driver must implement new-style lock handling. This
involves setting the CON_NBCON flag in console::flags and providing
implementations for the write_atomic, write_thread, device_lock, and
device_unlock callbacks.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
PREEMPT_RT architecture porting 개요
1-18SPDX 라이선스 식별자는 GPL-2.0입니다.
PREEMPT_RT를 지원하도록 architecture porting하기
저자: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
이 목록은 `PREEMPT_RT`를 활성화하기 위해 구현해야 하는 architecture별 요구 사항을 정리합니다. 모든 필수 기능을 구현한 뒤 architecture의 Kconfig에서 `ARCH_SUPPORTS_RT`를 선택하면 `PREEMPT_RT`를 선택 가능하게 만들 수 있습니다.
`genirq` 지원과 같은 많은 선행 조건은 common code가 강제하므로 여기서는 생략합니다.
선택 기능은 엄격한 필수 조건은 아니지만 함께 검토할 가치가 있습니다.
Threaded interrupt, preemption, POSIX timer와 KVM
19-40요구 사항
강제 threaded interrupt
`CONFIG_IRQ_FORCED_THREADING`을 선택해야 합니다. Hard-IRQ context에 남아야 하는 interrupt에는 `IRQF_NO_THREAD`를 표시해야 합니다. 예를 들면 clocksource event interrupt, perf interrupt, cascading interrupt-controller handler가 이 요구 사항에 해당합니다.
PREEMPTION 지원
Kernel preemption을 지원해야 하며 `CONFIG_ARCH_NO_PREEMPT`는 선택하지 않은 상태여야 합니다. Interrupt 또는 다른 exception handler에서 발생한 요청을 포함한 scheduling request를 즉시 처리해야 합니다.
POSIX CPU timer와 KVM
POSIX CPU timer는 timer interrupt 안에서 직접 만료시키지 않고 thread context에서 만료시켜야 합니다. Configuration option `CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK`를 설정하면 이 동작을 활성화할 수 있습니다.
KVM을 활성화한 경우에는 `CONFIG_KVM_XFER_TO_GUEST_WORK`도 설정해야 합니다. 그래야 POSIX timer 만료 같은 pending work를 guest mode로 전환하기 전에 처리할 수 있습니다.
Hard-IRQ와 Soft-IRQ stack
41-50Hard-IRQ와 Soft-IRQ stack
Soft interrupt는 자신을 발생시킨 thread context에서 처리합니다. Hard-IRQ context에서 soft interrupt가 발생하면 실행을 `ksoftirqd` thread로 미룹니다. Soft interrupt를 처리하는 동안에는 preemption을 끄지 않으므로 soft interrupt를 preempt할 수 있습니다.
Architecture가 별도 stack을 사용하는 custom `__do_softirq()` 구현을 제공한다면 `CONFIG_HAVE_SOFTIRQ_ON_OWN_STACK`을 선택해야 합니다. 이 기능은 `CONFIG_SOFTIRQ_ON_OWN_STACK`이 설정된 경우에만 활성화해야 합니다.
Kernel mode의 FPU와 SIMD 접근
51-68Kernel mode의 FPU와 SIMD 접근
FPU와 SIMD register는 보통 kernel mode에서 사용하지 않으므로 kernel preemption 때 저장하지 않습니다. 따라서 이 register를 사용하는 kernel code는 `kernel_fpu_begin()`과 `kernel_fpu_end()` 구간으로 감싸야 합니다.
일반적으로 `kernel_fpu_begin()`은 softirq의 간섭과 보통 preemption을 막기 위해 `local_bh_disable()`을 호출합니다. 그러면 보호된 code를 thread context와 softirq context 양쪽에서 안전하게 실행할 수 있습니다.
그러나 `PREEMPT_RT` kernel의 `kernel_fpu_begin()`은 `local_bh_disable()`을 호출하면 안 됩니다. `PREEMPT_RT`에서는 softirq를 항상 thread context에서 처리하므로 대신 `preempt_disable()`을 사용해야 하며, preemption만 비활성화해도 충분합니다.
Crypto subsystem은 memory page를 다루며 request를 처리하는 동안 사용자가 이 page를 "walk and map"하도록 요구합니다. 이 작업은 preemption이 활성화되어야 하므로 `kernel_fpu_begin()`과 `kernel_fpu_end()` 구간 밖에서 수행해야 합니다. 이러한 preemption point는 일반적으로 과도한 scheduling latency를 피하는 데 충분합니다.
Exception handler
69-80Exception handler
Page fault handler 같은 exception handler는 보통 exception을 처리하는 generic code를 호출하기 전에 interrupt를 일찍 활성화합니다. Page fault 처리에는 sleep할 수 있는 작업이 포함될 수 있으므로 이것이 필요합니다.
`PREEMPT_RT`에서는 `spinlock_t` 같은 일부 lock이 sleep 가능해지므로 interrupt를 활성화하는 일이 특히 중요합니다. 예를 들어 invalid opcode를 처리하면 user task에 `SIGILL` signal을 보낼 수 있고, debug exception은 `SIGTRAP` signal을 보냅니다.
두 경우 모두 exception이 user space에서 발생했다면 interrupt를 일찍 활성화해도 안전합니다. Signal 전송에는 interrupt와 kernel preemption이 모두 활성화되어 있어야 합니다.
Timer, clocksource와 lazy preemption
81-97선택 기능
Timer와 clocksource
High-resolution clocksource와 clockevents device를 권장합니다. 최적의 timer 동작을 위해 clockevents device는 `CLOCK_EVT_FEAT_ONESHOT` 기능을 지원해야 합니다. 대부분은 microsecond 수준의 정확도로 충분합니다.
Lazy preemption
이 메커니즘은 non-real-time task에 대한 in-kernel scheduling request를 task가 user space로 돌아가려는 시점까지 지연할 수 있게 합니다. Scheduling request가 발생한 순간 sleep 가능한 lock을 보유한 task가 preempt되는 것을 피하는 데 도움이 됩니다.
`CONFIG_GENERIC_IRQ_ENTRY`를 활성화한 상태에서 이 기능을 지원하려면 `TIF_NEED_RESCHED_LAZY` bit를 정의해야 하며, 가능하면 `TIF_NEED_RESCHED` 가까이에 두는 것이 좋습니다.
NBCON을 사용하는 serial console
98-109NBCON을 사용하는 serial console
`PREEMPT_RT`를 활성화하면 모든 console output은 `printk()`를 호출한 context에서 직접 처리하지 않고 전용 thread가 처리합니다. 이 설계 덕분에 atomic context에서도 `printk()`를 안전하게 사용할 수 있습니다.
하지만 kernel이 crash하여 printing thread로 전환할 수 없다면 output이 전혀 보이지 않아 system이 마지막 message를 출력하지 못할 수도 있습니다.
`panic()` 처리 중과 같이 즉시 출력하는 예외가 있습니다. 이를 지원하려면 console driver가 새로운 방식의 lock 처리를 구현해야 합니다. 구체적으로 `console::flags`에 `CON_NBCON` flag를 설정하고 `write_atomic`, `write_thread`, `device_lock`, `device_unlock` callback을 구현해야 합니다.
요약과 해설
architecture-porting.rst:1-109`ARCH_SUPPORTS_RT`를 제공하려는 architecture는 forced threaded interrupt와 즉시 preemption을 지원하고, POSIX timer와 KVM pending work를 thread context에서 안전하게 처리해야 합니다.
Softirq stack, kernel FPU/SIMD 구간, exception handler는 일반 kernel과 다른 PREEMPT_RT 실행 문맥 및 sleep 가능성을 고려해야 합니다. 특히 `kernel_fpu_begin()`은 `preempt_disable()`을 사용하고 user-space exception path에서는 interrupt를 일찍 활성화해야 합니다.
High-resolution timer, lazy preemption, NBCON console은 필수는 아니지만 latency와 crash-time 진단 품질을 높입니다. NBCON driver는 atomic output과 thread output을 모두 지원하는 callback을 구현해야 합니다.