« Previous Lecture | Next Lecture »
Free linux kernel development course: a plain spinlock treats every accessor the same way, even when most of them only want to read shared data. This lecture in our free linux device drivers course introduces the reader-writer spinlock, a locking construct built for exactly that read-heavy access pattern.
What you will learn:
- Why a plain spinlock over-serializes read-mostly data structures
- The reader-writer spinlock model: concurrent readers, exclusive writer
- The
rwlock_ttype and how it relates tospinlock_t - When a reader-writer spinlock helps — and when it does not
- Where kernel 6.x steers you instead (seqlock, RCU) for many read-mostly cases
Prerequisites: a solid understanding of the plain spinlock, covered earlier in this free embedded systems course.
The Problem: A Plain Spinlock Over-Serializes Readers
Picture a large, global, doubly linked list with a few thousand nodes that many threads search but only occasionally modify. Because the list is shared and writable, every access — even a read — is a critical section: without protection, a reader could walk a node that a writer is mid-way through unlinking, producing a dirty or torn read. The obvious fix is a plain spinlock around every traversal.
spin_lock(&mylist_lock);
list_for_each(pos, &my_list_head) {
/* search for something */
}
spin_unlock(&mylist_lock);
This is correct, but it is also needlessly slow when reads vastly outnumber writes. A plain spinlock only ever allows one CPU into the critical section at a time, so if ten threads are all just reading the list simultaneously on a multicore box, nine of them sit spinning even though none of them would ever conflict with each other — only a concurrent writer would be a real problem.
The Fix: Reader-Writer Spinlocks
A reader-writer spinlock separates the two kinds of access. Any thread that only needs to read acquires a read lock; any thread that needs to modify the data acquires an exclusive write lock. The rules are:
- A read lock is granted immediately to any thread that asks, as long as no writer currently holds the write lock.
- Multiple readers can hold the read lock at the same time — in effect, no real serialization happens between readers.
- The moment a writer requests the write lock, it must wait for every current reader to release its read lock first.
- Once the writer acquires the exclusive write lock, any new readers or writers that show up must wait for the writer to finish.
This construct pays off specifically when the access pattern is read-heavy, writes are rare, and the critical section is long enough that serializing every access (as a plain spinlock does) would waste real CPU time.
The rwlock_t Type
Having already used spinlock_t, the reader-writer variant will feel familiar: the lock is abstracted as an rwlock_t structure, declared from <linux/rwlock.h>, and the API names simply substitute “read” or “write” in place of “spin”:
#include <linux/rwlock.h>
rwlock_t my_list_lock;
rwlock_init(&my_list_lock);
Just as with spinlock_t, you can also declare and initialize a static reader-writer lock in one step using DEFINE_RWLOCK(my_list_lock);. We will cover the full read_lock()/read_unlock()/write_lock()/write_unlock() API, along with the interrupt-safe variants, in detail in the next lecture — this one is focused purely on the concept and when it is the right tool.
A Modern Caveat: Prefer seqlock or RCU For Many Read-Mostly Cases
One thing worth flagging clearly on kernel 6.x: reader-writer spinlocks are still present and supported, but the kernel community generally steers new read-mostly data structures toward seqlock_t or RCU (Read-Copy-Update) instead. The reason is writer starvation — if readers keep arriving in a steady stream, a waiting writer can be delayed indefinitely, because the lock only checks “are there readers holding it right now,” not “how long has a writer been waiting.” seqlock_t and RCU solve read-mostly access patterns without that starvation risk, and RCU in particular is the dominant technique for read-mostly kernel data today. We mention this now so you build the right instinct: know how rwlock_t works because you will still meet it in older and simpler code paths, but reach for RCU when you are designing new read-mostly data structures for a modern kernel.
| Lock Type | Concurrent Readers | Writer Starvation Risk | Typical Modern Use |
|---|---|---|---|
| Plain spinlock_t | No | N/A | Short critical sections, any access pattern |
| rwlock_t | Yes | Yes, under heavy read load | Simple read-mostly data, legacy code paths |
| seqlock_t / RCU | Yes (lock-free for readers) | No | New read-mostly kernel data structures |
Frequently Asked Questions
Q1. Can a thread hold a read lock and a write lock on the same rwlock_t at the same time?
No — the lock only tracks one state at a time (some number of readers, or one writer), never both.
Q2. Is rwlock_t interrupt-safe by default?
No, just like plain spinlocks, you need the interrupt-safe variants when the lock might also be taken from interrupt context; we cover those in the next lecture.
Q3. Does a reader-writer spinlock help if writes are frequent?
Not much — if writers show up often, readers spend most of their time waiting anyway, and you lose the benefit of concurrent reads.
Q4. Why does the kernel community prefer RCU over rwlock_t for new code?
RCU readers proceed without taking any lock at all, so there is no writer-starvation problem and no read-side lock contention, which scales better on modern many-core systems.
Q5. Is rwlock_t deprecated?
No, it is still a fully supported kernel API and appears throughout the tree; it is simply no longer the default first choice for brand-new read-mostly data structures.
Q6. What header declares rwlock_t?<linux/rwlock.h>, alongside DEFINE_RWLOCK() and rwlock_init().
Practice Exercises
- Sketch (in comments only, no need to compile) how you would convert the plain-spinlock list search shown above to use
rwlock_t. - List two data structures in a driver you have written where reads clearly outnumber writes.
- Research, in the kernel source tree on your own machine, one real subsystem that still uses
rwlock_ttoday.
This lecture is part of EmbeddedPathashala’s free linux kernel development course, free linux device drivers course and free embedded systems course.
More Free Lectures Join the Community
2 Comments