Device Drivers & Kernel Modules

driver-specific locking

A driver's data can be touched from two very different worlds at the same time. One is process context: your read or write handler, running on behalf of some application that called in, and able to sleep. The other is interrupt context: your IRQ handler, which fires asynchronously the instant the hardware signals, possibly in the middle of your read handler. If both touch the same buffer or counter, you have a race. Driver-specific locking is the discipline of protecting shared driver state across this process-versus-interrupt boundary.

The classic trap is this: your process-context code takes a normal spinlock to guard a shared queue. While it holds the lock, an interrupt arrives on the same CPU and your IRQ handler runs and tries to take the SAME lock. The interrupt handler cannot proceed (the lock is held) and cannot sleep to wait, and the code holding the lock cannot resume (the interrupt preempted it) — an instant deadlock against yourself. The fix is to disable interrupts on that CPU while holding a lock that an interrupt handler also takes: in Linux you use spin_lock_irqsave in process context (which saves the interrupt state and disables interrupts) and a plain spin_lock in the handler, so the two cannot interleave on one CPU. You must also never use a sleeping lock (a mutex) in interrupt context, because interrupt handlers cannot sleep.

This matters because device drivers are inherently concurrent — hardware does not wait for your handler to finish — and these races are timing-dependent, so they pass every test and then corrupt memory in the field. The honest summary: the general theory of locks lives elsewhere; what is driver-specific is the discipline of which lock variant to use given that an interrupt can strike at any moment. The rules are concrete: a lock shared with an interrupt handler must disable interrupts in the non-interrupt path, and interrupt context must never sleep, so spinlocks (never mutexes) guard data shared with handlers.

// process context: disable IRQs while holding the lock unsigned long flags; spin_lock_irqsave(&dev->lock, flags); ... touch shared queue ... spin_unlock_irqrestore(&dev->lock, flags); // interrupt handler: same lock, plain spin_lock spin_lock(&dev->lock); ... touch shared queue ... spin_unlock(&dev->lock); // a mutex here would be illegal

Data shared with an interrupt handler needs spin_lock_irqsave in process context so the handler cannot deadlock against the held lock.

A lock shared with an interrupt handler must disable interrupts in the non-interrupt path (spin_lock_irqsave), and interrupt context must never sleep — so never take a mutex there. Using a plain spinlock without disabling IRQs invites a self-deadlock.

Also called
driver lockinginterrupt vs process context locking驅動程式並行保護