Linux Device Driver Tutorial Part 24 – Read Write Spinlock in Linux Kernel (Spinlock Part 2)

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).

Prerequisites

In the example section, I had used Kthread to explain Mutex. If you don’t know what is Kthread and How to use it, then I would recommend you to explore that by using below link.

  1. Kthread Tutorial in Linux Kernel
  2. Spinlock Tutorial in Linux Kernel – Part 1

Introduction

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.

SpinLock

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.

Read Write Spinlocks

I told spinlock and read write spinlock are similar in operation. That means spinlock is enough. Then why do we need Read Write Spinlock? Ridiculous right?

Okay now we will take one scenario. I have five threads. All those threads are accessing one global variable. So we can use spinlock over there. Am I right? Well, yes it’s right.  In those threads, only one thread is writing data into that variable. Other four threads are only reading data from that variable. This the case. I hope you guys understand the scenario. Now I can ask you guys an question. If you implement spinlock,

Question : Let’s say thread-1 is writing. Other threads (thread-2 to thread-5) are only reading. 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. 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.

Where to use Read Write Spinlock?

  1. If you are only reading the data then you take read lock
  2. If you are writing then go for write lock

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.

Read Write SpinLock in Linux Kernel Device Driver

Initialize

We can initialize Read Write Spinlock in two ways.

  1. Static Method
  2. Dynamic Method

Static Method

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.

Dynamic Method

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.

Approach 1 (Locking between User context)

If you share data with user context (between Kernel Threads), then you can use this approach.

Read Lock

Lock:

read_lock(rwlock_t *lock)

This will take the lock if it is free, otherwise it’ll spin until that lock is free (Keep trying).

Unlock:

read_unlock(rwlock_t *lock)

It does the reverse of lock. It will unlock which is locked by above call.

Write Lock

Lock:

write_lock(rwlock_t *lock)

This will take the lock if it is free, otherwise it’ll spin until that lock is free (Keep trying).

Unlock:

write_unlock(rwlock_t *lock)

It does the reverse of lock. It will unlock which is locked by above call.

Example

Approach 2 (Locking between Bottom Halves)

If you want to share data between two different Bottom halves or same bottom halves, then you can use the Approach 1.

Approach 3 (Locking between User context and Bottom Halves)

If you share data with a bottom half and user context (like Kernel Thread), then this approach will be useful.

Read Lock

Lock:

read_lock_bh(rwlock_t *lock)

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“.

Unlock:

read_unlock_bh(rwlock_t *lock)

It will release the lock and re-enables the soft interrupts which is disabled by above call.

Write Lock

Lock:

write_lock_bh(rwlock_t *lock)

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“.

Unlock:

write_unlock_bh(rwlock_t *lock)

It will release the lock and re-enables the soft interrupts which is disabled by above call.

Example

Approach 4 (Locking between Hard IRQ and Bottom Halves)

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.

Read Lock

Lock:

read_lock_irq(rwlock_t *lock)

This will disable interrupts on that cpu, then grab the lock.

Unlock:

read_unlock_irq(rwlock_t *lock)

It will release the lock and re-enables the interrupts which is disabled by above call.

Write Lock

Lock:

write_lock_irq(rwlock_t *lock)

This will disable interrupts on that cpu, then grab the lock.

Unlock:

write_unlock_irq(rwlock_t *lock)

It will release the lock and re-enables the interrupts which is disabled by above call.

Example

Approach 5 (Alternative way of Approach 4)

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.

Read Lock

Lock:

read_lock_irqsave( rwlock_t *lock, unsigned long flags );

This will save whether interrupts were on or off in a flags word and grab the lock.

Unlock:

read_unlock_irqrestore( rwlock_t *lock, unsigned long flags );

This will releases the read/write spinlock and restores the interrupts using the flags argument.

Write Lock

Lock:

write_lock_irqsave( rwlock_t *lock, unsigned long flags );

This will save whether interrupts were on or off in a flags word and grab the lock.

Unlock:

write_unlock_irqrestore( rwlock_t *lock, unsigned long flags );

This will releases the read/write spinlock and restores the interrupts using the flags argument.

Approach 6 (Locking between Hard IRQs)

If you want to share data between two different IRQs, then you should use Approach 5.

Example Programming

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.

Driver Source Code

MakeFile

 

Download our new Android app. You can learn all Embedded Tutorials from your Android Phone easily.

Click Here to Download App!