我有一个 PCIe 设备,只有在计算机完全关闭然后再次打开时才能正常工作。发出简单的reboot
或reboot -p
命令似乎不会重新启动 PCIe 卡的电源,这会导致其在重新启动后无法工作。
有没有办法从操作系统为 PCIe 插槽中的设备循环供电?我可以在 中找到它/sys/bus/pci/devices/0000*/
,但我不知道如何正确重置板。切换电源似乎是唯一的方法。
除此之外,我可以在某处更改设置,从而导致reboot
命令完全重新启动吗?
顺便说一句,我正在运行 Ubuntu 12.10。
答案1
移除设备并重新扫描 PCIe 总线
通过其目录中的函数删除 PCIe 设备remove
并通过 PCIe 总线的rescan
函数重新加载它,会导致内核重新启动 PCIe 设备,而无需重新启动计算机。
echo "1" > /sys/bus/pci/devices/DDDD\:BB\:DD.F/remove
sleep 1
echo "1" > /sys/bus/pci/rescan
上面代码中,DDDD:BB:DD.F
是设备槽格式域:总线:设备.功能。
答案2
PCI Express 中的重置有点复杂。重置主要有两种类型 - 常规重置和功能级重置。常规重置也有两种类型,基本重置和非基本重置。有关所有详细信息,请参阅 PCI Express 规范。
“冷重置”是 PCIe 设备通电后发生的基本重置。除了关闭系统然后重新打开之外,似乎没有触发冷重置的标准方法。在我的机器上,该/sys/bus/pci/slots
目录是空的。
“热重置”是在不断开设备电源的情况下触发的基本重置。似乎没有触发热重置的标准方法。
“热重置”是通过 PCI Express 链路触发的传统重置。当链路强制进入电气空闲状态或通过发送带有热重置位设置的 TS1 和 TS2 有序集时,会触发热重置。软件可以通过设置然后清除设备桥端口上游 PCI 配置空间中的桥控制寄存器中的辅助总线复位位来启动热复位。
“功能级重置”(FLR) 是仅影响 PCI Express 设备的单个功能的重置。它不得重置整个 PCIe 设备。 PCIe 规范不要求实现功能级重置。通过设置 PCI 配置空间中 PCI Express 功能结构中功能设备控制寄存器中的启动功能级复位位来启动功能级复位。
Linux 以/sys/bus/pci/devices/$dev/reset
.向该文件写入 1 将启动相应功能的功能级重置。请注意,这仅影响设备的特定功能,而不是整个设备,并且设备不需要根据 PCIe 规范实现功能级重置。
我不知道有什么“好的”方法可以触发热重置(没有 sysfs 条目)。但是,可以使用 setpci 来执行此操作:
#!/bin/bash
dev=$1
if [ -z "$dev" ]; then
echo "Error: no device specified"
exit 1
fi
if [ ! -e "/sys/bus/pci/devices/$dev" ]; then
dev="0000:$dev"
fi
if [ ! -e "/sys/bus/pci/devices/$dev" ]; then
echo "Error: device $dev not found"
exit 1
fi
port=$(basename $(dirname $(readlink "/sys/bus/pci/devices/$dev")))
if [ ! -e "/sys/bus/pci/devices/$port" ]; then
echo "Error: device $port not found"
exit 1
fi
echo "Removing $dev..."
echo 1 > "/sys/bus/pci/devices/$dev/remove"
echo "Performing hot reset of port $port..."
bc=$(setpci -s $port BRIDGE_CONTROL)
echo "Bridge control:" $bc
setpci -s $port BRIDGE_CONTROL=$(printf "%04x" $(("0x$bc" | 0x40)))
sleep 0.01
setpci -s $port BRIDGE_CONTROL=$bc
sleep 0.5
echo "Rescanning bus..."
echo 1 > "/sys/bus/pci/devices/$port/rescan"
确保在运行此脚本之前卸载所有附加的驱动程序。该脚本将尝试删除 PCIe 设备,然后命令上游交换机端口发出热重置,然后尝试重新扫描 PCIe 总线。该脚本也仅在具有单一功能的设备上进行了测试,因此对于具有多种功能的设备可能需要进行一些修改。
答案3
潜在方法#1
我认为你可以用这些命令来做到这一点:
禁用
echo 0 > /sys/bus/pci/slots/$NUMBER/power
使能够
echo 1 > /sys/bus/pci/slots/$NUMBER/power
其中$NUMBER
是 PCI 插槽的编号。
lspci -vv
可能有助于识别设备。这没有很好的记录......
潜在方法#2
我遇到了这个U&L 上的线程,类似的问题:该问题的一些答案表明您可以使用此命令重置:
echo "1" > /sys/bus/pci/devices/$NUMBER/reset
但是,我会阅读那里的答案!这样做是有条件的!具体来说我会阅读这个答案!
潜在方法#3
有一个 Unix 命令 ,setpci
它可以为您提供重置 PCI 总线中的设备的方法。
我没有看到此命令的任何具体示例,因此您必须通过谷歌搜索示例并浏览手册页。在您对它的使用充满信心之前,我会谨慎使用此命令。根据我所读到的内容,它直接操作硬件,因此与使用公开此类功能的工具相比,自己操作总是存在风险!
答案4
以 alex.forencich 发布的答案为基础
我必须进行一些更改才能使其在 CentOS 7 上运行,部分原因是我没有以 root 身份运行。此版本显示正在运行的命令。
#!/bin/bash
# e.g. $ ./pcie_hot_reset.sh 04:00.0
DEV=$1
if [ -z "$DEV" ]; then
echo "Error: no device specified"
exit 1
fi
if [ ! -e "/sys/bus/pci/devices/$DEV" ]; then
DEV="0000:$DEV"
fi
if [ ! -e "/sys/bus/pci/devices/$DEV" ]; then
echo "Error: device $DEV not found"
exit 1
fi
PORT=$(basename $(dirname $(readlink "/sys/bus/pci/devices/$DEV")))
if [ ! -e "/sys/bus/pci/devices/$PORT" ]; then
echo "Error: device $PORT not found"
exit 1
fi
echo -e "\nRemoving $DEV"
CMD="echo 1 | sudo tee /sys/bus/pci/devices/$DEV/remove"
printf "> $CMD\n"
eval $CMD
echo -e "\nPerforming hot reset of port $PORT"
CMD="setpci -s $PORT BRIDGE_CONTROL"
printf "> $CMD\n"
BR_CTRL=$(eval $CMD)
echo "Bridge control: $BR_CTRL"
CMD="sudo setpci -s $PORT BRIDGE_CONTROL=$(printf "%04x" $((0x${BR_CTRL} | 0x40)))"
printf "> $CMD\n"
eval $CMD
sleep 0.01
CMD="sudo setpci -s $PORT BRIDGE_CONTROL=$BR_CTRL"
printf "> $CMD\n"
eval $CMD
sleep 0.5
echo -e "\nRescanning bus"
CMD="echo 1 | sudo tee /sys/bus/pci/devices/$PORT/rescan"
printf "> $CMD\n"
eval $CMD
示例输出:
$ ./pcie_hot_reset.sh 04:00.0
Removing 0000:04:00.0
> echo 1 | sudo tee /sys/bus/pci/devices/0000:04:00.0/remove
1
Performing hot reset of port 0000:00:03.0
> setpci -s 0000:00:03.0 BRIDGE_CONTROL
Bridge control: 0010
> sudo setpci -s 0000:00:03.0 BRIDGE_CONTROL=0050
> sudo setpci -s 0000:00:03.0 BRIDGE_CONTROL=0010
Rescanning bus
> echo 1 | sudo tee /sys/bus/pci/devices/0000:00:03.0/rescan
1