Interrupt-Driven Block Drivers

When a driver controls a real hardware device, operation is usually interrupt-driven. Using interrupts helps system performance by releasing the processor during I/O operations. In order for interrupt-driven I/O to work, the device being controlled must be able to transfer data asynchronously and to generate interrupts.

When the driver is interrupt-driven, the request function spawns a data transfer and returns immediately without calling end_request. However, the kernel doesn’t consider a request fulfilled unless end_request has been called. Therefore, the top-half or the bottom-half interrupt handler calls end_request when the device signals that the data transfer is complete.

Neither sbull nor spull can transfer data without using the system microprocessor; however, spull is equipped with the capability of faking interrupt-driven operation by specifying the irq=1 option at load time. When irq is not zero, the driver uses a kernel timer to delay fulfillment of the current request. The length of the delay is the value of irq: the greater the value, the longer the delay.

The request function for an interrupt-driven device instructs the hardware to perform the transfer and then return. The spull function performs the usual error checks and then calls memcpy to transfer the data (this is the task that a real driver performs asynchronously). It then delays acknowledgment until interrupt time:

void spull_irqdriven_request(void)
{
    Spull_Dev *device;
    u8 *ptr;
    int size, minor, devnr;
    /*
     * Check for errors and start data transfer for the current request.
     * The spull ramdisk performs the transfer right ahead,
     * but delays acknolegment using a kernel timer.
     */
    while(1) {
        INIT_REQUEST;

        devnr = DEVICE_NR(CURRENT_DEV);
        minor = MINOR(CURRENT_DEV);

        /* then the core of the function is unchanged.... */


        /* ... and this is how the function completes: xfer now ... */
        switch(CURRENT->cmd) {
          case READ:
            memcpy(CURRENT->buffer, ptr, size);
            break;
          case WRITE:
            memcpy(ptr, CURRENT->buffer, size);
            break;
          default: /* should't happen */
            end_request(0);
            continue;
        }

        /* ... and wait for the timer to expire--no end_request(1) */
        spull_timer.expires = jiffies + spull_irq;
        add_timer(&spull_timer);
        return;
    }
}

New requests can accumulate while the device is dealing with the current one, but the kernel doesn’t call the request function for the driver if it is already dealing with a request. That’s why the function just shown doesn’t check for double invocation.

It’s the responsibility of the interrupt handler to set up for the next data transfer after the last one is complete. To avoid code duplication, the handler usually calls the request function, which should therefore be able to run at interrupt time (see Section 6.4.1 in Chapter 6).

In our sample module, the role of the interrupt handler is performed by the function invoked when the timer expires. That function calls end_request and schedules the next data transfer by calling the request function.

/* this is invoked when the timer expires */
void spull_interrupt(unsigned long unused)
{
     /*
      * arg to end_request(), default to success, a real device might
      * signal a failure, if it detects one
      */
    int fulfilled = 1;

    end_request(fulfilled);    /* done one */

    if (CURRENT) /* more of them? */
        spull_irqdriven_request();  /* schedule the next */
}

Note that this interrupt handler calls the request function to schedule the next operation. This means that in this case the request function must be able to run at interrupt time.

If you try to run the interrupt-driven flavor of the spull module, you’ll barely notice the added delay. The device is almost as fast as it was before because the buffer cache avoids most data transfers between memory and the physical device. If you want to perceive how a slow device behaves, you can specify a bigger value for irq= when loading spull.

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

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