I was reading the implementation of Linux semaphores. Due to atomicity, signal and wait (up and down in the source code) use spin locks. Then I saw Linux disabled interrupt in spin_lock_irqsave and reenabled interrupt in spin_unlock. This confused me. In my opinion, there is really no point disabling interrupt within a critical section.
For example, proc A (currently active) acquired the lock, proc B (blocked) is waiting for the lock and proc C is doing some unrelated stuff. It makes perfect sense to context switch to C within the critical section between A and B. Even if C also tries to acquire the lock, since the lock is already locked by A, the result would be C being blocked and A resuming execution.
Therefore, I don't know why Linux decided to disable interrupt within critical sections guarded by spin locks. It probably won't cause any problems but seems like a redundant operation to me.
On return from spin_lock, the calling function owns the lock. spin_lock_irqsave(spinlock_t *lock, unsigned long flags); This version also acquires the lock; in addition, it disables interrupts on the local processor and stores the current interrupt state in flags .
Because they usage in the IH can dramatically increases the interrupt handling latencies (in case of IH running in the context of low priority thread) and is able to produce deadlocks (in case of IH running in the context of thread which hold the lock).
mutex_unlock() will also never sleep. It cannot be used in interrupt context either since a mutex must be released by the same task that acquired it.
Spin locks can be used in interrupt handlers, whereas semaphores cannot be used because they sleep. If a lock is used in an interrupt handler, you must also disable local interrupts (interrupt requests on the current processor) before obtaining the lock.
Allow me to start off with a disclaimer that I am not a Linux expert, so my answer may not be the most accurate. Please point out any flaws and problems that you may find.
Imagine if some shared data is used by various parts of the kernel, including operations such as interrupt handlers that need to be fast and cannot block. Let's say system call foo
is currently active and has acquired a lock to use/access shared data bar
, and interrupts are not disabled when/before acquiring said lock.
Now a (hardware) interrupt handler, e.g. the keyboard, kicks in and also needs access to bar
(hardware interrupts have higher priority than system calls). Since bar
is currently being locked by syscall foo
, the interrupt handler cannot do anything. Interrupt handlers do need to be fast & not be blocked though, so they just keep spinning while trying to acquire the lock, which would cause a deadlock (i.e. system freeze) since syscall foo
never gets a chance to finish and release its lock.
If you disable interrupts before trying to acquire the lock in foo
, though, then foo
will be able to finish up whatever it's doing and ultimately release the lock (and restore interrupts). Any interrupts trying to come in while foo
holds the spinlock will be left on the queue, and will be able to start when the lock is released. This way, you won't run into the problem described above. However, care must also be taken to ensure that the lock for bar
is held for as short as possible, so that other higher priority operations can take over whenever required.
The answer is very simple: There is no way for the thread that tries to acquire a lock, to know if the ISR that will interrupt it, will try to acquire the same lock. If that will happen, the ISR will spin forever on that same lock and the system will deadlock.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With