| Linux Kernel & Device Driver Programming↑ |
Independent mechanisms exist at several levels:
Terminology varies across different operating systems, machine architectures, and textbooks. The terms "enabled/disable", "block/unblock", "mask/unmask" are used for all the levels. You need to be aware of the specific context to intepret what is being said.
Be carefull when programming that you know the level at which you are working, and use the right mechanism.
local_irq_save(flags) local_irq_restore(flags);
You may still find calls to cli, which is deprecated. If you must disable interrupts, use local_irq_save.
int request_irq (unsigned int irq,
irqreturn_t (*handler)(int, void *, struct pt_regs ();
unsigned long flags,
const char *dev_name,
void *dev_id);
void free_irq (unsigned int irq, void *dev_id);
You should be aware that Linux provides quite a few layers between the device driver and the actual hardware interrupt handling machinery. That is good, for the writer of a device driver. It is not o good from the point of view of really understanding what is going on in the hardware.
To get at lower levels, you might look at the following files:
Observe that Linux provides a standard wrapper called common_interrupt in entry.S, which is the handler for several interrupts (the ones that don't require special treatment).
The macro SAVE_ALL is used to save state.
The restoration of state and the return from the interrupt ("iret") instruction are done via the RESTORE_ALL macro.
The jump to common_interrupt comes from the actual list of interrupt handler pointers, which start at irq_entries_start.
This common handler saves the hardware state and then calls do_irq (in arch/i386/irq.c), which in turn (indirectly) calls handle_IRQ_event. The latter finally loops through all the device-driver supplied interrupt handlers, calling each of the handlers that has been registered for the IRQ.
While you are looking at entry.S is a good time to look at sys_call_table, the list of kernel entry points for system calls.
/proc/interrupts (not architecture-dependent)
CPU0 CPU1 CPU2 CPU3 0: 1586341347 1585528157 1777830968 1777774150 IO-APIC-edge timer 1: 8 206 226 122 IO-APIC-edge i8042 8: 1 0 0 0 IO-APIC-edge rtc 9: 0 0 0 0 IO-APIC-level acpi 14: 5884978 15312126 16861986 11370803 IO-APIC-edge ide0 15: 11570235 9788762 11114601 16580399 IO-APIC-edge ide1 169: 0 0 0 0 IO-APIC-level uhci_hcd 177: 0 0 0 0 IO-APIC-level uhci_hcd 185: 0 0 0 0 IO-APIC-level uhci_hcd 201: 1141842659 0 0 0 IO-APIC-level eth0 NMI: 0 0 0 0 LOC: 2433069657 2433069656 2433069655 2433069654 ERR: 0 MIS: 0
The above gives you the number of interrupts handled so far, the kind of interrupt controller, and the current binding, just of IRQs that are currently allocated.
/proc/stat (architecture-dependent)
cpu 1233025445 63381 248114678 1196507856 11289489 541966 1281297 cpu0 254094243 19779 52695947 358138543 6237229 338999 1181299 cpu1 309512428 17953 56288118 304523505 2266333 60294 37391 cpu2 291520890 13109 71285374 308131585 1653202 69933 31928 cpu3 377897883 12538 67845237 225714222 1132723 72739 30678 intr 7967851846 2432553280 562 0 2 2 0 0 0 1 0 0 0 0 0 49430146 49054250 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1141846307 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ctxt 2796936863 btime 1110394705 processes 3104956 procs_running 2 procs_blocked 0
See example short_interrupt and others in short.c.
disable_irq_nosync calls the disable method of a specific IRQ's descriptor:
desc->handler->disable(irq)
These descriptors are in the array irq_desc[].
The disable method depends on the specific interrupt controller device. For example, for the i8259A it is set in make_8259A_irq to a values stored in i8259A_irq_type, which is a reference to the function disable_8259A_irq. This actually disables the IRQ at the interrupt controller (PIC) if that is possible.
The execution of the driver-level handler is also disabled in software. See the code of disable_irq_nosync via the link above, which ORs IRQ_DISABLED into the IRQ descriptor's status, and the corresponding test of IRQ_DISABLED in _do_irq.
Probing is necessary for some drivers. A driver needs some way to find out whether the device supports interrupts, and which IRQ(s) it is using. If the device is not capable of telling the driver what IRQ it wants to use, or letting the driver tell it what IRQ to use, in some reliably way, the driver needs to probe.
Note: There was an old "bottom half" mechanism that became deprecated in favor of tasklets by kernel 2.4, and in kernel 2.6 no longer exists. The term remains as an abstraction, meant to cover tasklets and work queues.
See example of tasklet scheduled from a handler in short_tl_interrupt.
To see a part of how tasklet scheduling is done, take a look at the definitions of tasklet_schedule (in include/linux/interrupt.h) and __tasklet_schedule (in kernel/softirq.c).
To see one place where tasklets are scheduled, look at the call to __do_softirq (declared in kernel/softirq.c) in do_softirq (declared in kernel/irq.c).
See example of a handler for a shared IRQ in short.c.
The parallel printer protocol does not allow for sharing, but the toy parallel device (LEDs, plus jumper between pins 9 and 10) does.
| © 2003, 2004, 2005 T. P. Baker. ($Id: ch10.html,v 1.1 2010/06/07 14:29:15 baker Exp baker $) |