Rocm rocr-libhsakmt Event 机制技术文章预告
主线故事一个进程通过 Thunk 层提交了多个 User Queue在某个时刻需要知道哪个 Queue 完成了——围绕这一场景分 4 篇文章逐层展开 Event 的机制、实现和应用。预计5月陆续上线请订阅关注。技术分析思路概览第 1 篇概念与架构 ↓ 读者建立全局视角 第 2 篇内核实现 ↓ 读者理解硬件→内核如何触发/传递事件 第 3 篇Thunk 层实现 ↓ 读者理解用户态如何封装/使用事件 第 4 篇实战多 Queue 完成检测 → 读者能结合前 3 篇写出/调试真实代码第 1 篇Event 机制全景——从 Doorbell 到 Signal目标让读者在不看代码的情况下理解 Event 在 ROCm 中的定位、与 Doorbell 的对比以及为什么需要它。大纲引言GPU 异步编程的两个核心问题“活儿来了”——CPU → GPU 通知Doorbell“活儿干完了”——GPU → CPU 通知EventEvent 的设计目标精确感知 Queue/Kernel 完成异常上报Page Fault、HW Exception多 Queue 间依赖编排流水线Event 类型一览结合hsakmttypes.h中的枚举逐一说明类型值用途HSA_EVENTTYPE_SIGNAL0用户态 GPU signal最常用HSA_EVENTTYPE_NODECHANGE1热插拔HSA_EVENTTYPE_DEVICESTATECHANGE2设备启停HSA_EVENTTYPE_HW_EXCEPTION3GPU shader 异常HSA_EVENTTYPE_SYSTEM_EVENT4GPU SYSCALLHSA_EVENTTYPE_DEBUG_EVENT5调试信号HSA_EVENTTYPE_PROFILE_EVENT6性能分析HSA_EVENTTYPE_QUEUE_EVENT7Queue idle / EOPHSA_EVENTTYPE_MEMORY8内存访问异常三层架构概览硬件层中断MSI-X 内存原子写RELEASE_MEM内核层KFD Event 对象 Signal Page wait_queue用户态层Thunk APIhsaKmtCreateEvent/hsaKmtWaitOnEvent→ ROCrhsa_signal_tEvent 生命周期概览图Create → Associate with Queue → GPU executes → Signal → Wait/Poll → Destroy与 Fence / Semaphore / Completion 的对比Linux kernel fence vs KFD eventCUDA event vs HSA signal关键代码引用hsakmttypes.h:HSA_EVENTTYPE_*枚举定义kfd_events.h:struct kfd_event结构体预览详细在第 2 篇第 2 篇内核实现——KFD Event 对象与中断处理目标深入kfd_events.c和kfd_events.h讲清楚内核如何管理 Event 对象、Signal Page、以及如何响应硬件中断并唤醒用户态。大纲核心数据结构struct kfd_eventstructkfd_event{u32 event_id;u64 event_age;// 用于 age-based wait 优化bool signaled;bool auto_reset;inttype;spinlock_tlock;wait_queue_head_twq;// 等待者链表uint64_t__user*user_signal_address;// 用户态 signal slot 指针union{memory_exception_data;hw_exception_data;};};struct kfd_signal_page一整页 64-bit slot每个 slot 对应一个 signal eventstruct kfd_event_waiter封装wait_queue_entry_t支持 multi-event waitSignal Page 机制重点分配allocate_signal_page()→__get_free_pages()初始化为UNSIGNALED_EVENT_SLOT(0xFFFFFFFFFFFFFFFF)映射到用户态通过mmapKFD 设备 fd用户态得到events_page指针GPU 写入GPU 执行RELEASE_MEMpacket 时将 event_id 写入对应 slot容量KFD_SIGNAL_EVENT_LIMIT默认 4096 或 256 兼容模式Event 创建流程kfd_event_create()→ 区分 signal event 和 other eventSignal eventcreate_signal_event()→allocate_event_notification_slot()→idr_alloc()分配 event_id同时是 slot indexOther eventcreate_other_event()→ event_id 从KFD_FIRST_NONSIGNAL_EVENT_ID开始中断触发路径核心kfd_signal_event_interrupt(pasid, partial_id, valid_id_bits)← 硬件中断处理调用lookup_signaled_event_by_partial_id()通过 partial ID signal page slot 值判断哪个 event 被 signal找到 event 后set_event(ev)→ev-signaled true→wake_up_all(ev-wq)Wait 机制kfd_wait_on_events()→ 为每个 event 创建kfd_event_waiter加入ev-wq支持WaitOnAll所有 event 都 signal和WaitOnAny任一 signal超时处理schedule_timeout()或KFD_EVENT_TIMEOUT_IMMEDIATE立即返回 / 轮询语义event_age优化避免重复处理已经看过的 signalEvent 销毁与清理destroy_event()→ 唤醒所有 waiter返回失败→idr_remove()→kfree_rcu()kfd_event_free_process()→destroy_events()shutdown_signal_page()异常事件特殊处理HSA_EVENTTYPE_MEMORYkfd_set_memory_exception_data()填充 VA、GPU ID、failure reasonHSA_EVENTTYPE_HW_EXCEPTION填充 reset_type、reset_cause、memory_lost关键代码引用kfd_events.h: 完整结构体定义kfd_events.c:allocate_signal_page,create_signal_event,kfd_signal_event_interrupt,kfd_wait_on_events,destroy_eventkfd_chardev.c或kfd_ioctl.c: ioctl 入口AMDKFD_IOC_CREATE_EVENT,AMDKFD_IOC_WAIT_EVENTS等第 3 篇Thunk 层实现——libhsakmt 如何封装 Event目标从libhsakmt/src/events.c出发讲清楚用户态如何通过 ioctl 与内核交互、如何管理 events_page、以及 Wait 的完整流程。大纲Thunk 层 Event API 总览API功能hsaKmtCreateEvent()创建 eventhsaKmtDestroyEvent()销毁 eventhsaKmtSetEvent()CPU 端手动 signalhsaKmtResetEvent()重置 eventhsaKmtWaitOnEvent()等待单个 eventhsaKmtWaitOnMultipleEvents()等待多个 eventhsaKmtWaitOnEvent_Ext()带 event_age 的扩展等待Event 创建详解hsaKmtCreateEventCtx()完整流程分配HsaEvent结构体构造kfd_ioctl_create_event_argsevent_type, node_id, auto_resetdGPU 特殊处理首次创建时分配 events_pageGPU 可见内存通过hsakmt_allocate_exec_aligned_memory_gpu()或mmapKFD fdioctl(AMDKFD_IOC_CREATE_EVENT)→ 内核创建 event 并返回 event_id 和 event_slot_index设置HWData2 events_page[event_slot_index]用户态 signal 地址Events Page 管理重点iGPU vs dGPU 差异iGPU通过mmap(fd, event_page_offset)映射内核 signal pagedGPU先用hsakmt_allocate_exec_aligned_memory_gpu()分配 GPU 可达内存再通过fmm_get_handle告知内核events_page 是一个uint64_t[]数组每个 slot 8 字节GPU 写入 slot → 用户态可直接读取shared memory 语义Wait 流程详解hsaKmtWaitOnMultipleEvents_ExtCtx()是核心实现构造kfd_event_data[]数组填充每个 event_id对 signal event 填入last_event_age优化避免重复唤醒ioctl(AMDKFD_IOC_WAIT_EVENTS)→ 阻塞在内核的schedule_timeout返回后检查wait_resultSUCCESS / TIMEOUT异常事件处理对 MEMORY 和 HW_EXCEPTION 类型从event_data中提取详细错误信息并填入HsaEvent结构异常事件的用户态分析analysis_memory_exception()打印 VA、node_id、failure reasonNotPresent / ReadOnly / NoExecute尝试fmm_get_mem_info()查询指针信息如果是 SVM 范围调用get_mem_info_svm_api()通过AMDKFD_IOC_SVM查询属性Signal vs System Event 的区分IsSystemEventType(): signal 和 debug 类型可被用户态 Set/Reset其余为系统事件只能由内核/硬件触发关键代码引用libhsakmt/src/events.c: 完整文件linux/kfd_ioctl.h: ioctl 命令定义libhsakmt/include/hsakmt/hsakmttypes.h:HsaEvent,HsaEventDescriptor,HSA_EVENTTYPE_*第 4 篇实战——多 Queue 完成检测与同步模式目标回到开头的场景一个进程提交了很多 User Queue如何知道某个 Queue 完成了结合前 3 篇知识给出完整的代码流程和最佳实践。大纲场景复现进程通过hsaKmtCreateQueue()创建 N 个 User Queue每个 Queue 提交一批 PM4/AQL 命令包问题如何高效地知道哪个 Queue 的哪个 batch 完成了方案一Signal Event EOPEnd of Pipe每个 Queue 创建一个HSA_EVENTTYPE_SIGNALevent在命令流末尾插入RELEASE_MEMPM4 包DATA_SEL signal slot addressINT_SEL send interrupt当 GPU 执行到该包时写 event_id 到 signal page slotGPU 直接写共享内存发送 MSI-X 中断内核kfd_signal_event_interrupt()→ 唤醒等待线程用户态hsaKmtWaitOnMultipleEvents(events[], N, WaitOnAny, timeout)返回后检查哪些 event 已 signaled方案二轮询 Signal Slot低延迟路径不走 ioctl wait直接在用户态循环读events_page[slot_index]当 slot 值从UNSIGNALED_EVENT_SLOT变为其他值 → 该 event 已 signal适用场景对延迟极敏感的 HPC / 推理场景注意需要适当的内存屏障__atomic_load方案三event_age 优化的增量等待hsaKmtWaitOnEvent_Ext()传入event_age内核只在 event_age 增长时唤醒避免重复处理同一 signal适用于 persistent queue 场景Queue 长期存活反复 signal多 Queue 依赖编排Queue A 完成后 signal Event XQueue B 在命令流头部插入WAIT_REG_MEM包等待 Event X 的 signal slot实现 GPU 内部的流水线不需 CPU 介入异常处理集成额外创建HSA_EVENTTYPE_MEMORYevent在WaitOnMultipleEvents中同时等待 signal memory event如果返回的是 memory event → 调用analysis_memory_exception处理完整代码示例伪代码 注释// 1. Create eventsfor(i0;iN;i)hsaKmtCreateEvent(desc_signal,false,false,events[i]);hsaKmtCreateEvent(desc_memory,false,false,mem_event);// 2. Submit work with signal packetsfor(i0;iN;i)submit_work_with_signal(queue[i],events[i]);// 3. Wait for any completion or exceptionall_events[0..N-1]events[];all_events[N]mem_event;while(completedN){statushsaKmtWaitOnMultipleEvents(all_events,N1,false,timeout);if(statusSUCCESS){for(i0;iN;i)if(is_signaled(events[i])){handle_completion(i);completed;}if(is_signaled(mem_event))handle_memory_fault(mem_event);}}// 4. Cleanupfor(i0;iN;i)hsaKmtDestroyEvent(events[i]);性能对比与最佳实践方式延迟CPU 开销适用场景ioctl Wait (中断)~μs低阻塞通用轮询 slot极低高忙等超低延迟event_age Wait~μs低Persistent Queue调试技巧用strace观察 ioctl 调用序列用rocm-smi --showevents监控 SMI 事件内核ftracetracekfd_signal_event_interrupt的触发频率检查/sys/kernel/debug/kfd/proc/pid/events