AMD KFD技术分析7-2:event机制-内核实现

KFD(Kernel Fusion Driver)中的 event 机制是 ROCm/HSA 用户空间与内核、GPU 之间进行异步事件通知和同步的核心基础。它为用户进程提供了高效、灵活的事件等待、信号、异常和调试等能力。

1. KFD Event 的设计目标和用法

  1. 异步通知:支持用户空间等待 GPU 或内核事件(如信号、异常、调试等)。

  2. 高效同步:允许进程高效地等待事件发生,支持超时、自动复位等特性。

  3. 多类型支持:支持信号事件、硬件异常、内存异常、调试事件等多种类型。

  4. 安全隔离:每个进程拥有独立的事件空间,防止跨进程干扰。

典型调用流程如下:

  1. 用户空间通过 ioctl 创建事件,驱动分配 event_id 和 slot。

  2. 用户空间提交任务,GPU 执行后写 slot 信号事件。

  3. 用户空间通过轮询 slot 或 ioctl 阻塞等待事件完成。

  4. 事件完成后,驱动唤醒等待队列,用户空间获知事件完成。

  5. 事件销毁时,驱动回收资源,唤醒所有 waiter。

2. 内核核心数据结构

2.1 struct kfd_event

struct kfd_event {
    u32 event_id;                // 事件唯一ID
    u64 event_age;               // 事件“代数”,每signal一次,就增加1
    bool signaled;               // 是否已被信号
    bool auto_reset;             // 是否自动复位
    int type;                    // 事件类型
    spinlock_t lock;             // 事件自旋锁
    wait_queue_head_t wq;        // 等待队列
  
    union {
        struct kfd_hsa_memory_exception_data memory_exception_data;
        struct kfd_hsa_hw_exception_data hw_exception_data;
    };
    struct rcu_head rcu;         // RCU 异步释放
};
  • event_id:唯一标识,信号事件和非信号事件分配不同区间。

  • event_age:用于事件去重和同步,防止误判。

  • signaled/auto_reset:事件状态与自动复位属性。

  • wq:Linux 等待队列,支持进程/线程阻塞等待事件。

  • memory_exception_data/hw_exception_data:异常事件的附加数据。

  • type:事件类型。当前定义了如下的事件类型。其中用户态最常用的是SIGNAL类型。

#define KFD_EVENT_TYPE_SIGNAL 0
#define KFD_EVENT_TYPE_HW_EXCEPTION 3
#define KFD_EVENT_TYPE_DEBUG 5
#define KFD_EVENT_TYPE_MEMORY 8

2.2 struct kfd_event_waiter

struct kfd_event_waiter {
    wait_queue_entry_t wait;
    struct kfd_event *event;
    bool activated;
    bool event_age_enabled;
};
  • 用于实现多事件等待和唤醒。

2.3 struct kfd_signal_page

struct kfd_signal_page {
    //内核空间分配的信号页(signal page)的首地址
	uint64_t *kernel_address;

    //仅当内核申请的才释放
	bool need_to_free_pages;
};

/*create signal_page for the given kfd_process*/
static struct kfd_signal_page *allocate_signal_page(
                                struct kfd_process *p)
{
	void *backing_store;
	struct kfd_signal_page *page;

	page = kzalloc(sizeof(*page), GFP_KERNEL);
	if (!page)
		return NULL;

	backing_store = (void *) __get_free_pages(GFP_KERNEL,
					get_order(KFD_SIGNAL_EVENT_LIMIT * 8));
	if (!backing_store)
		goto fail_alloc_signal_store;

	/* Initialize all events to unsignaled */
	memset(backing_store, (uint8_t) UNSIGNALED_EVENT_SLOT,
	       KFD_SIGNAL_EVENT_LIMIT * 8);

	page->kernel_address = backing_store;
	page->need_to_free_pages = true;

	return page;

fail_alloc_signal_store:
	kfree(page);
	return NULL;
}
  • 用于信号事件的内核/用户空间共享页,事件信号状态通过 slot 反映。该结构体实现了内核与用户空间对同一块物理内存的高效共享和管理。

  • allocate_signal_page内部使用__get_free_pages申请连续的物理页作为signal page使用。这是内核分配函数,但KFD都是user mode 去创建的,所以该函数不会被调用。

3. 事件的创建与管理

libhsakmt下发AMDKFD_IOC_CREATE_EVENT ioctl,内核的响应函数如下:

static int kfd_ioctl_create_event(struct file *filp, struct kfd_process *p,
					void *data)
{
	struct kfd_ioctl_create_event_args *args = data;
	int err;
    
    //第一次下发create event的请求时,event_page_offset非0,其他时候都是0 
    //ugly codes!!!
	if (args->event_page_offset) {
		mutex_lock(&p->mutex);
		err = kfd_kmap_event_page(p, args->event_page_offset);
		mutex_unlock(&p->mutex);
		if (err)
			return err;
	}

	err = kfd_event_create(filp, p, args->event_type,
				args->auto_reset != 0, args->node_id,
				&args->event_id, &args->event_trigger_data,
				&args->event_page_offset,
				&args->event_slot_index);

	return err;
}

3.1 信号页(signal page)映射

  • 每个kfd_process分配一页(或多页)内存,作为信号事件的 slot 区域。

  • 每个信号事件对应 signal page 的一个 slot(64bit),使用event id作为slot索引值,slot 值为 UNSIGNALED_EVENT_SLOT 表示未信号。

kfd_kmap_event_page函数将用户态申请的signal page bo映射到cpu的内核虚拟地址上去,可以让内核访问该bo。该bo是GPU/CPU交互的关键,GPU在做完queue中的packet时就会写该queue关联的event,就是这个bo的event_slot_index位置。然后GPU发中断,在KFD的中断处理中,就会检查该位置是否是UNSIGNALED_EVENT_SLOT(非UNSIGNALED_EVENT_SLOT表明GPU写入了数据),然后set event。如果用户态调用了wait_on_event,就会被唤醒执行。

4. 事件信号与唤醒

4.1. 信号事件(Signal Event)

  • 由 GPU 或内核通过写 signal page slot 或直接调用 set_event_from_interrupt 实现信号。

  • 用户空间可通过轮询 signal page slot 或通过 ioctl 阻塞等待事件。

  • 支持 auto_reset,事件被唤醒后自动复位。

4.2. 事件唤醒机制

  • 事件信号时,唤醒所有等待队列上的 waiter。

  • waiter 结构体用于支持多事件等待(如 wait_any/wait_all)。

  • 支持事件的 auto_reset,唤醒后自动清除 signaled 状态。

5. event的销毁与资源回收

  • 事件销毁时,从进程的 idr 哈希表移除,释放 slot 和结构体。

  • 唤醒所有等待该事件的 waiter,防止死锁。

  • 信号页在进程退出时整体释放。

6. 与 GPU/中断的交互

  • GPU 通过中断通知内核有事件发生(如队列完成、异常等)。

  • 中断处理函数调用 kfd_signal_event_interrupt,查找对应进程和事件,信号事件并唤醒等待者。

  • 对于部分事件(如 signal event),GPU 直接写 signal page slot,内核只需同步 slot 状态和唤醒 waiter。

这部分与interrupt处理有关,是event机制的重要环节,想了解的朋友查阅:AMD KFD技术分析3-2:event机制-KFD 中断机制.

7. CRIU(进程快照/恢复 Checkpoint and Restore in Userspace)支持

事件支持 CRIU,进程快照时保存所有事件状态,恢复时重建事件和信号页,保证事件语义一致性。

8. 总结

AMD KFD中的event实现要点与优势如下:

  • 高效:信号事件 slot 直接映射用户空间,支持低延迟轮询。

  • 灵活:支持多类型事件、wait_any/wait_all、auto_reset、超时等丰富语义。

  • 安全:事件ID分区、信号页隔离、RCU释放等保证多进程安全。

  • 可扩展:支持异常事件、调试事件、CRIU等高级特性。

KFD event 机制为 ROCm/HSA 异构计算提供了高效、灵活、安全的异步事件通知和同步能力。其设计兼顾了高性能(用户空间 slot 直读)、多样性(多类型事件)、安全性(ID/slot 隔离)和可扩展性(异常、调试、CRIU等),是 AMD GPU 计算栈中不可或缺的基础设施。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值