6.6 Locks and interrupt handlers
Some xv6 spinlocks protect data that is used by
both threads and interrupt handlers.
For example, the
clockintr
timer interrupt handler might increment
ticks
(kernel/trap.c:164)
at about the same time that a kernel
thread reads
ticks
in
sys_sleep
(kernel/sysproc.c:61).
The lock
tickslock
serializes the two accesses.
The interaction of spinlocks and interrupts raises a potential danger.
Suppose
sys_sleep
holds
tickslock
,
and its CPU is interrupted by a timer interrupt.
clockintr
would try to acquire
tickslock
,
see it was held, and wait for it to be released.
In this situation,
tickslock
will never be released: only
sys_sleep
can release it, but
sys_sleep
will not continue running until
clockintr
returns.
So the CPU will deadlock, and any code
that needs either lock will also freeze.
To avoid this situation, if a spinlock is used by an interrupt handler,
a CPU must never hold that lock with interrupts enabled.
Xv6 is more conservative: when a CPU acquires any
lock, xv6 always disables interrupts on that CPU.
Interrupts may still occur on other CPUs, so
an interrupt’s
acquire
can wait for a thread to release a spinlock; just not on the same CPU.
Xv6 re-enables interrupts when a CPU holds no spinlocks; it must
do a little book-keeping to cope with nested critical sections.
acquire
calls
push_off
(kernel/spinlock.c:89)
and
release
calls
pop_off
(kernel/spinlock.c:100)
to track the nesting level of locks on the current CPU.
When that count reaches zero,
pop_off
restores the interrupt enable state that existed
at the start of the outermost critical section.
The
intr_off
and
intr_on
functions execute RISC-V instructions to disable and enable
interrupts, respectively.
It is important that
acquire
call
push_off
strictly before setting
lk->locked
(kernel/spinlock.c:28).
If the two were reversed, there would be
a brief window when the lock
was held with interrupts enabled, and
an unfortunately timed interrupt would deadlock the system.
Similarly, it is important that
release
call
pop_off
only after
releasing the lock
(kernel/spinlock.c:66).