LWN.net Logo

Disabling IRQF_DISABLED

By Jonathan Corbet
March 30, 2010
Interrupt handlers run asynchronously in response to signals from the hardware. Since they pull the CPU away from whatever it was doing before, handlers are supposed to be very quick; they should, in most cases, tell the hardware to be quiet, arrange for any followup work to be done, and get out of the way. Historically, the situation has not been so simple, though, leading to a distinction between "fast" and "slow" handlers in the earliest days of Linux. It now seems, though, that this distinction could disappear as early as 2.6.35.

The core distinction between fast and slow handlers is this: fast handlers are run with further interrupts disabled, while slow handlers run with interrupts enabled. A slow handler, thus, can be interrupted by another handler, while a fast handler cannot. In an ideal world, slow handlers would not exist; they would all get their work done quickly and not monopolize the CPU, so there would be no point in interrupting them. In the real world, which includes problematic hardware, slow processors, and developers of varying ability, slow handlers have been a fact of life. The nature of some hardware (old IDE controllers, for example) makes it hard to avoid doing a lot of work in the interrupt handler. Meanwhile, other types of devices must have exceedingly fast interrupt response to avoid loss of data; a classic example here is a number of serial ports which are able to buffer exactly one character in the UART. The slow IDE work could not be allowed to delay serial processing; thus, the IDE interrupt handler had to be a slow one.

Over time, though, the situation has changed. Hardware has gotten smarter and better able to handle interrupt response latency. CPUs have gotten faster, so even a relatively slow handler can get a lot of work done quickly. The needs of the realtime tree (and other latency-sensitive workloads) have motivated the reworking of the worst interrupt-time offenders, and improvements in the kernel's deferred work mechanisms have made it easier to move work out of handlers. So the need for the distinction between the two types of interrupt handlers has been fading.

Simultaneously, problems associated with the fast/slow dichotomy have been growing. There is no way to run handlers for interrupts on shared lines (found on any system with a PCI bus) with interrupts disabled, because any other handler for a device on the same line can enable interrupts. Allowing interrupt handlers to interrupt each other leads to worse cache behavior and unpredictable completion times. What set off the recent discussion, though, was this patch from Andi Kleen which was aimed at addressing another problem: deeply nested interrupt handlers can overflow the processor's interrupt stack - a situation from which good things cannot be expected to ensue.

Andi's solution is to monitor the depth of the interrupt stack within the core kernel's interrupt-handling code. Should the stack become more than half full, the core code will no longer enable interrupts before calling slow handlers. In effect, it treats slow handlers as if they were fast handlers for the duration of the stack-space squeeze. This patch solved the problem that was being observed, but it ran into some trouble; in particular, Thomas Gleixner did not hesitate to make his dislike for the patch known. Your editor will try to rephrase the argument in slightly more polite terms; according to Thomas, the patch implemented a solution which was unreliable at best, was liable to create significant latencies in the system, and which ignored the real problem.

Said real problem, according to Thomas, is the fact that slow handlers exist at all. He would like to see a world where all interrupt handlers are run with interrupts disabled, and where all of those handlers get their work done quickly. Any extended interrupt processing should be moved to threaded handlers. In summary:

So what's the point of running a well written (short) interrupt handler with interrupts enabled ? Nothing at all. It just makes us deal with crap like stacks overflowing for no good reason.

Linus initially squashed the idea, saying that a world where we only have fast handlers is not really possible:

So Thomas, you're wrong. We can't fix all irq handlers to be really quick, and we MUST NOT run them with all other irq's disabled if they are not - because that obviates the whole point.

It is interesting to note, though, that this position shifted over time. Linus (and others) expressed a number of concerns about running all handlers with interrupts disabled:

  • The handlers for some devices simply have to do a lot of work, and that cannot be easily changed. Embedded systems, in particular, can have fussy hardware and slow processors.

  • Some handlers will not work properly if interrupts are not enabled. In the past, some drivers have done things like waiting for a certain amount of time to pass (as reflected in changes to the jiffies variable). This dubious practice fails outright if interrupts are disabled: the timer interrupt will be blocked, and jiffies will not advance.

  • Some hardware simply has strict latency requirements which cannot wait for another interrupt handler to finish its job.

Looking at all these worries, one might well wonder if a system which disabled interrupts for all handlers would function well at all. So it is interesting to note one thing: any system which has the lockdep locking checker enabled has been running all handlers that way for some years now. Many developers and testers run lockdep-enabled kernels, and they are available for some of the more adventurous distributions (Rawhide, for example) as well. So we have quite a bit of test coverage for this mode of operation already.

Another thing that happened over the last few years was the integration of the dynamic tick code, which disables the clock tick when the system is idle. Clock ticks are not turned back on for interrupt handlers. So any handler which expects jiffies to change while it is running will, sooner or later, go into a rather undignified infinite loop. Users tend to notice that kind of behavior, so most drivers which behave this way have long since been fixed.

Finally, the realtime tree developers have spent a great deal of time tracking down sources of latency; excessive time spent in interrupt handlers is one of the worst of those. So drivers which control hardware of interest have generally been fixed. The addition of threaded interrupt handlers has made it easier to fix drivers; most of the code can simply be pushed into the threaded handler with no other change at all.

Given all of this, Ingo Molnar felt confident in saying:

I'm fairly certain, based on having played with those aspects from many angles that disabling irqs in all drivers should work just fine today already.

After hearing this from a few core developers, and after doing some research of his own, Linus eventually stopped opposing the idea and started talking about how it should be implemented. Thomas then posted a patch implementing the change. With this patch, the IRQF_DISABLED flag (used to indicate a fast handler) becomes a no-op; it is expected to be removed altogether in 2.6.36.

There are still some concerns about the change, especially with regard to slow hardware on embedded systems. In some of these cases, the problem can be solved with threaded interrupt handlers. Some developers worry, though, that threaded handlers impose too much latency on interrupt response. Improving on that situation is a task for the future; in the mean time, some interrupt handlers may just have to enable interrupts internally to get the required behavior. The preferred function for this purpose is local_irq_enable_in_hardirq(); its use can already be found in the IDE layer.

Since all of the technical obstacles have seemingly been overcome, chances are good that this patch will find its way into the kernel in the 2.6.35 merge window.


(Log in to post comments)

Disabling IRQF_DISABLED

Posted Apr 1, 2010 17:54 UTC (Thu) by bronson (subscriber, #4806) [Link]

This story and the previous one (finally removing the BKL) are making me happy. These are potentially serious, scary changes that Linux's development model just skis right over.

Stable driver interface? If Linux had one, these changes would be close to impossible. And, if you're still unconvinced, take a look at what Microsoft's DDK has turned into.

Disabling IRQF_DISABLED

Posted Apr 2, 2010 23:44 UTC (Fri) by eparis123 (guest, #59739) [Link]

Yeah, the entire MS business depends on backward compatibility. Check Raymond Chen's book 'The Old New Thing' for really fun stories of the great lengths MS developers go to maintain such compatibility.

Also Check the mentioned book 'extras' online chapters on the publisher's page, where the author maintains backward compatibility for Win3.1 games in his youth days. At the time where hardware protection wasn't used by Windows and the code Windows was trying to be compatible to was doing amazingly silly stuff with the x86 processors and PC architecture nuances (GDT, IDT, PIT, etcetera).

Disabling IRQF_DISABLED

Posted Apr 2, 2010 23:35 UTC (Fri) by eparis123 (guest, #59739) [Link]

Having the possibility of a kernel stack overflow due to buggy hardware sounds scary. Hopefully we will all have handlers running with local interrupts disabled in the future! (I guess this might also make the 4K kernel stacks experience smoother)

Disabling IRQF_DISABLED

Posted Sep 5, 2012 9:58 UTC (Wed) by yech (guest, #86575) [Link]

Could anyone confirm for me that with this change interrupt preemption is actually disabled from 2.6.35?

Copyright © 2010, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds