Linux 内核 Compute Accelerators 子系统:DRM_ACCEL 框架的设计、配置与驱动接入
本文基于内核仓库中的 Compute Accelerators 子系统介绍文档,系统讲解 Linux 计算加速器(Compute Accelerators)子系统的定位、设备分类、与 DRM 子系统的复用关系,以及驱动开发者接入该框架的完整步骤:从 CONFIG_DRM_ACCEL 配置,到 DRIVER_COMPUTE_ACCEL 特性标志、accel_open() 与 DEFINE_DRM_ACCEL_FOPS 宏的使用,再到 major 号 261、/dev/accel/* 设备节点等用户空间暴露约定的源码级实现。读完后你将能够判断一款加速卡是否适合接入该框架,并知道在驱动中做哪两处改动即可把设备暴露为标准 accel 设备。
1. 子系统定位:以统一方式向用户空间暴露计算加速器
Linux 计算加速器子系统(compute accelerators subsystem)的设计目标是:以统一的方式向用户空间暴露计算加速器,并提供一组通用的基础功能(引自 introduction.rst)。
覆盖的设备形态包括两类:
- 独立的 ASIC 芯片(stand-alone ASICs);
- 集成在 SoC 或 GPU 内部的 IP 块。
这些设备通常被设计用来加速机器学习(ML)和深度学习(DL)计算,但 accel 层并不局限于这一类加速器——它是一个更通用的“计算加速设备”框架。
1.1 文档给出的三类典型设备
原文档将计算加速器归纳为三类,接入驱动设计时可直接作为硬件能力评估的参照:
| 类别 | 部署形态 | 关键硬件特征 |
|---|---|---|
| Edge AI(边缘 AI) | 边缘设备上的推理,如嵌入式 ASIC/FPGA,或 SoC 内部 IP(例如笔记本摄像头) | 通常通过寄存器配置,可带 DMA 也可不带 DMA |
| Inference data-center(数据中心推理) | 大服务器中的单用户/多用户设备,可独立存在,也可内嵌于 SoC 或 GPU | 板载 DRAM(存放 DL 拓扑)、DMA 引擎、命令提交队列(内核态或用户态);可能带 MMU 以管理多用户;可能支持 SR-IOV 虚拟化让多 VM 共享同一设备;通常还配 profiler、debugger 等工具 |
| Training data-center(数据中心训练) | 与推理卡类似,但算力与内存带宽更高(例如使用 HBM) | 通常具备横向/纵向扩展机制,可与服务器内或其他服务器中的训练卡互连 |
1.2 用户空间软件栈的现状
原文档指出:这些设备各自拥有针对不同硬件量身定做的运行时用户空间软件栈,且通常还包含一个编译器,用于向各自的定制计算引擎生成程序。用户空间中真正“公共”的层是 DL 框架,如 PyTorch 和 TensorFlow。
这意味着内核 accel 子系统要解决的并不是用户空间工具链碎片化问题,而是内核态驱动层的碎片化:让所有计算加速器共享一套 DRM 基础设施(设备管理、内存管理、提交/同步等),把差异留给具体驱动的硬件抽象部分。
2. 为什么构建在 DRM 之上:代码复用的设计决策
原文档“Sharing code with DRM”一节给出了这一子系统最重要的架构决策:
这类设备可以是 GPU 内部的 IP,也可能具有与 GPU 相似的特性。因此 accel 子系统将复用 DRM 子系统的代码和功能——accel 核心代码将成为 DRM 子系统的一部分,一个 accel 设备就是一种新的 DRM 设备类型。
这一决策带来三方面收益:
- 复用 DRM 庞大的代码库(drm_device/drm_minor/drm_file 管理、GEM 内存管理、syncobj 同步、debugfs/sysfs 基础设施等);
- 与拥有同类设备开发经验的 DRM 开发者协作;
- 为加速器驱动新增的特性同样可以回馈给 GPU 驱动。
从源码结构可以印证这一点:accel 的核心实现文件是 drivers/accel/drm_accel.c,它包含的文件头几乎全是 <drm/drm_*.h>(drm_drv.h、drm_file.h、drm_ioctl.h、drm_debugfs.h 等);而框架开关 DRM_ACCEL 的 Kconfig 直接嵌套在 if DRM 之内(见 drivers/accel/Kconfig)。
2.1 框架配置入口:CONFIG_DRM_ACCEL
框架的配置项定义在 drivers/accel/Kconfig:
if DRM
menuconfig DRM_ACCEL
bool "Compute Acceleration Framework"
help
Framework for device drivers of compute acceleration devices, such
as, but not limited to, Machine-Learning and Deep-Learning
acceleration devices.
...
This framework is integrated with the DRM subsystem as compute
accelerators and GPUs share a lot in common and can use almost the
same infrastructure code.
Having said that, acceleration devices will have a different
major number than GPUs, and will be exposed to user-space using
different device files, called accel/accel* (in /dev, sysfs
and debugfs).
source "drivers/accel/amdxdna/Kconfig"
source "drivers/accel/ethosu/Kconfig"
source "drivers/accel/habanalabs/Kconfig"
source "drivers/accel/ivpu/Kconfig"
source "drivers/accel/qaic/Kconfig"
source "drivers/accel/rocket/Kconfig"
endif
要点:
- 该选项是框架级开关(
menuconfig DRM_ACCEL),只有CONFIG_DRM开启时才可见; - 帮助文本明确说明:加速器与 GPU “共用大量公共部分,几乎可以复用同一套基础设施代码”,但会采用不同的 major 号,并以
accel/accel*的命名在/dev、sysfs 和 debugfs 中暴露; - 框架之下当前挂接的具体设备驱动 Kconfig 有:
amdxdna、ethosu、habanalabs、ivpu、qaic、rocket,与 drivers/accel/Makefile 所组织的子目录一一对应,目录结构即为drivers/accel/{amdxdna,ethosu,habanalabs,ivpu,qaic,rocket}加框架核心drm_accel.c。
因此,开发者接入的第一步就是确认内核已配置 CONFIG_DRM_ACCEL(文档“Getting Started”一节的要求)。
3. 与 GPU 的区分:独立 major 号与设备命名约定
为了防止庞大的用户空间图形软件栈(Mesa 等)把加速器误当作 GPU 来使用,原文档“Differentiation from GPUs”一节规定了两条隔离手段:
- 使用全新的 major 号 + 新的设备字符文件,把加速器与 GPU 在用户空间层面区隔开;
- 驱动源码单独放在内核树的
drivers/accel/目录(而不是drivers/gpu/drm/之下)。
加速器设备以专用 major 号 261 暴露给用户空间,并遵循如下命名约定:
| 暴露位置 | 约定 |
|---|---|
| 设备字符文件 | /dev/accel/accel* |
| sysfs | /sys/class/accel/accel*/ |
| debugfs | /sys/kernel/debug/accel/*/ |
3.1 major 号 261 的定义
ACCEL_MAJOR 常量定义在 include/drm/drm_accel.h 中:
#define ACCEL_MAJOR 261
注意它放在 include/drm/ 而非 include/uapi/——该 major 号是内核内部约定,用户空间通过设备节点路径访问,不需要在 uAPI 头文件中声明。
3.2 命名约定的源码实现:devnode 回调
/dev/accel/accel* 这一目录结构并非 udev 规则,而是由内核类(class)的 devnode 回调直接生成的。drivers/accel/drm_accel.c 中:
static char *accel_devnode(const struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "accel/%s", dev_name(dev));
}
static const struct class accel_class = {
.name = "accel",
.devnode = accel_devnode,
};
即:设备节点路径被硬编码为 accel/<设备名>,而设备名本身按约定是 accel0、accel1……,最终得到 /dev/accel/accel0 这样的节点;sysfs 侧由 class_register(&accel_class) 产生 /sys/class/accel/accel*/。
3.3 字符设备注册:stub fops 与按需切换
accel_core_init() 在 DRM 核心初始化阶段被调用(错误清理由 drm_core_exit() 转调 accel_core_exit() 完成),核心动作是把 major 261 注册为一个“stub”字符设备:
int __init accel_core_init(void)
{
int ret;
ret = accel_sysfs_init();
...
ret = register_chrdev(ACCEL_MAJOR, "accel", &accel_stub_fops);
...
}
其中 accel_stub_fops 只有一个真正的 open:
static int accel_stub_open(struct inode *inode, struct file *filp)
{
const struct file_operations *new_fops;
struct drm_minor *minor;
...
minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));
...
new_fops = fops_get(minor->dev->driver->fops);
...
replace_fops(filp, new_fops);
if (filp->f_op->open)
err = filp->f_op->open(inode, filp);
...
}
从源码结构看,其工作方式是:所有 accel 设备共用一个已注册的 major,文件打开时先根据 inode 的 minor 号从 accel_minors_xa(DEFINE_XARRAY_ALLOC(accel_minors_xa) 定义的 xarray)查找到对应的 drm_minor,再将该 file 的 fops 替换为具体驱动的 fops(replace_fops),最后调用驱动的 open。这与 DRM 自身的 drm_stub_open 机制完全同构——再次印证“accel 设备就是一种新的 DRM 设备类型”。
minor 号与设备实例的绑定则发生在设备实例化时:
void accel_set_device_instance_params(struct device *kdev, int index)
{
kdev->devt = MKDEV(ACCEL_MAJOR, index);
kdev->class = &accel_class;
kdev->type = &accel_sysfs_device_minor;
}
该函数用 MKDEV(261, index) 组合出 devt,并把设备实例挂到 accel sysfs 类(/sys/class/accel/accel*)之下。
3.4 公共 debugfs 节点
框架还为每个加速器创建了公共 debugfs 文件(位于 /sys/kernel/debug/accel/*/ 下),实现于 accel_debugfs_register():
static const struct drm_info_list accel_debugfs_list[] = {
{"name", accel_name_info, 0}
};
void accel_debugfs_register(struct drm_device *dev)
{
struct drm_minor *minor = dev->accel;
minor->debugfs_root = dev->debugfs_root;
drm_debugfs_create_files(accel_debugfs_list, ACCEL_DEBUGFS_ENTRIES,
dev->debugfs_root, minor);
}
公共节点 name 会打印驱动名、底层设备名、master unique 与设备 unique 信息,便于在 debugfs 中识别设备归属;具体驱动可在此基础上追加自己的调试节点。
4. Getting Started:驱动接入 accel 框架的两处改动
文档“Getting Started”一节给出了驱动接入清单,这里完整继承并结合源码逐条展开。
4.0 前置:先读 DRM 文档
文档明确要求:首先阅读 DRM 文档 Documentation/gpu/index.rst——它不仅讲解如何编写一个新的 DRM 驱动,还包含贡献流程、行为准则(Code of Conduct)、编码与文档风格,这些都同样适用于 accel 子系统。因为 accel 驱动在机制上就是一个 DRM 驱动,所有 drm_driver 回调(open/lastclose、GEM 回调、dumb 系列等)的语义完全一致。
4.1 改动一:设置 DRIVER_COMPUTE_ACCEL 特性标志
在你的 drm_driver.driver_features 字段中加入 DRIVER_COMPUTE_ACCEL。该标志定义在 include/drm/drm_drv.h:
/**
* @DRIVER_COMPUTE_ACCEL:
*
* Driver supports compute acceleration devices. This flag is mutually exclusive with
* @DRIVER_RENDER and @DRIVER_MODESET. Devices that support both graphics and compute
* acceleration should be handled by two drivers that are connected using auxiliary bus.
*/
DRIVER_COMPUTE_ACCEL = BIT(7),
两个关键约束(与文档表述一致):
- 互斥性:
DRIVER_COMPUTE_ACCEL与DRIVER_RENDER、DRIVER_MODESET互斥。同一个drm_device不能同时声明“渲染/显示”与“计算加速”身份,这是用户空间隔离(第 3 节)在驱动侧的强制点; - 图形+计算双能力设备的解法:若硬件既需要暴露图形设备文件又需要暴露计算设备文件,应当由两个驱动分别处理,并通过 auxiliary bus(辅助总线框架)把二者连接起来——典型场景如 GPU 内嵌的算力 IP 块。
4.2 改动二:使用 accel_open() 或 DEFINE_DRM_ACCEL_FOPS
把驱动 fops 结构中的 open 回调改为 accel_open()。drivers/accel/drm_accel.c 中的 accel_open 文档注释明确要求“drivers must use it as their &file_operations.open method”,其实现流程为:
int accel_open(struct inode *inode, struct file *filp)
{
struct drm_device *dev;
struct drm_minor *minor;
int retcode;
minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));
if (IS_ERR(minor))
return PTR_ERR(minor);
dev = minor->dev;
atomic_fetch_inc(&dev->open_count);
/* share address_space across all char-devs of a single device */
filp->f_mapping = dev->anon_inode->i_mapping;
retcode = drm_open_helper(filp, minor);
if (retcode)
goto err_undo;
return 0;
...
}
EXPORT_SYMBOL_GPL(accel_open);
可以看到它完成三件事:按 minor 号在 accel_minors_xa 中定位 drm_minor、递增设备打开计数、复用设备级 anon_inode 的 address_space 并交由 drm_open_helper() 完成 per-file 资源实例化与 drm_driver.open 回调。
更省事的方式是使用 DEFINE_DRM_ACCEL_FOPS 宏(定义于 include/drm/drm_accel.h)一次性生成整个 fops 结构:
#define DRM_ACCEL_FOPS \
.open = accel_open,\
.release = drm_release,\
.unlocked_ioctl = drm_ioctl,\
.compat_ioctl = drm_compat_ioctl,\
.poll = drm_poll,\
.read = drm_read,\
.llseek = noop_llseek, \
.mmap = drm_gem_mmap, \
.fop_flags = FOP_UNSIGNED_OFFSET
#define DEFINE_DRM_ACCEL_FOPS(name) \
static const struct file_operations name = { \
.owner = THIS_MODULE, \
DRM_ACCEL_FOPS, \
}
从源码结构看,DRM_ACCEL_FOPS 与 DRM_FOPS 的字段一一对应,唯一区别是 open 从 drm_open 换成了 accel_open。宏头部的内核文档注释还特别提醒:
- 生成的结构体已隐含
static,且内部引用THIS_MODULE,因此不能在多个驱动间共享——每个驱动必须各自DEFINE_DRM_ACCEL_FOPS(自己的名字)后赋给drm_driver.fops。
于是驱动的接入形态概括为:
drv->driver_features = DRIVER_COMPUTE_ACCEL;DEFINE_DRM_ACCEL_FOPS(accel_fops),然后drv->fops = &accel_fops;- 其余回调(GEM、
open/lastclose、命令提交等)按常规 DRM 驱动编写(参见 Documentation/gpu/index.rst)。
5. 框架核心代码导读:drivers/accel/drm_accel.c
drivers/accel/drm_accel.c 全文约 210 行,是 accel 子系统除各设备驱动外的全部框架代码,核心构件一览:
| 构件 | 作用 |
|---|---|
accel_minors_xa(xarray) |
保存 minor 号 → drm_minor 的全局映射,accel_stub_open/accel_open 均通过它定位设备 |
accel_class(class) |
创建 /sys/class/accel/;devnode = accel_devnode 使设备节点落在 /dev/accel/ 目录 |
accel_devnode() |
返回 accel/<设备名> 形式的节点路径 |
accel_core_init() / accel_core_exit() |
注册/注销 major 261 的字符设备与 accel class;由 DRM 核心的 init/exit 链路统一驱动 |
accel_set_device_instance_params() |
为设备实例设置 MKDEV(ACCEL_MAJOR, index) 与 class/type |
accel_open() |
驱动必须使用的 fops->open 实现(EXPORT_SYMBOL_GPL) |
accel_stub_open() |
字符设备统一入口,按 minor 切换 fops |
accel_debugfs_register() |
创建公共 debugfs name 节点 |
从源码结构看,整个框架不引入任何新的内存管理或提交队列机制——这些都直接复用 DRM/GEM/syncobj 基础设施;框架自身只负责“身份”:major 号、命名空间、minor 路由和公共调试入口。这也是文档中“accel 层提供 a common set of functionality”的具体所指。
6. 适用前提与限制小结
- 适用前提:设备属于计算加速器类别(含但不限于 ML/DL 加速),且驱动愿意按 DRM 驱动的形态编写(
drm_driver+ GEM + 自有 ioctl/提交接口)。 - 限制与约束:
CONFIG_DRM_ACCEL依赖CONFIG_DRM;DRIVER_COMPUTE_ACCEL与DRIVER_RENDER/DRIVER_MODESET互斥,图形+计算混合设备需借助 auxiliary bus 拆成两个驱动;DEFINE_DRM_ACCEL_FOPS生成的 fops 因THIS_MODULE而不可跨驱动共享;- 用户空间看到的设备节点固定为
/dev/accel/accel*(major 261),图形用户空间软件栈不会将其识别为 GPU 渲染设备。
7. 延伸阅读
- Documentation/accel/index.rst:accel 文档索引,另含
amdxdna、qaic、rocket等具体驱动的文档入口,可与 drivers/accel/ 下的驱动实现对照阅读; - Documentation/gpu/index.rst:DRM 驱动开发、贡献流程与编码风格,原文档将其列为 accel 驱动开发者的必读前置文档;
- drivers/accel/Kconfig、drivers/accel/Makefile:框架与已合入设备驱动的配置/构建入口;
- include/drm/drm_accel.h、include/drm/drm_drv.h:
ACCEL_MAJOR、DEFINE_DRM_ACCEL_FOPS、DRIVER_COMPUTE_ACCEL等接口的权威定义; - 该子系统的历史讨论可追溯至 Oded Gabbay 在 2022 年发起的新子系统方案讨论与补丁集(LKML 邮件线程),以及 Dave Airlie 在 LPC 2022 Accelerators BOF 后的总结文章,原文档 introduction.rst 的“External References”一节列有对应出处,可作为背景阅读。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00