Skip to content

SG2044 中断亲和性迁移后偶发中断失效 #246

Description

@swjtuer326

【OS版本】openEuler-24.03-LTS

【内核版本】OLK-6.6

【硬件平台】SG2044(RISC-V,thead,c900-plic)

【故障描述】

中断亲和性迁移后偶发中断失效。该问题通过 IGB 驱动的 i210/i350 网卡使用观察到:先出现网卡异常,dmesg 报 WATCHDOG: TX queue xxx timeout → Reset adapter,且网卡reset后依旧无法正常使用;进一步通过/proc/interrupts 观察到异常后网卡对应的中断计数不再增长,设备无法正常上报和处理中断。

PLIC 1.0.0 规范(https://github.com/riscv/riscv-plic-spec)要求:

§8:"the gateway will not forward an additional interrupt request until it receives an interrupt completion message"
§9:"If the completion ID does not match an interrupt source that is currently enabled for the target, the completion is silently ignored."
§5:"A pending bit in the PLIC core can be cleared by setting the associated enable bit then performing a claim."

即 completion 时源必须对本 context enabled,否则被硬件静默忽略;gateway 收不到 completion 不再转发该源(§8),pending 只能由 enabled+claim 清除(§5),故一次被忽略的 completion 会使该源中断永久无法正常触发和处理。

CPU-A                 PLIC                 CPU-B
    |--- claim -------> |                     |  t1 plic_handle_irq 取得 hwirq
    |                   |<-- clr en[A] -------|  t2 plic_set_affinity→plic_irq_toggle({A},0)
    |                   |<-- eff -> B --------|  t3
    |                   |<-- en[B] -----------|  t4
    |  run handler      |                     |  t5 handle_irq_event
    |--- complete ----> X                     |  t6 chained_irq_exit→plic_irq_eoi: ignored
    |                   |                     |  gateway 不再转发该源,中断永久失效

【问题复现步骤】

在网卡中断持续触发时反复高频修改亲和性:

echo 0 > /proc/irq/<N>/smp_affinity_list
echo 1 > /proc/irq/<N>/smp_affinity_list

【实际结果】

若干次后该中断计数停止增长,中断不再正常触发和处理,最终触发 TX watchdog timeout。

【期望结果】

亲和性迁移后中断正常触发和处理。

【修复思路】

修复涉及文件:drivers/irqchip/irq-sifive-plic.c

plic_irq_eoi() 整段加 per-hart enable_lock,使「读 enable 位→(必要时临时使能)→写 completion→(恢复禁用)」与 plic_set_affinity 清 enable 互斥:

raw_spin_lock_irqsave(&handler->enable_lock, flags);
enabled = readl(reg) & BIT(d->hwirq % 32);
if (unlikely(!enabled)) {
    plic_toggle(handler, d->hwirq, 1);                       // 临时使能
    writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);    // completion
    plic_toggle(handler, d->hwirq, 0);                       // 恢复禁用
} else {
    writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);
}
raw_spin_unlock_irqrestore(&handler->enable_lock, flags);

enable_lockplic_toggle(清 enable,持同一把锁)互斥:enable 清除要么先于 EOI(读到 0,走临时使能再 completion),要么阻塞在锁外至 EOI 释放,completion 不被忽略。enable_lock 为最内层锁,无 ABBA。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions