RMLOCK(9)
πΈ RMLOCK(9) β kernel reader/writer lock, but make it read-mostly π
okay bestie, so this is rmlock(9) from the Kernel Developer's Manual β¨ and it's literally the lock you reach for when your data is read-mostly. Not me being dramatic β the name says it. Let's get into it π«Ά
β¨ NAME β¨
rmlock, 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, RM_SYSINIT_FLAGS β a kernel reader/writer lock optimized for read-mostly access patterns π
β¨ SYNOPSIS β¨
#include <sys/param.h>
#include <sys/lock.h>
#include <sys/rmlock.h>
void
rm_init(struct rmlock *rm, const char *name);
void
rm_init_flags(struct rmlock *rm, const char *name, int opts);
void
rm_destroy(struct rmlock *rm);
void
rm_rlock(struct rmlock *rm, struct rm_priotracker* tracker);
int
rm_try_rlock(struct rmlock *rm, struct rm_priotracker* tracker);
void
rm_wlock(struct rmlock *rm);
void
rm_runlock(struct rmlock *rm, struct rm_priotracker* tracker);
void
rm_wunlock(struct rmlock *rm);
int
rm_wowned(const struct rmlock *rm);
int
rm_sleep(void *wchan, struct rmlock *rm, int priority,
const char *wmesg, int timo);
options INVARIANTS
options INVARIANT_SUPPORT
void
rm_assert(struct rmlock *rm, int what);
#include <sys/kernel.h>
RM_SYSINIT(name, struct rmlock *rm, const char *desc);
RM_SYSINIT_FLAGS(name, struct rmlock *rm, const char *desc, int flags);
β¨ DESCRIPTION β¨
So basically π read-mostly locks let multiple threads share access to protected data, OR let one single thread have it all to themselves. The shared-access girlies are called readers, because all they do is read the protected data. The exclusive-access one is called a writer, because they're the ones actually modifying stuff π
Read-mostly locks are built to be efficient for locks that are almost exclusively used as reader locks β so pls use them for protecting data that rarely changes. And heads up: acquiring an exclusive lock after the lock has already been locked for shared access is an expensive operation. Like, it's giving main character tantrum π
Normal read-mostly locks are similar to rwlock(9) locks and follow the same lock ordering rules as rwlock(9) locks. They also have full priority propagation, just like mutexes. But unlike rwlock(9), read-mostly locks propagate priority to both readers and writers π«Ά This is implemented via the rm_priotracker structure argument that you hand to rm_rlock() and rm_runlock(). Also: readers can recurse if the lock was initialized with the RM_RECURSE option β however, writers are never allowed to recurse. Periodt. π«
Sleepable read-mostly locks are created by passing RM_SLEEPABLE to rm_init_flags(). Unlike normal read-mostly locks, sleepable ones 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. And unlike other sleepable locks such as sx(9) locks, readers must use try operations on other sleepable locks to avoid sleeping. iykyk π΄
π Macros and Functions π
rm_init(struct rmlock *rm, const char *name)β Initializes the read-mostly lockrm. Thenamedescription is used solely for debugging purposes π. This function must be called before any other operations on the lock. No skipping steps, bestie.rm_init_flags(struct rmlock *rm, const char *name, int opts)β Just likerm_init(), but you get to init the read-mostly lockrmwith a set of optional flags β¨. Theoptsargument contains one or more of these:RM_NOWITNESSβ Instructwitness(4)to ignore this lock πRM_RECURSEβ Allow threads to recursively acquire shared locks forrmπRM_SLEEPABLEβ Create a sleepable read-mostly lock π΄RM_NEWβ If the kernel has been compiled withoption INVARIANTS,rm_init_flags()will assert that thermhas not been initialized multiple times without intervening calls torm_destroy()β unless this option is specified. So like, this is your "yes I know what I'm doing" pass π
rm_rlock(struct rmlock *rm, struct rm_priotracker* tracker)β Locksrmas a reader, usingtrackerto track read owners of the lock for priority propagation. This data structure is only used internally by rmlock and must persist untilrm_runlock()has been called. Good news: it can be allocated on the stack, since readers cannot sleep π₯³. If any thread holds this lock exclusively, the current thread blocks, and its priority gets propagated to the exclusive holder. And if the lock was initialized with theRM_RECURSEoption,rm_rlock()can be called even when the current thread has already acquired reader access onrm.rm_try_rlock(struct rmlock *rm, struct rm_priotracker* tracker)β Tries to lockrmas a reader.rm_try_rlock()returns0if the lock cannot be acquired immediately; otherwise the lock will be acquired and a non-zero value will be returned. Note thatrm_try_rlock()may fail even while the lock is not currently held by a writer π β it's just like that sometimes. If the lock was initialized with theRM_RECURSEoption,rm_try_rlock()will succeed if the current thread has already acquired reader access.rm_wlock(struct rmlock *rm)β Locksrmas a writer π. If there are any shared owners of the lock, the current thread blocks. And nope βrm_wlock()cannot be called recursively. Don't even try it.rm_runlock(struct rmlock *rm, struct rm_priotracker* tracker)β Releases a shared lock previously acquired byrm_rlock(). Thetrackerargument must match thetrackerargument used for acquiring the shared lock. Matching sets, bestie πrm_wunlock(struct rmlock *rm)β Releases an exclusive lock previously acquired byrm_wlock().rm_destroy(struct rmlock *rm)β Destroys a lock previously initialized withrm_init(). Thermlock must be unlocked. Like, unlocked unlocked. πrm_wowned(const struct rmlock *rm)β Returns a non-zero value if the current thread owns an exclusive lock onrm.rm_sleep(void *wchan, struct rmlock *rm, int priority, const char *wmesg, int timo)β Atomically releasesrmwhile waiting for an event π΄. Thermlock must be exclusively locked. For more details on the parameters to this function, go seesleep(9).rm_assert(struct rmlock *rm, int what)β Asserts that thermlock is in the state specified bywhat. If the assertions are not true and the kernel is compiled withoptions INVARIANTSandoptions INVARIANT_SUPPORT, the kernel will panic π. Currently the following base assertions are supported:RA_LOCKEDβ Assert that the current thread holds either a shared or exclusive lock ofrm.RA_RLOCKEDβ Assert that the current thread holds a shared lock ofrm.RA_WLOCKEDβ Assert that the current thread holds an exclusive lock ofrm.RA_UNLOCKEDβ Assert that the current thread holds neither a shared nor exclusive lock ofrm.
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 ofrm.RA_NOTRECURSEDβ Assert that the current thread does not hold a recursive lock ofrm.
β¨ SEE ALSO β¨
locking(9), mutex(9), panic(9), rwlock(9), sema(9), sleep(9), sx(9) β go read these too, they're all part of the same friend group π«Ά
β¨ HISTORY β¨
These functions appeared in FreeBSD 7.0. An icon since day one, fr fr π
β¨ AUTHORS β¨
The rmlock facility was written by Stephan Uphoff π This manual page was written by Gleb Smirnoff for rwlock and modified to reflect rmlock by Stephan Uphoff. Collab of the century, honestly π
β¨ BUGS β¨
Heads up, the fine print bestie π«£
- The rmlock implementation is currently not optimized for single processor systems.
rm_try_rlock()can fail transiently even when there is no writer, while another reader updates the state on the local CPU π- The rmlock 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. Growth era incoming π
and that's the whole page, babe π this was the RMLOCK(9) page, straight out of the Debian build dated November 11, 2017 β so yes, it's a lil vintage, but she still ate π₯β¨π
π¦ where this page comes from
- debian bookworm: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- debian trixie: freebsd-manpages 12.2-2 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu jammy: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu noble: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu questing: freebsd-manpages 12.2-2
- ubuntu resolute: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu stonking: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- Copyright (c) 2007 Stephan Uphoff <ups@FreeBSD.org>
- Copyright (c) 2006 Gleb Smirnoff <glebius@FreeBSD.org>
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms