简体   繁体   English

为什么 Linux kernel 不会在返回 IRQ_HANDLED 的共享 IRQ 的第一个处理程序处停止?

[英]Why does the Linux kernel not stop at the first handler for a shared IRQ that returns IRQ_HANDLED?

I'm sure there's a good reason for this, but I can't see what it is.我确信这是有充分理由的,但我看不出它是什么。 Inside __handle_irq_event_percpu the kernel loops over all the handlers registered for a particular IRQ line and calls it.__handle_irq_event_percpu内部,kernel 循环遍历为特定 IRQ 行注册的所有处理程序并调用它。 What I don't understand is why this loop isn't exited when the first handler returning IRQ_HANDLED is reached?我不明白的是,为什么到达第一个返回IRQ_HANDLED的处理程序时没有退出这个循环? It seems like a simple performance improvement, so there must be something I don't understand.看似简单的性能提升,肯定有什么我不明白的地方。

Does anyone know why?有谁知道为什么?

In the Linux source tree, __ handle_irq_event_percpu() is in kernel/irq/handle.c :在 Linux 源代码树中,__ handle_irq_event_percpu()kernel/irq/handle.c 中

irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
{
    irqreturn_t retval = IRQ_NONE;
    unsigned int irq = desc->irq_data.irq;
    struct irqaction *action;

    record_irq_time(desc);

    for_each_action_of_desc(desc, action) {
        irqreturn_t res;

        trace_irq_handler_entry(irq, action);
        res = action->handler(irq, action->dev_id);
        trace_irq_handler_exit(irq, action, res);

        if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pS enabled interrupts\n",
                  irq, action->handler))
            local_irq_disable();

        switch (res) {
        case IRQ_WAKE_THREAD:
            /*
             * Catch drivers which return WAKE_THREAD but
             * did not set up a thread function
             */
            if (unlikely(!action->thread_fn)) {
                warn_no_thread(irq, action);
                break;
            }

            __irq_wake_thread(desc, action);

            /* Fall through - to add to randomness */
        case IRQ_HANDLED:
            *flags |= action->flags;
            break;

        default:
            break;
        }

        retval |= res;
    }

    return retval;
}

The for_each_action_of_desc(desc, action) macro travels in the action list of the IRQ descriptor: for_each_action_of_desc(desc, action)宏在 IRQ 描述符的动作列表中移动:

#define for_each_action_of_desc(desc, act)          \
    for (act = desc->action; act; act = act->next)
[...]
struct irq_desc {
    struct irq_common_data  irq_common_data;
    struct irq_data     irq_data;
    unsigned int __percpu   *kstat_irqs;
    irq_flow_handler_t  handle_irq;
    struct irqaction    *action;    /* IRQ action list */
[...]
struct irqaction {
    irq_handler_t       handler;
    void            *dev_id;
    void __percpu       *percpu_dev_id;
    struct irqaction    *next;
    irq_handler_t       thread_fn;
    struct task_struct  *thread;
    struct irqaction    *secondary;
    unsigned int        irq;
    unsigned int        flags;
    unsigned long       thread_flags;
    unsigned long       thread_mask;
    const char      *name;
    struct proc_dir_entry   *dir;
} ____cacheline_internodealigned_in_smp;

There are multiple entries in the action list if the interrupt line is shared by several devices.如果中断线由多个设备共享,则操作列表中有多个条目。 So, several devices may enter in interrupt state at the same time .因此,多个设备可能同时进入中断 state Hence, the action is to be called for all the devices sharing the line to check if there is something to do.因此,将对共享线路的所有设备调用该操作,以检查是否有事情要做。

NB :注意

  • This answer is better argumented on the subject这个答案在这个主题上有更好的争论
  • This blog article depicts the steps of interrupt handling in the Linux kernel.这篇博客文章描述了 Linux kernel 中的中断处理步骤。

Inside __handle_irq_event_percpu the kernel loops over all the handlers registered for a particular IRQ line and calls it.在 __handle_irq_event_percpu 内部,kernel 循环遍历为特定 IRQ 行注册的所有处理程序并调用它。 What I don't understand is why this loop isn't exited when the first handler returning IRQ_HANDLED is reached?我不明白的是,为什么到达第一个返回 IRQ_HANDLED 的处理程序时没有退出这个循环? It seems like a simple performance improvement, so there must be something I don't understand.看似简单的性能提升,肯定有什么我不明白的地方。

There are 2 cases to consider - shared edge triggered IRQs and shared level triggered IRQs.有两种情况需要考虑——共享边沿触发的 IRQ 和共享电平触发的 IRQ。

Shared Edge Triggered IRQs共享边缘触发的 IRQ

In this case, 2 or more devices can send an IRQ at the same time or at similar times.在这种情况下,2 个或更多设备可以同时或在相似的时间发送一个 IRQ。 If this happens and the "for each driver" loop is exited when the first handler returns IRQ_HANDLED then other devices can/will become stuck in a "waiting for IRQ handler's attention" state (most likely causing devices to lock up permanently).如果发生这种情况并且当第一个处理程序返回IRQ_HANDLED时退出“for each driver”循环,那么其他设备可能/将陷入“等待 IRQ 处理程序的注意”state(很可能导致设备永久锁定)。 To avoid that, for edge triggered IRQs, the kernel's "for each driver" loop must notify all drivers (and can't stop as soon as one returns IRQ_HANDLED ).为了避免这种情况,对于边缘触发的 IRQ,内核的“for each driver”循环必须通知所有驱动程序(并且不能在一个返回IRQ_HANDLED时立即停止)。

Note that shared edge triggered IRQs are rare.请注意,共享边沿触发的 IRQ 很少见。 For 80x86 PCs it's possible when there are more than 2 serial port controllers (which can be solved by using the same driver for all serial port controllers and dealing with the problem in the driver and not in the kernel's IRQ management code), but apart from that shared edge triggered IRQs simply don't exist (on 80x86 PCs).对于 80x86 PC,当有超过 2 个串口控制器时是可能的(这可以通过对所有串口控制器使用相同的驱动程序并在驱动程序中而不是在内核的 IRQ 管理代码中处理问题来解决),但除了共享边缘触发的 IRQ 根本不存在(在 80x86 PC 上)。

Shared Level Triggered IRQs共享级别触发的 IRQ

In this case, 2 or more devices can send an IRQ at the same time or at similar times;在这种情况下,2个或更多设备可以同时或相似的时间发送一个IRQ; but if this happens and the "for each driver" loop is exited when the first handler returning IRQ_HANDLED then the other IRQs (from other devices) are not lost.但是如果发生这种情况并且当第一个处理程序返回IRQ_HANDLED时退出“for each driver”循环,那么其他 IRQ(来自其他设备)不会丢失。 Instead, the interrupt controller will see "level is still being triggered by at least one device" and will re-issue the IRQ (and keep sending more IRQs until all devices are satisfied).相反,中断 controller 将看到“级别仍被至少一个设备触发”并将重新发出 IRQ(并继续发送更多的 IRQ,直到所有设备都得到满足)。

For shared level triggered IRQs, it's a performance compromise (that has nothing to do with "correctness").对于共享级别触发的 IRQ,这是一种性能折衷(与“正确性”无关)。 More specifically:进一步来说:

  • If it's very likely that multiple devices will want attention at the same or similar time;如果很可能多个设备会在相同或相似的时间受到关注; then you can improve performance by continuing the loop (when a driver returns IRQ_HANDLED ) because it's likely that this will avoid the cost of the interrupt controller re-issuing the IRQ.那么您可以通过继续循环(当驱动程序返回IRQ_HANDLED时)来提高性能,因为这可能会避免中断 controller 重新发出 IRQ 的成本。

  • If it's very unlikely that multiple devices will want attention at the same or similar time;如果多个设备不太可能同时或相似的时间需要关注; then you can improve performance by stopping the loop as soon as driver returns IRQ_HANDLED because it's likely that this will avoid the cost of the executing unnecessary device drivers' interrupt handlers.那么您可以通过在驱动程序返回IRQ_HANDLED后立即停止循环来提高性能,因为这可能会避免执行不必要的设备驱动程序的中断处理程序的成本。

Note that this depends on the order that device drivers' IRQ handlers are called.请注意,这取决于调用设备驱动程序的 IRQ 处理程序的顺序。 To understand this imagine there are 2 devices sharing an IRQ line and almost all IRQs come from the first device.为了理解这个假设,有 2 个设备共享一条 IRQ 线,几乎所有的 IRQ 都来自第一个设备。 If the first device's driver's IRQ handler is called first and returns IRQ_HANDLED then it'd be unlikely that the second device also sent an IRQ at the same time;如果第一个设备的驱动程序的 IRQ 处理程序首先被调用并返回IRQ_HANDLED ,那么第二个设备不太可能同时发送一个 IRQ; but if the second device's driver's IRQ handler is called first and returns IRQ_HANDLED then it'd be likely that the second device also sent an IRQ at the same time.但是如果第二个设备的驱动程序的 IRQ 处理程序首先被调用并返回IRQ_HANDLED那么很可能第二个设备也同时发送了一个 IRQ。

In other words;换句话说; if the kernel sorted the list of device drivers in order of "chance the device sent an IRQ";如果 kernel 按“设备发送 IRQ 的机会”的顺序对设备驱动程序列表进行排序; then it becomes more likely that stopping the loop as soon as a driver returns IRQ_HANDLED will improve performance (and it becomes more likely that the first driver called will return IRQ_HANDLED sooner).那么一旦驱动程序返回IRQ_HANDLED就更有可能停止循环将提高性能(并且第一个调用的驱动程序更有可能更快地返回IRQ_HANDLED )。

However tracking statistics and "being smarter" (determining how to optimize performance dynamically based on those statistics) would also add a little overhead, and (at least in theory, especially if device drivers' interrupt handlers are extremely fast anyway) this could cost more performance than you'd gain.然而,跟踪统计和“更智能”(根据这些统计确定如何动态优化性能)也会增加一点开销,并且(至少在理论上,尤其是如果设备驱动程序的中断处理程序无论如何都非常快)这可能会花费更多性能比你得到的。

Essentially;本质上; it'd take a lot of work (research, benchmarking) to quantify and maximize the potential benefits;量化和最大化潜在收益需要大量工作(研究、基准测试); and it's a lot easier to not bother (and always call all device driver's interrupt handlers" even when it is worse).并且不打扰要容易得多(并且总是调用所有设备驱动程序的中断处理程序”,即使情况更糟)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM