Linux interrupt (Interrupt) subsystem 3: Central cut-off control Processing Layer

Source: Internet
Author: User
1. Introduction to the central cut-off control layer

In earlier kernel versions, almost all interruptions were handled by the _ do_irq function. However, the electrical characteristics of various interrupt requests may vary, or the features of the interrupt controller are different, which may lead to the following differences:

  • When to send an ACK response to the interrupt controller;
  • Processing of mask_irq and unmask_irq;
  • Does the interrupt controller need an EOI response?
  • When will the local IRQ of the CPU be disconnected? To allow nesting of IRQ;
  • Interrupt data structure synchronization and protection;

/*************************************** **************************************** **********************/
Statement: the content of this blog is created at http://blog.csdn.net/droidphone. please refer to it for help. Thank you!
/*************************************** **************************************** **********************/
To this end, the general interrupt subsystem abstracts several common traffic control types and implements corresponding standard functions for them. We only need to select the corresponding functions, assign the value to the handle_irq field of the irq_desc structure corresponding to IRQ. These standard callback functions are of the irq_flow_handler_t type:

typedefvoid (*irq_flow_handler_t)(unsigned int irq,    struct irq_desc *desc);

Currently, the general interrupt subsystem implements the following standard throttling callback functions, which are defined in: kernel/IRQ/chip. C,

  • Handle_simple_irq is used for simple traffic control;
  • Handle_level_irq is used for the throttling of level-triggered interruptions;
  • Handle_edge_irq is used for edge-triggered interrupt traffic control;
  • Handle_fasteoi_irq is used as the interrupt controller to respond to EOI;
  • Handle_percpu_irq is used to interrupt a single CPU response only;
  • Handle_nested_irq is used to handle nested thread interruptions;

The driver and board-level code can use the following APIs to set the flow control function of IRQ:

  • Irq_set_handler ();
  • Irq_set_chip_and_handler ();
  • Irq_set_chip_and_handler_name ();

The following sequence diagram shows the interrupt response process of the general interrupt subsystem. The flow_handle column shows the lifecycle of the central interruption control layer:

Figure 1.1 interrupt response process of the general interrupt Subsystem

2. handle_simple_irq

This function does not implement any substantive traffic control operations. After the irq_desc structure is locked, handle_irq_event is called directly to process the action linked list in irq_desc. It is usually used for Multiplexing (similar to interrupt controller cascade) the sub-interrupt in, called by the traffic control callback of the parent interrupt. Or it is used in an interrupt that does not require hardware control. The following is its simplified code:

voidhandle_simple_irq(unsigned int irq, struct irq_desc *desc){raw_spin_lock(&desc->lock);......handle_irq_event(desc);out_unlock:raw_spin_unlock(&desc->lock);}

3. handle_level_irq: This function is used to handle traffic control operations with level interruptions. The feature of level interruption is that as long as the device interrupt request pin (Central disconnection) remains at the preset trigger level, the interrupt will be continuously requested. Therefore, in order to avoid repeated responses to the same interrupt, the mask IRQ and ack irq must be put before the interrupt is processed to reset the interrupt request pin of the device, and then unmask IRQ after the response is complete. The actual situation is a little more complex. After the mask and ACK, you must determine the irq_inprogress flag. If the flag has been set, exit directly and no substantive processing will be performed, the irq_inprogress flag is set at the beginning of handle_irq_event and cleared at the end of handle_irq_event. If irq_inprogress is monitored to be set, the IRQ is being processed by another CPU, so exit directly, it is the correct method for handling level interruptions. However, in the arm system, this situation does not occur at all, because the interrupt controller does not receive an ACK notification before it enters handle_level_irq, and does not send an interrupt request to the second CPU, when the program enters handle_level_irq, the first action is mask.
IRQ, and then ack IRQ (usually associated with mask_ack_irq). Even if the device sends an interrupt request again, the handle_irq_event ends and the irq_inprogress mark is cleared after unmask IRQ. I don't know if other systems like x86 have different behaviors. If you know something, please let me know. The following Code simplifies handle_level_irq:
voidhandle_level_irq(unsigned int irq, struct irq_desc *desc){raw_spin_lock(&desc->lock);mask_ack_irq(desc);if (unlikely(irqd_irq_inprogress(&desc->irq_data)))goto out_unlock;        ......if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data)))goto out_unlock;handle_irq_event(desc);if (!irqd_irq_disabled(&desc->irq_data) && !(desc->istate & IRQS_ONESHOT))unmask_irq(desc);out_unlock:raw_spin_unlock(&desc->lock);}

Although handle_level_irq performs necessary processing on the traffic control of the level interrupt, as the level interrupt feature: as long as there is no ack irq, the central Disconnection will always be valid, so we will not miss a certain interruption request, however, if the driver developer does not have a thorough understanding of the process, it is particularly prone to the situation where a certain interruption is processed multiple times. In particular, when the interrupt thread (action-> thread_fn) is used to respond to the interrupt, mask_ack_irq usually only clears the pending status of the interrupt controller, many slow devices (such as devices controlled through I2C or SPI) need to clear the pending status of the disconnection in the disconnection process, but handle_level_irq has returned long before the interrupted thread is scheduled to run, after unmask_irq is executed, the device's disconnection pending is in a valid state, and the interrupt controller sends a request again. The result is that the device has an interruption request, resulting in two interruption responses. To avoid this situation, the best way is not to use the interrupt thread alone to handle the interruption, but to implement the second parameter irq_handler_t: handler of request_threaded_irq () and use disable_irq () in the handle callback () disable the IRQ and enable_irq () before exiting the interrupt thread callback (). Assuming action-> handler does not shield IRQ, the following figure shows the irq_progress flag, local interrupt status, and the status that triggers other CPUs during a level interrupt:

Figure 3.1 The colors in the level-triggered interrupt status indicate different states:
Status Red Green
Irq_progress True False
Allow local CPU interruption Disable Allow
Whether to allow the device to trigger another interrupt (may be responded by other CPUs) Disable Allow
4. handle_edge_irq: This function is used to handle traffic control operations that trigger interruptions on the edge. Edge interrupt is triggered only when the device's interrupt request pin (Central disconnection) level changes (from high to low or from low to high, because the hop is an instant and does not maintain power-on leveling like a level interrupt, it is very easy to miss an interrupt request if it is not properly handled. To avoid this situation, the shorter the interruption blocking time, the better. Kernel developers are aware of this. Before the interruption is handled, when the irq_progress flag is determined not to be set, it is only ack IRQ and there is no mask IRQ, to reset the interrupt request pin of the device. During the subsequent interrupt processing, another CPU can respond to the same IRQ request again. If irq_progress has been set, indicates that the other CPU is processing the last request of the IRQ. In this case, it simply sets the irqs_pending flag, and then stops after mask_ack_irq. The interrupted request is sent to the original CPU for further processing. Because it is mask_ack_irq, the system can only suspend one interruption.
if (unlikely(irqd_irq_disabled(&desc->irq_data) ||     irqd_irq_inprogress(&desc->irq_data) || !desc->action)) {if (!irq_check_poll(desc)) {desc->istate |= IRQS_PENDING;mask_ack_irq(desc);goto out_unlock;}}desc->irq_data.chip->irq_ack(&desc->irq_data);

From the above analysis, we can know that during the process of interruption, another request may be suspended after the response from another CPU. Therefore, after processing this request, we need to determine the irqs_pending flag. If it is set to a bit, the current CPU will then process the requests "delegated" by another CPU. The kernel sets a loop here to handle this situation until the irqs_pending flag is invalid, and because the other CPU will mask IRQ when it responds and suspends IRQ, so in the loop, unmask IRQ is required again, so that another CPU can respond again and suspend IRQ:

do {                ......if (unlikely(desc->istate & IRQS_PENDING)) {if (!irqd_irq_disabled(&desc->irq_data) &&    irqd_irq_masked(&desc->irq_data))unmask_irq(desc);}handle_irq_event(desc);} while ((desc->istate & IRQS_PENDING) && !irqd_irq_disabled(&desc->irq_data));

The irqs_pending flag is cleared in handle_irq_event.
Figure 4.1 The colors in the edge-triggered interrupt status indicate different states:

Status Red Green
Irq_progress True False
Allow local CPU interruption Disable Allow
Whether to allow the device to trigger another interrupt (may be responded by other CPUs) Disable Allow
Whether it is in the interrupt Context In the interrupt Context In process context

As shown in Figure 4.1, during the process of software interruption (softirq), it is still in the context of interruption, but the local interruption of CPU is in the open state, this indicates that nested interruptions are allowed at this time, but it doesn't matter, because the important processing has been completed and nested is only part of the software interruption. This is the original intention of the kernel to distinguish top and bottom. 5. handle_fasteoi_irq the modern Interrupt Controller usually implements the interrupt control function on the hardware, such as the general Interrupt Controller of GIC in the arm system. For such an interrupt controller, the CPU only needs to issue an end of Interrupt (EOI) after each interrupt is processed. We do not need to pay attention to the time when the mask and the time when the unmask. However, although the idea is perfect, there is always a special time, so the kernel still gives us the opportunity to intervene, it uses the preflow_handler field in the irq_desc structure, the callback will be called through the preflow_handler function before the interruption is officially handled.

voidhandle_fasteoi_irq(unsigned int irq, struct irq_desc *desc){raw_spin_lock(&desc->lock);if (unlikely(irqd_irq_inprogress(&desc->irq_data)))if (!irq_check_poll(desc))goto out;        ......if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {desc->istate |= IRQS_PENDING;mask_irq(desc);goto out;}if (desc->istate & IRQS_ONESHOT)mask_irq(desc);preflow_handler(desc);handle_irq_event(desc);out_eoi:desc->irq_data.chip->irq_eoi(&desc->irq_data);out_unlock:raw_spin_unlock(&desc->lock);return;        ......}

In addition, the kernel provides another EOI function:Handle_edge_eoi_irqIt is similar to handle_edge_irq, but does not need to implement the logic of mask and unmask. 6. handle_percpu_irq this function is used in the SMP system. When an IRQ is only processed on one CPU, we do not need to use the spin lock to protect the data, nor do we need to process the interrupt nesting re-import between CPUs, therefore, the function is simple:

voidhandle_percpu_irq(unsigned int irq, struct irq_desc *desc){struct irq_chip *chip = irq_desc_get_chip(desc);kstat_incr_irqs_this_cpu(irq, desc);if (chip->irq_ack)chip->irq_ack(&desc->irq_data);handle_irq_event_percpu(desc, desc->action);if (chip->irq_eoi)chip->irq_eoi(&desc->irq_data);}

7. handle_nested_irq

This function is used to implement one of the interrupt sharing mechanisms. When multiple interrupts share a single disconnection, we can use this interruption as the parent interrupt, each device that shares the interrupt is used as a sub-Interrupt. In the parent interrupt, the sub-device is disconnected to determine and distribute the request to which the sub-device responds. After obtaining the sub-device that actually sends the request, call handle_nested_irq to respond to the interruption. Therefore, this function is executed in the process context, and we do not need to scan and execute the action linked list in the irq_desc structure. During initialization, the parent interrupt must explicitly inform the interrupt subsystem through the irq_set_nested_thread function: These subinterrupts belong to the thread nested interrupt type, so that when the driver applies for these subinterrupts, the kernel does not set up its own interrupt threads for them, and all the sub-interrupts share the interrupted threads of the parent interrupt.

void handle_nested_irq(unsigned int irq){......        might_sleep();raw_spin_lock_irq(&desc->lock);        ......action = desc->action;if (unlikely(!action || irqd_irq_disabled(&desc->irq_data)))goto out_unlock;irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);raw_spin_unlock_irq(&desc->lock);action_ret = action->thread_fn(action->irq, action->dev_id);raw_spin_lock_irq(&desc->lock);irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);out_unlock:raw_spin_unlock_irq(&desc->lock);}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.