ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux服务器GPU识别:从PCI枚举到驱动Probe的6步详解

Linux服务器GPU识别:从PCI枚举到驱动Probe的6步详解 很多人以为 Linux 服务器“认不出 GPU”是驱动没装好或者驱动版本不对实际上大多数识别失败的问题都发生在更前面内核通过 PCI 枚举发现设备、分配资源、再由 GPU 驱动 probe 接管这条链路里任何一步出错结果都一样——设备在系统里消失或者能看到设备但驱动不绑定。这篇内容把 Linux 认出 GPU 的过程拆成 6 个步骤从 PCI 枚举到驱动 probe 完整讲一遍同时给出每步的判断方法和常见排查顺序。适合刚接触 GPU 服务器运维、嵌入式平台调试 PCIe 设备、或者想搞明白nvidia-smi为什么没有输出的人。我自己调试这类问题时的一个体会是不要把目光一开始就盯在驱动安装包上而是先回答一个问题——设备到底有没有被系统当成一个 PCI 设备“看见”。只要这个前提不成立后面装什么都白搭。下面按实际发生顺序拆开讲。1. 六个步骤不是驱动安装而是设备从无到有的过程1.1 为什么“装驱动”前要先确认“设备在没在”先举一个最常见的场景。新到一台 GPU 服务器系统装好了然后开始装 NVIDIA 驱动。命令敲完重启nvidia-smi提示No devices were found。这时候习惯性反应是换驱动版本、禁 Nouveau、重新安装一通操作之后问题可能还在。但如果先看lspci | grep -i nvidia发现输出里根本没有对应设备行那问题就完全不是驱动层面的了。设备可能在 PCIe 链路上就没有完成枚举可能是供电、PCIe 链路协商、BIOS/固件设置、CPU 平台对 PCIe 端口的使能方式等造成的。所以理解从 PCI 枚举到驱动 probe 的过程最大的价值不是让你去背一条内核设备模型流程而是给你一条排查路径设备没出现时先查枚举枚举过了但没驱动再查匹配匹配上了但功能异常再查 probe 里的资源申请和初始化。1.2 六步全景图Linux 从硬件角度看到一个 PCIe GPU 设备通常要经过下面 6 个阶段步骤做的事对应内核/硬件概念识别不到时看什么位置1. PCI 配置空间发现CPU 通过配置周期读取总线上的设备配置空间Configuration Space、Vendor ID/Device ID访问配置空间是否无响应2. 总线遍历与设备编号从 Root Port 出发逐层扫描生成 BDFBus/Device/Function Number总线上是否能看到设备节点3. BAR 空间与资源分配为设备申请 MMIO 地址区域BAR0~BAR5、Memory/IO 资源内核报cant claim BAR或no space4. 设备对象挂入 Linux 设备模型生成struct pci_dev加入 bus 子系统pci_dev、pci_bus、pci_driver/sys/bus/pci/devices/下是否有目录5. 驱动匹配按 Vendor ID、Device ID、Driver ID 表匹配pci_driver.id_table、driver_override驱动是否进入bind状态6. 驱动 probe 与初始化调用驱动的probe()做资源申请、中断申请、设备初始化probe()、request_mem_region、DMA 初始化内核日志里是否出现 probe 报错或 timeout后面的内容会按这六步展开但不会机械地一步一节而是把相关联的环节放到一起讲因为实际排查时它们经常是连在一起出现的。2. 第 1~2 步PCI 配置空间扫描与 BDF 编号2.1 从哪里拿到设备身份配置空间PCI/PCIe 设备接入系统后并不像内存那样天然有一个固定地址可以被 CPU 直接读取。设备必须通过配置空间来暴露自己的身份和能力。配置空间的前 256 字节是 PCI 规范定义的标准区域里面有Vendor ID厂商 IDDevice ID设备 IDCommand 和 Status 寄存器Class Code设备类别比如 VGA 兼容控制器、3D 控制器、显示控制器BAR 寄存器Capabilities 指针中断引脚信息PCIe 设备还有扩展配置空间最多到 4096 字节存放 AER、SR-IOV、ACS 等扩展能力。CPU 要读取这些信息需要发起“配置读”。传统 PCI 使用 IO 端口0xCF8和0xCFC实现配置周期PCIe 则推荐使用内存映射配置访问ECAM也就是把配置空间映射成一段普通内存区域。x86 平台由 MMCONFIG / ACPI 提供访问基础ARM64 平台一般从设备树或 ACPI 中拿到 ECAM 范围。不管哪种方式系统层面做的事情都一样先读配置空间确认总线上这个 BDF 位置是否存在设备。2.2 枚举过程中内核做了什么系统启动或 PCI 子系统初始化时会从根总线开始逐级向下遍历。每到一个 Bus 节点都会尝试读取该 Bus 上可能存在的 Device 和 Function 的 Vendor ID。读取时如果 Vendor ID 返回0xFFFFFFFF说明这个位置没有设备跳过如果读到有效值就认为这里有一台设备继续读取 Device ID、Class Code、BAR 等信息。这一步经常出问题的点不在标准 PCIe 显卡上而在桥接器。如果路径上有 PCIe Switch 或 PCIe Bridge内核需要先正确识别桥设备然后给桥的下游总线分配 Bus Number再继续往桥下游扫描。如果桥自身枚举失败或者桥下游的 Bus Number 分配空间不够GPU 就算物理上连接正常也不会被扫到。一个很有代表性的现象是服务器里插多张 GPU 时个别 GPU 在lspci输出里完全看不到。这时候往往不是 GPU 本身坏了而是 PCIe Bridge 扫描时没有识别到设备或者 BIOS 预设的总线范围覆盖不够。对于 x86 服务器一般需要进 BIOS 检查 PCIe Slot 的 Bifurcation 配置、Link Speed 设置以及 CPU 下挂的 Root Port 是否被禁用。对于嵌入式平台则要检查设备树里 PCIe 控制器的状态和 PHY 初始化。扫描完成后每个被发现的设备会得到一个唯一的 BDF 编号例如01:00.0。其中前两位表示 Bus Number中间一位表示 Device Number最后一位表示 Function Number从lspci的输出里能看到设备在哪个 Bus 上。GPU 通常出现在01:00.0或03:00.0这类位置。如果连 BDF 都没有说明枚举阶段就已失败后面不必要浪费时间。3. 第 3~4 步资源分配与设备树的生成3.1 BAR 空间GPU 在给 CPU 的“门牌地址”设备被识别后还不能直接被驱动使用因为驱动要访问设备的寄存器、显存、门铃机制都需要通过一段地址空间。这就是 BAR 空间。每个 PCI 设备最多有 6 个 BARGPU 一般使用多个 BARBAR0 通常是显存或寄存器映射区BAR1、BAR3 可能用于显存映射BAR2、BAR4 可能用于寄存器或 DoorbellBAR 的工作方式是设备上电时BAR 寄存器里保存的初始值不一定有效系统通过往 BAR 写全 1 再读回来确定设备需要多大的地址空间然后分配一个合适的物理地址范围写回 BAR。这个行为通常由固件或内核完成。在 x86 服务器上多数情况下 BIOS/UEFI 会在启动早期完成资源分配。在内核里看到的是资源已经被安排好的状态。但在某些情况比如插了很多 PCIe 设备、IOMMU 占用地址窗口、或 BIOS 分配策略不稳定内核会输出类似下面的信息pci 0000:03:00.0: cant claim BAR 0 ... no space for resource或者桥接设备报告pci_bus 0000:04: bridge window ... no space available这就是热词里提到的“pci bridge 分配不到足够内存映射空间BAR 地址”一类问题。出现这种错误时GPU 即使出现在lspci输出中驱动 probe 阶段也可能因为ioremap失败而终止。对这种问题常见的处理思路包括在 BIOS 里打开Above 4G Decoding或把MMIO High Base调整到足够大的位置尝试内核参数pcirealloc让内核重新分配资源减少同时占用大量 BAR 空间的设备如果是虚拟化或设备直通场景确认 IOMMU 地址窗口配置是否正确3.2 Linux 设备模型中的“设备对象”一旦设备被枚举并完成资源分配内核会创建一个struct pci_dev把它挂到对应pci_bus的devices链表上。到这一步用户态就可以在 sysfs 中看到设备了ls /sys/bus/pci/devices/正常情况下输出里会有类似0000:01:00.0 0000:03:00.0如果 GPU 已经被识别还应该能在/sys/bus/pci/devices/0000:01:00.0/vendor里读到厂商 IDcat /sys/bus/pci/devices/0000:01:00.0/vendor cat /sys/bus/pci/devices/0000:01:00.0/deviceNVIDIA GPU 的 Vendor ID 一般是0x10deAMD GPU 一般是0x1002。如果这些文件不存在说明设备模型里还没有这个对象问题基本可以锁定在枚举或资源分配阶段。值得注意设备对象出现不代表驱动已经绑定。很多时候lspci能看到设备但/sys/bus/pci/drivers/nvidia下没有新设备的绑定链接这才是真正卡在驱动匹配环节。4. 第 5~6 步驱动匹配与 probe4.1 驱动如何“认领”设备Linux 中 PCI 设备驱动不是通过名字去匹配设备的而是通过驱动里维护的一张ID 表。以 NVIDIA 官方驱动为例其内核模块里维护了支持的 GPU Device ID。加载模块后驱动框架会把模块里的 Device ID 表与总线上的pci_dev匹配。如果匹配上则自动调用驱动的probe()如果匹配不上设备就处于“有设备无驱动”的状态。查看驱动与设备的绑定情况ls -l /sys/bus/pci/drivers/nvidia/如果看到0000:01:00.0说明设备已经绑定到nvidia驱动。如果这个目录下没有设备链接可能是驱动模块没有加载成功设备 ID 不在驱动支持的列表里设备被driver_override指定到其他驱动模块加载顺序或依赖有问题普通 PCI 设备的 Driver ID 表可以在驱动源码中看到。更直接的排查方式是用lspci -nn看设备和厂商 ID然后和驱动支持列表比对。需要提一下driver_override。这个机制允许用户强制某个 PCI 设备绑定到指定驱动即使 ID 表不匹配。命令方法是echo 0000:01:00.0 /sys/bus/pci/drivers/vfio-pci/bind或者通过 sysfs 设置 driver_overrideecho vfio-pci /sys/bus/pci/devices/0000:01:00.0/driver_override这种机制在设备直通、虚拟机透传场景很常见因为用户希望把 GPU 直接交给虚拟机使用而不是被宿主机驱动占用。理解这一步也就理解了热词里的“存在 PCI/PCIe 直通设备时部分虚拟机操作不可用”这类提示设备被绑定到vfio-pci后宿主机不再原生驱动它虚拟机管理平台不能随意挂起、迁移带直通设备的虚拟机因为设备状态和物理硬件紧密绑定遇到这类提示不是故障是设备直通架构的正常限制4.2 probe 之内到底发生了什么当驱动 ID 表匹配成功内核会调用pci_driver结构体里的probe函数。这一步不是简单做个标记而是要完成启用设备写 Command 寄存器打开 Memory Space、Bus Master 等读取能力寄存器解析 MSI/MSI-X Capability、Power Management Capability、PCIe Capability申请 BAR 资源调用pci_request_regions或pcim_iomap_regions分配 MSI/MSI-X 中断向量调用pci_alloc_irq_vectors配置 DMA 掩码调用dma_set_mask_and_coherent设置设备可访问的地址宽度初始化设备固件或上下文加载 VBIOS 或 GPU 固件初始化显存建立 GPU 内部状态注册杂项设备或字符设备让用户态可以通过/dev/nvidiactl、/dev/nvidia0等方式访问 GPU其中 DMA 掩码步骤值得关注。如果系统启用 IOMMU设备访问内存需要通过 IOMMU 页表GPU 驱动需要知道 DMA 地址范围。某些平台如果在 BIOS 里开启了 CSM、或使用旧式内存映射DMA 配置会出现异常导致 probe 函数执行到一半失败。驱动 probe 失败时内核日志里会留下痕迹。常见日志有NVRM: GPU at PCI:0000:01:00.0 has fallen off the bus. NVRM: failed to copy vbios. NVRM: nvkm_device_del ... failed这些日志说明 probe 过程已经进入 GPU 初始化阶段但拿不到预期资源。所以排查链路到这里要分成两个方向如果日志里根本没有 NVIDIA 或 GPU 相关的 probe 信息说明匹配没发生如果日志有加载过程但报错说明问题在资源、固件或硬件状态5. 从系统日志到硬件状态完整排查顺序5.1 先看三个命令的输出当遇到 GPU 不被识别或驱动不工作时我一般的排查顺序是lspci -nn | grep -i vga\|3d\|display\|nvidia这条命令确认设备是否存在以及被识别成什么类别。注意 Class Code 很关键VGA compatible controller表示设备有 VGA 兼容能力3D controller表示不带 VGA 输出的计算卡类型Display controller一般是集显或特殊显示设备然后看详细信息和当前内核驱动lspci -vnn -s 01:00.0输出里重点看Subsystem信息是否正确Kernel driver in use是nvidia、nouveau、vfio-pci还是根本没有这行Memory at ...是否显示了已分配的 BAR 地址LnkSta显示的链路速率和宽度是否正常最后看内核日志dmesg | grep -i pci\|nvidia\|nvrm\|vfio如果lspci有设备但dmesg完全没有相关驱动信息通常就是模块没有加载成功或匹配没通过。5.2 从“识别不到”到“probe 失败”的分层定位按六步流程可以把常见问题分成下面几类现象可能阶段判断方法lspci无 GPU 信息PCI 枚举失败检查物理插槽、PCIe Link、BIOS 槽位设置lspci有 GPU但报 BAR 空间不足资源分配失败看dmesg中cant claim/no spacelspci有 GPUdmesg有 probe 错误驱动 probe 初始化失败看 NVRM 或驱动自身日志/sys/bus/pci/drivers/nvidia没有设备链接驱动没有匹配成功加载模块、检查 ID 表、检查驱动顺序设备绑定了nouveau而不是nvidia驱动模块冲突禁用 Nouveau 或调整模块黑名单设备被绑到vfio-pci设备直通导致宿主机不可见确认虚拟化需求和 PCI 地址一个容易被忽略的点是多 GPU 环境和驱动安装顺序。系统里同时有多张 GPU 时驱动加载后不是“一张失败全部失败”而是每张卡独立 probe。你需要针对具体 BDF 看日志而不是只盯最后的整体结果。如果一张卡 probe 失败先看这个槽位的 PCIe Link 是否正常。GPU 从待机状态被唤醒时PCIe 链路会重新协商。若金手指接触不良、辅助供电不够、PCIe Gen4 信号质量差就可能出现链路训练失败。内核日志里如果有类似link down、timeout的 PCIe 报错就不是软件能完全解决的需要查硬件。6. 常见边界和嵌入式平台差异6.1 “设备出现了但驱动不管它”怎么处理这种情况很常见。lspci能看到 GPUnvidia-smi却说什么都找不到。先确认内核驱动模块状态lsmod | grep nvidia没有输出说明根本没有加载模块。再查modprobe nvidia如果模块加载失败dmesg会给出具体原因。常见问题包括内核版本和驱动编译版本不匹配内核启用了 Secure Boot但驱动模块没有签名模块依赖缺失比如nvidia-modeset、nvidia-drm加载失败驱动安装脚本没有把模块复制到当前内核的 modules 目录如果模块已经加载但设备没有绑定到 nvidia 驱动可以手动触发绑定echo 0000:01:00.0 /sys/bus/pci/drivers/nvidia/bind触发时如果 probe 失败dmesg会输出具体在哪里失败。如果触发前设备已经绑到 Nouveau则需要先解绑并确保 Nouveau 被正确屏蔽。6.2 驱动 probe 成功但使用中掉卡和 crash dump还有一种情况是设备一开始识别正常驱动 probe 成功但在高负载运行时出现GPU crash dump triggered或者卡直接从总线上消失。这类问题的排查方向不是“如何认出 GPU”而是“为什么设备在运行中被系统丢失”。常见原因包括电源功率不足或瞬时跌落导致 GPU 触发保护PCIe 链路不稳定运行中链路重训练显存过热或 GPU 固件问题驱动与 GPU 固件版本不匹配系统发生 DMA 错误或地址访问异常GPU crash dump triggered这类日志说明 GPU 内部检测到了异常状态并把现场保存下来。如果只是偶发可以关注温度、供电、PCIe 链路如果是稳定复现则需要确认是否驱动、CUDA 版本、GPU 固件或具体应用触发的指令序列存在兼容问题。6.3 嵌入式平台上的差异枚举不完全由内核负责前面讲的大多是 x86 服务器场景。在 Zynq、RK3588、i.MX 等 ARM 平台调试 PCIe 设备时逻辑相似但有一个关键差异谁来做枚举和总线扫描。x86 平台往往由 BIOS/UEFI 率先完成 PCI 枚举并把资源分配结果通过 ACPI 表告诉内核。ARM 平台没有传统 BIOSPCIe 控制器的初始化、ECAM 地址、复位时序、PERST 引脚控制都要通过设备树或固件完成。所以 ARM 平台上 PCIe 设备不识别排查顺序通常是确认 PCIe PHY 和控制器在设备树中已经启用确认时钟、复位、电源管脚配置正确确认 ECAM 范围和 Bus 范围是否合理用lspci看是否扫描到设备如果没扫到查看控制器 probe 是否成功比如在 Zynq 平台上调试 PCIe 外设很多时候不是设备本身问题而是设备树里ranges、bus-range、phys等属性没有配置对导致控制器认为没有外接设备。另外嵌入式平台使用 PCIe 时还要特别关注PCIe 链路复位时序。GPU 这类复杂设备在复位后需要一段时间完成内部初始化如果主控在 PERST 释放后太早发起配置读取设备可能还没有准备好配置读取返回异常。这种情况可以通过调整复位延时、检查kernel log里的相位来解决。6.4 驱动开发者怎么看 probe如果本身是做 GPU 驱动开发或设备驱动开发上面这些步骤会对应到更具体的代码细节。写一个 PCI 驱动时需要关心的不是nvidia-smi而是这几个要素static const struct pci_device_id my_gpu_ids[] { { PCI_DEVICE(0x10de, 0x1e04) }, // 这只是示例 { } }; MODULE_DEVICE_TABLE(pci, my_gpu_ids); static int my_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; ret pcim_enable_device(pdev); if (ret 0) { dev_err(pdev-dev, failed to enable device\n); return ret; } pci_set_master(pdev); return 0; }这个最简单的 probe 只做了两件事开启设备、设置 Bus Master。实际 GPU 驱动会比这复杂得多需要处理大量 BAR、显存映射、中断、固件加载、电源管理等问题。一个常见的新手错误是在 probe 里访问 BAR 之前没有确认 BAR 资源是否已经分配成功。一个更完整一点的写法要先读取资源if (pci_resource_len(pdev, 0) 0) { dev_err(pdev-dev, BAR0 is empty, check resource allocation\n); return -ENODEV; }这个检查能帮你更快地区分“设备枚举成功但资源没分配”和“probe 逻辑本身有问题”。对驱动开发者来说查看设备是否被正确驱动调用也可以直接访问 sysfscat /sys/bus/pci/devices/0000:04:00.0/enable cat /sys/bus/pci/devices/0000:04:00.0/resourceenable文件的值代表设备是否被命令寄存器使能。如果为 0说明设备虽然被枚举了但没有被成功开启。6.5 关于驱动安装和容器化环境的一点提醒最后说一个很多人在踩的坑安装官方 GPU 驱动之后第一次运行nvidia-smi正常但重启后系统进入图形界面又报错或者容器里看不到 GPU。这个问题其实不属于“认出 GPU”而是驱动栈和系统集成的问题。我的建议是把“设备识别”“驱动加载”“应用层访问”三层分开排查。设备识别用lspci和dmesg判断驱动加载用lsmod、ls -l /sys/bus/pci/drivers/nvidia判断应用层访问用nvidia-smi、容器运行时状态判断很多人一上来就在容器侧配置环境结果越改越乱。如果设备层没有绑定到nvidia驱动容器里无论怎么配都看不到 GPU。另外如果服务器启用了 Secure Boot先确认模块签名是否被系统接受。当前 Ubuntu 和部分国产 Linux 发行版默认开启 Secure Boot没有签名的内核模块会被拒绝加载。这种情况下dmesg会有类似locked down或module verification failed的信息。不要反复换驱动版本先处理签名策略。还有一个小经验判断 GPU 是否被正确绑定nvidia-smi不是唯一方法因为有些设备是计算卡不带显示输出lspci显示的是3D controller。在显存报错或固件异常时nvidia-smi会启动失败但内核日志可能显示 probe 成功。所以最终还是回到内核日志和 sysfs 来确认状态。从 PCI 枚举到驱动 probe这 6 步并不复杂但它决定了你在遇到 GPU 问题时是拿到一个明确方向还是反复在驱动版本里循环。对正在排查服务器 GPU 识别问题的人来说先跑一遍lspci -nn再看一眼dmesg | grep -i pci最后去/sys/bus/pci/drivers/确认绑定状态比盲目重装驱动有效得多。
返回列表