Installing an Interrupt Handler

Interrupt lines are a precious and often limited resource, particularly when there are only 15 or 16 of them. The kernel keeps a registry of interrupt lines, similar to the registry of I/O ports. A module is allowed to request an interrupt channel (or IRQ, for Interrupt ReQuest) and release it when it’s done. The following functions, declared in <linux/sched.h>, implement the interface:

int request_irq(unsigned int irq,
                void (*handler)(int, void *, struct pt_regs *),
                unsigned long flags, 
                const char *device,
                void *dev_id);
void free_irq(unsigned int irq, void *dev_id);

Note that version 1.2 featured different prototypes. See Section 9.8 later in this chapter for portability issues.

The value returned to the requesting function is 0 to indicate success or a negative error code, as usual. It’s not uncommon for the function to return -EBUSY to signal that another driver is already using the requested interrupt line. The arguments to the functions are as follows:

unsigned int irq

This is the interrupt number. Sometimes the mapping from the Linux number to the hardware number isn’t one-to-one. Look, for example, at arch/ alpha/kernel/irq.c to see the Alpha mapping. The argument to the kernel functions is the Linux number rather than the hardware number.

void (*handler)(int, void *, struct pt_regs *)

The pointer to the handling function being installed.

unsigned long flags

As you might expect, a bitmask of options related to interrupt management.

const char *device

The string passed to request_irq is used in /proc/interrupts to show the owner of the interrupt (see the next section).

void *dev_id

This pointer is used for shared interrupt lines. It is a unique identifier, much like a ClientData (the this object of C++). The driver is free to use dev_id at will. dev_id is frequently set to NULL, unless interrupt sharing is in force. We’ll see a practical use for dev_id later, in Section 9.3.

The bits that can be set in flags are:

SA_INTERRUPT

When set, this indicates a ``fast'' interrupt handler. When clear, the handler is a ``slow'' one. The concept of ``fast'' and ``slow'' handlers is described under Section 9.2.3.

SA_SHIRQ

This bit signals that the interrupt can be shared between devices. The concept of sharing is outlined later in Section 9.5.

SA_SAMPLE_RANDOM

This bit indicates that the generated interrupts can contribute to the entropy pool used by /dev/random and /dev/urandom. These devices return truly random numbers when read and are designed to help application software choose secure keys for encryption. Such random numbers are extracted from an entropy pool that is contributed to by various random events. If your device generates interrupts at truly random times, you should set this flag. If, on the other hand, your interrupts will be predictable (for example, vertical blanking of a frame grabber), the flag is not worth setting--it wouldn’t contribute to system entropy anyway. See the comments in drivers/char/random.c for more information.

The interrupt handler can be installed either at driver initialization or when the device is first opened. While installing the interrupt handler from within init_module might sound like a good idea, it actually isn’t. Because the number of interrupt lines is limited, you don’t want to waste them. You can easily end up with more devices in your computer than there are interrupts. If a module requests an IRQ at initialization, it prevents any other driver from using the interrupt, even if the device holding it is never used. Requesting the interrupt at device open, on the other hand, allows a limited sharing of resources.

It is possible, for example, to run the frame grabber on the same interrupt as the modem, as long as you don’t use the two devices at the same time. It is quite common for users to load the module for a special device at system boot, even if the device is rarely used. A data acquisition gadget might use the same interrupt as the second serial port. While it’s not too hard to avoid connecting to your ISP during data acquisition, being forced to unload a module in order to use the modem is really unpleasant.

The correct place to call request_irq is when the device is first opened, before the hardware is instructed to generate interrupts. The place to call free_irq is the last time the device is closed, after the hardware is told not to interrupt the processor any more. The disadvantage of this technique is that you need to keep a per-device open count. Using the module count isn’t enough if you control two or more devices from the same module.

Despite what I’ve just said, short requests its interrupt line at load time. I did this so you can run the test programs without having to run an extra process to keep the device open. short, therefore, requests the interrupt from within init_module instead of doing it in short_open, as a real device would.

The interrupt requested by the code below is short_irq. The actual assignment of the variable is shown later, as it is not relevant to the current discussion. short_base is the base I/O address of the parallel interface being used; register 2 of the interface is written to enable interrupt reporting.

if (short_irq >= 0) {
    result = request_irq(short_irq, short_interrupt,
                         SA_INTERRUPT, "short", NULL);
    if (result) {
        printk(KERN_INFO "short: can't get assigned irq %i
",
               short_irq);
        short_irq = -1;
    }
    else { /* actually enable it--assume this *is* a parallel port */
        outb(0x10,short_base+2);
    }
}

The code shows that the handler being installed is a fast handler (SA_INTERRUPT), does not support interrupt sharing (SA_SHIRQ is missing), and doesn’t contribute to system entropy (SA_SAMPLE_RANDOM is missing too). The outb call then enables interrupt reporting for the parallel port.

The /proc Interface

Whenever a hardware interrupt reaches the processor, an internal counter is incremented, providing a way to check whether the device is working as expected. Reported interrupts are shown in /proc/interrupts. The following snapshot was taken after an hour and a half uptime of my 486:

 0:   537598   timer
 1:    23070   keyboard
 2:        0   cascade
 3:     7930 + serial
 5:     4568   NE2000
 7:    15920 + short
13:        0   math error
14:    48163 + ide0
15:     1278 + ide1

The first column is the IRQ number. You can see from the IRQs that are missing that the file shows only interrupts corresponding to installed handlers. For example, the first serial port (which uses interrupt number 4) is not shown, indicating that my modem isn’t being used. In fact, even if I’d used the modem earlier, but wasn’t using it at the time of the snapshot, it wouldn’t show up in the file; the serial ports are well-behaved and release their interrupt handlers when the device is closed. The plus sign that appears in half the records signals a fast interrupt handler.

The /proc tree contains another interrupt-related file, /proc/stat; sometimes you’ll find one more useful and sometimes you’ll prefer the other. /proc/stat records several low-level statistics about system activity, including (but not limited to) the number of interrupts received since system boot. Each line of stat begins with a text string that is the key to the line; the intr mark is what we are looking for. The following snapshot was taken half a minute later than the previous one:

intr 947102 540971 23346 0 8795 4907 4568 0 15920 0 0 0 0 0 0 48317 1278

The first number is the total of all interrupts, while each of the others represents a single IRQ line, starting with interrupt 0. This snapshot shows that interrupt number 4 has been used 4907 times, even though no handler is currently installed. If the driver you’re testing acquires and releases the interrupt at each open and close cycle, you may find /proc/stat more useful than /proc/interrupts.

Another difference between the two files is that interrupts is not architecture-dependent, while stat is: the number of fields depends on the hardware underlying the kernel. The number of available interrupts varies from as few as 15 on the Sparc to as many as 72 on the Atari (M68k processor).

The following snapshots show how the files appear inside my Alpha station (which has a total of 16 interrupts, just like my x86 box):

 1:        2   keyboard
 5:     4641   NE2000
15:    22909 + 53c7,8xx

intr 27555 0 2 0 1 1 4642 0 0 0 0 0 0 0 0 0 22909

The most noticeable feature of this snapshot is that the timer interrupt is missing. On the Alpha, the timer interrupt reaches the processor separately from other interrupts and has no IRQ number assigned.

Autodetecting the IRQ Number

One of the most compelling problems for a driver when it is initializing is how to determine which IRQ line is going to be used by the device. The driver needs the information in order to install the correct handler. Even though a programmer could require the user to specify the interrupt number at load time, this is a bad practice, as most of the time the user doesn’t know the number, either because he didn’t configure the jumpers or because the device is jumperless. Autodetection of the interrupt number is a basic requirement for driver usability.

Sometimes autodetection depends on the knowledge that some devices feature a default behavior which rarely, if ever, changes. In this case, the driver might assume that the default values apply. This is exactly how short behaves with the parallel port. The implementation is straightforward, as shown by short itself:

if (short_irq < 0) /* not yet specified: force the default on */
    switch(short_base) {
      case 0x378: short_irq = 7; break;
      case 0x278: short_irq = 2; break;
      case 0x3bc: short_irq = 5; break;
    }

The code assigns the interrupt number according to the chosen base I/O address, while allowing the user to override the default at load time by calling insmod short short_irq= x. short_base defaults to 0x378, so short_irq defaults to 7.

Some devices are more advanced in design and simply ``announce'' which interrupt they’re going to use. In this case, the driver retrieves the interrupt number by reading a status byte from one of the device’s I/O ports. When the target device is one that has the ability to tell the driver which interrupt it is going to use, autodetecting the IRQ number just means probing the device, with no additional work required to probe the interrupt.

It’s interesting to note here that modern devices supply their interrupt configuration. The PCI standard solves the problem by requiring peripheral devices to declare what interrupt line(s) they are going to use. The PCI standard is discussed in Chapter 15.

Unfortunately, not every device is programmer-friendly, and autodetection might require some probing. The technique is quite simple: the driver tells the device to generate interrupts and watches what happens. If everything goes well, only one interrupt line is activated.

Though probing is simple in theory, the actual implementation might be unclear. We’ll look at two ways to perform the task: calling kernel-defined helper functions and implementing our own version.

Kernel-helped probing

The mainstream kernel offers a low-level facility for probing the interrupt number. The facility consists of two functions, declared in <linux/interrupt.h> (which also describes the probing machinery):

unsigned long probe_irq_on(void);

This function returns a bitmask of unassigned interrupts. The driver must preserve the returned bitmask and pass it to probe_irq_off later. After this call, the driver should arrange for its device to generate at least one interrupt.

int probe_irq_off(unsigned long);

After the device has requested an interrupt, the driver calls this function, passing as argument the bitmask previously returned by probe_irq_on. probe_irq_off returns the number of the interrupt that was issued after ``probe_on.'' If no interrupts occurred, 0 is returned (thus IRQ 0 can’t be probed for, but no custom device can use it on any of the supported architectures anyway). If more than one interrupt occurred (ambiguous detection), probe_irq_off returns a negative value.

The programmer should be careful to enable the device after the call to probe_irq_on and to disable it before calling probe_irq_off. Additionally, you must remember to service the pending interrupt in your device, after probe_irq_off.

The short module demonstrates how to use such probing. If you load the module with probe=1, the following code is executed to detect your interrupt line, provided pins 9 and 10 of the parallel connector are bound together:

int count = 0;
do {
    unsigned long mask;

    mask = probe_irq_on();
    outb_p(0x10,short_base+2); /* enable reporting */
    outb_p(0x00,short_base);   /* clear the bit */
    outb_p(0xFF,short_base);   /* set the bit: interrupt! */
    outb_p(0x00,short_base+2); /* disable reporting */
    short_irq = probe_irq_off(mask);

    if (short_irq == 0) { /* none of them? */
        printk(KERN_INFO "short: no irq reported by probe
");
        short_irq = -1;
    }
    /*
     * if more than one line has been activated, the result is
     * negative. We should service the interrupt (no need for lpt port)
     * and loop over again. Loop at most five times, then give up
     */
} while (short_irq < 0 && count++ < 5);
if (short_irq < 0)
printk("short: probe failed %i times, giving up
", count);

Probing might be a lengthy task. While this is not true for short, probing a frame grabber, for example, requires a delay of at least 20ms (which is ages for the processor), and other devices might take even longer. Therefore, it’s best to probe for the interrupt line only once, at module initialization, independently of whether you install the handler at device open (as you should) or within init_module (which you shouldn’t do anyway).

It’s interesting to note that on the Sparc and M68k, probing is unnecessary and therefore isn’t implemented. Probing is a hack, and mature architectures are like PCI, which provides all the needed information. As a matter of fact, M68k and Sparc kernels export to the modules stub probing functions that always return 0--every architecture must define the functions, because they are exported by an architecture-independent source file. All the other supported architectures allow probing using the technique just shown.

The problem with probe_irq_on and probe_irq_off is that they are not exported by early kernel versions. Thus, if you want to write a module that ports back to 1.2, you must implement probing yourself.

Do-it-yourself probing

Probing can be implemented in the driver itself without too much trouble. The short module performs do-it-yourself detection of the IRQ line if it is loaded with probe=2.

The mechanism is the same as the one described above: enable all unused interrupts, then wait and see what happens. We can, however, exploit our knowledge of the device. Often a device can be configured to use one IRQ number from a set of three or four; probing just those IRQs enables us to detect the right one, without having to test for all possible IRQs.

The short implementation assumes that 3, 5, 7, and 9 are the only possible IRQ values. These numbers are actually the values that some parallel devices allow you to select.

The code below probes by testing all ``possible'' interrupts and looking at what happens. The trials array lists the IRQs to try and has 0 as the end marker; the tried array is used to keep track of which handlers have actually been registered by this driver.

int trials[] = {3, 5, 7, 9, 0};
int tried[]  = {0, 0, 0, 0, 0};
int i, count = 0;

/*
 * install the probing handler for all possible lines. Remember
 * the result (0 for success, or -EBUSY) in order to only free
 * what has been acquired
 */
for (i=0; trials[i]; i++)
    tried[i] = request_irq(trials[i], short_probing,
                           SA_INTERRUPT, "short probe", NULL);

do {
    short_irq = 0; /* none got, yet */
    outb_p(0x10,short_base+2); /* enable */
    outb_p(0x00,short_base);
    outb_p(0xFF,short_base); /* toggle the bit */
    outb_p(0x10,short_base+2); /* disable */

    /* the value has been set by the handler */
    if (short_irq == 0) { /* none of them? */
        printk(KERN_INFO "short: no irq reported by probe
");
    }
    /*
     * If more than one line has been activated, the result is
     * negative. We should service the interrupt (but the lpt port
     * doesn't need it) and loop over again. Do it at most 5 times
     */
} while (short_irq <=0 && count++ < 5);

/* end of loop, uninstall the handler */
for (i=0; trials[i]; i++)
    if (tried[i] == 0)
        free_irq(trials[i], NULL);

if (short_irq < 0)
    printk("short: probe failed %i times, giving up
", count);

You might not know in advance what the ``possible'' IRQ values are. In that case, you’ll need to probe all the free interrupts, instead of limiting yourself to a few trials[]. To probe for all interrupts, you have to probe from IRQ 0 to IRQ NR_IRQS-1, where NR_IRQS is defined in <asm/irq.h> and is platform-dependent.

Now we are missing only the probing handler itself. The handler’s role is to update short_irq according to which interrupts are actually received. A zero value in short_irq means ``nothing yet,'' while a negative value means ``ambiguous.'' I chose these values to be consistent with probe_irq_off and to use the same code to call either kind of probing within short.c.

void short_probing(int irq, void *dev_id, struct pt_regs *regs)
{
    if (short_irq == 0) short_irq = irq;    /* found */
    if (short_irq != irq) short_irq = -irq; /* ambiguous */
}

The arguments to the handler are described later. Knowing that irq is the interrupt being handled should be sufficient to understand the function just shown.

Fast and Slow Handlers

As you’ve seen, I’ve specified the SA_INTERRUPT flag for the short interrupt handler, thus asking for a ``fast'' handler. It’s high time to explain what ``fast'' and ``slow'' mean. Actually, not all the architectures support different implementations for fast and slow handlers. The Alpha and Sparc ports, for example, service fast and slow handlers in the same way. Versions 2.1.37 and later of the Intel port removed the difference as well, since with the available processing power of modern computers there’s no longer any need to differentiate between fast and slow interrupts.

The main difference between the two kinds of interrupt handlers is that fast handlers guarantee atomic processing of interrupts and slow handlers don’t (this difference is preserved in the new implementation of interrupt handling). In other words, the ``interrupt enable'' processor flag is turned off while a fast handler runs, thus preventing any interrupts from being serviced. When a slow handler is invoked, on the other hand, the kernel reenables interrupt reporting in the microprocessor, so other interrupts can be serviced while a slow handler runs.

Another task performed by the kernel before calling the actual interrupt handler, whether slow or fast, is to disable the interrupt line just reported. An IRQ service routine thus doesn’t need to be reentrant, to the joy of programmers. On the flip side, even a slow handler should be written to run as fast as possible, in order to avoid losing the next interrupt.

If a new interrupt arrives for a device while a handler is still processing the last interrupt, the new interrupt is lost forever. The interrupt controller doesn’t buffer disabled interrupts, whereas the processor does--as soon as sti is issued, pending interrupts are serviced. The sti function is the ``Set Interrupt Flag'' processor instruction (introduced in Section 2.5.2 in Chapter 2).

To summarize the slow and fast executing environments:

  • A fast handler runs with interrupt reporting disabled in the microprocessor, and the interrupt being serviced is disabled in the interrupt controller. The handler can nonetheless enable reporting in the processor by calling sti.

  • A slow handler runs with interrupt reporting enabled in the processor, and the interrupt being serviced is disabled in the interrupt controller.

But there is another difference between fast and slow handlers: the overhead added by the kernel. Slow handlers are actually slower because of additional housekeeping on the kernel’s side. This implies that frequent interrupts are best serviced by a fast handler. As far as short is concerned, several thousand interrupts per second can be generated by copying a large file to /dev/short0. Thus I chose to use a fast handler to control the amount of overhead being inserted into the system. This split behavior is what has been unified in the newer 2.1 kernels; the overhead is now added to all interrupt handlers.

A good candidate for a slow handler might be a frame grabber. It interrupts the processor 50 or 60 times per second, and a slow handler can choose to copy every frame from the interface board to physical RAM without blocking other system interrupts, such as those generated by serial ports or timer service.

This description should satisfy most readers, though I suspect someone with a taste for hardware and some experience with his or her computer might be interested in going deeper. If you don’t care about the internal details, you can skip to the next section.

The internals of interrupt handling on the x86

This description has been extrapolated from arch/i386/kernel/irq.c and include/asm-i386/irq.h as they appear in the 2.0.x kernels; although the general concepts remain the same, the hardware details differ on other platforms and have been slightly modified during 2.1 development.

The lowest level of interrupt handling resides in assembly code declared as macros in irq.h and expanded in irq.c. Three functions are declared for each interrupt: the slow, the fast, and the bad handlers.

The ``bad'' handler, the smallest, is the assembler entry point when no C-language handler has been installed for the interrupt. It acknowledges the interrupt to the proper PIC (Programmable Interrupt Controller) device[24] and disables it, to avoid losing any further processor time due to spurious interrupts. The bad handler is reinstalled by free_irq when a driver is done with an interrupt line. The bad handler doesn’t increment the counter in /proc/stat.

It’s interesting to note that IRQ probing in both the x86 and the Alpha is based on the behavior of the bad handler. probe_irq_on enables all the bad interrupts, without installing a handler; probe_irq_off simply checks which interrupts have been disabled since probe_irq_on. You can verify this behavior by observing that loading short with probe=1 (kernel-aided probing) doesn’t increment the interrupt counters, while loading it with probe=2 (home-made probing) increments them.

The assembler entry point for slow interrupts saves all the registers on the stack and makes data segments (the DS and ES processor registers) point into the kernel address space (CS has already been set by the processor). The code then acknowledges the interrupt to the PIC, disables notification of new interrupts on the same IRQ line, and issues an sti (set interrupt flag). Bear in mind that the processor automatically clears the flag when servicing an interrupt. The slow handler then passes the interrupt number and a pointer to the processor registers to do_IRQ, a C function that dispatches the right C-language handler. The struct pt_regs * argument that is passed to the interrupt handler in the driver is just a pointer to the position in the stack where the registers are stored.

When do_IRQ is finished, cli is issued, the specific interrupt is enabled in the PIC, and ret_from_sys_call is invoked. This last entry point (arch/i386/kernel/entry.S) restores all the registers from the stack, handles any pending bottom half (see Section 9.4 later in the chapter) and, if needed, reschedules the processor.

The fast entry point is different in that sti is not called prior to jumping to the C code, and not every machine register is saved before calling do_fast_IRQ. When the driver’s handler is called, the regs argument is NULL (because the registers aren’t stored on the stack) and interrupts are still disabled.

Finally, the fast handler reenables the interrupt in the 8259, restores the registers that were saved earlier, and returns without passing through ret_from_sys_call. Pending bottom halves are not run.

In all kernels up to 2.1.34, both handlers increment intr_count before passing control to C code (see Section 6.4.1 in Chapter 6).



[24] Each PC used to be equipped with two interrupt-controller chips, called 8259 chips. These devices don’t exist any more, but the same behavior is implemented in modern chipsets.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.186.6