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 的对比,以及为什么需要它。

大纲

  1. 引言:GPU 异步编程的两个核心问题

    • "活儿来了"------CPU → GPU 通知(Doorbell)
    • "活儿干完了"------GPU → CPU 通知(Event)
  2. Event 的设计目标

    • 精确感知 Queue/Kernel 完成
    • 异常上报(Page Fault、HW Exception)
    • 多 Queue 间依赖编排(流水线)
  3. Event 类型一览

    • 结合 hsakmttypes.h 中的枚举,逐一说明:

      类型 用途
      HSA_EVENTTYPE_SIGNAL 0 用户态 GPU signal,最常用
      HSA_EVENTTYPE_NODECHANGE 1 热插拔
      HSA_EVENTTYPE_DEVICESTATECHANGE 2 设备启停
      HSA_EVENTTYPE_HW_EXCEPTION 3 GPU shader 异常
      HSA_EVENTTYPE_SYSTEM_EVENT 4 GPU SYSCALL
      HSA_EVENTTYPE_DEBUG_EVENT 5 调试信号
      HSA_EVENTTYPE_PROFILE_EVENT 6 性能分析
      HSA_EVENTTYPE_QUEUE_EVENT 7 Queue idle / EOP
      HSA_EVENTTYPE_MEMORY 8 内存访问异常
  4. 三层架构概览

    • 硬件层:中断(MSI-X)+ 内存原子写(RELEASE_MEM
    • 内核层:KFD Event 对象 + Signal Page + wait_queue
    • 用户态层:Thunk API(hsaKmtCreateEvent / hsaKmtWaitOnEvent)→ ROCr hsa_signal_t
  5. Event 生命周期概览图

    复制代码
    Create → Associate with Queue → GPU executes → Signal → Wait/Poll → Destroy
  6. 与 Fence / Semaphore / Completion 的对比

    • Linux kernel fence vs KFD event
    • CUDA event vs HSA signal

关键代码引用

  • hsakmttypes.h: HSA_EVENTTYPE_* 枚举定义
  • kfd_events.h: struct kfd_event 结构体(预览,详细在第 2 篇)

第 2 篇:内核实现------KFD Event 对象与中断处理

目标

深入 kfd_events.ckfd_events.h,讲清楚内核如何管理 Event 对象、Signal Page、以及如何响应硬件中断并唤醒用户态。

大纲

  1. 核心数据结构

    • struct kfd_event

      c 复制代码
      struct kfd_event {
          u32 event_id;
          u64 event_age;        // 用于 age-based wait 优化
          bool signaled;
          bool auto_reset;
          int type;
          spinlock_t lock;
          wait_queue_head_t wq; // 等待者链表
          uint64_t __user *user_signal_address; // 用户态 signal slot 指针
          union { memory_exception_data; hw_exception_data; };
      };
    • struct kfd_signal_page:一整页 64-bit slot,每个 slot 对应一个 signal event

    • struct kfd_event_waiter:封装 wait_queue_entry_t,支持 multi-event wait

  2. Signal Page 机制(重点)

    • 分配:allocate_signal_page()__get_free_pages(),初始化为 UNSIGNALED_EVENT_SLOT (0xFFFFFFFFFFFFFFFF)
    • 映射到用户态:通过 mmap KFD 设备 fd,用户态得到 events_page 指针
    • GPU 写入:GPU 执行 RELEASE_MEM packet 时,将 event_id 写入对应 slot
    • 容量:KFD_SIGNAL_EVENT_LIMIT(默认 4096 或 256 兼容模式)
  3. Event 创建流程

    • kfd_event_create() → 区分 signal event 和 other event
    • Signal event:create_signal_event()allocate_event_notification_slot()idr_alloc() 分配 event_id(同时是 slot index)
    • Other event:create_other_event() → event_id 从 KFD_FIRST_NONSIGNAL_EVENT_ID 开始
  4. 中断触发路径(核心)

    • 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 = truewake_up_all(&ev->wq)
  5. Wait 机制

    • kfd_wait_on_events() → 为每个 event 创建 kfd_event_waiter,加入 ev->wq
    • 支持 WaitOnAll(所有 event 都 signal)和 WaitOnAny(任一 signal)
    • 超时处理:schedule_timeout()KFD_EVENT_TIMEOUT_IMMEDIATE(立即返回 / 轮询语义)
    • event_age 优化:避免重复处理已经看过的 signal
  6. Event 销毁与清理

    • destroy_event() → 唤醒所有 waiter(返回失败)→ idr_remove()kfree_rcu()
    • kfd_event_free_process()destroy_events() + shutdown_signal_page()
  7. 异常事件特殊处理

    • HSA_EVENTTYPE_MEMORYkfd_set_memory_exception_data() 填充 VA、GPU ID、failure reason
    • HSA_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_event
  • kfd_chardev.ckfd_ioctl.c: ioctl 入口(AMDKFD_IOC_CREATE_EVENT, AMDKFD_IOC_WAIT_EVENTS 等)

第 3 篇:Thunk 层实现------libhsakmt 如何封装 Event

目标

libhsakmt/src/events.c 出发,讲清楚用户态如何通过 ioctl 与内核交互、如何管理 events_page、以及 Wait 的完整流程。

大纲

  1. Thunk 层 Event API 总览

    API 功能
    hsaKmtCreateEvent() 创建 event
    hsaKmtDestroyEvent() 销毁 event
    hsaKmtSetEvent() CPU 端手动 signal
    hsaKmtResetEvent() 重置 event
    hsaKmtWaitOnEvent() 等待单个 event
    hsaKmtWaitOnMultipleEvents() 等待多个 event
    hsaKmtWaitOnEvent_Ext() 带 event_age 的扩展等待
  2. Event 创建详解

    • hsaKmtCreateEventCtx() 完整流程:
      1. 分配 HsaEvent 结构体
      2. 构造 kfd_ioctl_create_event_args(event_type, node_id, auto_reset)
      3. dGPU 特殊处理 :首次创建时分配 events_page(GPU 可见内存),通过 hsakmt_allocate_exec_aligned_memory_gpu()mmap KFD fd
      4. ioctl(AMDKFD_IOC_CREATE_EVENT) → 内核创建 event 并返回 event_id 和 event_slot_index
      5. 设置 HWData2 = &events_page[event_slot_index](用户态 signal 地址)
  3. Events Page 管理(重点)

    • iGPU vs dGPU 差异
      • iGPU:通过 mmap(fd, event_page_offset) 映射内核 signal page
      • dGPU:先用 hsakmt_allocate_exec_aligned_memory_gpu() 分配 GPU 可达内存,再通过 fmm_get_handle 告知内核
    • events_page 是一个 uint64_t[] 数组,每个 slot 8 字节
    • GPU 写入 slot → 用户态可直接读取(shared memory 语义)
  4. Wait 流程详解

    • hsaKmtWaitOnMultipleEvents_ExtCtx() 是核心实现:
      1. 构造 kfd_event_data[] 数组,填充每个 event_id
      2. 对 signal event 填入 last_event_age(优化避免重复唤醒)
      3. ioctl(AMDKFD_IOC_WAIT_EVENTS) → 阻塞在内核的 schedule_timeout
      4. 返回后检查 wait_result:SUCCESS / TIMEOUT
      5. 异常事件处理 :对 MEMORY 和 HW_EXCEPTION 类型,从 event_data 中提取详细错误信息并填入 HsaEvent 结构
  5. 异常事件的用户态分析

    • analysis_memory_exception():打印 VA、node_id、failure reason(NotPresent / ReadOnly / NoExecute)
    • 尝试 fmm_get_mem_info() 查询指针信息
    • 如果是 SVM 范围,调用 get_mem_info_svm_api() 通过 AMDKFD_IOC_SVM 查询属性
  6. 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 篇知识,给出完整的代码流程和最佳实践。

大纲

  1. 场景复现

    • 进程通过 hsaKmtCreateQueue() 创建 N 个 User Queue
    • 每个 Queue 提交一批 PM4/AQL 命令包
    • 问题:如何高效地知道哪个 Queue 的哪个 batch 完成了?
  2. 方案一:Signal Event + EOP(End of Pipe)

    • 每个 Queue 创建一个 HSA_EVENTTYPE_SIGNAL event
    • 在命令流末尾插入 RELEASE_MEM PM4 包:
      • DATA_SEL = signal slot address
      • INT_SEL = send interrupt
    • 当 GPU 执行到该包时:
      1. 写 event_id 到 signal page slot(GPU 直接写共享内存)
      2. 发送 MSI-X 中断
      3. 内核 kfd_signal_event_interrupt() → 唤醒等待线程
    • 用户态 hsaKmtWaitOnMultipleEvents(events[], N, WaitOnAny, timeout)
    • 返回后检查哪些 event 已 signaled
  3. 方案二:轮询 Signal Slot(低延迟路径)

    • 不走 ioctl wait,直接在用户态循环读 events_page[slot_index]
    • 当 slot 值从 UNSIGNALED_EVENT_SLOT 变为其他值 → 该 event 已 signal
    • 适用场景:对延迟极敏感的 HPC / 推理场景
    • 注意:需要适当的内存屏障(__atomic_load
  4. 方案三:event_age 优化的增量等待

    • hsaKmtWaitOnEvent_Ext() 传入 event_age
    • 内核只在 event_age 增长时唤醒(避免重复处理同一 signal)
    • 适用于 persistent queue 场景(Queue 长期存活,反复 signal)
  5. 多 Queue 依赖编排

    • Queue A 完成后 signal Event X
    • Queue B 在命令流头部插入 WAIT_REG_MEM 包,等待 Event X 的 signal slot
    • 实现 GPU 内部的流水线(不需 CPU 介入)
  6. 异常处理集成

    • 额外创建 HSA_EVENTTYPE_MEMORY event
    • WaitOnMultipleEvents 中同时等待 signal + memory event
    • 如果返回的是 memory event → 调用 analysis_memory_exception 处理
  7. 完整代码示例(伪代码 + 注释)

    c 复制代码
    // 1. Create events
    for (i = 0; i < N; i++)
        hsaKmtCreateEvent(&desc_signal, false, false, &events[i]);
    hsaKmtCreateEvent(&desc_memory, false, false, &mem_event);
    
    // 2. Submit work with signal packets
    for (i = 0; i < N; i++)
        submit_work_with_signal(queue[i], events[i]);
    
    // 3. Wait for any completion or exception
    all_events[0..N-1] = events[];
    all_events[N] = mem_event;
    while (completed < N) {
        status = hsaKmtWaitOnMultipleEvents(all_events, N+1, false, timeout);
        if (status == SUCCESS) {
            for (i = 0; i < N; i++)
                if (is_signaled(events[i])) { handle_completion(i); completed++; }
            if (is_signaled(mem_event))
                handle_memory_fault(mem_event);
        }
    }
    
    // 4. Cleanup
    for (i = 0; i < N; i++)
        hsaKmtDestroyEvent(events[i]);
  8. 性能对比与最佳实践

    方式 延迟 CPU 开销 适用场景
    ioctl Wait (中断) ~μs 低(阻塞) 通用
    轮询 slot 极低 高(忙等) 超低延迟
    event_age Wait ~μs Persistent Queue
  9. 调试技巧

    • strace 观察 ioctl 调用序列
    • rocm-smi --showevents 监控 SMI 事件
    • 内核 ftrace:trace kfd_signal_event_interrupt 的触发频率
    • 检查 /sys/kernel/debug/kfd/proc/<pid>/events

相关推荐
DeeplyMind1 个月前
AMDGPU驱动中Doorbell与Event机制简要对比
rocm·doorbell·hsa event
DeeplyMind1 个月前
第05章:HSA-API快速入门
agent·signal·queue·rocm·rocr-runtime·hsa
DeeplyMind3 个月前
ROCm rocr-libhsakmt分析系列4: HsaMemFlags分析
rocm·rocr·libhsakmt·hsamemflags
DeeplyMind7 个月前
rocr专栏介绍
linux·ai·amdgpu·rocm·rocr·libhsakmt·thunk