• Registering
an interrupt handler
• Defined in include/linux/interrupt.h
• int request_irq( Returns
0 if successful
unsigned int irq, Requested irq channel
irq_handler_t handler, Interrupt
handler
unsigned long irq_flags, Optional
flags
const char * devname, Registered
name
void *dev_id); Pointer to
some handler data, Cannot be NULL and must be unique for shared irqs!
• void free_irq(unsigned int irq, void
*dev_id);
• dev_id cannot be NULL and must be
unique for shared irqs. Otherwise, on a shared interrupt line, free_irq
wouldn't know which handler to free
• Main irq_flags bit values (can be
combined, none is fine too)
• IRQF_SHARED
• The interrupt channel can be shared
by several devices. Requires a hardware status register telling whether an IRQ
was raised or not.
• IRQF_SAMPLE_RANDOM
• Use the IRQ arrival time to feed the
kernel random number generator.
Interrupt handler
constraints
• No guarantee in which address space
the system will be in when the interrupt occurs: can't transfer data to and
from user space
• Interrupt handler execution is
managed by the CPU, not by the scheduler. Handlers can't run actions that may
sleep, because there is nothing to resume their execution. In particular, need
to allocate memory with GFP_ATOMIC.
• Interrupt handlers are run with all
interrupts disabled (since 2.6.36). Therefore, they have to complete their job
quickly enough, to avoiding blocking interrupts for too long.
Information of
installed handlers
/proc/interrupts PANDA
Board (OMAP4 ARM)
CPU0 CPU1
39: 4
0 GIC TWL6030-PIH
41: 0 0
GIC l3-dbg-irq
42: 0 0
GIC l3-app-irq
43: 0 0
GIC prcm
44: 20294 0 GIC DMA
52: 0 0
GIC gpmc
53: 47590 0 GIC SGX ISR
57: 6 0
GIC OMAP DISPC
69: 14 0
GIC gp timer
85: 0 0
GIC omapdss_dsi1
88: 300 0
GIC omap_i2c
89: 0 0
GIC omap_i2c
91: 6909 0 G IC mmc1
93: 0 0
GIC omap_i2c
94: 0 0
GIC omap_i2c
102: 0 0 GIC
serial idle
Interrupt handler
prototype
• irqreturn_t foo_interrupt (int irq,
void *dev_id)
• Arguments
– irq, the IRQ number
– dev_id, the opaque pointer passed at
request_irq()
• Return value
– IRQ_HANDLED: recognized and handled
interrupt
– IRQ_NONE: not on a device managed by
the module. Useful to share interrupt channels and/or report spurious
interrupts to the kernel.
Interrupt handler’s job
• Acknowledge the interrupt to the
device (otherwise no more interrupts
will be generated, or the interrupt will keep firing over and over again)
• Read/write data from/to the device
• Wake up any waiting process waiting
for the completion of an operation, typically using wait queues
• wake_up_interruptible(&module_queue);
Threaded interrupts
• In 2.6.30, support for threaded
interrupts has been added to the
• Linux kernel
• The interrupt handler is executed
inside a thread.
• Allows to block during the interrupt
handler, which is often needed for I2C/SPI devices as the interrupt handler
needs to communicate with them.
• Allows to set a priority for the
interrupt handler execution, which is useful for real-time usage of Linux
• int request_threaded_irq(unsigned
int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags,
const char *name, void *dev);
– handler, “hard IRQ” handler
– thread_fn, executed in a thread
Top half and bottom
half processing
• Splitting the execution of interrupt
handlers in 2 parts
• Top half
– This is the real interrupt handler,
which should complete as quickly as possible since all interrupts are disabled.
If possible, take the data out of the device and schedule a bottom half to
handle it.
• Bottom half
• Is the general Linux name for
various mechanisms which allow to postpone the handling of interrupt-related
work.
• Implemented in Linux as softirqs,
tasklets or workqueues.
SoftIRQs
• Softirqs are a form of bottom half
processing
• The softirqs handlers are executed
with all interrupts enabled, and a given softirq handler can run simultaneously
on multiple CPUs
• They are executed once all interrupt
handlers have completed, before the kernel resumes scheduling processes, so sleeping
is
• not allowed.
• The number of softirqs is fixed in
the system, so softirqs are not directly used by drivers, but by complete
kernel subsystems (network, etc.)
• The list of softirqs is defined in
include/linux/interrupt.h:
– HI, TIMER, NET_TX, NET_RX, BLOCK,
BLOCK_IOPOLL, TASKLET, SCHED, HRTIMER, RCU
• The HI and TASKLET softirqs are used
to execute tasklets
Tasklets
• Tasklets are executed within the HI
and TASKLET softirqs.
• They are executed with all
interrupts enabled, but a given tasklet is guaranteed to execute on a single
CPU at a time.
• A tasklet can be declared statically
with the
– DECLARE_TASKLET() macro or
dynamically with the
– tasklet_init() function.
• A tasklet is simply implemented as a
function.
• Tasklets can easily be used by
individual device drivers, as opposed to softirqs.
• The interrupt handler can schedule
the execution of a tasklet With
– tasklet_schedule() to get it
executed in the TASKLET softirq
– tasklet_hi_schedule() to get it
executed in the HI softirq (higher priority)
Work queues
• Workqueues are a general mechanism
for deferring work. It is not limited in usage to handling interrupts.
• The function registered as workqueue
is executed in a thread, which means :
– All interrupts are enabled
– Sleeping is allowed
• A workqueue is registered with
INIT_WORK and typically triggered with queue_work()
• The complete API, in
include/linux/workqueue.h provides many other possibilities (creating its own
workqueue threads, etc.)
Examples
• Softirq
• Tasklet
• Work
queue
Summery
• Device driver
– When the device file is first
opened, register an interrupt handler for the device's interrupt channel.
• Interrupt handler
– Called when an interrupt is raised.
• Acknowledge the interrupt
• If needed, schedule a tasklet taking
care of handling data. Otherwise, wake up processes waiting for the data.
• Tasklet
– Process the data
– Wake up processes waiting for the
data
– Device driver
– When the device is no longer opened
by any process, unregister the interrupt handler.
No comments:
Post a Comment