首页
/ Linux 内核 Compute Accelerators 子系统:DRM_ACCEL 框架的设计、配置与驱动接入

Linux 内核 Compute Accelerators 子系统:DRM_ACCEL 框架的设计、配置与驱动接入

2026-09-04 23:38:57作者:何将鹤

本文基于内核仓库中的 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 设备类型。

这一决策带来三方面收益:

  1. 复用 DRM 庞大的代码库(drm_device/drm_minor/drm_file 管理、GEM 内存管理、syncobj 同步、debugfs/sysfs 基础设施等);
  2. 与拥有同类设备开发经验的 DRM 开发者协作;
  3. 为加速器驱动新增的特性同样可以回馈给 GPU 驱动。

从源码结构可以印证这一点:accel 的核心实现文件是 drivers/accel/drm_accel.c,它包含的文件头几乎全是 <drm/drm_*.h>drm_drv.hdrm_file.hdrm_ioctl.hdrm_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 有:amdxdnaethosuhabanalabsivpuqaicrocket,与 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”一节规定了两条隔离手段:

  1. 使用全新的 major 号 + 新的设备字符文件,把加速器与 GPU 在用户空间层面区隔开;
  2. 驱动源码单独放在内核树的 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/<设备名>,而设备名本身按约定是 accel0accel1……,最终得到 /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_xaDEFINE_XARRAY_ALLOC(accel_minors_xa) 定义的 xarray)查找到对应的 drm_minor,再将该 filefops 替换为具体驱动的 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_ACCELDRIVER_RENDERDRIVER_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_FOPSDRM_FOPS 的字段一一对应,唯一区别是 opendrm_open 换成了 accel_open。宏头部的内核文档注释还特别提醒:

  • 生成的结构体已隐含 static,且内部引用 THIS_MODULE,因此不能在多个驱动间共享——每个驱动必须各自 DEFINE_DRM_ACCEL_FOPS(自己的名字) 后赋给 drm_driver.fops

于是驱动的接入形态概括为:

  1. drv->driver_features = DRIVER_COMPUTE_ACCEL
  2. DEFINE_DRM_ACCEL_FOPS(accel_fops),然后 drv->fops = &accel_fops
  3. 其余回调(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_ACCELDRIVER_RENDER/DRIVER_MODESET 互斥,图形+计算混合设备需借助 auxiliary bus 拆成两个驱动;
    • DEFINE_DRM_ACCEL_FOPS 生成的 fops 因 THIS_MODULE 而不可跨驱动共享;
    • 用户空间看到的设备节点固定为 /dev/accel/accel*(major 261),图形用户空间软件栈不会将其识别为 GPU 渲染设备。

7. 延伸阅读

  • Documentation/accel/index.rst:accel 文档索引,另含 amdxdnaqaicrocket 等具体驱动的文档入口,可与 drivers/accel/ 下的驱动实现对照阅读;
  • Documentation/gpu/index.rst:DRM 驱动开发、贡献流程与编码风格,原文档将其列为 accel 驱动开发者的必读前置文档;
  • drivers/accel/Kconfigdrivers/accel/Makefile:框架与已合入设备驱动的配置/构建入口;
  • include/drm/drm_accel.hinclude/drm/drm_drv.hACCEL_MAJORDEFINE_DRM_ACCEL_FOPSDRIVER_COMPUTE_ACCEL 等接口的权威定义;
  • 该子系统的历史讨论可追溯至 Oded Gabbay 在 2022 年发起的新子系统方案讨论与补丁集(LKML 邮件线程),以及 Dave Airlie 在 LPC 2022 Accelerators BOF 后的总结文章,原文档 introduction.rst 的“External References”一节列有对应出处,可作为背景阅读。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384