| .. SPDX-License-Identifier: GPL-2.0 | |
| .. _kernel_hacking_locktypes: | |
| ========================== | |
| Lock types and their rules | |
| ========================== | |
| Introduction | |
| ============ | |
| The kernel provides a variety of locking primitives which can be divided | |
| into three categories: | |
| - Sleeping locks | |
| - CPU local locks | |
| - Spinning locks | |
| This document conceptually describes these lock types and provides rules | |
| for their nesting, including the rules for use under PREEMPT_RT. | |
| Lock categories | |
| =============== | |
| Sleeping locks | |
| -------------- | |
| Sleeping locks can only be acquired in preemptible task context. | |
| Although implementations allow try_lock() from other contexts, it is | |
| necessary to carefully evaluate the safety of unlock() as well as of | |
| try_lock(). Furthermore, it is also necessary to evaluate the debugging | |
| versions of these primitives. In short, don't acquire sleeping locks from | |
| other contexts unless there is no other option. | |
| Sleeping lock types: | |
| - mutex | |
| - rt_mutex | |
| - semaphore | |
| - rw_semaphore | |
| - ww_mutex | |
| - percpu_rw_semaphore | |
| On PREEMPT_RT kernels, these lock types are converted to sleeping locks: | |
| - local_lock | |
| - spinlock_t | |
| - rwlock_t | |
| CPU local locks | |
| --------------- | |
| - local_lock | |
| On non-PREEMPT_RT kernels, local_lock functions are wrappers around | |
| preemption and interrupt disabling primitives. Contrary to other locking | |
| mechanisms, disabling preemption or interrupts are pure CPU local | |
| concurrency control mechanisms and not suited for inter-CPU concurrency | |
| control. | |
| Spinning locks | |
| -------------- | |
| - raw_spinlock_t | |
| - bit spinlocks | |
| On non-PREEMPT_RT kernels, these lock types are also spinning locks: | |
| - spinlock_t | |
| - rwlock_t | |
| Spinning locks implicitly disable preemption and the lock / unlock functions | |
| can have suffixes which apply further protections: | |
| =================== ==================================================== | |
| _bh() Disable / enable bottom halves (soft interrupts) | |
| _irq() Disable / enable interrupts | |
| _irqsave/restore() Save and disable / restore interrupt disabled state | |
| =================== ==================================================== | |
| Owner semantics | |
| =============== | |
| The aforementioned lock types except semaphores have strict owner | |
| semantics: | |
| The context (task) that acquired the lock must release it. | |
| rw_semaphores have a special interface which allows non-owner release for | |
| readers. | |
| rtmutex | |
| ======= | |
| RT-mutexes are mutexes with support for priority inheritance (PI). | |
| PI has limitations on non-PREEMPT_RT kernels due to preemption and | |
| interrupt disabled sections. | |
| PI clearly cannot preempt preemption-disabled or interrupt-disabled | |
| regions of code, even on PREEMPT_RT kernels. Instead, PREEMPT_RT kernels | |
| execute most such regions of code in preemptible task context, especially | |
| interrupt handlers and soft interrupts. This conversion allows spinlock_t | |
| and rwlock_t to be implemented via RT-mutexes. | |
| semaphore | |
| ========= | |
| semaphore is a counting semaphore implementation. | |
| Semaphores are often used for both serialization and waiting, but new use | |
| cases should instead use separate serialization and wait mechanisms, such | |
| as mutexes and completions. | |
| semaphores and PREEMPT_RT | |
| ---------------------------- | |
| PREEMPT_RT does not change the semaphore implementation because counting | |
| semaphores have no concept of owners, thus preventing PREEMPT_RT from | |
| providing priority inheritance for semaphores. After all, an unknown | |
| owner cannot be boosted. As a consequence, blocking on semaphores can | |
| result in priority inversion. | |
| rw_semaphore | |
| ============ | |
| rw_semaphore is a multiple readers and single writer lock mechanism. | |
| On non-PREEMPT_RT kernels the implementation is fair, thus preventing | |
| writer starvation. | |
| rw_semaphore complies by default with the strict owner semantics, but there | |
| exist special-purpose interfaces that allow non-owner release for readers. | |
| These interfaces work independent of the kernel configuration. | |
| rw_semaphore and PREEMPT_RT | |
| --------------------------- | |
| PREEMPT_RT kernels map rw_semaphore to a separate rt_mutex-based | |
| implementation, thus changing the fairness: | |
| Because an rw_semaphore writer cannot grant its priority to multiple | |
| readers, a preempted low-priority reader will continue holding its lock, | |
| thus starving even high-priority writers. In contrast, because readers | |
| can grant their priority to a writer, a preempted low-priority writer will | |
| have its priority boosted until it releases the lock, thus preventing that | |
| writer from starving readers. | |
| local_lock | |
| ========== | |
| local_lock provides a named scope to critical sections which are protected | |
| by disabling preemption or interrupts. | |
| On non-PREEMPT_RT kernels local_lock operations map to the preemption and | |
| interrupt disabling and enabling primitives: | |
| =============================== ====================== | |
| local_lock(&llock) preempt_disable() | |
| local_unlock(&llock) preempt_enable() | |
| local_lock_irq(&llock) local_irq_disable() | |
| local_unlock_irq(&llock) local_irq_enable() | |
| local_lock_irqsave(&llock) local_irq_save() | |
| local_unlock_irqrestore(&llock) local_irq_restore() | |
| =============================== ====================== | |
| The named scope of local_lock has two advantages over the regular | |
| primitives: | |
| - The lock name allows static analysis and is also a clear documentation | |
| of the protection scope while the regular primitives are scopeless and | |
| opaque. | |
| - If lockdep is enabled the local_lock gains a lockmap which allows to | |
| validate the correctness of the protection. This can detect cases where | |
| e.g. a function using preempt_disable() as protection mechanism is | |
| invoked from interrupt or soft-interrupt context. Aside of that | |
| lockdep_assert_held(&llock) works as with any other locking primitive. | |
| local_lock and PREEMPT_RT | |
| ------------------------- | |
| PREEMPT_RT kernels map local_lock to a per-CPU spinlock_t, thus changing | |
| semantics: | |
| - All spinlock_t changes also apply to local_lock. | |
| local_lock usage | |
| ---------------- | |
| local_lock should be used in situations where disabling preemption or | |
| interrupts is the appropriate form of concurrency control to protect | |
| per-CPU data structures on a non PREEMPT_RT kernel. | |
| local_lock is not suitable to protect against preemption or interrupts on a | |
| PREEMPT_RT kernel due to the PREEMPT_RT specific spinlock_t semantics. | |
| raw_spinlock_t and spinlock_t | |
| ============================= | |
| raw_spinlock_t | |
| -------------- | |
| raw_spinlock_t is a strict spinning lock implementation in all kernels, | |
| including PREEMPT_RT kernels. Use raw_spinlock_t only in real critical | |
| core code, low-level interrupt handling and places where disabling | |
| preemption or interrupts is required, for example, to safely access | |
| hardware state. raw_spinlock_t can sometimes also be used when the | |
| critical section is tiny, thus avoiding RT-mutex overhead. | |
| spinlock_t | |
| ---------- | |
| The semantics of spinlock_t change with the state of PREEMPT_RT. | |
| On a non-PREEMPT_RT kernel spinlock_t is mapped to raw_spinlock_t and has | |
| exactly the same semantics. | |
| spinlock_t and PREEMPT_RT | |
| ------------------------- | |
| On a PREEMPT_RT kernel spinlock_t is mapped to a separate implementation | |
| based on rt_mutex which changes the semantics: | |
| - Preemption is not disabled. | |
| - The hard interrupt related suffixes for spin_lock / spin_unlock | |
| operations (_irq, _irqsave / _irqrestore) do not affect the CPU's | |
| interrupt disabled state. | |
| - The soft interrupt related suffix (_bh()) still disables softirq | |
| handlers. | |
| Non-PREEMPT_RT kernels disable preemption to get this effect. | |
| PREEMPT_RT kernels use a per-CPU lock for serialization which keeps | |
| preemption enabled. The lock disables softirq handlers and also | |
| prevents reentrancy due to task preemption. | |
| PREEMPT_RT kernels preserve all other spinlock_t semantics: | |
| - Tasks holding a spinlock_t do not migrate. Non-PREEMPT_RT kernels | |
| avoid migration by disabling preemption. PREEMPT_RT kernels instead | |
| disable migration, which ensures that pointers to per-CPU variables | |
| remain valid even if the task is preempted. | |
| - Task state is preserved across spinlock acquisition, ensuring that the | |
| task-state rules apply to all kernel configurations. Non-PREEMPT_RT | |
| kernels leave task state untouched. However, PREEMPT_RT must change | |
| task state if the task blocks during acquisition. Therefore, it saves | |
| the current task state before blocking and the corresponding lock wakeup | |
| restores it, as shown below:: | |
| task->state = TASK_INTERRUPTIBLE | |
| lock() | |
| block() | |
| task->saved_state = task->state | |
| task->state = TASK_UNINTERRUPTIBLE | |
| schedule() | |
| lock wakeup | |
| task->state = task->saved_state | |
| Other types of wakeups would normally unconditionally set the task state | |
| to RUNNING, but that does not work here because the task must remain | |
| blocked until the lock becomes available. Therefore, when a non-lock | |
| wakeup attempts to awaken a task blocked waiting for a spinlock, it | |
| instead sets the saved state to RUNNING. Then, when the lock | |
| acquisition completes, the lock wakeup sets the task state to the saved | |
| state, in this case setting it to RUNNING:: | |
| task->state = TASK_INTERRUPTIBLE | |
| lock() | |
| block() | |
| task->saved_state = task->state | |
| task->state = TASK_UNINTERRUPTIBLE | |
| schedule() | |
| non lock wakeup | |
| task->saved_state = TASK_RUNNING | |
| lock wakeup | |
| task->state = task->saved_state | |
| This ensures that the real wakeup cannot be lost. | |
| rwlock_t | |
| ======== | |
| rwlock_t is a multiple readers and single writer lock mechanism. | |
| Non-PREEMPT_RT kernels implement rwlock_t as a spinning lock and the | |
| suffix rules of spinlock_t apply accordingly. The implementation is fair, | |
| thus preventing writer starvation. | |
| rwlock_t and PREEMPT_RT | |
| ----------------------- | |
| PREEMPT_RT kernels map rwlock_t to a separate rt_mutex-based | |
| implementation, thus changing semantics: | |
| - All the spinlock_t changes also apply to rwlock_t. | |
| - Because an rwlock_t writer cannot grant its priority to multiple | |
| readers, a preempted low-priority reader will continue holding its lock, | |
| thus starving even high-priority writers. In contrast, because readers | |
| can grant their priority to a writer, a preempted low-priority writer | |
| will have its priority boosted until it releases the lock, thus | |
| preventing that writer from starving readers. | |
| PREEMPT_RT caveats | |
| ================== | |
| local_lock on RT | |
| ---------------- | |
| The mapping of local_lock to spinlock_t on PREEMPT_RT kernels has a few | |
| implications. For example, on a non-PREEMPT_RT kernel the following code | |
| sequence works as expected:: | |
| local_lock_irq(&local_lock); | |
| raw_spin_lock(&lock); | |
| and is fully equivalent to:: | |
| raw_spin_lock_irq(&lock); | |
| On a PREEMPT_RT kernel this code sequence breaks because local_lock_irq() | |
| is mapped to a per-CPU spinlock_t which neither disables interrupts nor | |
| preemption. The following code sequence works perfectly correct on both | |
| PREEMPT_RT and non-PREEMPT_RT kernels:: | |
| local_lock_irq(&local_lock); | |
| spin_lock(&lock); | |
| Another caveat with local locks is that each local_lock has a specific | |
| protection scope. So the following substitution is wrong:: | |
| func1() | |
| { | |
| local_irq_save(flags); -> local_lock_irqsave(&local_lock_1, flags); | |
| func3(); | |
| local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock_1, flags); | |
| } | |
| func2() | |
| { | |
| local_irq_save(flags); -> local_lock_irqsave(&local_lock_2, flags); | |
| func3(); | |
| local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock_2, flags); | |
| } | |
| func3() | |
| { | |
| lockdep_assert_irqs_disabled(); | |
| access_protected_data(); | |
| } | |
| On a non-PREEMPT_RT kernel this works correctly, but on a PREEMPT_RT kernel | |
| local_lock_1 and local_lock_2 are distinct and cannot serialize the callers | |
| of func3(). Also the lockdep assert will trigger on a PREEMPT_RT kernel | |
| because local_lock_irqsave() does not disable interrupts due to the | |
| PREEMPT_RT-specific semantics of spinlock_t. The correct substitution is:: | |
| func1() | |
| { | |
| local_irq_save(flags); -> local_lock_irqsave(&local_lock, flags); | |
| func3(); | |
| local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock, flags); | |
| } | |
| func2() | |
| { | |
| local_irq_save(flags); -> local_lock_irqsave(&local_lock, flags); | |
| func3(); | |
| local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock, flags); | |
| } | |
| func3() | |
| { | |
| lockdep_assert_held(&local_lock); | |
| access_protected_data(); | |
| } | |
| spinlock_t and rwlock_t | |
| ----------------------- | |
| The changes in spinlock_t and rwlock_t semantics on PREEMPT_RT kernels | |
| have a few implications. For example, on a non-PREEMPT_RT kernel the | |
| following code sequence works as expected:: | |
| local_irq_disable(); | |
| spin_lock(&lock); | |
| and is fully equivalent to:: | |
| spin_lock_irq(&lock); | |
| Same applies to rwlock_t and the _irqsave() suffix variants. | |
| On PREEMPT_RT kernel this code sequence breaks because RT-mutex requires a | |
| fully preemptible context. Instead, use spin_lock_irq() or | |
| spin_lock_irqsave() and their unlock counterparts. In cases where the | |
| interrupt disabling and locking must remain separate, PREEMPT_RT offers a | |
| local_lock mechanism. Acquiring the local_lock pins the task to a CPU, | |
| allowing things like per-CPU interrupt disabled locks to be acquired. | |
| However, this approach should be used only where absolutely necessary. | |
| A typical scenario is protection of per-CPU variables in thread context:: | |
| struct foo *p = get_cpu_ptr(&var1); | |
| spin_lock(&p->lock); | |
| p->count += this_cpu_read(var2); | |
| This is correct code on a non-PREEMPT_RT kernel, but on a PREEMPT_RT kernel | |
| this breaks. The PREEMPT_RT-specific change of spinlock_t semantics does | |
| not allow to acquire p->lock because get_cpu_ptr() implicitly disables | |
| preemption. The following substitution works on both kernels:: | |
| struct foo *p; | |
| migrate_disable(); | |
| p = this_cpu_ptr(&var1); | |
| spin_lock(&p->lock); | |
| p->count += this_cpu_read(var2); | |
| migrate_disable() ensures that the task is pinned on the current CPU which | |
| in turn guarantees that the per-CPU access to var1 and var2 are staying on | |
| the same CPU while the task remains preemptible. | |
| The migrate_disable() substitution is not valid for the following | |
| scenario:: | |
| func() | |
| { | |
| struct foo *p; | |
| migrate_disable(); | |
| p = this_cpu_ptr(&var1); | |
| p->val = func2(); | |
| This breaks because migrate_disable() does not protect against reentrancy from | |
| a preempting task. A correct substitution for this case is:: | |
| func() | |
| { | |
| struct foo *p; | |
| local_lock(&foo_lock); | |
| p = this_cpu_ptr(&var1); | |
| p->val = func2(); | |
| On a non-PREEMPT_RT kernel this protects against reentrancy by disabling | |
| preemption. On a PREEMPT_RT kernel this is achieved by acquiring the | |
| underlying per-CPU spinlock. | |
| raw_spinlock_t on RT | |
| -------------------- | |
| Acquiring a raw_spinlock_t disables preemption and possibly also | |
| interrupts, so the critical section must avoid acquiring a regular | |
| spinlock_t or rwlock_t, for example, the critical section must avoid | |
| allocating memory. Thus, on a non-PREEMPT_RT kernel the following code | |
| works perfectly:: | |
| raw_spin_lock(&lock); | |
| p = kmalloc(sizeof(*p), GFP_ATOMIC); | |
| But this code fails on PREEMPT_RT kernels because the memory allocator is | |
| fully preemptible and therefore cannot be invoked from truly atomic | |
| contexts. However, it is perfectly fine to invoke the memory allocator | |
| while holding normal non-raw spinlocks because they do not disable | |
| preemption on PREEMPT_RT kernels:: | |
| spin_lock(&lock); | |
| p = kmalloc(sizeof(*p), GFP_ATOMIC); | |
| bit spinlocks | |
| ------------- | |
| PREEMPT_RT cannot substitute bit spinlocks because a single bit is too | |
| small to accommodate an RT-mutex. Therefore, the semantics of bit | |
| spinlocks are preserved on PREEMPT_RT kernels, so that the raw_spinlock_t | |
| caveats also apply to bit spinlocks. | |
| Some bit spinlocks are replaced with regular spinlock_t for PREEMPT_RT | |
| using conditional (#ifdef'ed) code changes at the usage site. In contrast, | |
| usage-site changes are not needed for the spinlock_t substitution. | |
| Instead, conditionals in header files and the core locking implementation | |
| enable the compiler to do the substitution transparently. | |
| Lock type nesting rules | |
| ======================= | |
| The most basic rules are: | |
| - Lock types of the same lock category (sleeping, CPU local, spinning) | |
| can nest arbitrarily as long as they respect the general lock ordering | |
| rules to prevent deadlocks. | |
| - Sleeping lock types cannot nest inside CPU local and spinning lock types. | |
| - CPU local and spinning lock types can nest inside sleeping lock types. | |
| - Spinning lock types can nest inside all lock types | |
| These constraints apply both in PREEMPT_RT and otherwise. | |
| The fact that PREEMPT_RT changes the lock category of spinlock_t and | |
| rwlock_t from spinning to sleeping and substitutes local_lock with a | |
| per-CPU spinlock_t means that they cannot be acquired while holding a raw | |
| spinlock. This results in the following nesting ordering: | |
| 1) Sleeping locks | |
| 2) spinlock_t, rwlock_t, local_lock | |
| 3) raw_spinlock_t and bit spinlocks | |
| Lockdep will complain if these constraints are violated, both in | |
| PREEMPT_RT and otherwise. |