← Documents Documentation/core-api/real-time/differences.rst GitHub 원문 ↗

Linux 6.18.37 · Core API

How realtime kernels differ

PREEMPT_RT가 lock, interrupt, softirq, per-CPU data, timer, memory allocator, irq_work, RCU와 sequence lock의 실행 의미론을 어떻게 바꾸는지 설명합니다.

Source pathDocumentation/core-api/real-time/differences.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

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

1. 요약·해설

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

요약과 해설

differences.rst:1-242

`PREEMPT_RT`는 interrupt와 여러 kernel 작업을 thread context로 옮기고 `spinlock_t`를 sleep 가능하게 만들어 긴 non-preemptible 구간을 줄입니다. 반면 `raw_spinlock_t`는 전통적인 hard IRQ 의미론을 유지합니다.

Softirq와 timer, `irq_work`, RCU callback이 thread context에서 실행될 수 있으므로 preemption disabled 상태를 암묵적으로 기대하는 per-CPU 보호, memory allocation, spin-wait pattern을 다시 설계해야 합니다.

`local_lock_t`, `HRTIMER_MODE_HARD`, `IRQ_WORK_HARD_IRQ`, process-context RCU, priority inheritance가 가능한 composite sequence counter는 realtime 환경에서 latency와 forward progress를 함께 보장하는 핵심 수단입니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ===========================
4 How realtime kernels differ
5 ===========================
6
7 :Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
8
9 Preface
10 =======
11
12 With forced-threaded interrupts and sleeping spin locks, code paths that
13 previously caused long scheduling latencies have been made preemptible and
14 moved into process context. This allows the scheduler to manage them more
15 effectively and respond to higher-priority tasks with reduced latency.
16
17 The following chapters provide an overview of key differences between a
18 PREEMPT_RT kernel and a standard, non-PREEMPT_RT kernel.
19
20 Locking
21 =======
22
23 Spinning locks such as spinlock_t are used to provide synchronization for data
24 structures accessed from both interrupt context and process context. For this
25 reason, locking functions are also available with the _irq() or _irqsave()
26 suffixes, which disable interrupts before acquiring the lock. This ensures that
27 the lock can be safely acquired in process context when interrupts are enabled.
28
29 However, on a PREEMPT_RT system, interrupts are forced-threaded and no longer
30 run in hard IRQ context. As a result, there is no need to disable interrupts as
31 part of the locking procedure when using spinlock_t.
32
33 For low-level core components such as interrupt handling, the scheduler, or the
34 timer subsystem the kernel uses raw_spinlock_t. This lock type preserves
35 traditional semantics: it disables preemption and, when used with _irq() or
36 _irqsave(), also disables interrupts. This ensures proper synchronization in
37 critical sections that must remain non-preemptible or with interrupts disabled.
38
39 Execution context
40 =================
41
42 Interrupt handling in a PREEMPT_RT system is invoked in process context through
43 the use of threaded interrupts. Other parts of the kernel also shift their
44 execution into threaded context by different mechanisms. The goal is to keep
45 execution paths preemptible, allowing the scheduler to interrupt them when a
46 higher-priority task needs to run.
47
48 Below is an overview of the kernel subsystems involved in this transition to
49 threaded, preemptible execution.
50
51 Interrupt handling
52 ------------------
53
54 All interrupts are forced-threaded in a PREEMPT_RT system. The exceptions are
55 interrupts that are requested with the IRQF_NO_THREAD, IRQF_PERCPU, or
56 IRQF_ONESHOT flags.
57
58 The IRQF_ONESHOT flag is used together with threaded interrupts, meaning those
59 registered using request_threaded_irq() and providing only a threaded handler.
60 Its purpose is to keep the interrupt line masked until the threaded handler has
61 completed.
62
63 If a primary handler is also provided in this case, it is essential that the
64 handler does not acquire any sleeping locks, as it will not be threaded. The
65 handler should be minimal and must avoid introducing delays, such as
66 busy-waiting on hardware registers.
67
68
69 Soft interrupts, bottom half handling
70 -------------------------------------
71
72 Soft interrupts are raised by the interrupt handler and are executed after the
73 handler returns. Since they run in thread context, they can be preempted by
74 other threads. Do not assume that softirq context runs with preemption
75 disabled. This means you must not rely on mechanisms like local_bh_disable() in
76 process context to protect per-CPU variables. Because softirq handlers are
77 preemptible under PREEMPT_RT, this approach does not provide reliable
78 synchronization.
79
80 If this kind of protection is required for performance reasons, consider using
81 local_lock_nested_bh(). On non-PREEMPT_RT kernels, this allows lockdep to
82 verify that bottom halves are disabled. On PREEMPT_RT systems, it adds the
83 necessary locking to ensure proper protection.
84
85 Using local_lock_nested_bh() also makes the locking scope explicit and easier
86 for readers and maintainers to understand.
87
88
89 per-CPU variables
90 -----------------
91
92 Protecting access to per-CPU variables solely by using preempt_disable() should
93 be avoided, especially if the critical section has unbounded runtime or may
94 call APIs that can sleep.
95
96 If using a spinlock_t is considered too costly for performance reasons,
97 consider using local_lock_t. On non-PREEMPT_RT configurations, this introduces
98 no runtime overhead when lockdep is disabled. With lockdep enabled, it verifies
99 that the lock is only acquired in process context and never from softirq or
100 hard IRQ context.
101
102 On a PREEMPT_RT kernel, local_lock_t is implemented using a per-CPU spinlock_t,
103 which provides safe local protection for per-CPU data while keeping the system
104 preemptible.
105
106 Because spinlock_t on PREEMPT_RT does not disable preemption, it cannot be used
107 to protect per-CPU data by relying on implicit preemption disabling. If this
108 inherited preemption disabling is essential and if local_lock_t cannot be used
109 due to performance constraints, brevity of the code, or abstraction boundaries
110 within an API then preempt_disable_nested() may be a suitable alternative. On
111 non-PREEMPT_RT kernels, it verifies with lockdep that preemption is already
112 disabled. On PREEMPT_RT, it explicitly disables preemption.
113
114 Timers
115 ------
116
117 By default, an hrtimer is executed in hard interrupt context. The exception is
118 timers initialized with the HRTIMER_MODE_SOFT flag, which are executed in
119 softirq context.
120
121 On a PREEMPT_RT kernel, this behavior is reversed: hrtimers are executed in
122 softirq context by default, typically within the ktimersd thread. This thread
123 runs at the lowest real-time priority, ensuring it executes before any
124 SCHED_OTHER tasks but does not interfere with higher-priority real-time
125 threads. To explicitly request execution in hard interrupt context on
126 PREEMPT_RT, the timer must be marked with the HRTIMER_MODE_HARD flag.
127
128 Memory allocation
129 -----------------
130
131 The memory allocation APIs, such as kmalloc() and alloc_pages(), require a
132 gfp_t flag to indicate the allocation context. On non-PREEMPT_RT kernels, it is
133 necessary to use GFP_ATOMIC when allocating memory from interrupt context or
134 from sections where preemption is disabled. This is because the allocator must
135 not sleep in these contexts waiting for memory to become available.
136
137 However, this approach does not work on PREEMPT_RT kernels. The memory
138 allocator in PREEMPT_RT uses sleeping locks internally, which cannot be
139 acquired when preemption is disabled. Fortunately, this is generally not a
140 problem, because PREEMPT_RT moves most contexts that would traditionally run
141 with preemption or interrupts disabled into threaded context, where sleeping is
142 allowed.
143
144 What remains problematic is code that explicitly disables preemption or
145 interrupts. In such cases, memory allocation must be performed outside the
146 critical section.
147
148 This restriction also applies to memory deallocation routines such as kfree()
149 and free_pages(), which may also involve internal locking and must not be
150 called from non-preemptible contexts.
151
152 IRQ work
153 --------
154
155 The irq_work API provides a mechanism to schedule a callback in interrupt
156 context. It is designed for use in contexts where traditional scheduling is not
157 possible, such as from within NMI handlers or from inside the scheduler, where
158 using a workqueue would be unsafe.
159
160 On non-PREEMPT_RT systems, all irq_work items are executed immediately in
161 interrupt context. Items marked with IRQ_WORK_LAZY are deferred until the next
162 timer tick but are still executed in interrupt context.
163
164 On PREEMPT_RT systems, the execution model changes. Because irq_work callbacks
165 may acquire sleeping locks or have unbounded execution time, they are handled
166 in thread context by a per-CPU irq_work kernel thread. This thread runs at the
167 lowest real-time priority, ensuring it executes before any SCHED_OTHER tasks
168 but does not interfere with higher-priority real-time threads.
169
170 The exception are work items marked with IRQ_WORK_HARD_IRQ, which are still
171 executed in hard interrupt context. Lazy items (IRQ_WORK_LAZY) continue to be
172 deferred until the next timer tick and are also executed by the irq_work/
173 thread.
174
175 RCU callbacks
176 -------------
177
178 RCU callbacks are invoked by default in softirq context. Their execution is
179 important because, depending on the use case, they either free memory or ensure
180 progress in state transitions. Running these callbacks as part of the softirq
181 chain can lead to undesired situations, such as contention for CPU resources
182 with other SCHED_OTHER tasks when executed within ksoftirqd.
183
184 To avoid running callbacks in softirq context, the RCU subsystem provides a
185 mechanism to execute them in process context instead. This behavior can be
186 enabled by setting the boot command-line parameter rcutree.use_softirq=0. This
187 setting is enforced in kernels configured with PREEMPT_RT.
188
189 Spin until ready
190 ================
191
192 The "spin until ready" pattern involves repeatedly checking (spinning on) the
193 state of a data structure until it becomes available. This pattern assumes that
194 preemption, soft interrupts, or interrupts are disabled. If the data structure
195 is marked busy, it is presumed to be in use by another CPU, and spinning should
196 eventually succeed as that CPU makes progress.
197
198 Some examples are hrtimer_cancel() or timer_delete_sync(). These functions
199 cancel timers that execute with interrupts or soft interrupts disabled. If a
200 thread attempts to cancel a timer and finds it active, spinning until the
201 callback completes is safe because the callback can only run on another CPU and
202 will eventually finish.
203
204 On PREEMPT_RT kernels, however, timer callbacks run in thread context. This
205 introduces a challenge: a higher-priority thread attempting to cancel the timer
206 may preempt the timer callback thread. Since the scheduler cannot migrate the
207 callback thread to another CPU due to affinity constraints, spinning can result
208 in livelock even on multiprocessor systems.
209
210 To avoid this, both the canceling and callback sides must use a handshake
211 mechanism that supports priority inheritance. This allows the canceling thread
212 to suspend until the callback completes, ensuring forward progress without
213 risking livelock.
214
215 In order to solve the problem at the API level, the sequence locks were extended
216 to allow a proper handover between the the spinning reader and the maybe
217 blocked writer.
218
219 Sequence locks
220 --------------
221
222 Sequence counters and sequential locks are documented in
223 Documentation/locking/seqlock.rst.
224
225 The interface has been extended to ensure proper preemption states for the
226 writer and spinning reader contexts. This is achieved by embedding the writer
227 serialization lock directly into the sequence counter type, resulting in
228 composite types such as seqcount_spinlock_t or seqcount_mutex_t.
229
230 These composite types allow readers to detect an ongoing write and actively
231 boost the writer’s priority to help it complete its update instead of spinning
232 and waiting for its completion.
233
234 If the plain seqcount_t is used, extra care must be taken to synchronize the
235 reader with the writer during updates. The writer must ensure its update is
236 serialized and non-preemptible relative to the reader. This cannot be achieved
237 using a regular spinlock_t because spinlock_t on PREEMPT_RT does not disable
238 preemption. In such cases, using seqcount_spinlock_t is the preferred solution.
239
240 However, if there is no spinning involved i.e., if the reader only needs to
241 detect whether a write has started and not serialize against it then using
242 seqcount_t is reasonable.
243

3. 한국어 전문 번역

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

서문

1-18

SPDX 라이선스 식별자는 GPL-2.0입니다.

Realtime kernel의 차이점

저자: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

서문

강제 threaded interrupt와 sleep 가능한 spin lock을 사용하면서, 과거에 긴 scheduling latency를 만들던 code path를 preempt 가능하게 바꾸고 process context로 옮겼습니다. 따라서 scheduler가 이 path를 더 효과적으로 관리하고 높은 priority task에 더 짧은 latency로 응답할 수 있습니다.

다음 장에서는 `PREEMPT_RT` kernel과 표준 non-`PREEMPT_RT` kernel 사이의 주요 차이점을 개괄합니다.

Locking 의미론

19-37

Locking

`spinlock_t` 같은 spinning lock은 interrupt context와 process context 양쪽에서 접근하는 data structure를 동기화하는 데 사용합니다. 그래서 lock을 얻기 전에 interrupt를 비활성화하는 `_irq()` 또는 `_irqsave()` suffix의 locking function도 제공합니다. 이를 통해 interrupt가 활성화된 process context에서도 lock을 안전하게 얻을 수 있습니다.

하지만 `PREEMPT_RT` system에서는 interrupt를 강제로 thread화하여 더 이상 hard IRQ context에서 실행하지 않습니다. 따라서 `spinlock_t`를 사용할 때 locking 절차의 일부로 interrupt를 끌 필요가 없습니다.

Interrupt 처리, scheduler, timer subsystem 같은 low-level core component에는 kernel이 `raw_spinlock_t`를 사용합니다. 이 lock type은 전통적인 의미론을 유지하여 preemption을 끄고, `_irq()` 또는 `_irqsave()`와 함께 사용하면 interrupt도 끕니다. 이로써 non-preemptible 상태나 interrupt 비활성 상태를 유지해야 하는 critical section을 올바르게 동기화합니다.

Execution context와 interrupt 처리

38-67

Execution context

`PREEMPT_RT` system의 interrupt 처리는 threaded interrupt를 통해 process context에서 호출됩니다. Kernel의 다른 부분도 여러 메커니즘으로 실행을 threaded context로 옮깁니다. 목표는 실행 path를 preempt 가능하게 유지하여 더 높은 priority task가 실행되어야 할 때 scheduler가 이를 중단할 수 있게 하는 것입니다.

아래에서는 threaded, preemptible execution으로 전환하는 데 관련된 kernel subsystem을 개괄합니다.

Interrupt 처리

`PREEMPT_RT` system의 모든 interrupt는 강제로 thread화합니다. 단, `IRQF_NO_THREAD`, `IRQF_PERCPU`, `IRQF_ONESHOT` flag로 요청한 interrupt는 예외입니다.

`IRQF_ONESHOT` flag는 `request_threaded_irq()`로 등록하고 threaded handler만 제공하는 threaded interrupt와 함께 사용합니다. 이 flag의 목적은 threaded handler가 완료될 때까지 interrupt line을 masked 상태로 유지하는 것입니다.

이 경우 primary handler도 제공한다면 그 handler는 thread화되지 않으므로 sleep 가능한 lock을 절대 얻으면 안 됩니다. Handler는 최소한으로 유지해야 하며 hardware register를 busy-wait하는 것과 같은 지연을 만들지 않아야 합니다.

Soft interrupt와 bottom half 처리

68-87

Soft interrupt와 bottom half 처리

Soft interrupt는 interrupt handler가 발생시키고 handler가 반환한 뒤 실행합니다. Thread context에서 실행되므로 다른 thread가 preempt할 수 있습니다. Softirq context가 preemption disabled 상태로 실행된다고 가정해서는 안 됩니다.

따라서 process context에서 per-CPU variable을 보호하려고 `local_bh_disable()` 같은 메커니즘에 의존하면 안 됩니다. `PREEMPT_RT`에서 softirq handler는 preempt 가능하므로 이 방식은 신뢰할 수 있는 동기화를 제공하지 않습니다.

성능상 이유로 이러한 보호가 필요하면 `local_lock_nested_bh()`를 검토하십시오. Non-`PREEMPT_RT` kernel에서는 bottom half가 비활성화되었는지 lockdep이 확인할 수 있고, `PREEMPT_RT` system에서는 적절한 보호에 필요한 locking을 추가합니다.

`local_lock_nested_bh()`를 사용하면 locking scope도 명시적으로 드러나 reader와 maintainer가 이해하기 쉬워집니다.

per-CPU variable 보호

88-113

per-CPU variable

Critical section의 실행 시간이 제한되지 않거나 sleep 가능한 API를 호출할 수 있다면 특히, `preempt_disable()`만으로 per-CPU variable 접근을 보호하는 방식을 피해야 합니다.

성능상 `spinlock_t`의 비용이 지나치게 크다고 판단되면 `local_lock_t`를 검토하십시오. Non-`PREEMPT_RT` configuration에서 lockdep을 끄면 runtime overhead가 없습니다. Lockdep을 켜면 이 lock을 process context에서만 얻고 softirq 또는 hard IRQ context에서는 얻지 않는지 검증합니다.

`PREEMPT_RT` kernel에서 `local_lock_t`는 per-CPU `spinlock_t`로 구현되며, system의 preempt 가능성을 유지하면서 per-CPU data에 안전한 local protection을 제공합니다.

`PREEMPT_RT`의 `spinlock_t`는 preemption을 끄지 않으므로 암묵적인 preemption 비활성화에 기대어 per-CPU data를 보호할 수 없습니다. 이 상속된 비활성화가 꼭 필요하지만 성능 제약, 짧은 code, API abstraction boundary 때문에 `local_lock_t`를 사용할 수 없다면 `preempt_disable_nested()`가 적절한 대안일 수 있습니다.

Non-`PREEMPT_RT` kernel에서는 `preempt_disable_nested()`가 preemption이 이미 꺼졌는지 lockdep으로 검증하고, `PREEMPT_RT`에서는 preemption을 명시적으로 끕니다.

Timer 실행 context

114-127

Timer

기본적으로 hrtimer는 hard interrupt context에서 실행합니다. `HRTIMER_MODE_SOFT` flag로 초기화한 timer는 예외로, softirq context에서 실행합니다.

`PREEMPT_RT` kernel에서는 이 동작이 반대입니다. Hrtimer는 기본적으로 softirq context, 대개 `ktimersd` thread 안에서 실행됩니다. 이 thread는 가장 낮은 real-time priority로 실행되어 모든 `SCHED_OTHER` task보다 먼저 실행되지만 priority가 더 높은 real-time thread를 방해하지 않습니다.

`PREEMPT_RT`에서 hard interrupt context 실행을 명시적으로 요청하려면 timer에 `HRTIMER_MODE_HARD` flag를 표시해야 합니다.

Memory 할당과 해제

128-151

Memory 할당

`kmalloc()`과 `alloc_pages()` 같은 memory allocation API는 allocation context를 나타내는 `gfp_t` flag를 요구합니다. Non-`PREEMPT_RT` kernel에서는 interrupt context 또는 preemption disabled section에서 memory를 할당할 때 `GFP_ATOMIC`을 사용해야 합니다. 이 context에서는 allocator가 memory를 기다리며 sleep하면 안 되기 때문입니다.

그러나 이 방식은 `PREEMPT_RT` kernel에서 동작하지 않습니다. `PREEMPT_RT`의 memory allocator는 내부적으로 sleep 가능한 lock을 사용하며, preemption이 꺼진 상태에서는 이 lock을 얻을 수 없습니다.

다행히 `PREEMPT_RT`는 전통적으로 preemption 또는 interrupt disabled 상태에서 실행하던 context 대부분을 sleep 가능한 threaded context로 옮기므로 일반적으로 문제가 되지 않습니다.

명시적으로 preemption이나 interrupt를 끄는 code는 여전히 문제가 됩니다. 이 경우 memory allocation을 critical section 밖에서 수행해야 합니다.

이 제한은 내부 locking이 개입할 수 있는 `kfree()`와 `free_pages()` 같은 memory deallocation routine에도 적용됩니다. 이러한 routine도 non-preemptible context에서 호출하면 안 됩니다.

IRQ work 실행 model

152-174

IRQ work

`irq_work` API는 interrupt context에서 callback을 예약하는 메커니즘입니다. NMI handler 내부나 scheduler 안처럼 전통적인 scheduling이 불가능하고 workqueue 사용이 안전하지 않은 context를 위해 설계되었습니다.

Non-`PREEMPT_RT` system에서는 모든 `irq_work` item을 interrupt context에서 즉시 실행합니다. `IRQ_WORK_LAZY`가 표시된 item은 다음 timer tick까지 미루지만 여전히 interrupt context에서 실행합니다.

`PREEMPT_RT` system에서는 실행 model이 달라집니다. `irq_work` callback이 sleep 가능한 lock을 얻거나 제한 없이 오래 실행될 수 있으므로 per-CPU `irq_work` kernel thread의 thread context에서 처리합니다. 이 thread는 가장 낮은 real-time priority로 실행되어 모든 `SCHED_OTHER` task보다 먼저 실행되지만 더 높은 priority의 real-time thread를 방해하지 않습니다.

`IRQ_WORK_HARD_IRQ`가 표시된 work item은 예외로, 계속 hard interrupt context에서 실행합니다. Lazy item인 `IRQ_WORK_LAZY`는 계속 다음 timer tick까지 미뤄지며 `irq_work/` thread가 실행합니다.

RCU callback 실행 context

175-188

RCU callback

기본적으로 RCU callback은 softirq context에서 호출됩니다. 사용 사례에 따라 memory를 해제하거나 state transition의 진행을 보장하므로 이 callback의 실행은 중요합니다. 하지만 softirq chain의 일부로 실행하면 `ksoftirqd` 안에서 다른 `SCHED_OTHER` task와 CPU resource를 두고 경쟁하는 등 바람직하지 않은 상황이 생길 수 있습니다.

RCU subsystem은 callback을 softirq context 대신 process context에서 실행하는 메커니즘을 제공합니다. Boot command-line parameter `rcutree.use_softirq=0`을 설정하면 이 동작을 활성화할 수 있으며, `PREEMPT_RT`로 구성한 kernel에서는 이 설정을 강제합니다.

Spin until ready와 priority inheritance

189-218

준비될 때까지 spin하기

"spin until ready" pattern은 data structure를 사용할 수 있을 때까지 그 상태를 반복해서 확인하며 spin합니다. 이 pattern은 preemption, soft interrupt 또는 interrupt가 비활성화되었다고 가정합니다. Data structure가 busy라면 다른 CPU가 사용 중인 것으로 간주하고, 그 CPU가 진행함에 따라 spin이 결국 성공해야 합니다.

`hrtimer_cancel()`과 `timer_delete_sync()`가 예입니다. 이 function은 interrupt 또는 soft interrupt를 끈 상태에서 실행하는 timer를 취소합니다. Thread가 active timer를 취소하려 할 때 callback 완료까지 spin해도 안전한데, callback은 다른 CPU에서만 실행할 수 있고 결국 끝나기 때문입니다.

하지만 `PREEMPT_RT` kernel의 timer callback은 thread context에서 실행됩니다. Timer를 취소하려는 높은 priority thread가 timer callback thread를 preempt할 수 있습니다. Affinity 제약으로 scheduler가 callback thread를 다른 CPU로 migrate할 수 없으므로 multiprocessor system에서도 spin이 livelock을 일으킬 수 있습니다.

이를 피하려면 취소 측과 callback 측 모두 priority inheritance를 지원하는 handshake 메커니즘을 사용해야 합니다. 그러면 취소 thread가 callback 완료까지 suspend되어 livelock 없이 forward progress를 보장할 수 있습니다.

API level에서 이 문제를 해결하기 위해 sequence lock을 확장하여 spinning reader와 block될 수 있는 writer 사이에 적절한 handover가 가능하도록 했습니다.

Sequence lock 확장

219-242

Sequence lock

Sequence counter와 sequential lock은 `Documentation/locking/seqlock.rst`에 설명되어 있습니다.

Writer와 spinning reader context에 올바른 preemption 상태를 보장하도록 interface를 확장했습니다. Writer serialization lock을 sequence counter type에 직접 포함하며, 그 결과 `seqcount_spinlock_t`나 `seqcount_mutex_t` 같은 composite type이 만들어졌습니다.

이 composite type을 사용하면 reader가 진행 중인 write를 감지하고 완료될 때까지 spin하는 대신 writer의 priority를 능동적으로 높여 update를 마치도록 도울 수 있습니다.

일반 `seqcount_t`를 사용한다면 update 중 reader와 writer를 동기화할 때 각별히 주의해야 합니다. Writer는 reader에 대해 update가 직렬화되고 non-preemptible임을 보장해야 합니다. `PREEMPT_RT`의 일반 `spinlock_t`는 preemption을 끄지 않으므로 이를 달성할 수 없으며, 이런 경우 `seqcount_spinlock_t`가 권장 해법입니다.

반면 spin이 전혀 없고 reader가 write 시작 여부만 감지하면 되며 write와 직렬화할 필요가 없다면 `seqcount_t`를 사용해도 합리적입니다.