ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从PCI枚举到驱动probe:Linux GPU识别与驱动加载链路排查

2026/9/4 21:06:47 拓冰建站 浏览量
从PCI枚举到驱动probe:Linux GPU识别与驱动加载链路排查 在 Linux 服务器上排查 GPU 识别问题时一个常见的现象是lspci能看到显卡设备nvidia-smi却报找不到设备驱动模块明明已经加载/dev 下却没有对应节点或者系统自动绑定了nouveau导致闭源驱动安装后始终不生效。大多数情况下问题根源不在安装脚本而在内核从 PCI 枚举到驱动 probe 的链路中某一步没有走通。理解这条链路就等于理解 Linux 设备模型的核心。一张 GPU 从插入 PCIe 插槽到被系统识别、分配资源、匹配驱动并调用 probe中间要经历总线枚举、配置空间扫描、pci_dev建立、资源分配、ID 表匹配和 probe 初始化六个阶段。每个阶段失败的表现在屏幕上都不一样。这篇文章把这 6 步分别拆开讲清楚每步的内核动作、对应日志、验证命令并给出一个最小 PCI 驱动的可运行骨架以及一张用于日常排错的对照表。1. 先看懂六步链路PCI 枚举到驱动 probe 的完整路径1.1 六步分别完成什么Linux 并不像普通应用软件那样“读一下设备文件”就能认出一张 GPU。硬件从物理上存在到被驱动接管必须经过内核设备模型的完整处理。以下六步可以概括整条链路步骤内核动作对应机制失败后的常见现象1. PCI 总线枚举扫描 PCIe 总线查找每个插槽上的设备pci_scan_bus相关流程lspci看不到 GPU2. 配置空间解析读取 Vendor ID、Device ID、Class Code、BAR 等字段PCI configuration space 访问设备能看到但信息异常3. 建立pci_dev把设备挂入内核的pci_bus设备模型pci_scan_single_devicesysfs 中没有对应目录4. 资源分配为 BAR 映射地址范围为设备分配中断pcibios_allocate_resourcesdmesg 出现 BAR / bridge window 报错5. 驱动匹配用pci_device_id表或 class 信息匹配驱动pci_bus_match设备存在但driver目录为空6. probe 初始化调用驱动的probe使能设备、申请资源并注册用户空间接口pci_device_probe模块加载成功但没有 GPU 设备节点这张表也是排错的主索引。看到什么现象先判断是断在哪一步再决定看哪类日志。1.2 为什么问题最常出现在第 5 步和第 6 步之间很多人一提到 GPU 识别失败第一反应是“显卡坏了”或“驱动没装好”。实际上前 4 步由内核 PCI 子系统和固件完成只要硬件物理正常绝大多数情况都能通过。真正的分水岭是驱动匹配如果第 5 步匹配到了错误的驱动例如 GPU 被nouveau抢先绑定闭源 NVIDIA 驱动后续加载时会发现设备已经被占用。如果第 6 步 probe 失败例如显存资源申请失败、IOMMU 翻译异常、固件初始化超时驱动的probe会返回错误码内核会打印调用栈与错误信息但设备并不会消失。因此训练排错思路时应当先确认“设备在不在”再确认“被谁绑定”最后确认“probe 返回了什么”。下面按 6 步顺序展开。2. 前两步PCI 总线枚举与配置空间扫描2.1 BDF 地址和 PCIe 设备树PCIe 设备在系统中的唯一坐标由 Bus、Device、Function 组成即 BDF。在 x86 服务器上常看到类似0000:01:00.0的字符串含义是 domain 0000、bus 01、device 00、function 0。在 x86 平台UEFI/BIOS 固件开机早期通常会完成大部分物理扫描和资源预分配。Linux 启动后通过 ACPI 找到 host bridge再对总线下挂设备重新扫描把它们纳入自己的设备模型。在 ARM 和嵌入式平台则由设备树或 ACPI 描述 host bridge内核同样会对总线做扫描。这意味着即使固件已经识别设备内核仍然会重新建立一套属于 Linux 的设备抽象。内核扫描 PCI 总线时以配置空间读写为基本手段向某个 BDF 的配置空间发起访问如果读到有效的 Vendor ID就认为这个位置存在设备如果读回 0xFFFF 或无效值就跳过该位置。这也是 USB 枚举和 PCI 枚举思维的相似之处。2.2 配置空间里有哪些关键字段PCI 配置空间是一块固定布局的寄存器区域前 64 字节是 Header 0常见的设备关键字段如下偏移字段作用0x00Vendor ID厂商编号NVIDIA 通常是 0x10DEAMD 是 0x1002Intel 是 0x80860x02Device ID具体型号编号0x04Command控制 I/O 空间、内存空间、总线主控等0x08Revision ID芯片修订版本0x09Class Code设备类别显卡通常属于 0x03Display Controller0x10 - 0x24BAR0 - BAR5设备地址空间请求GPU 的显存和寄存器区通过 BAR 暴露0x2CSubsystem Vendor ID板卡厂商编号0x3CInterrupt Line中断号分配结果Class Code 和 Vendor/Device ID 是驱动匹配的入口。lspci能识别厂商名和型号本质上就是读了配置空间。2.3 用 lspci 和 setpci 查看枚举结果查看 GPU 是否被 PCI 枚举出来的最直接命令是lspcilspci -nn | grep -i nvidia典型输出类似01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA106 [GeForce RTX 3060 Lite Hash Rate] [10de:2504] (rev a1)解释一下这一段01:00.0是 BDFVGA compatible controller是根据 Class Code 翻译出的类型10de:2504是 Vendor ID:Device ID。如果这条命令没有任何输出说明 PCI 枚举阶段就没有发现设备问题更接近硬件链路或固件配置。如果lspci能看到设备但想验证具体配置字段可以读原始寄存器# 读取 BDF 为 01:00.0 的 Vendor ID 和 Device ID sudo setpci -s 01:00.0 0x00.w sudo setpci -s 01:00.0 0x02.w结果会以十六进制输出。要确认设备类别是否是显示控制器可以读 Class Code# 0x0b 是 Base Class0x0a 是 Sub Class sudo setpci -s 01:00.0 0x0b.b得到03表示显示控制器。这一步的意义在于验证内核读到的配置空间数据与显卡实物一致防止固件或 PCIe 链路问题导致配置空间被破坏。注意lspci -nn显示正常只能说明枚举和配置空间解析成功。后续驱动匹配和 probe 是否成功还需要继续看 sysfs 和 dmesg。3. 第三步和第四步从配置空间到内核设备模型3.1 pci_dev 和 pci_bus 的建立内核把扫描到的每个 PCI 设备封装成一个struct pci_dev对象并挂到对应的struct pci_bus上。设备在 sysfs 中的位置通常是/sys/bus/pci/devices/0000:01:00.0/这个目录下的文件可以直接反映内核设备模型的状态/sys/bus/pci/devices/0000:01:00.0/ ├── class ├── vendor ├── device ├── subsystem_vendor ├── subsystem_device ├── irq ├── resource ├── resource0 ├── driver - ../../../bus/pci/drivers/nvidia └── ...读取方式很简单DEV0000:01:00.0 cat /sys/bus/pci/devices/$DEV/vendor cat /sys/bus/pci/devices/$DEV/device cat /sys/bus/pci/devices/$DEV/class如果设备存在但driver是空链接或目录不存在说明设备已经被内核登记但还没有任何驱动与它绑定。这个阶段的pci_dev只是硬件的内核抽象不具备可用的显存映射和中断上下文。3.2 BAR 资源分配显存地址空间如何进入系统GPU 与 CPU 通信的关键地址资源是 BAR。BAR 是设备向系统声明“我需要一段地址空间”的窗口。对于独立显卡BAR0 可能暴露寄存器区BAR1 或 BAR2 可能直接映射一部分显存。BAR 分配要解决的是系统内存地址空间有限PCIe bridge 的窗口大小必须能覆盖下游设备所有 BAR 的需求。如果一张 GPU 需要 8GB 或 24GB 的地址窗口而它所在的 PCIe bridge 只预留了 512MB就会产生资源冲突。用lspci -v可以查看 BAR 分配结果lspci -vv -s 01:00.0输出中会包含类似内容Region 0: Memory at 地址范围 (64-bit, prefetchable) [size256M] Region 2: Memory at 地址范围 (64-bit, prefetchable) [size8G]size是最终分配给该 BAR 的可用范围。如果这里显示disabled或没有实际地址说明资源分配阶段没有成功。sysfs 也提供二进制接口来表示 BAR 映射cat /sys/bus/pci/devices/0000:01:00.0/resource输出的是一组起始地址、结束地址和标志位。第一行对应 BAR0第二行对应 BAR1依此类推。3.3 常见 BAR 分配失败现场在 dmesg 里经常能看到这类日志[ 3.472179] pci 0000:01:00.0: cant claim BAR 1 [mem 0x00000000-0xefffffff 64bit pref]: no compatible bridge window [ 4.058352] pci 0000:03:00.0: BAR 0: no space for [mem size 0x20000000 64bit pref] [ 4.058359] pci 0000:03:00.0: BAR 0: failed to assign [mem size 0x20000000 64bit pref]不同内核版本和硬件平台的关键字不完全一致但核心含义都是PCIe bridge 的可用窗口无法覆盖子设备 BAR 请求的大小。显卡的显存越大越容易触发这类问题。对这一类问题常见的处理方向包括在固件中打开Above 4G Decoding让 64 位 BAR 可以使用 4GB 以上的地址空间。内核启动参数加入pcirealloc允许内核在启动阶段重新分配 bridge 窗口。对旧平台检查 CSM 或 PCIe 资源分配相关的固件策略。确认物理插槽所在的 bridge 是否只有该 GPU 一个下游设备减少窗口被其他设备挤占的可能。这里要强调一个原则先确认“资源有没有给够”再怀疑驱动。nvidia-smi报错并不能直接证明闭源驱动安装文件损坏很多情况是内核层面 BAR 映射失败导致用户空间工具根本无法访问设备寄存器。4. 第五步驱动注册与 ID 匹配机制4.1 pci_device_id 与 MODULE_DEVICE_TABLEPCI 设备找到对应驱动并不依赖设备“主动上报自己的名字”而是反过来驱动模块声明自己支持哪些 Vendor ID 和 Device ID内核把设备 ID 与驱动 ID 表逐一比较。驱动源码中通常这样声明static const struct pci_device_id mygpu_ids[] { { PCI_DEVICE(0x10DE, 0x2504) }, { } }; MODULE_DEVICE_TABLE(pci, mygpu_ids);MODULE_DEVICE_TABLE的作用有两个一是把数组信息编入模块的 alias二是让内核在模块加载时知道这个驱动能匹配哪些 PCI 设备。编译完成后可以用modinfo查看模块生成的 aliasmodinfo mygpu_pci.ko | grep alias典型的 alias 格式如下alias: pci:v000010DEd00002504sv*sd*bc03sc00i00*这段 alias 的含义是Vendor ID 为 0x10DE、Device ID 为 0x2504任意 Subsystem 厂商和型号Class Code 为显示控制器。内核在匹配时会解析设备 ID 和 alias 规则命中就调用驱动的probe。4.2 为什么同一块 GPU 会匹配到 nouveauLinux 内核主线自带nouveau驱动它面向 NVIDIA GPU但遵循的是内核自带驱动的加载顺序。闭源 NVIDIA 驱动通常是后安装的独立内核模块。当两者都存在于系统时可能出现lspci -k -s 01:00.0输出01:00.0 VGA compatible controller: NVIDIA Corporation Device 2504 Subsystem: Gigabyte Technology Co., Ltd Device 3754 Kernel driver in use: nouveau这里的Kernel driver in use: nouveau说明第 5 步匹配的是nouveau而不是预期中的nvidia。根本原因是设备模型在设备注册或驱动注册时谁先完成匹配谁就绑定设备。nouveau通常被打包进 initramfs启动早期就存在因此会优先占用设备。解决方向不是删除内核自带驱动源码而是在模块层面禁止nouveau自动加载。常见做法是新建 modprobe 配置blacklist nouveau options nouveau modeset0然后重建 initramfs再重启系统。blacklist阻止了该模块被自动加载但并不能阻止用户用modprobe nouveau手动加载。options nouveau modeset0是额外保险避免 nouveau 在加载时开启 modeset。4.3 干预匹配结果的两种方式如果系统同时存在多个驱动要匹配同一设备除了 blacklist还可以使用 sysfs 的driver_override指定唯一驱动。例如强制设备绑定到nvidiaecho -n nvidia | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver_override执行后需要先把当前绑定的驱动解绑再触发重新绑定。这种方式在生产环境控制 PCI 设备直通和驱动选型时很常见但要对 do. 提前确认目标驱动已经加载且模块名称正确。写错驱动名会导致内核找不到控制函数设备停留在无驱动状态。注意先查看/sys/bus/pci/devices/0000:01:00.0/driver当前指向哪个驱动再决定是 blacklist、unbind 还是 driver_override。不要直接删除驱动模块否则系统里可能出现悬空设备。5. 第六步probe 被调用后发生了什么5.1 probe 函数的标准流程当设备与驱动匹配成功后内核会调用驱动在struct pci_driver中注册的probe函数。以 PCI GPU 驱动为例probe 要做的事情通常包括调用pci_enable_device打开设备的 Memory、I/O 或 Bus Master 位。调用pci_request_regions申请设备的 BAR 资源避免其他驱动重复占用。读取 BAR 长度并对需要的 BAR 做ioremap或pci_iomap建立内核虚拟地址映射。申请中断注册中断处理函数。初始化 GPU 内部的寄存器状态、显存管理器。如果这是 DRM 驱动还要注册 DRM 设备最终在用户空间创建/dev/dri/card0等节点。在 probe 成功时返回 0任何一步失败都要返回负的错误码并清理已申请资源。这一步是“设备从硬件变成可用设备”的关键。只有 probe 成功返回 0内核才认为设备已经就绪。5.2 最小 PCI 驱动骨架为了把机制说清楚下面给出一个可编译的最小 PCI 驱动骨架。它模拟了 GPU 驱动最核心的注册和 probe 流程实际场景中需要根据自己的设备调整 Vendor ID、Device ID 和资源映射逻辑。// mygpu_pci.c #include linux/module.h #include linux/pci.h #define MYGPU_VENDOR_ID 0x10DE static int mygpu_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; ret pci_enable_device(pdev); if (ret) { dev_err(pdev-dev, pci_enable_device failed: %d\n, ret); return ret; } ret pci_request_regions(pdev, mygpu); if (ret) { dev_err(pdev-dev, pci_request_regions failed: %d\n, ret); pci_disable_device(pdev); return ret; } dev_info(pdev-dev, BAR0 length %llu\n, (unsigned long long)pci_resource_len(pdev, 0)); // 真正的 GPU 驱动在这里会做 pci_iomap、 // 申请中断、初始化显存、注册 DRM 设备等操作。 pci_set_drvdata(pdev, NULL); dev_info(pdev-dev, mygpu probe ok\n); return 0; } static void mygpu_remove(struct pci_dev *pdev) { pci_release_regions(pdev); pci_disable_device(pdev); dev_info(pdev-dev, mygpu removed\n); } static const struct pci_device_id mygpu_ids[] { { PCI_DEVICE(MYGPU_VENDOR_ID, 0x2504) }, { } }; MODULE_DEVICE_TABLE(pci, mygpu_ids); static struct pci_driver mygpu_driver { .name mygpu, .id_table mygpu_ids, .probe mygpu_probe, .remove mygpu_remove, }; module_pci_driver(mygpu_driver); MODULE_LICENSE(GPL); MODULE_DESCRIPTION(Minimal PCI driver example for GPU probe);配套 Makefileobj-m mygpu_pci.o KVER ? $(shell uname -r) KDIR ? /lib/modules/$(KVER)/build all: $(MAKE) -C $(KDIR) M$(PWD) modules clean: $(MAKE) -C $(KDIR) M$(PWD) clean编译前需要安装与当前内核版本匹配的开发头文件。# Debian / Ubuntu 系 sudo apt update sudo apt install linux-headers-$(uname -r) # RHEL / Rocky / CentOS 系 # sudo yum install kernel-devel kernel-headers编译并加载make sudo insmod mygpu_pci.ko加载后查看日志dmesg | tail -n 30如果设备 ID 匹配成功且 probe 正常会看到类似mygpu 0000:01:00.0: BAR0 length 268435456 mygpu 0000:01:00.0: mygpu probe ok这段代码的最大价值不在功能而在于展示 probe 失败时通常如何体现。把代码中的 Vendor ID 改成不存在的值再加载会发现模块能加载但 dmesg 没有任何 probe 日志因为设备 ID 表没命中内核不会调用该驱动的 probe。5.3 probe 成功后的用户空间节点完整 GPU 驱动在 probe 成功之后会在用户空间建立可供应用程序访问的设备文件使用内核 DRM 框架的驱动通常会产生/dev/dri/card0、/dev/dri/renderD128等节点。使用 NVIDIA 闭源驱动的系统probe 并初始化后会产生/dev/nvidiactl和按 GPU 编号排列的/dev/nvidia0。nvidia-smi能列出设备前提是内核模块 probe 成功、用户空间驱动库版本与内核模块版本一致并且当前用户有权限访问设备节点。如果ls /dev/nvidia*一个都没有先不要重复重装驱动而应该回到内核模块加载日志里查找 probe 是否失败。6. 验证链路完整性的四个检查点6.1 通过 sysfs 检查设备、驱动绑定和资源推荐按顺序执行一组命令把第 3 步到第 6 步的状态一次看完。假设 BDF 是0000:01:00.0DEV0000:01:00.0 # 1. 设备是否被内核登记 ls -l /sys/bus/pci/devices/$DEV # 2. 设备身份信息 cat /sys/bus/pci/devices/$DEV/vendor cat /sys/bus/pci/devices/$DEV/device cat /sys/bus/pci/devices/$DEV/class # 3. 当前绑定驱动 readlink /sys/bus/pci/devices/$DEV/driver # 4. BAR 资源映射 cat /sys/bus/pci/devices/$DEV/resource # 5. 中断号 cat /sys/bus/pci/devices/$DEV/irq如果第 1 步目录不存在说明内核没有完成设备登记问题在枚举阶段。如果第 3 步readlink返回为空说明驱动没有绑定。如果第 4 步 resource 中地址全部为 0说明 BAR 分配失败。6.2 通过 dmesg 判断失败阶段dmesg 是排错时信息量最大的来源。建议按关键词过滤sudo dmesg -T | grep -iE pci|nvidia|nouveau|drm | tail -n 200逐段确认启动早期有没有出现pci 0000:01:00.0: [10de:2504] type 00 class 0x030000它代表配置空间扫描完成。有没有出现BAR: failed to assign或no compatible bridge window它代表资源分配失败。加载闭源驱动后有没有出现NVRM: failed to initialize或probe of 0000:01:00.0 failed with error -16它代表 probe 阶段出错。如果是 DRM 驱动确认drm相关日志是否创建了card0。6.3 驱动重扫与重新 bind如果确认硬件和资源都正常只是希望重新触发一次设备扫描和驱动绑定可以使用以下流程# 先解除当前绑定条件是设备当前可以被安全解绑 echo 1 | sudo tee /sys/bus/pci/devices/0000:01:00.0/remove # 重新扫描整个 PCI 总线 echo 1 | sudo tee /sys/bus/pci/rescan # 观察重新枚举和 probe 日志 sudo dmesg | tail -n 100生产环境中对正在运行 CUDA 任务的 GPU 执行该操作风险较大可能导致显存内容丢失、进程崩溃。更稳妥的做法是先停止任务再执行重扫。6.4 用一段可执行脚本快速定位把上面检查点整理成脚本可以显著缩短定位时间#!/bin/bash DEV${1:-0000:01:00.0} echo lspci lspci -nn -s ${DEV#*:} 2/dev/null || echo lspci 中未找到设备 echo sysfs 存在性 ls /sys/bus/pci/devices/$DEV /dev/null 21 echo 设备目录存在 || echo 设备目录不存在 echo 设备身份 echo vendor: $(cat /sys/bus/pci/devices/$DEV/vendor 2/dev/null) echo device: $(cat /sys/bus/pci/devices/$DEV/device 2/dev/null) echo class : $(cat /sys/bus/pci/devices/$DEV/class 2/dev/null) echo 绑定驱动 readlink /sys/bus/pci/devices/$DEV/driver 2/dev/null || echo 无驱动绑定 echo BAR 资源 cat /sys/bus/pci/devices/$DEV/resource 2/dev/null echo 最近内核日志 sudo dmesg | grep -iE $(basename $DEV)|nvidia|nouveau | tail -n 50脚本的输入参数是完整 BDF 路径默认使用0000:01:00.0。它把前面所有关键检查点串成一次执行输出顺序与内核处理流程一致。7. 常见问题与排错对照表结合前面的链路分析下面整理真正的生产环境中频繁出现的问题。对每个问题都从现象追溯原因而不是直接怀疑驱动文件失效。问题现象链路断点优先检查内容处理方向lspci完全看不到 GPU第 1 步BIOS 设置、物理插槽、PCIe 链路检查固件是否关闭插槽更换插槽验证设备在lspci中可见但没有 driver第 5 步/sys/bus/pci/devices/.../driver加载对应模块并确认 ID 表匹配绑定驱动为nouveau但需要 NVIDIA 闭源驱动第 5 步lsmod,modinfo nvidia配置 blacklist重建 initramfsdmesg 出现 BAR 或 bridge window 报错第 4 步lspci -vv、完整 dmesg开启 Above 4G Decoding尝试pcirealloc模块加载成功但无/dev/nvidia0或/dev/dri/card0第 6 步probe 返回值、NVRM 日志查看 dmesg检查 IOMMU、电源状态、硬件错误重新插拔 GPU 后/dev节点残留或乱序第 6 步udev 规则、设备移除流程先 remove 再 rescan必要时重启nvidia-smi找不到设备但lspci正常第 5/6 步内核模块版本与用户空间版本确认两部分版本一致查看nvidia-smi -q报错initramfs 阶段加载旧驱动导致开不了图形第 5 步/etc/modprobe.d和 initramfs只保留一种显示驱动并重建 initramfs这张表不是用来逐条背诵而是用来配合 6.2 的日志过滤方法做“二分定位”。看到报错先归类到链路阶段再深入该阶段。8. 从入门到生产实践建议与扩展方向8.1 学习环境怎么快速验证想理解 PCI probe 机制不一定要有一张真实显卡。可以使用支持直通或拥有第二块 PCIe 设备的机器也可以直接在虚拟化平台模拟 PCI 设备。对于学习目的重点不是设备是否高端而是能否走通“编译模块、加载模块、观察匹配和 probe 日志”这个闭环。建议练习顺序用lspci -nn找到一块真实 PCIe 设备例如网卡或 GPU。用lspci -vvv查看该设备的 BAR、中断、能力链表。编写最小 PCI 驱动只打印 BAR0 长度和中断号不做真实初始化。用modinfo验证模块 alias 与设备 ID 是否匹配。用echo 1 remove和echo 1 rescan观察设备生命周期。这套练习能建立对设备模型的基本体感以后再遇到NVRM: failed to initialize这类错误就不会把时间浪费在反复重装上。8.2 生产服务器需要额外确认的事项生产服务器与学习环境不同安装显驱和排查识别问题时必须考虑影响面。先确认 GPU 没有正在运行 CUDA、训练或推理任务再进行驱动卸载、设备解绑和重新扫描。升级内核前确认 GPU 驱动与新版内核兼容建议使用发行版维护的 DKMS 或 kmod 打包机制避免内核升级后模块失效。闭源驱动与nouveau不能同时生效。安装闭源驱动前blacklist 和 initramfs 重建都要落到配置管理中不能只在当前机器手动改一次。检查 BAR 和 IOMMU 状态时记录两组基线一组是正常工作的机器一组是故障机器。对比lspci -vvv的资源窗口和 dmesg 设备树片段通常能更快找出差异。用户空间工具与内核模块版本需要对齐。nvidia-smi和控制驱动属于同一版本发布体系混装会导致工具找不到设备。生产环境中还应该把排查命令的结果落成日志。例如用dmesg --ctime或journalctl -k把设备绑定和 probe 日志保存到独立文件这样出现问题后可以直接看上次成功时的状态而不是只凭记忆。8.3 扩展方向驱动调试和内核追踪熟悉 PCI 驱动框架后可以从两个方向继续深入。第一个方向是阅读内核源码中的 PCI 文档和示例。内核源码树中的Documentation/PCI/和Documentation/admin-guide/pci.rst提供了官方说明drivers/pci/下的枚举和资源分配代码则解释了所有细节。第二个方向是使用内核 ftrace 观察设备绑定和 probe 调用链。部分发行版的 ftrace 挂载点在/sys/kernel/tracing可以临时追踪pci_device_probe# 关闭追踪并清理 echo 0 | sudo tee /sys/kernel/tracing/tracing_on echo /sys/kernel/tracing/trace # 选择函数追踪 echo function_graph | sudo tee /sys/kernel/tracing/current_tracer echo pci_device_probe | sudo tee /sys/kernel/tracing/set_graph_function # 开启追踪然后触发模块加载或设备 rescan echo 1 | sudo tee /sys/kernel/tracing/tracing_on # 查看调用链 cat /sys/kernel/tracing/trace这种方式可以看到内核在 probe 过程中究竟调用了哪些函数、在哪一层返回错误。GPU 驱动领域越往后走越需要这种“跟着调用轨迹看代码”的能力而不是停留在反复输入安装命令。识别 GPU 的起点从来不在驱动安装界面而在内核的总线扫描和设备匹配过程。把 PCI 枚举、配置空间、BAR 资源、ID 匹配和 probe 这条链路理清之后面对任何硬件识别问题都能从lspci开始顺着 sysfs、dmesg 和驱动日志逐步定位到真正断掉的那一环。