0% found this document useful (0 votes)
164 views2 pages

Linux Kernel Concurrency Cheat Sheet: Barriers Reference Counters Mutexes (Sleeping)

The document discusses various synchronization primitives in Linux including barriers, atomic operations, mutexes, semaphores, spinlocks, reader-writer semaphores, and reader-writer spinlocks.

Uploaded by

Abbas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
164 views2 pages

Linux Kernel Concurrency Cheat Sheet: Barriers Reference Counters Mutexes (Sleeping)

The document discusses various synchronization primitives in Linux including barriers, atomic operations, mutexes, semaphores, spinlocks, reader-writer semaphores, and reader-writer spinlocks.

Uploaded by

Abbas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 2

Barriers asm/barrier.h Reference counters linux/refcount.h Mutexes (sleeping) linux/mutex.

h
barrier() Compiler barrier refcount_t Atomic reference count type struct mutex Mutex type
mb() Full system (I/O) memory barrier r = REFCOUNT_INIT(n) Initialize r DEFINE_MUTEX(name) Variable definition
rmb() ,→ reads only refcount_read(r) Read from r mutex_init(mut) Initialize mut
wmb() ,→ writes only refcount_set(r, i) Write i to r mutex_is_locked(mut) True when mut is locked (by any thread)
smp_mb() SMP (conditional) memory barrier refcount_inc 1 (r) Increment r by 1 mutex_trylock(mut) Try to acquire mut without sleeping;
smp_rmb() ,→ reads only refcount_add 1 (i, r) Add i to r ! returns true if mutex was acquired

smp_wmb() ,→ writes only refcount_dec 2 (r) Decrement r by 1 mutex_lock 1 2 3 (mut) Acquire mut; sleeping
smp_store_mb(v, val) Write val to v; then full memory barrier refcount_dec_and_test(r) ,→ return true if new value is 0 mutex_unlock(mut) Release mut (may schedule)
smp_load_acquire() Order preceding accesses against following read refcount_dec_and_lock(r, mut) ,→ lock mutex if new value is 0
Variants:
smp_store_release() Order following accesses against preceding write refcount_dec_and_lock_irqsave(r, spin, flags) 1 _interruptible 3 Return -EINTR if a signal arrives
smp_mb__before_atomic() Order preceding accesses against atomic op. ,→ disable interrupts if enabled 1 _killable 3 Return -EINTR if killed
smp_mb__after_atomic() Order following accesses against atomic op. refcount_dec_and_mutex_lock(r, spin) 2 _io 3 Account sleeping time as IO wait time
,→ lock mutex if new value is 0 3 _nested(mut, c) Used when acquiring two mutexes of the same class;
Barriers must always be paired to be effective, although some operations refcount_sub_and_test(i, r)
(e.g. acquiring a lock) contain memory barriers implicitly. c is a nesting level/class
Variants: Mutexes cannot be held, acquired, or released in atomic contexts.
1 _not_zero only if the original value is not 0
Atomic operations asm/rwonce.h 2 _not_one only if the original value is not 1
READ_ONCE(x) Emit single instruction to load x 2 _if_one only if the original value is 1 Semaphores (sleeping) linux/semaphore.h
2 _and_test return true if the new value is 0
WRITE_ONCE(x, val) Emit single instruction to store x struct semaphore Semaphore type
DEFINE_SEMAPHORE(name) Variable definition
asm/atomic.h Spinlocks asm/spinlock.h sema_init(sem, val) Initialize
atomic_t Atomic 32-bit (signed) integer type down_trylock(sem) Try to acquire sem without sleeping;
spinlock_t Spinlock type ! returns 0 if semaphore was acquired
atomic_read(v) Read from v DEFINE_SPINLOCK() Variable definition
atomic_set(v, i) Write i to v down 1 2 4 (sem) Acquire sem; sleeping
spin_lock_init() Initialize spinlock up(sem) Release sem
atomic_inc 1 (v) Increment by 1 spin_is_locked() Return true if spinlock is held (by any CPU)
atomic_inc_not_zero(v) ,→ if the original value 6= 0 spin_trylock 1 () Try to acquire spinlock without spinning; Variants: ↑ see mutexes;
atomic_dec 1 (v) Decrement by 1 ! returns true if spinlock was acquired 4 _timeout(sem, timeout) Return if timeout expires
atomic_dec_and_test(v) ,→ return true if the new value = 0 spin_lock 1 () Acquire spinlock; busy-looping
atomic_add 1 (i, v) Add i to (and write to) v spin_unlock 2 () Release spinlock linux/rwsem.h
atomic_add_return*(i, v) ,→ return the new value
Variants: struct rw_semaphore Reader-writer semaphore type
atomic_fetch_add*(i, v) ,→ return the old value
1 2 _bh Disable soft-IRQs while locked DECLARE_RWSEM(name) Variable definition
atomic_add_unless(v, i, u) ,→ unless the existing value is u 1 2 _irq Disable interrupts while locked init_rwsem(sem) Initialize
atomic_sub 1 (i, v) Subtract i from (and write to) v 1 _irqsave Conditionally disable interrupts if enabled rwsem_is_locked(sem) Return true if sem is locked
atomic_sub_and_test() ,→ return true if the new value is 0 2 _irqrestore Conditionally reenable interrupts if originally enabled down_read_trylock(sem) → see down_trylock()
atomic_and 1 (i, v) v &= i;
down_read 1 3 (sem) → see down()
atomic_andnot 1 (i, v) v &= ~i; In general, the variants must be paired, e.g. spin_lock_bh() with
up_read(sem) → see up()
atomic_or 1 (i, v) v |= i; spin_unlock_bh() or spin_lock_irqsave() with
atomic_xor 1 (i, v) v = i; spin_unlock_irqrestore(). down_write_trylock(sem) → see down_trylock()
down_write 1 3 (sem) → see down()
atomic_xchg 1 (v, n) Swap v and n; return original value
up_write(sem) → see up()
atomic_cmpxchg 1 (v, o, n) ,→ if the original value = o linux/rwlock.h
atomic_try_cmpxchg 1 (v, &o, n) ,→ return true if swapped rwlock_t Reader-writer spinlock type Variants: ↑ see mutexes.
Variants: DEFINE_RWLOCK() Variable definition The lock can be held by either a single writer or multiple readers.
1 _relaxed unordered rwlock_init Initialize
1 _acquire read is ordered against subsequent reads read_trylock 1 () → see spin_trylock()
1 _release write is ordered against preceding writes read_lock 1 () → see spin_lock()
Overflow/underflow is defined as two’s complement.
read_unlock 2 ()
write_trylock 1 ()
→ see spin_unlock()
→ see spin_trylock()
Linux kernel
write_lock 1 () → see spin_lock()

atomic_long_t Atomic 64-bit (signed) integer type


asm/atomic-long.h write_unlock 2 () → see spin_unlock()
Variants: ↑ see spinlocks.
concurrency
Operations are the same as for atomic_t, i.e. atomic_inc() becomes
atomic_long_inc().
The lock can be held by either a single writer or multiple readers.
cheat sheet
Copyright c 2021, Oracle and/or its aliates.  SPDX-License-Identier: GPL-2.0 WITH Linux-syscall-note  Vegard Nossum <vegard.nossum@oracle.com> / @vegard_no
Interrupts & preemption linux/irqflags.h Per-CPU variables linux/percpu.h Wait queues linux/wait.h
local_irq_disable() Unconditionally disable interrupts cpu = get_cpu() Disable preemption; return CPU number Queues:
local_irq_enable() Unconditionally enable interrupts put_cpu() Reenable preemption wait_queue_head_t Wait queue type
local_irq_save(flags) Conditionally disable interrupts DECLARE_PER_CPU 1 (type, name) Variable declaration DECLARE_WAIT_QUEUE_HEAD(name) Variable definition
local_irq_restore(flags) Conditionally reenable interrupts DEFINE_PER_CPU 1 (type, name) Variable definition DECLARE_WAIT_QUEUE_HEAD_ONSTACK(name) ,→ for local variables
irqs_disabled() True when interrupts are disabled EXPORT_PER_CPU_SYMBOL(name) Export symbol init_waitqueue_head(wq) Initialize
per_cpu(var, cpu) Dereference per-CPU variable wait_event 1 2 3 (wq, cond) Sleep until condition is true
Interrupt handlers run with interrupts disabled, are non-preemptible, and io_wait_event(wq, cond) ,→ using io_schedule()
are atomic (cannot sleep). get_cpu_var(var) ,→ disabling preemption
put_cpu_var(var) ,→ enabling preemption wake_up(wq) Wake up waiters
Disabling interrupts implicitly disables soft-IRQs. per_cpu_ptr(var, cpu) Get address of per-CPU variable Variants: ( ! incomplete)
Disabling interrupts implicitly disables preemption. get_cpu_ptr(var) ,→ disabling preemption 1 _interruptible 2 3 Returns -ERESTARTSYS if interrupted
put_cpu_ptr(var) ,→ enabling preemption 1 _killable 2 Returns -ERESTARTSYS if killed
linux/bottom_half.h this_cpu_ptr(var) Get address of this CPU’s value 1 _freezable 2 Allow freezing while waiting
local_bh_disable() Disable soft-IRQs (on this CPU) this_cpu_read(var) Read this CPU’s value 2 _timeout(wq, cond, t) Also returns when timeout expires
local_bh_enable() Enable soft-IRQs (on this CPU) this_cpu_write(var) Write this CPU’s value 3 _lock_irq(wq, cond, lock) Hold spinlock while checking condition
local_bh_blocked() True when soft-IRQs are disabled (on this CPU) this_cpu_*() → see atomic operations
Entries:
Soft-IRQs (also known as bottom halves or bh) run with interrupts enabled. Variants: wait_queue_entry_t Wait queue entry type
1 _ALIGNED Cacheline-aligned DEFINE_WAIT(e) Variable definition
1 _SHARED_ALIGNED
linux/preempt.h ,→ accessible by other CPUs DEFINE_WAIT_FUNC(e, fn) ,→ using custom wake function
1 _PAGE_ALIGNED Page-aligned init_wait(e) Initialize
in_nmi() True when in NMI context 1 _READ_MOSTLY Rarely written to prepare_to_wait(wq, e, state) Enqueue wait-queue entry
in_hardirq() True when in interrupt context prepare_to_wait_exclusive(...) ,→ only wake the first thread
in_serving_softirq() True when in soft-IRQ context finish_wait(wq, e)
in_task() True when in task context RCU (Read-Copy-Update) linux/rcupdate.h Dequeue wait-queue entry
in_atomic() True when the caller cannot sleep rcu_read_lock 1 () Enter critical section
( ! with exceptions) rcu_read_unlock 1 () Leave critical section Lists linux/list.h
preemptible() True when in preemptible context rcu_dereference 1 (p) Dereference p struct list_head Type
preempt_disable() Disable preemption (nested) rcu_access_pointer(p) Fetch pointer p without dereferencing LIST_HEAD() Define
preempt_enable() Enable preemption (nested) rcu_assign_pointer(p, v) Assign v to *p INIT_LIST_HEAD(head) Initialize
in_irq() (deprecated) Same as in_hardirq() rcu_replace_pointer(p, v) ,→ return original value list_add(e, head) Add e to the start of head
in_softirq() (deprecated) True when in soft-IRQ or soft-IRQ disabled struct rcu_head RCU head type list_add_tail(e, head) Add e to the end of head
in_interrupt() (deprecated) True when in NMI, interrupt, soft-IRQ, or rcu_head_init(head) Initialize list_del(e) Remove e
soft-IRQ disabled call_rcu(head, fn) Call fn after grace period list_del_init(e) ,→ reinitialize e
Preemption refers to being scheduled out. A non-preemptible context kfree_rcu(p, name) Free p after grace period, using p->name list_replace(old, new) Replace old by new
cannot be scheduled out, but may be interrupted. synchronize_rcu() Wait for readers to complete list_replace_init(old, new) ,→ reinitialize old
Variants: list_swap(e1, e2) Swap e1 and t2
preempt_disable() and preempt_enable() nest in such a way that 1 _bh() list_move(e, head) Remove e; add to the start of head
Also disable (reenable) soft-IRQs
preemption remains disabled as long as there is at least one unmatched 1 _sched() list_move_tail(e, head) Remove e; add to the end of head
Also disable (reenable) preemption
call to preempt_disable() active.
list_is_head(e, head, member)
Writers must always use either a single atomic update or exclude other
True when e is the head of the list
Completions writers using other synchronization mechanisms (like spinlocks).
linux/completion.h list_is_first(e, head) True when e is the first element of head
struct completion Type list_is_last(e, head) True when e is the last element of head
DECLARE_COMPLETION(name) Variable definition
Sequence locks linux/seqlock.h list_empty(head) True when head is an empty list
init_completion(work) Initialize work seqcount_t Type list_is_singular(head) True when head contains one element
reinit_completion(work) Reinitialize after completion SEQCOUNT_ZERO Static initializer list_for_each_entry 1 (...) Iterate over list
completion_done(w) True when completion is done seqcount_init(s) Initialize Variants:
wait_for_completion 1 2 (w) Wait for a completion (sleeping) 1 (e, head, member) Forward iteration
Writer:
try_wait_for_completion(w) ,→ without blocking; return 1 if done 1 _safe(e, tmp, head, member) ,→ allow node deletion
write_seqcount_begin(&s);
complete(w) Wake up a single waiting thread 1 _reverse(e, head, member) Backwards iteration
...
complete_all(w) Wake up all waiting threads 1 _safe_reverse(e, tmp, head, member) ,→ allow node deletion
write_seqcount_end(&s);
Variants: Reader:
1 _interruptible Return -ERESTARTSYS if a signal arrives
1 _killable do {
Return -ERESTARTSYS if killed
1 _io seq = read_seqcount_begin(&s);
Account sleeping time as IO wait time
2 _timeout(w, timeout) ...
Return if timeout expires
} while (read_seqcount_retry(&s, seq));

You might also like