This is the Series on Linux Device Driver. The aim of this series is to provide easy and practical examples that anyone can understand. This is the Linux Device Driver Tutorial Part 24 – Read Write Spinlock in Linux Kernel (Spinlock Part 2).
4.2 Where to use Read Write Spinlock?
In our previous tutorial we have understood the use of Common Spinlock and its Implementation. If you have understood Spinlock then this Read Write Spinlock is also similar except some difference. We will cover those things below.
The spinlock is a very simple single-holder lock. If a process attempts to acquire a spinlock and it is unavailable, the process will keep trying (spinning) until it can acquire the lock. Read Write spinlock also does the same but it has separate locks for read and write operation.
I told that spinlock and read write spinlock are similar in operation. That means spinlock is enough. Then why do we need Read Write Spinlock? Ridiculous right?
Question : Now we forgot the thread-1(writer thread). So we have 4 reader thread (thread-2 to thread-5). Now those all four threads are want to read the data from the variable at the same time. What will happen in this situation if you use spinlock? What about the processing speed and performance?
Let’s assume thread-2 got the lock while other reading threads are trying hardly for the lock. That variable won’t change even thread-2’s access. Because no one is writing. But here only thread-2 is accessing. But other reading threads are simply wasting time to take lock since variable won’t change. That means performance will be reduced. Isn’t it? Yes you are correct.
In this case if you implement read and write lock, thread-2 will take the read lock and read the data. And other reading threads also will take the read lock without spinning (blocking) and read the data. Because no one is writing. So what about the performance now? There is no waiting between reading operations. Right? Then in this case read write spinlock is useful. Isn’t It?
So, If multiple threads require read access to the same data, there is no reason why they should not be able to execute simultaneously. Spinlocks don’t differentiate between read and read/write access. Thus spinlocks do not exploit this potential parallelism. To do so, read-write locks are required.
When there is no thread in the critical section, any reader or writer thread can enter into critical section by taking respective read or write lock. But only one thread can enter into critical section.
If reader thread is in critical section, the new reader thread can enter arbitrarily, but the writer thread cannot enter. Writer thread has to wait until all the reader thread finish their process.
If writer thread is in critical section, no reader thread or writer thread can enter.
If one or more reader threads are in critical section by taking it’s lock, the writer thread can of course not enter the critical section, but the writer thread cannot prevent the entry of the subsequent read thread. He has to wait until the critical section has a reader thread. So this read write spinlock is giving importance to reader thread and not writer thread. If you want to give importance to writer thread than reader thread, then another lock is available in linux which is seqlock.
Where to use Read Write Spinlock?
Note : Many people can hold a read lock, but a writer must be sole holder. Read-Write locks are more useful in scenarios where the architecture is clearly divided into reader and writers, with more number of reads.
In Read Write spinlock multiple readers are permitted at same time but only one writer. (i.e) If a writer has the lock, no reader is allowed to enter the critical section. If only a reader has the lock, then multiple readers are permitted in the critical section.
We can initialize Read Write Spinlock in two ways.
You can statically initialize a Read Write Spinlock using the macro given below.
The macro given above will create rwlock_t variable in the name of etx_rwlock.
If you want to initialize dynamically you can use the method as given below.
You can use any one of the methods.
After initializing the read/write spinlock, there are several ways to use read/write spinlock to lock or unlock, based on where the read/write spinlock is used; either in user context or interrupt context. Let’s look at the approaches with these situations.
If you share data with user context (between Kernel Threads), then you can use this approach.
This will take the lock if it is free, otherwise it’ll spin until that lock is free (Keep trying).
It does the reverse of lock. It will unlock which is locked by above call.
If you want to share data between two different Bottom halves or same bottom halves, then you can use the Approach 1.
If you share data with a bottom half and user context (like Kernel Thread), then this approach will be useful.
It disables soft interrupts on that CPU, then grabs the lock. This has the effect of preventing softirqs, tasklets, and bottom halves from running on the local CPU. Here the suffix ‘_bh‘ refers to “Bottom Halves“.
It will release the lock and re-enables the soft interrupts which is disabled by above call.
If you share data between Hardware ISR and Bottom halves then you have to disable the IRQ before lock. Because, the bottom halves processing can be interrupted by a hardware interrupt. So this will be used in that scenario.
This will disable interrupts on that cpu, then grab the lock.
It will release the lock and re-enables the interrupts which is disabled by above call.
If you want to use different variant rather than using read_lock_irq( )/write_lock_irq() and read_unlock_irq( )/write_unlock_irq() then you can use this approach.
This will save whether interrupts were on or off in a flags word and grab the lock.
This will releases the read/write spinlock and restores the interrupts using the flags argument.
If you want to share data between two different IRQs, then you should use Approach 5.
This code snippet explains how to create two threads that accesses a global variable (etx_gloabl_variable). So before accessing the variable, it should lock the read/write spinlock. After that it will release the read/write spinlock. This example is using Approach 1.