1. 中断注册流程
1.1 KFD 驱动初始化
KFD 驱动在初始化时,会为每个支持的 GPU 节点(kfd_node)注册中断处理类(event_interrupt_class)。每一代 GPU(如 GFX10、GFX11)有不同的 event_interrupt_class 实现,主要区别在于 .interrupt_isr 和 .interrupt_wq 两个回调函数。
struct kfd_event_interrupt_class {
//中断上半部:快速判定
bool (*interrupt_isr)(struct kfd_node *dev,
const uint32_t *ih_ring_entry,
uint32_t *patched_ihre,
bool *patched_flag);
//中断下半部:处理中断
void (*interrupt_wq)(struct kfd_node *dev,
const uint32_t *ih_ring_entry);
};
1.2 注册到 AMDGPU 中断框架
KFD 并不直接向 Linux 内核注册硬件中断,而是集成在 AMDGPU 驱动的中断处理体系中。
-
AMDGPU 驱动通过
amdgpu_irq_dispatch()统一分发所有 GPU 相关的中断(见 amdgpu_ih.c)。 -
KFD 相关的中断(如 VM fault、Fence、SQ interrupt 等)会在分发时被识别,并调用 KFD 的中断处理回调。
2. 中断分发与快速判定(ISR)
2.1 硬件中断到达
GPU 产生中断后,AMDGPU 的中断处理函数(如 amdgpu_ih_process())会被调用。该函数从 IH(Interrupt Handler)ring buffer 读取中断向量(IV),并解析出 client_id、source_id、vmid、pasid 等信息。
2.2 分发到 KFD
amdgpu_irq_dispatch() 根据 IV 的 client_id/source_id 判断是否为 KFD 关心的中断。
如果是,则调用 KFD 注册的 .interrupt_isr 回调(如 event_interrupt_isr_v11())。
2.3 快速判定(ISR)
.interrupt_isr 只做快速判定,判断该中断是否需要 KFD 进一步处理。典型判定逻辑(见 event_interrupt_isr_v11):
-
检查 client_id/source_id 是否为 KFD 关心的类型(如 SQ_INTERRUPT_MSG、CP_END_OF_PIPE、VM fault 等)。
-
检查 vmid 是否属于 KFD 管理的范围。
-
检查 pasid 是否有效。
如果需要处理,返回 true,否则返回 false。
3. 推送到工作队列(Work Queue)
如果 .interrupt_isr 返回 true,AMDGPU 驱动会将该中断信息推送到 KFD 的工作队列(work queue)。这样做的目的是避免在硬件中断上下文中做复杂处理,将耗时操作延后到内核线程上下文。那为什么要用工作队列?因为:
中断上下文限制:硬件中断处理(ISR)运行在中断上下文,不能做阻塞、耗时或复杂的操作,只能做快速判定和简单处理。
复杂事件处理:KFD 需要解析中断、查找进程、上报事件、唤醒用户空间等,这些操作可能阻塞或耗时,必须在进程上下文(内核线程)中完成。
解耦与性能:将复杂处理推迟到工作队列,可以提升中断响应速度,避免影响系统实时性和稳定性。
4. 详细处理(WQ 回调)
4.1 工作队列回调
KFD 的 .interrupt_wq 回调(如 event_interrupt_wq_v11())会在工作队列线程中被调用,进行详细的事件处理。
4.2 事件分类与处理
-
根据 client_id/source_id/context_id0/context_id1 等,分为不同类型事件:
-
VM Fault/UTCL2 Fault:解析 fault 信息,构造异常数据,上报给用户空间调试工具或 SMI。
-
CP_END_OF_PIPE:信号事件,通知用户空间某个 GPU 队列任务完成。
-
CP_BAD_OPCODE:非法指令,可能需要暂停/重置队列,并上报调试事件。
-
SDMA_TRAP/SDMA_ECC:信号或 ECC 错误,可能触发 poison 消耗和队列重置。
-
SQ_INTERRUPT_MSG:进一步区分为 AUTO、INST、ERROR 类型,分别处理 trace、调试、硬件错误等。
-
Fence:调用
kfd_process_close_interrupt_drain,关闭进程相关队列,防止重复中断。
-
-
处理过程中,会调用如下函数:
-
kfd_signal_event_interrupt():向用户空间 signal page 写入信号,唤醒等待的应用。 -
kfd_set_dbg_ev_from_interrupt():上报调试事件。 -
event_interrupt_poison_consumption_v11():处理 ECC/poison 消耗,可能触发 GPU reset。 -
kfd_dqm_suspend_bad_queue_mes():暂停异常队列。
-
5. 用户空间事件通知
事件处理后,KFD 通过 signal page 或 event fd 等机制通知用户空间应用(如 ROCm runtime、调试器)。用户空间应用可以通过 mmap 的 signal page 轮询事件,或通过 poll/epoll 等机制等待事件 fd。
6. 关键结构与函数关系图
硬件中断
│
▼
amdgpu_ih_process (amdgpu_ih.c)
│
▼
amdgpu_irq_dispatch
│
▼
event_interrupt_isr_v11 (kfd_int_process_v11.c)
│
├─ 返回 false → 忽略
└─ 返回 true → 推送到 KFD 工作队列→ schedule_work
│
▼
kfd_interrupt_work_handler (内核线程上下文)
│
▼
event_interrupt_wq_v11
│
├─ kfd_signal_event_interrupt
| |---set_event_from_interrupt
| |----set_event
├─ kfd_set_dbg_ev_from_interrupt
├─ event_interrupt_poison_consumption_v11
└─ kfd_process_close_interrupt_drain
这里与我们event系列相关的是kfd_signal_event_interrupt下的调用链,其实现如下。在这里实现了event的signal,还有后面我们要涉及的event_age。
static void set_event_from_interrupt(struct kfd_process *p,
struct kfd_event *ev)
{
if (ev && event_can_be_gpu_signaled(ev)) {
acknowledge_signal(p, ev);
spin_lock(&ev->lock);
set_event(ev);
spin_unlock(&ev->lock);
}
}
static void set_event(struct kfd_event *ev)
{
struct kfd_event_waiter *waiter;
/* Auto reset if the list is non-empty and we're waking
* someone. waitqueue_active is safe here because we're
* protected by the ev->lock, which is also held when
* updating the wait queues in kfd_wait_on_events.
*/
ev->signaled = !ev->auto_reset || !waitqueue_active(&ev->wq);
if (!(++ev->event_age)) {
/* Never wrap back to reserved/default event age 0/1 */
ev->event_age = 2;
WARN_ONCE(1, "event_age wrap back!");
}
list_for_each_entry(waiter, &ev->wq.head, wait.entry)
WRITE_ONCE(waiter->activated, true);
wake_up_all(&ev->wq);
}
7. 总结
-
注册阶段:KFD 通过
event_interrupt_class注册中断处理回调,集成到 AMDGPU 的中断分发体系。 -
快速判定:
.interrupt_isr负责判断中断是否需要 KFD 处理,避免无关中断进入工作队列。 -
详细处理:
.interrupt_wq在工作队列中完成事件分类、信号上报、异常处理等复杂逻辑。 -
用户通知:通过 signal page 或 event fd 通知用户空间,实现高效的 GPU 事件响应。
这种设计既保证了中断处理的实时性和高效性,又避免了在硬件中断上下文中做耗时操作,提升了系统的健壮性和可维护性。
1798

被折叠的 条评论
为什么被折叠?



