rmlock(9) kernel reader/writer lock optimized for read-mostly access patterns

Other Alias

rm_init, rm_init_flags, rm_destroy, rm_rlock, rm_try_rlock, rm_wlock, rm_runlock, rm_wunlock, rm_wowned, rm_sleep, rm_assert, RM_SYSINIT

SYNOPSIS

In sys/param.h In sys/lock.h In sys/rmlock.h Ft void Fn rm_init struct rmlock *rm const char *name Ft void Fn rm_init_flags struct rmlock *rm const char *name int opts Ft void Fn rm_destroy struct rmlock *rm Ft void Fn rm_rlock struct rmlock *rm struct rm_priotracker* tracker Ft int Fn rm_try_rlock struct rmlock *rm struct rm_priotracker* tracker Ft void Fn rm_wlock struct rmlock *rm Ft void Fn rm_runlock struct rmlock *rm struct rm_priotracker* tracker Ft void Fn rm_wunlock struct rmlock *rm Ft int Fn rm_wowned const struct rmlock *rm Ft int Fn rm_sleep void *wchan struct rmlock *rm int priority const char *wmesg int timo

options INVARIANTS options INVARIANT_SUPPORT Ft void Fn rm_assert struct rmlock *rm int what In sys/kernel.h Fn RM_SYSINIT name struct rmlock *rm const char *desc int opts

DESCRIPTION

Read-mostly locks allow shared access to protected data by multiple threads, or exclusive access by a single thread. The threads with shared access are known as readers since they only read the protected data. A thread with exclusive access is known as a writer since it can modify protected data.

Read-mostly locks are designed to be efficient for locks almost exclusively used as reader locks and as such should be used for protecting data that rarely changes. Acquiring an exclusive lock after the lock has been locked for shared access is an expensive operation.

Normal read-mostly locks are similar to rwlock(9) locks and follow the same lock ordering rules as rwlock(9) locks. Read-mostly locks have full priority propagation like mutexes. Unlike rwlock(9), read-mostly locks propagate priority to both readers and writers. This is implemented via the rm_priotracker structure argument supplied to Fn rm_rlock and Fn rm_runlock . Readers can recurse if the lock is initialized with the RM_RECURSE option; however, writers are never allowed to recurse.

Sleepable read-mostly locks are created by passing RM_SLEEPABLE to Fn rm_init_flags . Unlike normal read-mostly locks, sleepable read-mostly locks follow the same lock ordering rules as sx(9) locks. Sleepable read-mostly locks do not propagate priority to writers, but they do propagate priority to readers. Writers are permitted to sleep while holding a read-mostly lock, but readers are not. Unlike other sleepable locks such as sx(9) locks, readers must use try operations on other sleepable locks to avoid sleeping.

Macros and Functions

Fn rm_init struct rmlock *rm const char *name
Initialize the read-mostly lock Fa rm . The Fa name description is used solely for debugging purposes. This function must be called before any other operations on the lock.
Fn rm_init_flags struct rmlock *rm const char *name int opts
Similar to Fn rm_init , initialize the read-mostly lock Fa rm with a set of optional flags. The Fa opts arguments contains one or more of the following flags:

RM_NOWITNESS
Instruct witness(4) to ignore this lock.
RM_RECURSE
Allow threads to recursively acquire shared locks for Fa rm .
RM_SLEEPABLE
Create a sleepable read-mostly lock.

Fn rm_rlock struct rmlock *rm struct rm_priotracker* tracker
Lock Fa rm as a reader using Fa tracker to track read owners of a lock for priority propagation. This data structure is only used internally by and must persist until Fn rm_runlock has been called. This data structure can be allocated on the stack since readers cannot sleep. If any thread holds this lock exclusively, the current thread blocks, and its priority is propagated to the exclusive holder. If the lock was initialized with the RM_RECURSE option the Fn rm_rlock function can be called when the current thread has already acquired reader access on Fa rm .
Fn rm_try_rlock struct rmlock *rm struct rm_priotracker* tracker
Try to lock Fa rm as a reader. Fn rm_try_rlock will return 0 if the lock cannot be acquired immediately; otherwise, the lock will be acquired and a non-zero value will be returned. Note that Fn rm_try_rlock may fail even while the lock is not currently held by a writer. If the lock was initialized with the RM_RECURSE option, Fn rm_try_rlock will succeed if the current thread has already acquired reader access.
Fn rm_wlock struct rmlock *rm
Lock Fa rm as a writer. If there are any shared owners of the lock, the current thread blocks. The Fn rm_wlock function cannot be called recursively.
Fn rm_runlock struct rmlock *rm struct rm_priotracker* tracker
This function releases a shared lock previously acquired by Fn rm_rlock . The Fa tracker argument must match the Fa tracker argument used for acquiring the shared lock
Fn rm_wunlock struct rmlock *rm
This function releases an exclusive lock previously acquired by Fn rm_wlock .
Fn rm_destroy struct rmlock *rm
This functions destroys a lock previously initialized with Fn rm_init . The Fa rm lock must be unlocked.
Fn rm_wowned const struct rmlock *rm
This function returns a non-zero value if the current thread owns an exclusive lock on Fa rm .
Fn rm_sleep void *wchan struct rmlock *rm int priority const char *wmesg int timo
This function atomically releases Fa rm while waiting for an event. The Fa rm lock must be exclusively locked. For more details on the parameters to this function, see sleep(9).
Fn rm_assert struct rmlock *rm int what
This function asserts that the Fa rm lock is in the state specified by Fa what . If the assertions are not true and the kernel is compiled with options INVARIANTS and options INVARIANT_SUPPORT the kernel will panic. Currently the following base assertions are supported:

RA_LOCKED
Assert that current thread holds either a shared or exclusive lock of Fa rm .
RA_RLOCKED
Assert that current thread holds a shared lock of Fa rm .
RA_WLOCKED
Assert that current thread holds an exclusive lock of Fa rm .
RA_UNLOCKED
Assert that current thread holds neither a shared nor exclusive lock of Fa rm .

In addition, one of the following optional flags may be specified with RA_LOCKED RA_RLOCKED or RA_WLOCKED

RA_RECURSED
Assert that the current thread holds a recursive lock of Fa rm .
RA_NOTRECURSED
Assert that the current thread does not hold a recursive lock of Fa rm .

HISTORY

These functions appeared in Fx 7.0 .

AUTHORS

An -nosplit The facility was written by An Stephan Uphoff . This manual page was written by An Gleb Smirnoff for rwlock and modified to reflect rmlock by An Stephan Uphoff .

BUGS

The implementation is currently not optimized for single processor systems.

Fn rm_try_rlock can fail transiently even when there is no writer, while another reader updates the state on the local CPU.

The implementation uses a single per CPU list shared by all rmlocks in the system. If rmlocks become popular, hashing to multiple per CPU queues may be needed to speed up the writer lock process.