Interrupt Handling Linux Kernel Programming CIS 4930COP 5641

  • Slides: 36
Download presentation
Interrupt Handling Linux Kernel Programming CIS 4930/COP 5641

Interrupt Handling Linux Kernel Programming CIS 4930/COP 5641

Topics n n n Overview Registration of handlers Interrupt sharing Interaction with hardware Limitations

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

Interrupts n Provides a mechanism other than busy waiting to be notified of an

Interrupts n Provides a mechanism other than busy waiting to be notified of an event q E. g. , Interrupt-driven I/O n Hardware generates interrupts when q q n n 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

Interrupt-Driven I/O n Hardware generates interrupts when q q New data arrives and is

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

Overview of Interrupts

Overview of Interrupts

Parallel Port Interrupts with short n LDD 3 illustrates interrupt handling with the short

Parallel Port Interrupts with short n LDD 3 illustrates interrupt handling with the short module n Setting bit 4 of the parallel port HW’s control port (0 x 37 a or 0 x 27 a) 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) n

Hardware Configuration of LED Devices

Hardware Configuration of LED Devices

Interrupts with short n n Pins 9 and 10 of the parallel connector are

Interrupts with short n n 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/short 0 will not generate any interrupts An interrupt is raised whenever the electrical signal at pin 10 (ACK bit) changes from low to high

Installing (Registering) an Interrupt Handler n n Without an installed interrupt handler, Linux simply

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

request_irq() n n n returns zero on success Common error is –EBUSY, which denotes

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

Flags (include/linux/interrupt. h) n IRQF_DISABLED q q n n n IRQF_SHARED IRQF_TIMER IRQF_NO_THREAD IRQF_NO_SUSPEND

Flags (include/linux/interrupt. h) n IRQF_DISABLED q q n n n IRQF_SHARED IRQF_TIMER IRQF_NO_THREAD IRQF_NO_SUSPEND IRQF_SAMPLE_RANDOM q n local_irq_enable_in_hardirq(); http: //lwn. net/Articles/380931/ http: //lwn. net/Articles/507115/ …

IRQF_SHARED n n n If interrupt lines are few (typically 16), sharing is likely

IRQF_SHARED n n n 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 q Allows a means to distinguish between interrupt handlers when removal is requested

Interrupt Sharing n When an interrupt arrives, the kernel invokes every handler registered for

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

Installing an Interrupt Handler n The short example if (short_irq >= 0) { result

Installing an Interrupt Handler n 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 %in", short_irq); short_irq = -1; } else { /* enable it -- assume this *is* a parallel port */ outb(0 x 10, short_base+2); } }

The /proc Interface n /proc/interrupts shows interrupts 0: 2: 8: 10: 11: 12: NMI:

The /proc Interface n /proc/interrupts shows interrupts 0: 2: 8: 10: 11: 12: NMI: LOC: ERR: MIS: CPU 0 4848108 0 3 4335 8903 49 0 4848187 0 0 CPU 1 34 IO-APIC-edge 0 XT-PIC 1 IO-APIC-edge 1 IO-APIC-level 0 IO-APIC-level 1 IO-APIC-edge 0 4848186 timer cascade rtc aic 7 xxx uhci_hcd i 8042 Device names Linux often handles individual interrupts on the same CPU to maximize cache locality

The /proc Interface n /proc/stat shows number of interrupts received since system boot q

The /proc Interface n /proc/stat shows number of interrupts received since system boot q q Architecture dependent file format Look for the intr string intr 5167833 5154006 2 0 2 4907 0 2 68 4 0 4406 9291 50 0 0 Total number Interrupt number 4 used 4907 times

Implementing a Handler n n No transferring data to and from user space Cannot

Implementing a Handler n n No transferring data to and from user space Cannot sleep q q n Cannot call schedule, wait_event Can only use GFP_ATOMIC to allocate memory Might need to clear a bit on the device q Inform device subsequent interrupts should be provided

Implementing a Handler n Wakes up processes waiting for device services q E. g.

Implementing a Handler n Wakes up processes waiting for device services q E. g. , network card n n n 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 q Uses bottom half (typically tasklet or workqueue) to schedule computation later n E. g. network card – to sort packets and send them to correct application

Implementing a Handler n The short example irqreturn_t short_interrupt(int irq, void *dev_id) { struct

Implementing a Handler n 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, "%08 u. %06 un", (int)(tv. tv_sec % 10000), (int)(tv. tv_usec)); short_incr_bp(&short_head, written); /* bp = buffer pointer */ wake_up_interruptible(&short_queue); return IRQ_HANDLED; }

Variable can be Implementing a Handleraccessed externally at any time static inline void short_incr_bp(volatile

Variable can be Implementing a Handleraccessed 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; } n Without barrier… static inline void short_incr_bp(volatile unsigned long *index, int delta) { *index = *index + delta; /* could expose an incorrect value */ if (*index >= (short_buffer + PAGE_SIZE)) *index = short_buffer; }

Implementing a Handler n To read the buffer, use /dev/shortint ssize_t short_i_read(struct file *filp,

Implementing a Handler n 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 count 0; 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 */ }

Implementing a Handler /* count 0 is the number of readable data bytes */

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

Handler Arguments and Return Value n Typical use of the argument in an interrupt

Handler Arguments and Return Value n 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 */ /*. . */ } q q irq: for printk dev_id: for finding out which instance of device is in charge of the current interrupt event

Handler Arguments and Return Value n Returns IRQ_HANDLED if from drivers device; otherwise, returns

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

Handler return Value n In the short example, use shared=1 to install a shared

Handler return Value n 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); Check the most if (!(value & 0 x 80)) significant bit return IRQ_NONE; /* clear the interrupting bit */ outb(value & 0 x 7 F, short_base);

Enabling and Disabling Interrupts n Interfaces for manipulating state of interrupts q q q

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

Enabling and Disabling Interrupts n For current processor only q local_irq_disable() disables interrupts n

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

Enabling and Disabling Interrupts n Sometimes a mechanism is needed to restore interrupts to

Enabling and Disabling Interrupts n Sometimes a mechanism is needed to restore interrupts to a previous state q n 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 q q local_irq_save(flags) local_irq_restore(flags)

Disabling a Single Interrupt n Can disable (mask out) a specific interrupt line for

Disabling a Single Interrupt n Can disable (mask out) a specific interrupt line for an entire system q 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);

Disabling a Single Interrupt n Calls can be nested q n The calling thread

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

Checking Interrupt Status n n Macro irqs_disabled() returns nonzero if the interrupt system on

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

Top and Bottom Halves n Interrupt handling sometimes needs to perform lengthy tasks q

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

Top and Bottom Halves n Two mechanisms may be used to implement bottom halves

Top and Bottom Halves n Two mechanisms may be used to implement bottom halves q Tasklets n q Workqueues n n No sleep Can sleep short module can be loaded to use either tasklet or workqueue

Tasklets n n n Cannot run in parallel with itself Can run in parallel

Tasklets n n n 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

Tasklets n In the short example, use tasklet=1 to install the tasklet-based interrupt handler

Tasklets n 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; }

Workqueues n n n Invoke a function at some future time in the context

Workqueues n n n 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