10.5. Interrupt Sharing
The notion of an IRQ conflict is
almost synonymous with the PC
architecture. In the past, IRQ lines on the PC have not been able to
serve more than one device, and there have never been enough of them.
As a result, frustrated users have often spent much time with their
computer case open, trying to find a way to make all of their
peripherals play well together.
Modern hardware, of course, has been designed to allow the sharing of
interrupts; the PCI bus requires it. Therefore, the Linux kernel
supports interrupt sharing on all buses, even those (such as the ISA
bus) where sharing has traditionally not been supported. Device
drivers for the 2.6 kernel should be written to work with shared
interrupts if the target hardware can support that mode of operation.
Fortunately, working with shared interrupts is easy, most of the
time.
10.5.1. Installing a Shared Handler
Shared interrupts are installed through
request_irq just
like nonshared ones, but there are two differences:
The
SA_SHIRQ bit must be specified in the
flags argument when requesting the interrupt. The dev_id argument
must be unique. Any pointer into the
module's address space will do, but
dev_id definitely cannot be set to
NULL.
The kernel keeps a list of shared handlers associated with the
interrupt, and dev_id can be thought of as the
signature that differentiates between them. If two drivers were to
register NULL as their signature on the same
interrupt, things might get mixed up at unload time, causing the
kernel to oops when an interrupt arrived. For this reason, modern
kernels complain loudly if passed a NULL
dev_id when registering shared interrupts. When a
shared interrupt is requested, request_irq
succeeds if one of the following is true:
Whenever two or more drivers are sharing an interrupt line and the
hardware interrupts the processor on that line, the kernel invokes
every handler registered for that interrupt, passing each its own
dev_id. Therefore, a shared handler must be able
to recognize its own interrupts and should quickly exit when its own
device has not interrupted. Be sure to return
IRQ_NONE whenever your handler is called and finds
that the device is not interrupting.
If you need to probe for your device before requesting the IRQ line,
the kernel can't help you. No probing function is
available for shared handlers. The standard probing mechanism works
if the line being used is free, but if the line is already held by
another driver with sharing capabilities, the probe fails, even if
your driver would have worked perfectly. Fortunately, most hardware
designed for interrupt sharing is also able to tell the processor
which interrupt it is using, thus eliminating the need for explicit
probing.
Releasing
the handler is performed in the
normal
way, using free_irq. Here the
dev_id argument is used to select the correct
handler to release from the list of shared handlers for the
interrupt. That's why the dev_id
pointer must be unique.
A
driver using a shared handler needs to be careful about one more
thing: it can't play with
enable_irq or disable_irq.
If it does, things might go haywire for other devices sharing the
line; disabling another device's interrupts for even
a short time may create latencies that are problematic for that
device and it's user. Generally, the programmer must
remember that his driver doesn't own the IRQ, and
its behavior should be more
"social" than is necessary if one
owns the interrupt line.
10.5.2. Running the Handler
As suggested earlier, when
the kernel receives an interrupt, all
the registered handlers are invoked. A shared handler must be able to
distinguish between interrupts that it needs to handle and interrupts
generated by other devices.
Loading short with the option
shared=1 installs the following handler instead
of the default:
irqreturn_t short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
int value, written;
struct timeval tv;
/* If it wasn't short, return immediately */
value = inb(short_base);
if (!(value & 0x80))
return IRQ_NONE;
/* clear the interrupting bit */
outb(value & 0x7F, short_base);
/* the rest is unchanged */
do_gettimeofday(&tv);
written = sprintf((char *)short_head,"%08u.%06u\n",
(int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
short_incr_bp(&short_head, written);
wake_up_interruptible(&short_queue); /* awake any reading process */
return IRQ_HANDLED;
}
An explanation is due here. Since the parallel port has no
"interrupt-pending" bit to check,
the handler uses the ACK bit for this purpose. If the bit is high,
the interrupt being reported is for short, and
the handler clears the bit.
The handler resets the bit by zeroing the high bit of the parallel
interface's data
port—short assumes that pins 9 and 10 are
connected together. If one of the other devices sharing the IRQ with
short generates an interrupt,
short sees that its own line is still inactive
and does nothing.
A full-featured driver probably splits the work into top and bottom
halves, of course, but that's easy to add and does
not have any impact on the code that implements sharing. A real
driver would also likely use the dev_id argument
to determine which, of possibly many, devices might be interrupting.
Note that if you are using a printer (instead of the jumper wire) to
test interrupt management with short, this
shared handler won't work as advertised, because the
printer protocol doesn't allow for sharing, and the
driver can't know whether the interrupt was from the
printer.
10.5.3. The /proc Interface and Shared Interrupts
Installing shared handlers in
the system doesn't affect
/proc/stat, which doesn't even
know about handlers. However, /proc/interrupts
changes slightly.
All the handlers installed for the same interrupt number appear on
the same line of /proc/interrupts. The following
output (from an x86_64 system) shows how shared interrupt handlers
are displayed:
CPU0
0: 892335412 XT-PIC timer
1: 453971 XT-PIC i8042
2: 0 XT-PIC cascade
5: 0 XT-PIC libata, ehci_hcd
8: 0 XT-PIC rtc
9: 0 XT-PIC acpi
10: 11365067 XT-PIC ide2, uhci_hcd, uhci_hcd, SysKonnect SK-98xx, EMU10K1
11: 4391962 XT-PIC uhci_hcd, uhci_hcd
12: 224 XT-PIC i8042
14: 2787721 XT-PIC ide0
15: 203048 XT-PIC ide1
NMI: 41234
LOC: 892193503
ERR: 102
MIS: 0
This system has several shared
interrupt lines. IRQ 5 is used for the serial ATA and IEEE 1394
controllers; IRQ 10 has several devices, including an IDE controller,
two USB controllers, an Ethernet interface, and a sound card; and IRQ
11 also is used by two USB controllers.
|