GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||
GeezerVison is showing docs.kernel.org — Lock types and their rules — The Linux Kernel documentation
English
Lock types and their rules¶Introduction¶The kernel provides a variety of locking primitives which can be divided into three categories:
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 Sleeping lock types:
On PREEMPT_RT kernels, these lock types are converted to sleeping locks:
CPU local locks¶
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¶
On non-PREEMPT_RT kernels, these lock types are also spinning locks:
Spinning locks implicitly disable preemption and the lock / unlock functions can have suffixes which apply further protections:
Owner semantics¶The aforementioned lock types except semaphores have strict owner semantics:
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:
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:
The named scope of local_lock has two advantages over the regular primitives:
local_lock and PREEMPT_RT¶PREEMPT_RT kernels map local_lock to a per-CPU spinlock_t, thus changing semantics:
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. CPU local scope and bottom-half¶Per-CPU variables that are accessed only in softirq context should not rely on the assumption that this context is implicitly protected due to being non-preemptible. In a PREEMPT_RT kernel, softirq context is preemptible, and synchronizing every bottom-half-disabled section via implicit context results in an implicit per-CPU “big kernel lock.” A local_lock_t together with When lockdep is enabled, these functions verify that data structure access
occurs within softirq context.
Unlike On a PREEMPT_RT kernel, local_lock_t behaves as a real lock and
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:
PREEMPT_RT kernels preserve all other spinlock_t semantics:
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:
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(&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 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 On PREEMPT_RT kernel this code sequence breaks because RT-mutex requires a
fully preemptible context. Instead, use 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 struct foo *p; migrate_disable(); p = this_cpu_ptr(&var1); spin_lock(&p->lock); p->count += this_cpu_read(var2);
The func()
{
struct foo *p;
migrate_disable();
p = this_cpu_ptr(&var1);
p->val = func2();
This breaks because 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_obj(*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_obj(*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:
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:
Lockdep will complain if these constraints are violated, both in PREEMPT_RT and otherwise. | ||||||||||||||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |