Thrashing the interrupt code
[Posted July 24, 2002 by corbet]
Dig into the source of an old Unix system, and you will almost certainly
find calls to
cli() and
sti(), which disable and enable
interrupts, respectively. The Linux kernel, too, has these calls. In the
Good Old Days, when Linux did not run on SMP systems, a call to
cli() was sufficient to guarantee exclusive access to any resource
of interest. Kernel code was not preemptable, so, in the absence of
interrupts, no other kernel code had any possibility of running.
SMP changed all that, of course. The cli() call remained,
however, for the few places that really needed it - and to avoid having to
change a great deal of code which relied on cli() for mutual
exclusion. The cli() call became global, in that it disabled the
handling of interrupts on all processors in the system. Note that it did
not disable the interrupts themselves, just the processing of those
interrupts. This was accomplished by way of the "big IRQ lock"
(global_irq_lock); once cli() was called, any processor
attempting to handle an interrupt would spin on that lock until things were
released with sti(). Needless to say, spending a lot of time with
interrupts globally disabled in this way is not good for performance; thus
the use of cli() and sti() has been discouraged for a
long time.
As of 2.5.28, these functions are no longer discouraged - they are gone.
Ingo Molnar sent out a patch (since revised
an unbelievable number of times) which removes the
global_irq_lock, the cli() and sti() primitives,
and more. The result is the removal of a bunch of old legacy code, a
faster IRQ handling subsystem, and a great many broken drivers. Said
drivers are being fixed, but building Linux kernels for SMP systems could
be a bit challenging for the next release or two.
This patch also merges three different counters that the kernel used to
maintain:
- The hard IRQ counter (__local_irq_count), which tracked the
number of hardware interrupts currently being serviced by each
processor;
- The soft IRQ counter (__local_bh_count), which tracked
software interrupts (bottom halves, tasklets, etc.); and
- The preemption counter (preempt_count, in the task structure)
which noted whether the process had been preempted in kernel space.
The soft IRQ and preemption counters could also be used to disable software
IRQs and kernel preemption by setting them to a nonzero value. The two IRQ
counters, taken together, indicate whether the processor is currently
responding to an interrupt.
In other words, all of these counters are related to each other - they
describe what kind of code is running at the moment and what sorts of
diversions the processor is allowed to take. So, with Ingo's patch, all
three have been merged into the per-process preemption counter. This
change results in some simplified code; it should be mostly transparent to
the rest of the kernel.
The cli() change is not transparent, though. People maintaining
or writing drivers will now need to bear in mind that there is no longer
any way to globally disable interrupts. You can still disable interrupts
for the current processor (with local_irq_save() and friends), but
other processors will still accept and handle interrupts. The only really
safe way of protecting resources is most situations is with
spin_lock_irq(); a number of drivers will need to be (finally)
converted over to real locking before they will work again. Ingo has
included a document (cli-sti-removal.txt) in
the kernel source to help driver maintainers who are wondering how to
handle this change.
(
Log in to post comments)