Threaded interrupt handlers

Handlers registered through request_irq() are executed by the interrupt-handling path of the kernel. This code path is asynchronous, and runs by suspending scheduler preemption and hardware interrupts on the local processor, and so is referred to as a hard IRQ context. Thus, it is imperative to program the driver's interrupt handler routines to be short (do as little work as possible) and atomic (non blocking), to ensure responsiveness of the system. However, not all hardware interrupt handlers can be short and atomic: there are a magnitude of convoluted devices generating interrupt events, whose responses involve complex variable-time operations.

Conventionally, drivers are programmed to handle such complications with a split-handler design for the interrupt handler, called top half and bottom half. Top half routines are invoked in hard interrupt context, and these functions are programmed to execute interrupt critical operations, such as physical I/O on the hardware registers, and schedule the bottom half for deferred execution. Bottom half routines are usually programmed to deal with the rest of the interrupt non-critical and deferrable work, such as processing of data generated by the top half, interacting with process context, and accessing user address space. The kernel offers multiple mechanisms for scheduling and execution of bottom half routines, each with a distinct interface API and policy of execution. We'll elaborate on the design and usage details of formal bottom half mechanisms in the next section.

As an alternative to using formal bottom-half mechanisms, the kernel supports setting up interrupt handlers that can execute in a thread context, called threaded interrupt handlers. Drivers can set up threaded interrupt handlers through an alternate interface routine called request_threaded_irq():

/**
* request_threaded_irq - allocate an interrupt line
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs.
* Primary handler for threaded interrupts
* If NULL and thread_fn != NULL the default
* primary handler is installed
* @thread_fn: Function called from the irq handler thread
* If NULL, no irq thread is created
* @irqflags: Interrupt type flags
* @devname: An ascii name for the claiming device
* @dev_id: A cookie passed back to the handler function
*/
int
request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id);

The function assigned to handler serves as the primary interrupt handler that executes in a hard IRQ context. The routine assigned to thread_fn is executed in a thread context, and is scheduled to run when the primary handler returns IRQ_WAKE_THREAD. With this split handler setup, there are two possible use cases: the primary handler can be programmed to execute interrupt-critical work and defer non-critical work to the thread handler for later execution, similar to that of the bottom half. The alternative is a design that defers the entire interrupt-handling code into the thread handler and restricts the primary handler only for verification of the interrupt source and waking up thread routine. This use case might require the corresponding interrupt line to be masked until completion of the thread handler, to avoid the nesting of interrupts. This can be accomplished either by programming the primary handler to turn off the interrupt at source before waking up the thread handler or through a flag bit IRQF_ONESHOT assigned while registering the threaded interrupt handler.

The following are irqflags related to threaded interrupt handlers:

  • IRQF_ONESHOT: The interrupt is not re-enabled after the hard IRQ handler is finished. This is used by threaded interrupts that need to keep the IRQ line disabled until the threaded handler has been run.
  • IRQF_NO_THREAD: The interrupt cannot be threaded. This is used in shared IRQs to restrict the use of threaded interrupt handlers.

A call to this routine with NULL assigned to handler will cause the kernel to use the default primary handler, which simply returns IRQ_WAKE_THREAD. And a call to this function with NULL assigned to thread_fn is synonymous with request_irq():

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

Another alternate interface for setting up an interrupt handler is request_any_context_irq(). This routine has a similar signature to that of requeust_irq() but slightly varies in its functionality:

/**
* request_any_context_irq - allocate an interrupt line
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs.
* Threaded handler for threaded interrupts.
* @flags: Interrupt type flags
* @name: An ascii name for the claiming device
* @dev_id: A cookie passed back to the handler function
*
* This call allocates interrupt resources and enables the
* interrupt line and IRQ handling. It selects either a
* hardirq or threaded handling method depending on the
* context.
* On failure, it returns a negative value. On success,
* it returns either IRQC_IS_HARDIRQ or IRQC_IS_NESTED..
*/
int request_any_context_irq(unsigned int irq,irq_handler_t handler,
unsigned long flags,const char *name,void *dev_id)

This function differs from request_irq() in that it looks into the IRQ descriptor for properties of the interrupt line as set up by the architecture-specific code, and decides whether to establish the function assigned as a traditional hard IRQ handler or as a threaded interrupt handler. On success, IRQC_IS_HARDIRQ is returned if the handler was established to run in hard IRQ context, or IRQC_IS_NESTED otherwise.

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

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