This philosophy relies on the SMP capabilities of the Linux kernel to handle races with
interrupt handlers, keeping in mind that most interrupt handlers run in process
context. Any code that interacts with an interrupt handler must be prepared to deal
with that interrupt handler running concurrently on some other CPU.
Therefore, spin_lock_irqsave() and related primitives need not disable preemption. The
reason this is safe is that if the interrupt handler runs, even if it preempts the code
holding the spinlock_t, it will block as soon as it attempts to acquire that spinlock_t.
The critical section will therefore still be preserved.
However, local_irq_save() still disables preemption, since there is no corresponding
lock to rely on. Using locks instead of local_irq_save() therefore can help reduce
scheduling latency, but substituting locks in this manner can reduce SMP performance,
Code that must interact with SA_NODELAY interrupts cannot use local_irq_save(), since
this does not disable hardware interrupts. Instead, raw_local_irq_save() should be
used. Similarly, raw spinlocks (raw_spinlock_t, raw_rwlock_t, and raw_seqlock_t) need
to be used when interacting with SA_NODELAY interrupt handlers. However, raw
spinlocks and raw interrupt disabling should -not- be used outside of a few low-level
areas, such as the scheduler, architecture-dependent code, and RCU.
Priority inheritance for in-kernel spinlocks and semaphores
Realtime programmers are often concerned about priority inversion, which can happen
Low-priority task A acquires a resource, for example, a lock.
Medium-priority task B starts executing CPU-bound, preempting low-priority task
High-priority task C attempts to acquire the lock held by low-priority task A, but
blocks because of medium-priority task B having preempted low-priority task A.
Such priority inversion can indefinitely delay a high-priority task. There are two main
ways to address this problem: (1) suppressing preemption and (2) priority inheritance.
In the first case, since there is no preemption, task B cannot preempt task A, preventing
priority inversion from occurring. This approach is used by PREEMPT kernels for
spinlocks, but not for semaphores. It does not make sense to suppress preemption for
semaphores, since it is legal to block while holding one, which could result in priority
inversion even in absence of preemption. For some realtime workloads, preemption
cannot be suppressed even for spinlocks, due to the impact to scheduling latencies.
Priority inheritance can be used in cases where suppressing preemption does not make
sense. The idea here is that high-priority tasks temporarily donate their high priority to
lower-priority tasks that are holding critical locks. This priority inheritance is transitive:
in the example above, if an even higher priority task D attempted to acquire a second
lock that high-priority task C was already holding, then both tasks C and A would be be
temporarily boosted to the priority of task D. The duration of the priority boost is also
sharply limited: as soon as low-priority task A releases the lock, it will immediately lose
A realtime preemption overview [LWN.net] https://lwn.net/Articles/146861/
4 of 19 12/7/18, 5:14 PM