Edge Triggered Interrupts

Edge-triggered interrupts are most common on the majority of today's hardware, so I consider this type first. The default handler is implemented in handle_edge_irq. The code flow diagram is shown in Figure 14-5.

Edge-triggered IRQs are not masked when they are processed — in contrast to level-triggered IRQs, there is no need to do so. This has one important implication for SMP systems: When an IRQ is handled on one CPU, another IRQ with the same number can appear on another CPU that we denote as the second CPU. This implies that the flow handler will be called once more while it is still running on the CPU that triggered the first IRQ. But why should two CPUs be engaged with running the same IRQ handler simultaneously? The kernel wants to avoid this situation: The handler should only be processed on a single CPU. The initial portion of handle_edge_irq has to deal with this case. If the IRQ_INPROGRESS flag is set, the IRQ is already being processed on another CPU. By setting the irq_pending flag, the kernel remembers that another IRQ needs to be served later. After masking the IRQ and sending an acknowledgment to the controller via mask_ack_irq, processing can be aborted. The second CPU can thus go back to work as usual, while the first CPU will handle the IRQ later.

Handle Edge Irq
Figure 14-5: Code flow diagram for handle_edge_irq.

Note that processing is also aborted if no ISR handler is available for the IRQ or if it is disabled. (Faulty hardware might nevertheless generate the IRQ, so this case needs to be taken into account by the kernel.)

Now the proper work to handle the IRQ starts. After sending an acknowledgment to the interrupt controller with the chip-specific function chip->ack, the kernel sets the irq_inprogress flag. This signals that the IRQ is being processed and can be used to avoid the same handler executing on multiple CPUs.

Let us assume that only a single IRQ needs to be processed. In this case, the high-level ISR handlers are activated by calling handle_iRQ_event, and the irq_inprogress flag can be removed afterward. However, the situation is more complicated in reality, as the source code shows:

kernel/irq/chip.c void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc) {

struct irqaction *action = desc->action; irqreturn_t action_ret;

* When another irq arrived while we were handling

* one, we could have masked the irq.

* Renable it, if it was not disabled in meantime.

if (unlikely((desc->status &

(IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) == (IRQ_PENDING | IRQ_MASKED))) { desc->chip->unmask(irq); desc->status &= ~IRQ_MASKED;

desc->status &= ~IRQ_PENDING; action_ret = handle_IRQ_event(irq, action); } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);

Processing the IRQ runs in a loop. Suppose we are at the point right beneath the call to handle_IRQ_event. While the ISR handlers for the first IRQ were running, a second IRQ could have appeared as shown before. This is indicated by irq_pending. If the flag is set (and the IRQ has not been disabled in the meantime), another IRQ is waiting to be processed, and the loop is started again from the beginning.

In this case, however, the IRQ will have been masked. The IRQ must thus be unmasked with chip->unmask and the irq_masked flag be removed. This guarantees that only one interrupt can occur during the execution of handle_IRQ_event.

After removing the irq_pending flag — technically, one IRQ is still pending right now, but it is going to be processed immediately — handle_iRQ_event can also serve the second IRQ.

Was this article helpful?

0 0

Responses

  • Bellina
    How to clear an edge interrupt in linux?
    9 months ago
  • leon
    What is edge treiggered interrupt in linux?
    5 months ago

Post a comment