Deferred Interrupts
Interrupts must be handled quickly, and as such the interrupt handlers must be executed quickly as well.
Some interrupt handlers have to do large amounts of work, or have long processes to finish. This does not allow the interrupt handler to finish executing quickly.
One way to tackle this problem is by deferring the interrupt to execute at a time where the CPU is less loaded.
Top and Bottom Halves
The interrupt handlers can be split into top and bottom halves.
The top halves are the quick initialization of the interrupt handler, while the bottom half does all the heavy work.
In a typical scenario it runs like this:
- The top half saves device data to a device-specific buffer
- The bottom half is scheduled (but not ran yet)
- The top half exits
This operation by the top half is very fast. The bottom half then performs whatever other work is required.
This setup permits the top half to service a new interrupt while the bottom half is still working.
The main difference between the top half and the bottom half is that all interrupts are enabled in the bottom half.
Another example would be that of networking:
When a network interface reports the arrival of a new packet, the handler (Top half) just retrieves the data and pushes it up to the protocol layer (Bottom half). The actual processing of the packet is performed in a bottom half.
Softirqs and tasklets replaced bottom halves, because bottom halves are bottle neck on SMP systems. If a bottom half was running on one CPU no other bottom halves could run on any other CPU.
There are three different ways to implement deferred interrupts:
- Softirqs
- Tasklets
- Workqueues
Tasklets are implemented on top of Softirqs
Softirqs
Whenever a system call is about to return to userspace, or a hardware interrupt handler exits, any 'software interrupts' which are marked pending are run
Softirqs were originally designed as a vector of 32 softirq entries supporting a variety of software interrupt behaviors.
Today, only nine vectors are used for softirqs, one being the TASKLET_SOFTIRQ
Softirqs are statically allocated (during compile time)
Softirqs can run concurrently on several CPUs even if they are of the same type. Therefore softirqs must have their data integrity protected by spinlocks
Softirqs are often a pain to deal with, since the same softirq will run simultaneously on more than one CPU. For this reason, tasklets are more often used: they are dynamically-registrable and they also guarantee that any tasklet will only run on one CPU at any time, although different tasklets can run simultaneously.
Tasklets
Tasklets are a deferral scheme to schedule for a registered function to run later. The top half (the interrupt handler) performs a small amount of work, and then schedules the tasklet to execute later at the bottom half.
Tasklets are allocated and initialized at runtime (i.e. when loading a kernel module)
Tasklets of the same type cannot be executed by two CPUs at the same time
Tasklets of different types can execute concurrently on several CPUs
Tasklets are declared with
DECLARE_TASKLET(name, function, data);
and scheduled with
tasklet_schedule(&short_tasklet);
Workqueues
Rather than providing a one-shot deferral scheme as is the case with tasklets, workqueues are a generic deferral mechanism in which the handler function for the work queue can sleep (not possible in the tasklet model)
Tasklets run atomically in a run-to-complete fashion, where workqueues permit handlers to sleep, if necessary.