Intr
Intr
Description
The Kernel Contexts There are two contexts (patterns of execution flow) in the Linux kernel: interrupt and user(space) contexts. User contexts are code which is entered from userspace: a system call. Unless the kernel code sleeps for some reason (explicitly allowing other code to run), no other user context will run on that CPU; this is the non-preemptive part. They are always associated with a particular process. However, an interrupt can occur at any time, which halts the user context in its tracks and runs an interrupt context. This is not associated with any process; it is caused by a timer, an external hardware interrupt, or a bottom-half (bottom halves may be run off the timer or other interrupts). When it is finished, the user context will resume.
Kernel Threads Even though Linux is a monolithic OS, a few ''kernel threads'' exist to do housekeeping work. These Tasks don't utilize USER memory; they share KERNEL memory.
A list of most common kernel threads (from ''ps x'' command): PID COMMAND 1 init 2 keventd 3 kswapd 4 kreclaimd 5 bdflush 6 kupdated 7 kacpid 67 khubd 'init' kernel thread is the first process created, at boot time. It will call all other User Mode Tasks (from file /etc/inittab) like console daemons,
Interrupt Handling
Hardware Software (Interrupt Handler)
Device 1
Device 2
IRQn
IDT[32+n}
IRQn_interrupt()
PIC
An example of IRQ assignment to I/O Devices IRQ 0 1 2 3 4 6 7 8 11 12 13 14 15 INT 32 33 34 35 36 38 39 40 43 44 45 46 47 Hardware Device Timer Keyboard PIC cascading Second serial port First serial port Floppy disk Parallel port System clock Network interface PS/2 mouse Maths coprocessor I IDE Controller First IDE Controller Second
Interrupt Handling An interrupt is simply a signal that the hardware can send when it wants the processors attention. A module is expected to request an interrupt channel before using it, and to release it when its done.
Interrupt handler can be installed either at driver initialization or when the device is first opened. IMPORTANT FILES: /proc/ interrupts
Freeing an interrupt handler When your driver unloads, you need to unregister your interrupt handler and disable the interrupt line. To do this call void free_irq(unsigned int irq, void *dev_id);
If the specified interrupt line is not shared, this function removes the handler and disables the line.
Implementing a Handler
Its ordinary C code with some restrictions . Cant transfer data to or from user space, because it does not execute in the context of process. Cannot do anything that would sleep . Cannot allocate memory with anything other than GFP_ATOMIC . Cannot lock a semaphore Cannot call schedule . Give feedback to device about interrupt reception ( by clearing a bit or byte on the interface board Interrupt Pending Write a routine that executes in a minimum of time. If a long computation needs to be performed, use a tasklet.
Interrupt handling #include <linux/interrupt.h> In interrupt handler: If interrupt for someone else return IRQ_NONE otherwise return IRQ_HANDLED
Control of Interrupts The use of functions cli and sti to disable and enable interrupts has been used. To disable interrupts, it is better to use the following calls: unsigned long flags; save_flags(flags); cli(); /* this code runs with interrupts disabled */ restore_flags(flags); The macros save_flags() and restore_flags() must be called from the same function.
Bottom Half Processing An interrupt handler should execute in a minimum of time and not to keep interrupts blocked for long. Often a substantial amount of work must be done in response to device interrupt . This can be accomplished by splitting the interrupt handler into two halves . Top Half : is the routine that actually responds to the interrupt . Bottom Half : is a routine that is scheduled by the top half to be executed later, at a safer time . Difference : All interrupts are enabled during execution of bottom half - thats why it runs at a Safer time . Restrictions : All of the restrictions that apply to interrupt handlers also apply to bottomhalfs .
blah(); blah(); Interrupt do_sth(); queue_task(task, tq); do_sth_else(); return; Return from Interrupt
blah(); blah();
As soon as possible at a safe time run_task_queue(tq_immediate); do_the_task() return; blah(); blah();
Bottom half Status Currently there are three methods for deferring work: softirq, tasklet and work queues. Tasklets are built on softirqs and work queues are entirely different. Bottom half -------------BH Task Queues Softirq Tasklet Work queue status ------Removed in 2.5 Removed in 2.5 Available since 2.3 Available since 2.3 Available since 2.5
Softirqs
Softirqs are rarely used, tasklets are much more common form of bottom half. Because tasklets are built on softirqs, it is required to be studied. The softirq code lives in kernel/softirq.c.
Implementation of Softirqs
Softirqs are statically allocated at compile time. Unlike tasklets you can not dynamically register and destroy softirqs. Softirqs are represented by the softirq_action structure, which is defined in <linux/interrupt.h> struct softirq_action { void (*action(struct softirq_action*); /* function to run */ void *data; /* data to pass to function */ } A 32-entry array of this structure is declared in kernel/softirq.c static struct softirq_action softirq_vec[32]; Each registered softirq consumes one entry in the array. So there can be maximum of 32 registered softirqs.
Using Softirqs Softirqs are reserved for the most timing-critical and important bottom half processing on the system. Currently, only two subsystems networking and SCSI directly use softirqs. Additionally, kernel timers and tasklets are built on top of softirqs.
Assigning an Index You declare softirqs statically at compile time via an enum in <linux/interrupt.h>. The kernel uses this index as relative priority. Tasklet priority HI_SOFTIRQ 0 TIMER_SOFTIRQ 1 NET_TX_SOFTIRQ 2 NET_RX_SOFTIRQ 3 SCSI_SOFTIRQ 4 TASKLET_SOFTIRQ 5 Softirq Description high priority tasklets timer bottom half send network packets receive network packets SCSI bottom half Tasklets
Registering Your Handler The softirq handler is assigned at run-time via open_softirq(), which takes three parameters: the softirqs index, its handler and a value for the data field. open_softirq(NET_TX_SOFTIRQ, netx_tx_action, NULL); The softirq handlers runs with interrupts enabled and cannot sleep.
Raising Your Softirq After a handler is added to the enum list and registered via open_softirq(), it is ready to run. To mark it pending, so that it is run at the next invocation of do_softirq(), call raise_softirq(). For example, the networking subsystem would call raise_softirq(NET_TX_SOFTIRQ);
Tasklets
A tasklet is a special function that may be scheduled to run , in interrupt context at a system defined safe time . They may be scheduled to run multiple times , but will only run once . No tasklet will ever run in parallel with itself , since they only run once . Tasklet can run in parallel with other tasklets on SMP systems . They are guaranteed to run on the CPU that first schedules them .
Linux Support
DECLARE_TASKLET ( name , function , data ) ; DECLARE_TASKLET_DISABLED ( name, function, data); It can be scheduled , but will not be executed until enabled at some future time .
tasklet_schedule ( name ) ; void tasklet_disable ( struct tasklet_struct &t ) ; void tasklet_enable ( struct tasklet_struct &t ) ; void tasklet_kill ( struct tasklet_struct &t ) ;
Work Queues
Work queues are different form of deferring work. Work queues defer work into a kernel thread this bottom half always runs in a process context. Work queues are schedulable and can therefore sleep. It is easy to decide between using work queue and softirqs/tasklets. If the deferred work needs to sleep, work queues are used.
Work Queue Two mechanism to implement bottom half processing: tasklets and workqueue. Tasklets are very fast, but all tasklet code must be atomic. Workqueue higher latency, but allowed to sleep.
static struct work_struct short_wq; INIT_WORK(&short_wq, (void (*) (void *))short_do_tasklet, NULL); /* in interrupt handler */ schedule_work(&short_wq);
Workqueue have a type of struct workqueue_struct defined in <linux/workqueue.h>