单NVMe设备场景下modprobe与PCI文件操作的设备管理差异及链路上下电后的异常排查
你遇到的这个问题其实是两种PCI设备管理机制在链路上下电后的行为差异导致的,咱们一步步拆解来看:
一、两种操作流程及结果对比
先把你测试的两种操作梳理清楚:
方法1:Sysfs设备移除+PCI链路上下电+PCI总线重扫
# 查看NVMe块设备的sysfs路径 ls -l /sys/block/nvme0n1 # 输出:lrwxrwxrwx 1 root root 0 Apr 28 11:59 /sys/block/nvme0n1 -> ../devices/pci0000:00/0000:00:03.1/0000:09:00.0/nvme/nvme0/nvme0n1 # 从PCI总线移除设备 sudo sh -c "echo 1 > /sys/block/nvme0n1/device/device/remove" # 禁用PCI桥链路 sudo setpci -s 0000:00:03.1 BRIDGE_CONTROL.W=0x40:0x40 # 重新启用PCI桥链路 sudo setpci -s 0000:00:03.1 BRIDGE_CONTROL.W=0x00:0x40 # 重扫PCI总线 sudo sh -c "echo 1 > /sys/bus/pci/rescan"
结果:执行sudo nvme list后,NVMe设备正常识别。
方法2:NVMe模块卸载+PCI链路上下电+模块重新加载
# 卸载NVMe内核模块 sudo modprobe -r nvme # 禁用PCI桥链路 sudo setpci -s 0000:00:03.1 BRIDGE_CONTROL.W=0x40:0x40 # 重新启用PCI桥链路 sudo setpci -s 0000:00:03.1 BRIDGE_CONTROL.W=0x00:0x40 # 重新加载NVMe内核模块 sudo modprobe nvme
结果:执行sudo nvme list后,NVMe设备丢失;只有先从PCI总线移除设备再重扫,才能恢复识别。
二、dmesg日志揭示的核心差异
从你提供的dmesg日志可以看到两种方式的关键区别:
方法1对应的日志(sysfs移除+重扫)
[Fri Apr 28 12:11:04 2023] pci 0000:09:00.0: Removing from iommu group 23
[Fri Apr 28 12:11:04 2023] pci 0000:09:00.0: [1d82:0401] type 00 class 0x010802
...(设备BAR地址重新分配的日志)
[Fri Apr 28 12:11:04 2023] pci 0000:09:00.0: Adding to iommu group 23
...(各个BAR地址完成分配的日志)
[Fri Apr 28 12:11:04 2023] nvme nvme0: pci function 0000:09:00.0
[Fri Apr 28 12:11:04 2023] nvme nvme0: 15/0/0 default/read/poll queues
这里可以看到,sysfs的remove操作触发了设备从IOMMU组中移除,之后重扫时系统重新为设备分配了所有BAR地址,并重新加入IOMMU组,相当于完成了一次完整的PCI设备枚举流程,包括MMU空间和BAR的重置。
方法2对应的日志(modprobe卸载/加载)
[Fri Apr 28 12:23:31 2023] nvme nvme0: pci function 0000:09:00.0
[Fri Apr 28 12:23:31 2023] nvme 0000:09:00.0: enabling device (0000 -> 0002)
[Fri Apr 28 12:23:31 2023] nvme nvme0: 15/0/0 default/read/poll queues
[Fri Apr 28 12:23:31 2023] blk_update_request: I/O error, dev nvme0n1, sector 264 op 0x0:(READ) flags 0x80700 phys_seg 30 prio class 0
...(连续的I/O错误日志)
这里的问题在于,modprobe -r nvme只是卸载了NVMe驱动模块,但并没有将PCI设备从总线中移除——设备仍然在PCI总线上,只是没有驱动绑定。当你执行链路上下电后重新加载模块时,驱动只是重新绑定到设备,但没有触发完整的PCI设备枚举流程:没有重置MMU空间,也没有重新分配BAR地址,导致设备的I/O地址空间处于异常状态,最终引发I/O错误,设备无法正常被识别。
三、结论:链路上下电后的必要操作
你的猜测是对的:PCI链路上下电后,确实需要重置MMU空间和BAR地址才能让设备正常工作。
- 使用sysfs的
remove+rescan操作时,系统会自动完成MMU组移除/重新加入、BAR地址重新分配这些关键步骤,适配链路上下电后的设备状态。 - 而单纯的modprobe卸载/加载只是操作驱动模块,没有触发布备的PCI总线层面的重置,因此无法适配链路上下电后的状态,必须额外执行从PCI总线移除设备+重扫的操作才能恢复。
如果想要用modprobe的方式实现正常恢复,需要在链路上下电后,先执行PCI设备移除,再重新加载模块并重扫总线,比如:
sudo modprobe -r nvme sudo sh -c "echo 1 > /sys/bus/pci/devices/0000:09:00.0/remove" sudo setpci -s 0000:00:03.1 BRIDGE_CONTROL.W=0x40:0x40 sudo setpci -s 0000:00:03.1 BRIDGE_CONTROL.W=0x00:0x40 sudo sh -c "echo 1 > /sys/bus/pci/rescan" sudo modprobe nvme
备注:内容来源于stack exchange,提问作者pointnotfoe

