Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
  Previous   Contents   Next 
   
 
Chapter 7

Interrupt Handlers

This chapter describes interrupt handling mechanisms, such as registering, servicing, and removing interrupts. This chapter provides information on the following subjects:

Interrupt Handler Overview

An interrupt is a hardware signal from a device to a CPU. It tells the CPU that the device needs attention and that the CPU should stop performing what it is doing and respond to the device. If a CPU is available (it is not performing a task with higher priority), it suspends the current thread and eventually invokes the interrupt handler for that device. The job of the interrupt handler is to service the device and stop it from interrupting. Once the handler returns, the CPU resumes what it was doing before the interrupt occurred.

The DDI/DKI provides interfaces for registering and servicing interrupts.

Interrupt Specification

The interrupt specification is information the system uses to bind a device interrupt source with a specific device interrupt handler. The specification describes the information provided by the hardware to the system when making an interrupt request. Because an interrupt specification is bus specific, the information it contains varies from bus to bus.

Interrupt specifications typically include a bus-interrupt level. For vectored interrupts the specifications include an interrupt vector. On IA platforms the interrupt specification defines the relative interrupt priority of the device. Because interrupt specifications are bus specific, see the man pages for isa(4), eisa(4), sbus(4), and pci(4) for information on interrupt specifications for these buses.

Interrupt Number

When registering interrupts the driver must provide the system with an interrupt number. This interrupt number identifies the interrupt specification for which the driver is registering a handler. Most devices have one interrupt: interrupt number 0. However, there are devices that have different interrupts for different events. A communications controller may have one interrupt for receive ready and one for transmit ready. The device driver normally knows how many interrupts the device has, but if the driver has to support several variations of a controller, it can call ddi_dev_nintrs(9F) to find out the number of device interrupts.

Interrupt Block Cookies

An iblock cookie is an opaque data structure that represents the information the system needs to block interrupts and is returned from ddi_get_iblock_cookie(9F) or ddi_get_soft_iblock_cookie(9F). This interface uses an interrupt number to return the iblock cookie associated with a specific interrupt source. The value of the iblock cookie (not the address) must be passed to mutex_init(9F) when initializing driver mutexes that will be used in the interrupt routine. The value of the iblock cookie is obtained by passing the address of the cookie to ddi_get_iblock_cookie() or ddi_get_soft_iblock_cookie(). For example:

       ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_HI,
               &xsp->low_iblock_cookie)
       mutex_init(&xsp->low_mu, NULL, MUTEX_DRIVER,
               (void *)xsp->low_iblock_cookie);

Device Interrupts

There are two common ways in which buses implement interrupts: vectored and polled. Both methods commonly supply a bus-interrupt priority level. However, vectored devices also supply an interrupt vector; polled devices do not.

High-Level Interrupts

Buses prioritize device interrupts at one of several bus-interrupt levels. These bus interrupt levels are then mapped to different processor-interrupt levels. A bus interrupt level that maps to a CPU interrupt priority level above the scheduler priority level is called a high-level interrupt. High-level interrupt handlers are restricted in what DDI interfaces they can call. In particular, the only DDI routines that high-level interrupt handlers are allowed to call are:

  • mutex_enter(9F) and mutex_exit(9F) on a mutex initialized with an iblock cookie associated with the high-level interrupt

  • ddi_trigger_softintr(9F)

  • the ddi_getX/ddi_putX families of routines (such as ddi_get8(9F))

A bus-interrupt level by itself does not determine whether a device interrupts at high level: a given bus-interrupt level may map to a high-level interrupt on one platform, but map to an ordinary interrupt on another platform.

The driver can choose whether to support devices that have high-level interrupts, but it always has to check--it cannot assume that its interrupts are not high level. The function ddi_intr_hilevel(9F), given an interrupt number, returns a value indicating whether the interrupt is high level.

Normal Interrupts

The only information the system has about a device interrupt is either the bus interrupt priority level (IPL, on an SBus in a SPARC machine, for example) or the interrupt request number (IRQ on an ISA bus in an IA machine, for example).

When an interrupt handler is registered, the system adds the handler to a list of potential interrupt handlers for each IPL or IRQ. Once the interrupt occurs, the system must determine which device, of all the devices associated with a given IPL or IRQ, actually interrupted. It does this by calling all the interrupt handlers for the designated IPL or IRQ, until one handler claims the interrupt.

The SBus, ISA, EISA, and PCI buses are capable of supporting polled interrupts.

Software Interrupts

The Solaris 9 DDI/DKI supports software interrupts, also known as soft interrupts. Soft interrupts are initiated by software, rather than by a hardware device. Handlers for these interrupts must also be added to and removed from the system. Soft interrupt handlers run in interrupt context and therefore can be used to do many of the tasks that belong to an interrupt handler.

Hardware interrupt handlers are supposed to perform their tasks quickly, since they may suspend other system activity while running. This is particularly true for high-level interrupt handlers, which operate at priority levels greater than that of the system scheduler. High-level interrupt handlers mask the operations of all lower-priority interrupts--including those of the system clock. Consequently, the interrupt handler must avoid involving itself in an activity (such as acquiring a mutex) that might cause it to sleep.

If the handler sleeps, then the system may hang because the clock is masked and incapable of scheduling the sleeping thread. For this reason, high-level interrupt handlers normally perform a minimum amount of work at high-priority levels and delegate remaining tasks to software interrupts, which run below the priority level of the high-level interrupt handler. Because software interrupt handlers run below the priority level of the system scheduler, they can do the work that the high-level interrupt handler was incapable of doing.

Registering Interrupts

Before a device driver can receive and service interrupts, it must register an interrupt handler with the system by calling ddi_add_intr(9F). Registering interrupts provides the system with a way to associate an interrupt handler with an interrupt specification. The interrupt handler is called when the device might have been responsible for the interrupt. The handler has the responsibility of determining if it should handle the interrupt and, if so, of claiming it.


Caution - There is a potential race condition between adding the interrupt handler and initializing mutexes. The interrupt routine is eligible to be called as soon as ddi_add_intr(9F) returns, as another device might interrupt and cause the handler to be invoked. This may result in the interrupt routine being called before any mutexes have been initialized with the returned interrupt block cookie. If the interrupt routine acquires the mutex before it has been initialized, undefined behavior may result. To ensure that this race condition does not occur, always initialize mutexes and any other data used in the interrupt handler before adding the interrupt.


To register a driver's interrupt handler, the driver usually performs the following steps in attach(9E).

  1. Test for high-level interrupts by calling ddi_intr_hilevel(9F) to find out if the interrupt specification maps to a high-level interrupt. If it does, one possibility is to post a message to that effect and return DDI_FAILURE. See Example 7-1.

  2. Get the iblock cookie by calling ddi_get_iblock_cookie(9F).

  3. Initialize any associated mutexes with the iblock cookie by calling mutex_init(9F).

  4. Register the interrupt handler by calling ddi_add_intr(9F).

Example 7-1 shows how to install an interrupt handler.


Example 7-1 attach(9E) Routine Installing an Interrupt Handler

static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
      struct xxstate *xsp;
      switch (cmd) {
      case DDI_ATTACH:
              ...
              if (ddi_intr_hilevel(dip, inumber) != 0){
                    cmn_err(CE_CONT,
                        "xx: high-level interrupts are not supported\n");
              return (DDI_FAILURE);
              }
              ddi_get_iblock_cookie(dip, inumber, &xsp->iblock_cookie);
              mutex_init(&xsp->mu, NULL, MUTEX_DRIVER,
                  (void *)xsp->iblock_cookie);
              cv_init(&xsp->cv, NULL, CV_DRIVER, NULL);
              if (ddi_add_intr(dip, inumber, NULL, NULL, xxintr,
                  (caddr_t)xsp) != DDI_SUCCESS){
                      cmn_err(CE_WARN, "xx: cannot add interrupt handler.");
                      goto failed;
              }
              return (DDI_SUCCESS);
      case DDI_RESUME:
          For information, see Chapter 9, Power Management
      default:
          return (DDI_FAILURE);
      }
failed:
     remove interrupt handler if necessary, destroy mutex and condition variable
     return (DDI_FAILURE);
}

 
 
 
  Previous   Contents   Next