1. 前言
在 Linux 内核开发中,workqueue(工作队列)是一种极为重要的异步任务处理机制。它允许内核模块和驱动程序将需要在进程上下文(而非中断上下文)中执行的任务延后、异步地交由内核线程处理,从而实现中断处理与复杂/耗时操作的解耦,提升系统实时性和健壮性。
KFD(Kernel Fusion Driver),广泛使用 workqueue 机制来处理异步事件、内存管理、SVM(共享虚拟内存)区间管理、GPU 迁移等复杂任务。本文将系统介绍 workqueue 的原理、实现、API、典型用法,并结合 KFD 驱动源码进行深入分析。
2. Workqueue 的基本概念
2.1 为什么需要 Workqueue
-
中断上下文限制:在中断处理函数(ISR)中,不能执行阻塞、耗时或可能睡眠的操作,只能做快速判定和简单处理。
-
异步任务需求:许多内核任务(如 IO、内存回收、设备管理等)需要在进程上下文中异步完成,不能阻塞关键路径。
-
解耦与性能:workqueue 机制将复杂操作延后到内核线程中执行,提升中断响应速度,避免系统卡顿。
2.2 Workqueue 的基本原理
-
工作项(work item) :用
struct work_struct
或struct delayed_work
描述的任务单元,包含待执行的回调函数和上下文。 -
工作队列(workqueue):内核维护的任务队列,负责调度和执行工作项。可以是全局队列(system_wq)、专用队列,也可以是延迟队列。
-
内核线程 :workqueue 由内核线程(如
kworker/*
)驱动,负责从队列中取出工作项并调用其回调函数。
2.3 Workqueue 的优势
-
支持异步、延迟、定时任务。
-
自动并发调度,可根据 CPU 数量自动扩展 worker 线程。
-
支持多队列、优先级、绑定 CPU 等高级特性。
-
API 简单,易于集成到驱动和内核模块中。
3. Workqueue 的实现机制
3.1 关键数据结构
-
struct work_struct
:普通工作项,适合立即执行的任务。 -
struct delayed_work
:延迟工作项,适合定时/延后执行的任务。 -
struct workqueue_struct
:工作队列对象,描述一个 workqueue 的属性和状态。 -
struct worker
:内核 worker 线程,负责实际执行工作项。
3.2 工作项的生命周期
-
初始化: 使用
INIT_WORK()
或INIT_DELAYED_WORK()
宏初始化工作项,指定回调函数。 -
调度: 调用
schedule_work()
或queue_delayed_work()
将工作项加入队列。 -
**执行:**内核 worker 线程从队列中取出工作项,调用其回调函数。
-
完成/复用: 工作项执行完毕后可复用或释放。可通过
cancel_work_sync()
等 API 取消未执行的工作项。
3.3 工作队列的类型
-
system_wq:全局普通工作队列,适合大多数场景。
-
system_highpri_wq:高优先级队列。
-
system_long_wq:适合长时间运行任务。
-
system_unbound_wq:不绑定 CPU,可跨 NUMA 节点调度。
-
自定义 workqueue :驱动可通过
alloc_workqueue()
创建专用队列,支持更多定制。
3.4 典型 API
API 名称 | 作用说明 |
---|---|
INIT_WORK() | 初始化普通工作项 |
INIT_DELAYED_WORK() | 初始化延迟工作项 |
schedule_work() | 调度普通工作项到 system_wq |
queue_delayed_work() | 调度延迟工作项到队列 |
flush_work() | 等待指定工作项完成 |
cancel_work_sync() | 取消并同步等待工作项 |
alloc_workqueue() | 创建自定义工作队列 |
destroy_workqueue() | 销毁自定义工作队列 |
4. Workqueue 的实现细节
4.1 工作队列的调度机制
-
内核为每个 workqueue 维护一个任务队列和若干 worker 线程。
-
当有工作项被调度时,worker 线程会被唤醒,从队列中取出工作项并执行其回调。
-
对于延迟工作项,内核使用定时器机制,在到期后将其加入队列。
4.2 并发与同步
-
多个 worker 线程可并发处理不同的工作项,提升吞吐。
-
同一工作项不能被重复调度,内核通过
work->data
字段防止重复入队。 -
可通过
flush_work()
等 API 等待工作项完成,保证同步。
4.3 取消与回收
-
cancel_work_sync()
可取消未执行的工作项,并等待正在执行的工作项完成。 -
flush_workqueue()
可等待整个队列的所有工作项完成。
4.4 性能与扩展性
-
支持 NUMA、CPU 亲和性、优先级等高级特性。
-
可根据系统负载自动扩展 worker 线程数量,避免瓶颈。
5. Workqueue 在 KFD 驱动中的应用
KFD 驱动作为 AMD ROCm 平台的核心组件,广泛使用 workqueue 机制来处理异步事件、内存管理、SVM 区间管理、GPU 迁移等复杂任务。下面结合 KFD 源码,简要分析SVM中的应用场景。
5.1 SVM 区间管理中的 deferred work
5.1.1 deferred_list_work
cpp
INIT_WORK(&svms->deferred_list_work, svm_range_deferred_list_work);
-
用途 :
-
处理 SVM 区间的延迟操作,如区间拆分、合并、unmap、notifier 更新等。
-
保证复杂区间操作在进程上下文中安全完成,避免死锁和竞态。
-
-
典型流程 :
-
某些操作(如 unmap、属性变更)将区间加入
deferred_range_list
。 -
调用
schedule_work(&svms->deferred_list_work)
异步处理。 -
svm_range_deferred_list_work
回调中遍历 deferred list,依次处理每个区间的具体操作(如 unlink、free、notifier 更新等)。
-
5.1.2 restore_work
cpp
INIT_DELAYED_WORK(&svms->restore_work, svm_range_restore_work);
-
用途 :
-
处理 SVM 区间被驱逐(evict)后的恢复操作,如重新映射、迁移、恢复 GPU 队列等。
-
支持延迟重试机制,保证恢复操作在资源可用时自动重试。
-
-
典型流程 :
-
区间被 evict 后,调用
queue_delayed_work(system_freezable_wq, &svms->restore_work, delay)
。 -
svm_range_restore_work
回调中遍历所有被驱逐区间,尝试恢复映射和队列。 -
如果恢复失败,自动再次调度自身,直到成功。
-
5.2 VRAM BO 驱逐与释放
5.2.1 eviction_work
cpp
INIT_WORK(&svm_bo->eviction_work, svm_range_evict_svm_bo_worker);
-
用途 :
-
当 VRAM buffer object 需要被驱逐时,异步迁移数据到系统内存,并释放相关资源。
-
保证驱逐操作不会阻塞关键路径,提升系统响应能力。
-
-
典型流程 :
-
触发驱逐时,调用
schedule_work(&svm_bo->eviction_work)
。 -
svm_range_evict_svm_bo_worker
回调中完成数据迁移、资源释放等操作。
-
5.2.2 release_work
cpp
INIT_WORK(&svm_bo->release_work, svm_range_bo_wq_release);
- 用途 :
- 异步释放 VRAM BO 相关资源,避免在关键路径中直接释放导致死锁或性能抖动。
5.3 其他典型应用
KFD 中断处理 :KFD 的中断处理采用"快速判定 + 工作队列"模式,ISR 只做判定,复杂事件推送到工作队列异步处理(interrupt_work)
。
CRIU 检查点/恢复:部分 SVM 区间的恢复、属性设置等操作也通过 workqueue 机制异步完成。
6. KFD SVM 区间管理中的 Workqueue 代码分析
以 SVM 区间属性设置为例,流程如下:
-
属性设置请求
用户空间通过 ioctl 发起 SVM 区间属性设置请求,驱动进入
svm_range_set_attr
。 -
区间拆分/合并/克隆
调用
svm_range_add
,根据新属性和现有区间关系,拆分、克隆、合并区间,生成 insert_list、update_list、remove_list、remap_list。 -
事务性应用
-
insert_list:通过
svm_range_add_to_svms
和svm_range_add_notifier_locked
插入区间和注册 notifier。 -
update_list:应用新属性,必要时触发迁移和映射。
-
remove_list:通过
svm_range_unlink
、svm_range_remove_notifier
、svm_range_free
异步移除和释放区间。
-
-
延迟操作
某些操作(如 unmap、notifier 更新)通过
svm_range_add_list_work
加入 deferred_list,调用schedule_work(&svms->deferred_list_work)
异步处理。 -
驱逐与恢复
区间被驱逐时,通过
queue_delayed_work(&svms->restore_work)
异步恢复,保证系统健壮性。