工作队列启动、中断且从未完成导致 CPU 停止

Workqueue starts, interrupted, and never finished causing CPU stall

提供一个最小的代码示例会很困难,但我会提供一些 sudo 代码,希望能得到 point/question。

TL;DR:我的工作队列启动,被中​​断,然后永远不会完成导致 CPU 停顿。

我正在为 PCIe 设备创建网络驱动程序。对于语言,Tx=host out,Rx=host in。对于事物的 Tx 端,我使用工作队列 (work_struct)。所以.

ndo_start_xmit(){
//Perform some operations and load a DMA.
}

request_irq(irq_handler);
INIT_WORK(work,work_handler);

irq_handler(){
//Check what caused the IRQ
if(ndo_xmit_dma caused irq){
schedule_work(work);
}
}

work_handler(){
if(xmit_called){
spin_lock()
//Do some stuff
spin_unlock()
}
}

然后对于 Rx 方面的事情,它是相似的,但现在使用 NAPI 而不是工作队列,因为我正在学习并且老实说可能会将所有工作转移到 napi(请说明是否可以解决问题)。

irq_handler(){
if(Rx caused the irq){
napi_schedule();
}
}

//Do a bunch of napi releated stuff (never try to grab the spin_lock).

所以问题是什么?在我的 work_handler 中途发生了 Tx 和 Rx IRQ(到目前为止没什么大不了的)。 IRQ 显然将我从安排 NAPI 的工作队列中弹回。现在,它不再返回工作队列,而是处理 NAPI 函数(这对我的程序来说也不是什么大问题,我认为这是优先事项)。然后我的内核调用 ndo_start_xmit 再次到达 spin_lock,此时 CPU 停止。程序绝不会从 work_handler 返回到预定但中断的工作。在测试中,它实际上是在 2 个打印语句之间被打断的,所以我知道它甚至从来没有做过部分 return.

那么为什么工作队列从不 return?有办法解决这个问题吗?我最初的猜测是 flush_work 但这感觉更像是问题的补丁而不是解决问题的根源。将我的 Tx schedule_work 移至 NAPI 处理程序的一部分会更好吗?

感谢您的洞察力。

更新: 这是在我接受了一个非常好的答案之后。在随后的讨论中,我提出了多个 NAPI 实例。简而言之,每个网络开发 1 个 NAPI,否则会出现很多问题。我无法仅用 napi 结构来区分导致 napi 的原因(也许有人看到了我除了滥用预算数字之外没有的方法)。至于我的问题,我发现这是一个三步问题。工作队列被 RX irq/napi 中断。然后 Rx napi 被对 ndo_start_xmit 的调用阻塞。 ndo_start_xmit 试图获取工作队列正在使用的自旋锁,所以我被困在一个没有任何东西可以移动的位置,因此 CPU 停滞了。

如果spin_lock..spin_unlock之间的区域很短,spin_lock_irqsave可能适用。至少,尝试一下,看看它是否能解决您的问题。我怀疑 NAPI 固定了您的 work_handler 上下文。

虽然 _irqsave 可能会起作用,但您应该进行适当的锁顺序分析。

看看https://www.kernel.org/doc/Documentation/locking/spinlocks.txt;特别是底部位:

spin_lock(&lock);
...
    <- interrupt comes in:
        spin_lock(&lock);