1 / 36

Interrupt Handling

Interrupt Handling. Linux Kernel Programming CIS 4930/COP 5641. Topics. Overview Registration of handlers Interrupt sharing Interaction with hardware Limitations of handlers Bottom halves. Interrupts. Provides a mechanism other than busy waiting to be notified of an event

Download Presentation

Interrupt Handling

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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(); • http://lwn.net/Articles/380931/ • IRQF_SHARED • IRQF_TIMER • IRQF_NO_THREAD • IRQF_NO_SUSPEND • IRQF_SAMPLE_RANDOM • http://lwn.net/Articles/507115/ • …

  12. IRQF_SHARED • If interrupt lines are few (typically 16), sharing is likely necessary • Each installed interrupt handler will be called • devparameter 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); } }

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

  16. The /proc Interface • /proc/stat shows number of interrupts received since system boot • 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 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

  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_tshort_interrupt(intirq, void *dev_id) { structtimevaltv; int written; do_gettimeofday(&tv); written = sprintf((char *)short_head,"%08u.%06u\n", (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec)); short_incr_bp(&short_head, written); /* bp = buffer pointer */ wake_up_interruptible(&short_queue); return IRQ_HANDLED; }

  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… 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; }

  21. Implementing a Handler • To read the buffer, use /dev/shortint ssize_tshort_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. Handler return Value • In the short example, use shared=1 to install a shared interrupted handler irqreturn_tshort_sh_interrupt(intirq, void *dev_id, structpt_regs *regs) { int value, written; structtimevaltv; /* 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); 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)

  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(intirq); void disable_irq_nosync(intirq); void enable_irq(intirq);

  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

  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

More Related