c-为什么Linux内核不在返回IRQ_HANDLED的共享IRQ的第一个处理程序处停止



我确信这是有充分理由的,但我看不出它是什么。在__handle_irq_event_percpu内部,内核循环所有为特定IRQ行注册的处理程序并调用它。我不明白的是,当到达第一个返回IRQ_HANDLED的处理程序时,为什么这个循环没有退出?这似乎是一个简单的性能改进,所以肯定有什么我不明白的地方。

有人知道为什么吗?

在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 interruptsn",
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;
}

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;

如果中断线路由多个设备共享,则操作列表中有多个条目因此,多个设备可能同时进入中断状态。因此,行动是呼吁所有共享线路的设备检查是否有什么事情要做

N.B.

  • 这个答案在这个问题上更有说服力
  • 这篇博客文章描述了Linux内核中的中断处理步骤

在__handle_irq_event_percpu内部,内核在为特定irq行注册的所有处理程序上循环并调用它。我不明白的是,当到达第一个返回irq_HANDLED的处理程序时,为什么这个循环没有退出?这似乎是一个简单的性能改进,所以肯定有什么我不明白的地方。

有两种情况需要考虑-共享边缘触发的IRQ和共享级别触发的IRQ。

共享边缘触发IRQ

在这种情况下,2个或多个设备可以在同一时间或类似时间发送IRQ。如果发生这种情况;对于每个驱动器";当第一处理程序返回CCD_ 3时退出循环;等待IRQ处理程序的注意";状态(很可能导致设备永久锁定)。为了避免这种情况,对于边缘触发的IRQ,内核的";对于每个驱动器";循环必须通知所有驱动程序(并且不能在返回IRQ_HANDLED时立即停止)。

请注意,共享边缘触发的IRQ非常罕见。对于80x86 PC,当有两个以上的串行端口控制器时(可以通过对所有串行端口控制器使用相同的驱动程序并在驱动程序中而不是在内核的IRQ管理代码中处理问题来解决),但除此之外,共享边缘触发的IRQ根本不存在(在80x86 PC上)。

共享级别触发的IRQ

在这种情况下,2个或多个设备可以在同一时间或类似时间发送IRQ;但如果发生这种情况;对于每个驱动器";当第一个处理程序返回CCD_ 5,则(来自其他设备的)其他IRQ没有丢失时。相反,中断控制器将看到";电平仍然由至少一个设备"触发";并且将重新发布IRQ(并且继续发送更多的IRQ,直到满足所有设备为止)。

对于共享级别触发的IRQ,这是一种性能折衷(与"正确性"无关)。更具体地说:

  • 如果多个设备很可能在同一时间或相似时间需要关注;那么您可以通过继续循环(当驱动程序返回IRQ_HANDLED时)来提高性能,因为这可能会避免中断控制器重新发布IRQ的成本。

  • 如果多个设备不太可能在同一时间或相似时间引起关注;那么您可以通过在驱动程序返回IRQ_HANDLED时立即停止循环来提高性能,因为这可能会避免执行不必要的设备驱动程序的中断处理程序的成本。

请注意,这取决于设备驱动程序的IRQ处理程序的调用顺序。为了理解这一点,假设有两个设备共享一条IRQ线,并且几乎所有的IRQ都来自第一个设备。如果第一个设备的驱动程序的IRQ处理程序首先被调用并返回IRQ_HANDLED,那么第二个设备不太可能同时发送IRQ;但是如果首先调用第二设备的驱动程序的IRQ处理程序并返回CCD_ 9。

换句话说;如果内核按照"0"的顺序对设备驱动程序的列表进行排序;该设备发送一个IRQ"机会";;则一旦驱动器返回CCD_ 10就停止循环将提高性能(并且被调用的第一个驱动器将更快地返回IRQ_HANDLED的可能性变得更大)。

然而,跟踪统计数据和";变得更聪明";(根据这些统计信息确定如何动态优化性能)也会增加一点开销,而且(至少在理论上,尤其是在设备驱动程序的中断处理程序非常快的情况下)这可能会使性能损失超过预期。

本质上;量化和最大化潜在利益需要大量的工作(研究、基准测试);而且它更容易不麻烦(并且总是调用所有设备驱动程序的中断处理程序,即使情况更糟)。

最新更新