Cover image for open-gpu-kernel-modules 编译

open-gpu-kernel-modules 编译

字数 2k
阅读
访客

时间轴

时间轴

2026-07-18

init


源码

1
git clone https://github.com/NVIDIA/open-gpu-kernel-modules.git

交叉工具链

本文使用 Arm GNU Toolchain 11.2-2022.02aarch64-none-linux-gnu-三元组)。

ARM目前总共发布了8种架构:ARMv1、ARMv2、ARMv3、ARMv4、ARMv5、ARMv6、ARMv7、ARMv8。

针对于支持ARMv8指令集的处理器可以使用-march=armv8-a参数编译代码,ARM GNU编译器可通过下面的链接下载

推荐使用ARM官网的编译器,它是Portable的,解压后即可使用。

本文使用的是11.2版本的交叉编译器

可以将其添加到环境变量

1
2
3
# ~/.bashrc或~/.bash_profile
emacs ~/.bashrc
export PATH="$HOME/tools/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu/bin:$PATH"

使用11.2.1版本的aarch64-none-linux-gnu-gcc

aarch64-none-linux-gnu-gcc
aarch64-none-linux-gnu-gcc

如果工具链三元组是aarch64-linux-gnu-(如 Ubuntu 的gcc-aarch64-linux-gnu包),把下面所有aarch64-none-linux-gnu-替换成aarch64-linux-gnu-即可。

编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
make -j$(nproc) modules \
ARCH=arm64 \
CROSS_COMPILE=aarch64-none-linux-gnu- \
TARGET_ARCH=aarch64 \
TARGET_OS=Linux \
CC=aarch64-none-linux-gnu-gcc \
CXX=aarch64-none-linux-gnu-g++ \
LD=aarch64-none-linux-gnu-ld \
AR=aarch64-none-linux-gnu-ar \
STRIP=aarch64-none-linux-gnu-strip \
OBJCOPY=aarch64-none-linux-gnu-objcopy \
HOSTCC=gcc \
SYSSRC="$HOME/repository/linux/linux-5.10.238" \
SYSOUT="$HOME/repository/linux/linux-5.10.238"

要点:

  1. ARCH=arm64 给 Kbuild,TARGET_ARCH=aarch64 给 NVIDIA 的 utils.mk(两者不能混)。
  2. 工具链通过 PATH + 显式 CC/CXX/LD/AR 双保险,因为 utils.mk 不读 CROSS_COMPILE。
  3. SYSSRC/SYSOUT 指向已用同一工具链配置好的 linux-5.10.238。

要清理的话:

1
2
3
4
make clean \
ARCH=arm64 \
SYSSRC="$HOME/repository/linux/linux-5.10.238" \
SYSOUT="$HOME/repository/linux/linux-5.10.238"
1
2
3
4
5
6
7
8
make -j$(nproc) modules \
ARCH=x86_64 \
TARGET_ARCH=x86_64 \
TARGET_OS=Linux \
CC=gcc CXX=g++ LD=ld AR=ar STRIP=strip OBJCOPY=objcopy \
HOSTCC=gcc \
SYSSRC="$HOME/repository/linux/linux-5.10.238-x86_64" \
SYSOUT="$HOME/repository/linux/linux-5.10.238-x86_64"

说明

  1. 原生编译不需要 CROSS_COMPILE,工具用本机 gcc 即可。仍要显式传 CC/CXX/LD/AR,因为 utils.mk 不读 CROSS_COMPILE。
  2. 和 arm64 的区别只在 ARCH/TARGET_ARCH:arm64 时是 ARCH=arm64 + TARGET_ARCH=aarch64;x86_64 时两者都是 x86_64。注意 utils.mk 里没有叫 arm64 的 TARGET_ARCH,只有 aarch64。

清理

1
2
3
4
make clean \
ARCH=x86_64 \
SYSSRC="$HOME/repository/linux/linux-5.10.238-x86_64" \
SYSOUT="$HOME/repository/linux/linux-5.10.238-x86_64"

目录结构

1. 顶层目录结构

1
2
3
4
5
6
7
8
9
10
open-gpu-kernel-modules/
├── Makefile # 顶层入口:make modules → 先编译 src/ 再编译 kernel-open/
├── utils.mk # NVIDIA 自有构建框架(CC/CXX/ARCH/对象规则等)
├── version.mk # 版本定义(例如:NVIDIA_VERSION = 610.43.03)
├── nv-compiler.sh # 探测编译器类型 (gcc/clang) 的脚本
├── src/ # ① OS 无关核心驱动源码(freestanding,编译成 .o)
├── kernel-open/ # ② Linux 内核适配层(Kbuild,编译成 .ko)
├── nouveau/ # 从 nouveau 提取 GPU 固件的脚本(不是驱动本体)
├── README.md / COPYING / SECURITY.md / CODE_OF_CONDUCT.md / CONTRIBUTING.md
└── .github/ # CI 自动化配置

核心机制:两层构建模型

理解该仓库的关键在于其两层构建模型。这个仓库并不是一个单一的 Linux 内核模块,而是由 NVIDIA OS 无关核心 与 Linux 内核适配层 拼装组合,最终生成 5 个 .ko 模块。

nvidia-two-layer-build.drawio
nvidia-two-layer-build.drawio

  • 第一层产物: 产生在src/nvidia/_out/Linux_<ARCH>/nv-kernel.o。代码使用-ffreestanding-fno-stack-protector编译,从而能在不同的操作系统(如 Linux、FreeBSD 等)间复用核心逻辑。

  • 第二层: 将第一层生成的 .o 文件当作“黑盒二进制”链接进最终的 .ko 模块中,并包装上 Linux 专属的钩子代码。

2. src: OS 无关核心

(约 211 万行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
src/
├── nvidia/ # ★ RM(资源管理器)核心,150 万行,仓库主体
│ ├── src/kernel/ # 内核态驱动实现
│ │ ├── gpu/ # GPU IP 分模块(详细见下文)
│ │ ├── core/ # 核心框架(NVOC 对象系统、线程、调度)
│ │ ├── rmapi/ # RM API(用户态 ioctl 入口)
│ │ ├── mem_mgr/ # 内存管理
│ │ └── os/ # OS 抽象层接口(由 kernel-open 实现)
│ ├── inc/ # 头文件(与 src/ 结构镜像,外加 libraries/ containers/ mmu/ nvlog/ ...)
│ ├── interface/ # 对外接口(rmapi, deprecated)
│ ├── arch/nvalloc/ # CPU 架构相关代码(common / unix / x86emu)
│ ├── generated/ # 自动生成的代码(NVOC 元数据等)
│ └── _out/ # 编译输出目录(Linux_aarch64, Linux_x86_64)

├── nvidia-modeset/ # 显示 modeset 逻辑,10 万行
│ └── src/ interface/ os-interface/ kapi/ lib/ include/

└── common/ # 跨模块共享代码,49 万行
├── sdk/nvidia/inc/ # 公共 SDK 头文件
├── nvswitch/ # NVSwitch 芯片驱动(lr10/ls10/soe/flcn/...)
├── nvlink/ # NVLink 高速互连
├── displayport/ # DisplayPort 协议实现
├── modeset/ # timing / hdmipacket
├── softfloat/ # 软浮点实现(Berkeley softfloat,8086-SSE 后端)
├── unix/ # nvidia-push / nvidia-3d / xzminidec / headsurface
├── uproc/os/ # 微处理器 OS 抽象(libos-v3.1.0)
└── shared/ inc/ src/ # 公共工具与状态码

3.src/nvidia/src/kernel/gpu/子目录(GPU IP 引擎)

这是驱动最核心的硬件控制部分,按硬件引擎划分:

  • gr/:Graphics(光栅化/着色/渲染核心)
  • fifo/:命令提交队列管理
  • ce/:Copy Engine(高性能内存拷贝引擎)
  • intr/:中断处理
  • sec2/:安全引擎
  • falcon/:Falcon 微控制器核心框架
  • gsp/:★ GSP(GPU System Processor) 核心
  • fsp/:Firmware Security Processor
  • gsplite/:轻量级 GSP 逻辑
  • pmu/:性能监控单元
  • nvdec/ nvenc/ nvjpg/ ofa/:视频编解码及光流加速引擎
  • bif/ bus/:PCIe 总线接口及总线控制
  • mem_mgr/ mem_sys/ mmu/ mig_mgr/:显存管理、MMU 页表、MIG 实例切分
  • disp/:显示输出控制
  • perf/ hwpm/ rc/ timer/ …:性能、硬件监控、Reset 恢复及定时器

架构演进背景: 现代 NVIDIA GPU 的 RM(资源管理器)核心主要运行在 GPU 内部的 GSP(一个 RISC-V 微处理器)上。src/nvidia 里的很大一部分代码实际上就是 GSP 固件本身的源码——这也是 NVIDIA 在 2020 年后逐步走向开源的关键底层演进。

4. kernel-open/ Linux 适配层

(约 22 万行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kernel-open/
├── Kbuild # 模块组装入口:遍历导入所有子模块的 *.Kbuild
├── Makefile # 被 Kbuild 调用,负责定位 Linux 内核源码树 (SYSSRC/SYSOUT)
├── conftest.sh # ★ 核心机制:编译时动态探测内核 API
├── conftest/ # conftest 的测试用例清单
│ ├── compile-tests/ # "内核是否有符号 X / 结构体成员 Y" 的 C 源码
│ └── header_presence/# "某头文件是否存在" 的检测
├── common/inc/ # 适配层公共头文件

├── nvidia/ # → nvidia.ko 主驱动 Linux 钩子(nv.o, nv-pci.o, nv-acpi.o...)
├── nvidia-modeset/ # → nvidia-modeset.ko 显示 modeset 钩子
├── nvidia-drm/ # → nvidia-drm.ko DRM/KMS 标准图形框架适配
├── nvidia-uvm/ # → nvidia-uvm.ko ★ 统一虚拟内存,15 万行(含按代际划分的硬件引用:ampere/turing/hopper/blackwell/rubin)
└── nvidia-peermem/ # → nvidia-peermem.ko InfiniBand/RDMA peer-to-peer 内存支持

conftest.sh机制: 这是 NVIDIA 驱动能“一份源码兼容无数内核版本”的核心秘密。在编译前,它会针对目标内核进行大量的片段编译测试(例如检测是否存在 kmem_cache_create_usercopy、pgprot_t 等)。测试结果会被写入 conftest/headers.h,随后源码中通过#if NV_IS_ENABLED(...)宏来条件编译对应的兼容代码。

最终生成的内核模块

模块名称主要作用主要源码构成
nvidia.ko主驱动核心:创建/dev/nvidia*设备节点,提供核心 ioctl 业务入口。kernel-open/nvidia/+src/nvidia/编译出的nv-kernel.o
nvidia-modeset.ko显示模式设置:控制分辨率、刷新率、多屏显示拼接等。kernel-open/nvidia-modeset/+src/nvidia-modeset/编译出的核心.o
nvidia-drm.koDRM/KMS 集成:现代 Linux 图形栈(如 Wayland)必需的直接渲染管理器适配。kernel-open/nvidia-drm/
nvidia-uvm.ko统一虚拟内存管理:实现 CPU/GPU 内存统一寻址。HPC 和 AI 计算的核心,体积巨大。kernel-open/nvidia-uvm/(约 15 万行独立实现)
nvidia-peermem.koRDMA P2P 支持:允许第三方 PCIe 设备(如 Mellanox 网卡)通过 GPUDirect 直接读写显存。kernel-open/nvidia-peermem/

代码量分布

1
2
3
4
5
6
7
src/nvidia              150 万行  ████████████████████  ← RM/GSP 核心,绝对主体
src/common 49 万行 ██████ ← 共享协议与芯片支持
kernel-open/nvidia-uvm 15 万行 ██ ← UVM 独立计算大头
src/nvidia-modeset 10 万行 █ ← 显示逻辑
kernel-open/nvidia 5 万行 █ ← Linux 主驱动钩子
kernel-open/nvidia-drm 1.7 万行 ← DRM 适配层
其他 <1 万行 ← 构建脚本与配置