Presentation is loading. Please wait.

Presentation is loading. Please wait.

Linux Kernel Programming CIS 4930/COP 5641

Similar presentations


Presentation on theme: "Linux Kernel Programming CIS 4930/COP 5641"— Presentation transcript:

1 Linux Kernel Programming CIS 4930/COP 5641
Interrupt Handling Linux Kernel Programming CIS 4930/COP 5641

2 Topics Overview Registration of handlers Interrupt sharing
Interaction with hardware Limitations of handlers Bottom halves

3 Interrupts Provides a mechanism other than busy waiting to be notified of an event E.g., Interrupt-driven I/O Hardware generates interrupts when New data arrives and is ready for retrieval Ready to accept new data or to acknowledge a successful data transfer Signal from device to notify the CPU of an event (hardware desires attention) Concurrency issues arise

4 Interrupt-Driven I/O Hardware generates interrupts when
New data arrives and is ready for retrieval Ready to accept new data or to acknowledge a successful data transfer

5 Overview of Interrupts

6 Parallel Port Interrupts with short
LDD3 illustrates interrupt handling with the short module Setting bit 4 of the parallel port HW’s control port (0x37a or 0x27a) enables interrupt reporting Once enabled, the parallel interface generates an interrupt whenever the electrical signal at pin 10 (ACK bit) changes from low to high (edge-triggered)

7 Hardware Configuration of LED Devices

8 Interrupts with short Pins 9 and 10 of the parallel connector are shorted Pin 9 is the most significant bit of the data byte Writing ASCII values to /dev/short0 will not generate any interrupts An interrupt is raised whenever the electrical signal at pin 10 (ACK bit) changes from low to high

9 Installing (Registering) an Interrupt Handler
Without an installed interrupt handler, Linux simply supplies an ack request_irq() irq number handler irqreturn_t (*irq_handler_t)(int, void *) flags name Dev void free_irq(unsigned int, void *)

10 request_irq() returns zero on success
Common error is –EBUSY, which denotes given interrupt line is already in use and not shared can sleep Calls kmalloc() Make sure device is completely set up before calling request_irq Interrupt may occur during/after called

11 Flags (include/linux/interrupt.h)
IRQF_DISABLED local_irq_enable_in_hardirq(); IRQF_SHARED IRQF_TIMER IRQF_NO_THREAD IRQF_NO_SUSPEND IRQF_SAMPLE_RANDOM RANDOM – currently applied to all interrupts and uses additional properties of the interrupt event in addition to time of interrupt like the instruction pointer at time of interrupt

12 IRQF_SHARED If interrupt lines are few (typically 16), sharing is likely necessary Each installed interrupt handler will be called dev parameter passed to request_irq cannot be NULL Allows a means to distinguish between interrupt handlers when removal is requested

13 Interrupt Sharing When an interrupt arrives, the kernel invokes every handler registered for that interrupt The handler must be able to recognize its own interrupts No probing function is available for shared handlers Most hardware designed for interrupt sharing can tell the CPU which interrupt it is using No need for explicit probing

14 Installing an Interrupt Handler
The short example if (short_irq >= 0) { result = request_irq(short_irq, short_interrupt, NULL, "short", NULL); if (result) { printk(KERN_INFO "short: can't get assigned irq %i\n", short_irq); short_irq = -1; } else { /* enable it -- assume this *is* a parallel port */ outb(0x10,short_base+2); } Notice the order of request_irq() and outb(). Reverse these and an interrupt may be missed. If a dev were used and not initialized, could result in an error.

15 The /proc Interface /proc/interrupts shows interrupts Device names
CPU0 CPU1 0: IO-APIC-edge timer 2: XT-PIC cascade 8: IO-APIC-edge rtc 10: IO-APIC-level aic7xxx 11: IO-APIC-level uhci_hcd 12: IO-APIC-edge i8042 NMI: LOC: ERR: 0 MIS: 0 Device names Linux often handles individual interrupts on the same CPU to maximize cache locality

16 Interrupt number 4 used 4907 times
The /proc Interface /proc/stat shows number of interrupts received since system boot Architecture dependent file format Look for the intr string intr Interrupt number 4 used 4907 times Total number

17 Implementing a Handler
No transferring data to and from user space Cannot sleep Cannot call schedule, wait_event Can only use GFP_ATOMIC to allocate memory Might need to clear a bit on the device Inform device subsequent interrupts should be provided Design of the OS is not to sleep because interrupts do not have “logical” backing process – does not have a task structure of its own and therefore scheduler does not have the information to wake-up if needed. Wakeup could only occur when the shared backing process were restarted. However, supervisor instructions would be required, therefore, scheduling the interrupt context would likely crash the backing process.

18 Implementing a Handler
Wakes up processes waiting for device services E.g., network card Must process packets while waiting for more packets The interrupt handler copies new networking packets into main memory and readies network card for more packets The handler needs to execute in a minimum amount of time Uses bottom half (typically tasklet or workqueue) to schedule computation later E.g. network card – to sort packets and send them to correct application

19 Implementing a Handler
The short example irqreturn_t short_interrupt(int irq, void *dev_id) { struct timeval tv; int written; do_gettimeofday(&tv); written = sprintf((char *)short_head,"%08u.%06u\n", (int)(tv.tv_sec % ), (int)(tv.tv_usec)); short_incr_bp(&short_head, written); /* bp = buffer pointer */ wake_up_interruptible(&short_queue); return IRQ_HANDLED; } Sample code in short responds to the interrupt by calling do_gettimeofday and printing the current time into a page-sized circular buffer. It then awakens any reading process, because there is now data available to be read.

20 Implementing a Handler
Variable can be accessed externally at any time static inline void short_incr_bp(volatile unsigned long *index, int delta) { unsigned long new = *index + delta; barrier(); /* Don't optimize these two together */ *index = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new; } Without barrier… *index = *index + delta; /* could expose an incorrect value */ if (*index >= (short_buffer + PAGE_SIZE)) *index = short_buffer; The barrier call is there to block compiler optimizations across the other two lines of the function. Without the barrier, the compiler might decide to optimize out the new variable and assign directly to *index. That optimization could expose an incorrect value of the index for a brief period in the case where it wraps. By taking care to prevent in inconsistent value from ever being visible to other threads, we can manipulate the circular buffer pointers safely without locks.

21 Implementing a Handler
To read the buffer, use /dev/shortint ssize_t short_i_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int count0; DEFINE_WAIT(wait); while (short_head == short_tail) { prepare_to_wait(&short_queue, &wait, TASK_INTERRUPTIBLE); if (short_head == short_tail) { schedule(); } finish_wait(&short_queue, &wait); if (signal_pending(current)) /* a signal arrived */ return -ERESTARTSYS; /* tell the fs layer to handle it */

22 Implementing a Handler
/* count0 is the number of readable data bytes */ count0 = short_head - short_tail; if (count0 < 0) {/* wrapped */ count0 = short_buffer + PAGE_SIZE - short_tail; } if (count0 < count) { count = count0; if (copy_to_user(buf, (char *)short_tail, count)) { return -EFAULT; short_incr_bp(&short_tail, count); /* wrap the tail pointer */ return count;

23 Handler Arguments and Return Value
Typical use of the argument in an interrupt handler static irqreturn_t sample_interrupt(int irq, void *dev_id) { struct sample_dev *dev = dev_id; /* now `dev' points to the right hardware item */ /* .... */ } irq: for printk dev_id: for finding out which instance of device is in charge of the current interrupt event

24 Handler Arguments and Return Value
Returns IRQ_HANDLED if from drivers device; otherwise, returns IRQ_NONE Kernel can detect “spurious interrupts” if all interrupts on line return IRQ_NONE

25 Check the most significant bit
Handler return Value In the short example, use shared=1 to install a shared interrupted handler irqreturn_t short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int value, written; struct timeval tv; /* If it wasn't short, return immediately */ value = inb(short_base); if (!(value & 0x80)) return IRQ_NONE; /* clear the interrupting bit */ outb(value & 0x7F, short_base); Since the parallel port has no “interrupt-pending” bit to check, the handler uses the ACK bit for this purpose. If the bit is high, the interrupt being reported is for short, and the handler clears the bit. The handler resets the bit by zeroing the high bit of the parallel interface’s data port Check the most significant bit

26 Enabling and Disabling Interrupts
Interfaces for manipulating state of interrupts Disable interrupt system for current processor Mask out interrupt line for entire machine <asm/system.h> and <asm/irq.h> Why? Synchronization Common scenario Obtain lock to prevent another processor from accessing shared data Disabling interrupts provides protection against concurrent access from a possible interrupt handler

27 Enabling and Disabling Interrupts
For current processor only local_irq_disable() disables interrupts Dangerous to call if interrupts were already disabled prior to invocation local_irq_enable() enables interrupts Unconditionally enables interrupts

28 Enabling and Disabling Interrupts
Sometimes a mechanism is needed to restore interrupts to a previous state E.g. common code path can be reached both with and without interrupts enabled Since kernel is complex, much safer to restore to previous state using flags local_irq_save(flags) local_irq_restore(flags) Calls with flags must be used in the same function, because at least one supported architecture incorporates stack information into the value.

29 Disabling a Single Interrupt
Can disable (mask out) a specific interrupt line for an entire system E.g. disable delivery of a device’s interrupts before manipulating its state void disable_irq(int irq); void disable_irq_nosync(int irq); void enable_irq(int irq);

30 Disabling a Single Interrupt
Calls can be nested If disable_irq is called twice, two enable_irq calls are required to reenable the IRQ The calling thread of the disable_irq should not hold resource needed by the target interrupt handler to disable Returns only when any currently executing handlers complete disable_irq_nosync returns immediately Need to handle potential race conditions

31 Checking Interrupt Status
Macro irqs_disabled() returns nonzero if the interrupt system on the local processor is disabled Checking current context in_interrupt() Returns nonzero if in interrupt handler or bottom half in_irq() Returns nonzero only if in interrupt handler

32 Top and Bottom Halves Interrupt handling sometimes needs to perform lengthy tasks This problem is resolved by splitting the interrupt handler into two halves Top half responds to the interrupt The one registered to request_irq Saves data to device-specific buffer and schedules the bottom half Bottom half is scheduled by the top half to execute later With all interrupts enabled Wakes up processes, starts I/O operations, etc.

33 Top and Bottom Halves Two mechanisms may be used to implement bottom halves Tasklets No sleep Workqueues Can sleep short module can be loaded to use either tasklet or workqueue Actually a third – softirq. Discussed in LKD Ch 8.

34 Tasklets Cannot run in parallel with itself
Can run in parallel with other tasklets on SMP systems Guaranteed to run on the same CPU that first scheduled them

35 Tasklets In the short example, use tasklet=1 to install the tasklet-based interrupt handler void short_do_tasklet(unsigned long); DECLARE_TASKLET(short_tasklet, short_do_tasklet, 0); irqreturn_t short_tl_interrupt(int irq, void *dev_id) { /* cast to stop 'volatile' warning */ do_gettimeofday((struct timeval *) tv_head); short_incr_tv(&tv_head); tasklet_schedule(&short_tasklet); short_wq_count++; /* record that an interrupt arrived */ return IRQ_HANDLED; }

36 Workqueues Invoke a function at some future time in the context of a special worker process Can sleep Generally do not copy data to and from user space


Download ppt "Linux Kernel Programming CIS 4930/COP 5641"

Similar presentations


Ads by Google