Linux Device Drivers

Linux Device Drivers


Linux
Modules
Character drivers
IO & Memory
Linux Kernel
Process Management
Process Address space

Linux Scheduler
Memory Management
Interrupts
Signals
System Calls
Kernel Synchronization
Linux Inter Process Communications




Serial Ports
Parallel Ports
Introduction to Hardware
Linux Timers
DMA in Linux
Linux Threads
Linux Thread Synchronization

Linux Multi Threading
Debugging in Linux
GDB GNU Debugger
KDB Kernel Debugger
KGDB Kernel GNU Debugger
Example Ethernet Driver




Interrupt Handling

Interrupt Handling

 

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 anytime, 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.

ü     They also operate at the highest privilege (RING 0 on a i386 architecture) like any other kernel mode piece of code.

ü     Kernel threads are created by ''kernel_thread[arch/i386/kernel/process]'function, which calls ''clone'' [arch/i386/kernel/process.c] system callfrom assembler (which is a ''fork'' like system call):

 

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, tty daemons and network daemons (''rc'' scripts).

 

A very slow resource is used because we would have a task switching overhead otherwise.

 

 

 

I RQ

INT

Hardware Device

0

32

Timer

1

33

Keyboard

2

34

PIC cascading

3

35

Second serial port

4

36

First serial port

6

38

Floppy disk

7

39

Parallel port

8

40

System clock

11

43

Network interface

12

44

PS/2 mouse

13

45

Maths coprocessor I

14

46

IDE Controller First

15

47

IDE Controller Second

 

 

Interrupt Handling

 

ü     An interrupt is simply a signal that the hardware can send when it wants the processor's attention.

ü     A module is expected to request an interrupt channel before using it, and to release it when its done.

 

 

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 intirq, void *dev_id);

 

If the specified interrupt line is not shared, this function removes the handler and disables the line.

 

Fast & Slow Handlers

 

Fast interrupts are those that could be handled very quickly,

Whereas handling slow interrupts take significantly longer.

 

ü     A Fast handler runs with interrupt reporting disabled in the microprocessor , and the interrupt being serviced is disabled in the interrupt controller.

ü     A slow handler runs with interrupt reporting enabled in the processor, and the interrupt being serviced is disabled in the interrupt controller.

ü     Fast handlers should not be used generally.

 

Implementing a Handler

 

ü     Its ordinary C code with some restrictions

§       Can’t 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 –that’s why it runs at a “ Safer time”

§       Restrictions : All of the restrictions that apply to interrupt handlers also apply to bottom-halfs

 

 

Bottom half Status

 

Currently there are three methods for deferring work: softirq, tasklet and work queues. Taskletsare built on softirqsand work queues are entirely different.

 

Bottom half                                                    Status

 

BH                                                                   Removed in 2.5

Task Queues                                                   Removed in 2.5

Softirq                                                             Available since 2.3

Tasklet                                                                        Available since 2.3

Work queue                                                    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>

 

structsoftirq_action{

void (*action(structsoftirq_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 taskletsare 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                        Softirq Description

 

HI_SOFTIRQ                          0                      high priority tasklets

TIMER_SOFTIRQ                  1                      timer bottom half

NET_TX_SOFTIRQ               2                      send network packets

NET_RX_SOFTIRQ               3                      receive network packets

SCSI_SOFTIRQ                      4                      SCSI bottom half

TASKLET_SOFTIRQ 5                      Tasklets

 

 

 

Registering Your Handler

 

The softirq handler is assigned at run-time via open_softirq(), which takes three parameters: the softirq.sindex, 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 enumlist 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 mechanisms to implement bottom half processing:

tasklets            &

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>