We saw in the post “Introduction to spinlocks” how spinlock can be used to achieve mutual exclusion.
Let us further look at the practical implementation of spinlocks in Linux.
Spinlocks are declared in Linux using the datatype “spinlock_t”. The spin lock variable can one of the two values
We can initialize the value of the spinlock to either of the values by just eqating it to the value, i.e.
static spinlock_t lock=SPIN_LOCK_UNLOCKED
Thus the lock starts in an unlocked state. We can also assign the value to the spinlock at runtime by using the function spin_lock_init
void spin_lock_init(spinlock *lock)
After the initialization we can use the spin lock any where in the code. To lock the spinlock before entering the critical section we can use one of the following functions
Locking of a spinlock is nothing but changing the value of the spinlock variable from SPIN_LOCK_UNLOCKED to SPIN_LOCK_LOCKED.
The functions available to acquire the lock are as follows.
void spin_lock(spinlock_t *lock)
Locks the spinlock if it is not already locked. If unable to obtain the lock, it will keep spinning on the lock until the lock becomes free.
int spin_trylock(spinlock_t *lock)
Locks the spinlock if it is not already locked. If unable to obtain the lock it exits with an error and does not spin lock.
Locks the spinlock if it is not already locked and also disables the interrupts after locking. If lock is not available it continues to spin on the lock until it becomes available
spin_lock_irqsave(lock, flags) :
Locks the spinlock if it is not already locked and also disables the interrupts, storing the state of the interrupts in the variable flags which is an unsigned long datatype, after locking. If lock is not available it continues to spin on the lock until it becomes available.
Before exiting the critical section any process that locks the spinlock has to unlock the spinlock. For every lock function that we saw above there are corresponding unlock functions as listed below.
Unlocking a spinlock is nothing but changing the value of the lock variable from SPIN_LOCK_LOCKED to SPIN_LOCK_UNLOCKED.
The functions available for unlocking a spinlock are as follows
void spin_unlock(spinlock_t *lock)
Unlock the spinlock, changing the value of the spinlock variable from SPIN_LOCK_UNLOCKED to SPIN_LOCKED.
void spin_unlock_irq(spinlock_t *lock)
If the spinlock was locked using spin_lock_irq then the interrupts would have been disabled. Using this unlock function makes sure that the spinlock is unlocked as well as the interrupts are enabled.
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
If the spinlock was locked using the function spin_lock_irqsave, then the state of the interrupts which was stored also needs to be restored. Thus this function not only unlocks the function but also restores the state of interrupts to the same state as they were before they were disabled which was saved in the variable flags.