Linux 6.6内核 PCIe 深度解析(九):复位机制 — 从 FLR 到 Secondary Bus Reset 的降级链

〇、全景:复位不是单一操作,是一条降级链

设备出错了要复位、VFIO 直通前要复位、驱动重装要复位------但"复位一个 PCIe 设备"这件事,内核里不是一句话能说清的。因为复位有多种方式,从轻到重,各有各的适用条件和副作用:

复制代码
从轻(只影响自己)到重(影响一片):
  FLR(Function Level Reset)        → 只复位单个 function
  AF FLR(Advanced Features FLR)     → SR-IOV VF / Conventional PCI 的 FLR
  PM reset(D3hot→D0)                → 借电源状态切换触发复位
  Slot reset                          → 热插拔槽位的复位
  Secondary Bus Reset(SBR)          → 复位整条 bus(会影响别的设备!)

内核的复位框架(drivers/pci/pci.c)把每种方式封装成一个"reset method",排成一个降级链 :复位时从第一个方法开始试,这个方法不支持(返回 -ENOTTY)就换下一个,直到有一个成功。
#mermaid-svg-0dlF0G6ei8qTOKNH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0dlF0G6ei8qTOKNH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0dlF0G6ei8qTOKNH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0dlF0G6ei8qTOKNH .error-icon{fill:#552222;}#mermaid-svg-0dlF0G6ei8qTOKNH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0dlF0G6ei8qTOKNH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0dlF0G6ei8qTOKNH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0dlF0G6ei8qTOKNH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0dlF0G6ei8qTOKNH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0dlF0G6ei8qTOKNH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0dlF0G6ei8qTOKNH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0dlF0G6ei8qTOKNH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0dlF0G6ei8qTOKNH .marker.cross{stroke:#333333;}#mermaid-svg-0dlF0G6ei8qTOKNH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0dlF0G6ei8qTOKNH p{margin:0;}#mermaid-svg-0dlF0G6ei8qTOKNH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0dlF0G6ei8qTOKNH .cluster-label text{fill:#333;}#mermaid-svg-0dlF0G6ei8qTOKNH .cluster-label span{color:#333;}#mermaid-svg-0dlF0G6ei8qTOKNH .cluster-label span p{background-color:transparent;}#mermaid-svg-0dlF0G6ei8qTOKNH .label text,#mermaid-svg-0dlF0G6ei8qTOKNH span{fill:#333;color:#333;}#mermaid-svg-0dlF0G6ei8qTOKNH .node rect,#mermaid-svg-0dlF0G6ei8qTOKNH .node circle,#mermaid-svg-0dlF0G6ei8qTOKNH .node ellipse,#mermaid-svg-0dlF0G6ei8qTOKNH .node polygon,#mermaid-svg-0dlF0G6ei8qTOKNH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0dlF0G6ei8qTOKNH .rough-node .label text,#mermaid-svg-0dlF0G6ei8qTOKNH .node .label text,#mermaid-svg-0dlF0G6ei8qTOKNH .image-shape .label,#mermaid-svg-0dlF0G6ei8qTOKNH .icon-shape .label{text-anchor:middle;}#mermaid-svg-0dlF0G6ei8qTOKNH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0dlF0G6ei8qTOKNH .rough-node .label,#mermaid-svg-0dlF0G6ei8qTOKNH .node .label,#mermaid-svg-0dlF0G6ei8qTOKNH .image-shape .label,#mermaid-svg-0dlF0G6ei8qTOKNH .icon-shape .label{text-align:center;}#mermaid-svg-0dlF0G6ei8qTOKNH .node.clickable{cursor:pointer;}#mermaid-svg-0dlF0G6ei8qTOKNH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0dlF0G6ei8qTOKNH .arrowheadPath{fill:#333333;}#mermaid-svg-0dlF0G6ei8qTOKNH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0dlF0G6ei8qTOKNH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0dlF0G6ei8qTOKNH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0dlF0G6ei8qTOKNH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0dlF0G6ei8qTOKNH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0dlF0G6ei8qTOKNH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0dlF0G6ei8qTOKNH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0dlF0G6ei8qTOKNH .cluster text{fill:#333;}#mermaid-svg-0dlF0G6ei8qTOKNH .cluster span{color:#333;}#mermaid-svg-0dlF0G6ei8qTOKNH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0dlF0G6ei8qTOKNH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0dlF0G6ei8qTOKNH rect.text{fill:none;stroke-width:0;}#mermaid-svg-0dlF0G6ei8qTOKNH .icon-shape,#mermaid-svg-0dlF0G6ei8qTOKNH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0dlF0G6ei8qTOKNH .icon-shape p,#mermaid-svg-0dlF0G6ei8qTOKNH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0dlF0G6ei8qTOKNH .icon-shape .label rect,#mermaid-svg-0dlF0G6ei8qTOKNH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0dlF0G6ei8qTOKNH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0dlF0G6ei8qTOKNH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0dlF0G6ei8qTOKNH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} -ENOTTY: 不支持
-ENOTTY
-ENOTTY
-ENOTTY
-ENOTTY
-ENOTTY
pci_reset_function(dev)
① probe 阶段:pci_init_reset_methods

把支持的 method 存进 dev->reset_methods\[\]
② 复位阶段:__pci_reset_function_locked

遍历 reset_methods\[\] 降级链
device_specific

设备驱动特定复位
acpi

ACPI _RST 方法
flr

Function Level Reset
af_flr

AF FLR(VF)
pm

D3hot→D0
bus

Slot / Secondary Bus Reset
都不支持 → -ENOTTY

一句话主线:复位 = 遍历 dev->reset_methods[] 降级链,每个 method 返回 -ENOTTY 表示"我不支持这个设备",就试下一个;返回 0 成功就停。其中最常用的是 FLR(轻量、只影响自己),最重的是 Secondary Bus Reset(会波及整条 bus)。


一、概念先行:为什么需要复位

1.1 三个典型场景

场景 为什么要复位
VFIO 直通 把设备直通给 VM 前,必须复位清除宿主遗留的脏状态(BAR 配置、DMA 状态、中断),防止泄漏给 VM
AER 错误恢复 设备报不可纠正错误后,复位是恢复流程的关键一步
驱动重装/热插拔 设备状态乱了,复位回到"刚上电"的干净状态

其中 VFIO 直通是复位的最硬消费者 ------VFIO 在把设备交给 VM 前会调 pci_reset_function 复位,确保 VM 拿到的是一个"干净"的设备。这也是你(简历 KVM/GPU 虚拟化方向)最该关心的场景。

1.2 复位后配置空间是"随机"的

__pci_reset_function_locked 的注释点破了复位的一个本质:

Resetting the device will make the contents of PCI configuration space random, so any caller of this must be prepared to reinitialise the device including MSI, bus mastering, BARs, decoding IO and memory spaces, etc.

复位后配置空间里的值不可信 (硬件复位把寄存器打回默认值甚至随机值),所以复位前后要 save/restore:复位前把配置空间存下来,复位后写回去(BAR、MSI、bus mastering 等都要重新配置)。


二、六种 reset method + 降级链

2.1 reset method 数组

c 复制代码
// drivers/pci/pci.c (v6.6, line 5331-5339)
/* dev->reset_methods[] is a 0-terminated list of indices into this array */
static const struct pci_reset_fn_method pci_reset_fn_methods[] = {
	{ },
	{ pci_dev_specific_reset, .name = "device_specific" },
	{ pci_dev_acpi_reset, .name = "acpi" },
	{ pcie_reset_flr, .name = "flr" },
	{ pci_af_flr, .name = "af_flr" },
	{ pci_pm_reset, .name = "pm" },
	{ pci_reset_bus_function, .name = "bus" },
};

六种方法,索引 1~6(0 是"终止"哨兵):

索引 name 方法 适用
1 device_specific 设备驱动提供的特定复位 某些设备有私有复位寄存器
2 acpi ACPI _RST 方法 平台固件提供的复位
3 flr Function Level Reset PCIe 设备(DEVCAP 有 FLR 位)
4 af_flr Advanced Features FLR Conventional PCI / 部分 VF
5 pm D3hot→D0 支持 PM 且 NO_SOFT_RESET 未置
6 bus Slot / Secondary Bus Reset 最后的手段(会波及别人)

2.2 降级链:__pci_reset_function_locked

c 复制代码
// drivers/pci/pci.c (v6.6, line 5483-5510)
int __pci_reset_function_locked(struct pci_dev *dev)
{
	int i, m, rc;

	might_sleep();

	/*
	 * A reset method returns -ENOTTY if it doesn't support this device and
	 * we should try the next method.
	 *
	 * If it returns 0 (success), we're finished.  If it returns any other
	 * error, we're also finished: this indicates that further reset
	 * mechanisms might be broken on the device.
	 */
	for (i = 0; i < PCI_NUM_RESET_METHODS; i++) {
		m = dev->reset_methods[i];
		if (!m)
			return -ENOTTY;

		rc = pci_reset_fn_methods[m].reset_fn(dev, PCI_RESET_DO_RESET);
		if (!rc)
			return 0;
		if (rc != -ENOTTY)
			return rc;
	}

	return -ENOTTY;
}

-ENOTTY 是这个降级链的协议:每个 method 的返回值有三种含义:

返回值 含义 降级链动作
-ENOTTY "我不支持这个设备" 试下一个 method
0 复位成功 停止
其他错误 设备有问题,更深层的复位可能也坏 也停止(不再降级)

注意最后一条:只有 -ENOTTY 才降级 。如果某个 method 返回 -EIO 之类的真实错误,说明设备本身有更深的问题,继续降级没意义,直接停。

2.3 探测:pci_init_reset_methods

dev->reset_methods[] 不是写死的,而是在设备初始化时 probe 出来的:

c 复制代码
// drivers/pci/pci.c (v6.6, line 5525-5543)
void pci_init_reset_methods(struct pci_dev *dev)
{
	int m, i, rc;

	BUILD_BUG_ON(ARRAY_SIZE(pci_reset_fn_methods) != PCI_NUM_RESET_METHODS);

	might_sleep();

	i = 0;
	for (m = 1; m < PCI_NUM_RESET_METHODS; m++) {
		rc = pci_reset_fn_methods[m].reset_fn(dev, PCI_RESET_PROBE);
		if (!rc)
			dev->reset_methods[i++] = m;
		else if (rc != -ENOTTY)
			break;
	}

	dev->reset_methods[i] = 0;
}

probe 阶段(PCI_RESET_PROBE)每个 method 只检查"能不能用" (比如 FLR 检查 DEVCAP 有没有 FLR 位),不真的执行复位。能用的存进 reset_methods[]。这样复位时直接按序试,不用每次重新探测。

sysfs 还能看/改这个链:cat /sys/bus/pci/devices/.../reset_method 看当前链,echo "flr pm" > reset_method 自定义(只保留 flr 和 pm),echo default 恢复默认,echo "" 禁用所有复位。


三、FLR:最常用的 function 级复位

FLR(Function Level Reset)是"只复位一个 function、不影响同设备其他 function"的复位,也是 VFIO 直通最常用的方式。

3.1 前提:DEVCAP 有 FLR 位

pcie_reset_flr 先检查设备是否支持 FLR:

c 复制代码
// drivers/pci/pci.c (v6.6, line 4791-4803)
int pcie_reset_flr(struct pci_dev *dev, bool probe)
{
	if (dev->dev_flags & PCI_DEV_FLAGS_NO_FLR_RESET)
		return -ENOTTY;

	if (!(dev->devcap & PCI_EXP_DEVCAP_FLR))
		return -ENOTTY;

	if (probe)
		return 0;

	return pcie_flr(dev);
}

两个前提:设备没被标记 NO_FLR_RESET(quirk 标记的怪设备)、DEVCAP 寄存器有 PCI_EXP_DEVCAP_FLR 位(bit 28,表示硬件支持 FLR)。

3.2 完整时序:pcie_flr

c 复制代码
// drivers/pci/pci.c (v6.6, line 4763-4781)
int pcie_flr(struct pci_dev *dev)
{
	if (!pci_wait_for_pending_transaction(dev))
		pci_err(dev, "timed out waiting for pending transaction; performing function level reset anyway\n");

	pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);

	if (dev->imm_ready)
		return 0;

	/*
	 * Per PCIe r4.0, sec 6.6.2, a device must complete an FLR within
	 * 100ms, but may silently discard requests while the FLR is in
	 * progress.  Wait 100ms before trying to access the device.
	 */
	msleep(100);

	return pci_dev_wait(dev, "FLR", PCIE_RESET_READY_POLL_MS);
}

FLR 的完整时序分四步:

  1. pci_wait_for_pending_transaction(pci.c:4746):先等设备把 pending 的 transaction 做完(Transaction Pending 位清空),避免复位时还有 DMA 在飞。
  2. PCI_EXP_DEVCTL_BCR_FLR(DEVCTL bit 15):这是触发 FLR 的动作------向设备控制寄存器写一个 bit,硬件就开始复位这个 function。
  3. msleep(100):PCIe r4.0 sec 6.6.2 规定 FLR 必须在 100ms 内完成,所以等 100ms(期间设备可能静默丢弃请求)。
  4. pci_dev_wait :轮询等设备 ready。设备复位后可能用 CRS(Configuration Request Retry Status) 响应配置读表示"还没好",pci_dev_wait 反复读 PCI_COMMAND 寄存器,直到返回非 CRS(最多等 60 秒,PCIE_RESET_READY_POLL_MS)。

dev->imm_ready 是 Immediate Readiness 优化(PCI Status 寄存器的 IMM_READY 位,probe 时读入):如果设备声明复位后"立即 ready",就不用等 100ms + 轮询,直接返回。


四、其他复位方式

4.1 AF FLR:Conventional PCI(和部分 VF)的 FLR

AF(Advanced Features)是 PCI 的传统 capability (不是 PCIe Extended Capability),它给没有标准 PCIe FLR 位的设备提供复位能力------主要是 Conventional PCI(传统 PCI 总线)设备,也覆盖部分 SR-IOV VF:

c 复制代码
// drivers/pci/pci.c (v6.6, line 4806-4848,节选)
static int pci_af_flr(struct pci_dev *dev, bool probe)
{
	int pos;
	u8 cap;

	pos = pci_find_capability(dev, PCI_CAP_ID_AF);
	if (!pos)
		return -ENOTTY;

	if (dev->dev_flags & PCI_DEV_FLAGS_NO_FLR_RESET)
		return -ENOTTY;

	pci_read_config_byte(dev, pos + PCI_AF_CAP, &cap);
	if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR))
		return -ENOTTY;

	if (probe)
		return 0;

	if (!pci_wait_for_pending(dev, pos + PCI_AF_CTRL,
				 PCI_AF_STATUS_TP << 8))
		pci_err(dev, "timed out waiting for pending transaction; performing AF function level reset anyway\n");

	pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);

	if (dev->imm_ready)
		return 0;

	msleep(100);

	return pci_dev_wait(dev, "AF_FLR", PCIE_RESET_READY_POLL_MS);
}

和 FLR 几乎一模一样的时序(等 pending → 写 FLR 位 → 100ms → poll ready),只是寄存器不同:AF capability 的 AF_CTRL 寄存器写 FLR 位(bit 0),前提是 AF_CAPTP(Transaction Pending)和 FLR 位。

4.2 PM reset:D3hot→D0

借电源状态切换触发复位------设备从 D3hot 回到 D0 时,如果 NO_SOFT_RESET 位未置,硬件会把它内部状态复位:

c 复制代码
// drivers/pci/pci.c (v6.6, line 4865-4893,节选)
static int pci_pm_reset(struct pci_dev *dev, bool probe)
{
	u16 csr;

	if (!dev->pm_cap || dev->dev_flags & PCI_DEV_FLAGS_NO_PM_RESET)
		return -ENOTTY;

	pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &csr);
	if (csr & PCI_PM_CTRL_NO_SOFT_RESET)
		return -ENOTTY;

	if (probe)
		return 0;

	if (dev->current_state != PCI_D0)
		return -EINVAL;

	csr &= ~PCI_PM_CTRL_STATE_MASK;
	csr |= PCI_D3hot;
	pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
	pci_dev_d3_sleep(dev);

	csr &= ~PCI_PM_CTRL_STATE_MASK;
	csr |= PCI_D0;
	pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
	pci_dev_d3_sleep(dev);

	return pci_dev_wait(dev, "PM D3hot->D0", PCIE_RESET_READY_POLL_MS);
}

前提是 PM_CTRLNO_SOFT_RESET 位(bit 3)未置------这个位如果置了,表示"从 D3hot 回 D0 时不要复位内部状态"(设备会保留状态),那 PM reset 就没用了。流程就是写 D3hot → 等 → 写 D0 → 等 ready。

这和本系列"电源管理"篇呼应------D3hot→D0 的复位副作用,是电源状态切换的隐藏行为。


五、Secondary Bus Reset:整条 bus 复位

前面的方法都只影响单个设备,但 Secondary Bus Reset(SBR)会复位整条 bus------这是最重的复位手段,也是最后的选择。

5.1 触发:写 bridge 的 SBR 位

c 复制代码
// drivers/pci/pci.c (v6.6, line 5165-5181)
void pci_reset_secondary_bus(struct pci_dev *dev)
{
	u16 ctrl;

	pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
	ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);

	/*
	 * PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms.  Double
	 * this to 2ms to ensure that we meet the minimum requirement.
	 */
	msleep(2);

	ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
}

动作就是给 bridge 的 BRIDGE_CONTROL 寄存器写 PCI_BRIDGE_CTL_BUS_RESET 位(bit 6,0x40),断言下游 bus 的复位信号,等 2ms(PCI spec 3.0 7.6.4.2 要求 Trst ≥ 1ms,加倍保险),再清掉。下游 bus 上所有设备都被复位。

5.2 严格约束:bus 上只能有一个设备

正因为 SBR 会波及整条 bus,pci_parent_bus_reset 有严格的约束:

c 复制代码
// drivers/pci/pci.c (v6.6, line 5203-5219)
static int pci_parent_bus_reset(struct pci_dev *dev, bool probe)
{
	struct pci_dev *pdev;

	if (pci_is_root_bus(dev->bus) || dev->subordinate ||
	    !dev->bus->self || dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)
		return -ENOTTY;

	list_for_each_entry(pdev, &dev->bus->devices, bus_list)
		if (pdev != dev)
			return -ENOTTY;

	if (probe)
		return 0;

	return pci_bridge_secondary_bus_reset(dev->bus->self);
}

五个约束:

  1. 不能是 root buspci_is_root_bus)------root bus 没有上游 bridge 可以写 SBR。
  2. 设备不能是 bridgedev->subordinate 非空说明它下面还挂着 bus,不能这样复位自己)。
  3. 必须有上游 bridgedev->bus->self 非空)。
  4. bus 上只有这一个设备list_for_each_entry 检查,有别的设备就 -ENOTTY)------这是关键:如果有第二个设备,SBR 会误伤它。
  5. 设备没被标记 NO_BUS_RESET

5.3 bus 复位 = slot reset 优先,SBR 兜底

pci_reset_bus_function 把两种 bus 级复位串起来:

c 复制代码
// drivers/pci/pci.c (v6.6, line 5245-5253)
static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
{
	int rc;

	rc = pci_dev_reset_slot_function(dev, probe);
	if (rc != -ENOTTY)
		return rc;
	return pci_parent_bus_reset(dev, probe);
}

先试 slot resetpci_dev_reset_slot_function,通过热插拔槽位的 reset_slot 操作,可能有专门的电源控制),不行再降级到 Secondary Bus Reset


六、对外 API + save/restore

6.1 pci_reset_function:save → reset → restore

c 复制代码
// drivers/pci/pci.c (v6.6, line 5561-5577)
int pci_reset_function(struct pci_dev *dev)
{
	int rc;

	if (!pci_reset_supported(dev))
		return -ENOTTY;

	pci_dev_lock(dev);
	pci_dev_save_and_disable(dev);

	rc = __pci_reset_function_locked(dev);

	pci_dev_restore(dev);
	pci_dev_unlock(dev);

	return rc;
}

完整流程三段:

  1. pci_dev_save_and_disable:保存配置空间(BAR、MSI、bus mastering 等)+ disable 设备(停止 DMA/中断)。
  2. __pci_reset_function_locked:走降级链执行复位。
  3. pci_dev_restore:恢复配置空间(把保存的值写回去)。

这呼应了 1.2 节------复位后配置空间随机,所以必须 save/restore

6.2 其他 API

API 作用
pci_reset_function save + reset + restore(最常用)
pci_reset_function_locked 同上,但调用者已持有锁
pci_probe_reset_function 只探测能不能复位(不执行)
pci_try_reset_function trylock 版本(锁被占就不复位)
pci_reset_bus 复位整条 bus(slot 优先,SBR 兜底)
pci_reset_supported 设备是否有任何可用的复位方法

pci_reset_supported(pci.c:95)的实现最简单:return dev->reset_methods[0] != 0;------只要 probe 出了至少一个 method,就算支持复位。


七、关键函数 / 文件索引

函数 位置 作用
pci_reset_supported pci.c:95 判断设备是否有可用复位方法
pci_dev_wait pci.c:1163 轮询等设备复位后 ready(读 CRS)
pci_wait_for_pending_transaction pci.c:4746 等 pending transaction 清空
pcie_flr pci.c:4763 执行 FLR(写 BCR_FLR + 100ms + poll)
pcie_reset_flr pci.c:4791 FLR 的 probe/执行(检查 DEVCAP FLR 位)
pci_af_flr pci.c:4806 AF FLR(SR-IOV VF / Conventional PCI)
pci_pm_reset pci.c:4865 D3hot→D0 复位
pci_reset_secondary_bus pci.c:5165 写 bridge 的 SBR 位(+2ms)
pci_parent_bus_reset pci.c:5203 Secondary Bus Reset(含约束检查)
pci_dev_reset_slot_function pci.c:5236 Slot reset
pci_reset_bus_function pci.c:5245 slot reset 优先,SBR 兜底
pci_reset_fn_methods pci.c:5331 六种 reset method 数组
__pci_reset_function_locked pci.c:5483 降级链核心(遍历 reset_methods)
pci_init_reset_methods pci.c:5525 probe 阶段探测支持的 method
pci_reset_function pci.c:5561 对外 API(save + reset + restore)
pci_reset_bus pci.c:6019 复位整条 bus
文件(v6.6) 关键内容
drivers/pci/pci.c 复位框架(method 数组、降级链、各复位实现)
include/uapi/linux/pci_regs.h 复位相关寄存器位(BCR_FLR、SBR、NO_SOFT_RESET 等)
include/linux/pci.h pci_dev 的 reset_methods\[\]、对外 API

八、记住三点

① 复位是一条降级链,-ENOTTY 是降级协议 :内核把 6 种复位封装成 method(device_specific → acpi → flr → af_flr → pm → bus),__pci_reset_function_locked 遍历 dev->reset_methods[]。method 返回 -ENOTTY(不支持)就试下一个,返回 0 成功就停,返回其他错误也停 (设备有更深问题,降级没意义)。pci_init_reset_methods 在 probe 阶段就把支持的 method 探测好存进 reset_methods[]
② FLR 是最常用的 function 级复位,时序有讲究pcie_flr 四步------pci_wait_for_pending_transaction 等 pending 清空 → 写 PCI_EXP_DEVCTL_BCR_FLR(DEVCTL bit 15)触发 → msleep(100)(PCIe r4.0 sec 6.6.2 规定 100ms 完成)→ pci_dev_wait 轮询(设备用 CRS 响应表示"还没好",最多等 60s)。前提是 DEVCAP 有 FLR 位(bit 28)。
③ SBR 是最重的复位,约束严格pci_reset_secondary_bus 写 bridge 的 BRIDGE_CONTROL SBR 位(bit 6,0x40),等 2ms(Trst ≥ 1ms 加倍)。但 pci_parent_bus_reset 要求 bus 上只有这一个设备 (否则会误伤别人),还不能是 root bus、设备不能是 bridge。另外复位后配置空间随机 ,所以 pci_reset_function 必须 save → reset → restore。


相关推荐
Wang's Blog1 小时前
Vibe Coding一人即团队系列59:基于PPTmaster与Claude Desktop的本地可编辑PPT自动化生成方案
运维·自动化·powerpoint
不剪发的Tony老师1 小时前
Navop:一款工具搞定数据库、SSH、SFTP、远程桌面、AI Agent
运维·数据库·ssh
学烹饪的小胡桃1 小时前
WGCLOUD支持哪些告警方式
linux·运维·服务器·网络·安全
qq7590353661 小时前
2026 docker部署Hub 监控中心管理多台服务器硬盘和内存
运维·服务器
Jay Kay1 小时前
深入理解 RDMA 内存管理:海思 HNS RoCE 架构中的 HEM 表与 MTR 表有什么区别?
服务器·网络·架构
weixin_440730501 小时前
socket简单介绍-三次握手四次断开+一个例子
运维·服务器
墨有6661 小时前
#Linux系统命令行操作指南
linux
做运维的阿瑞1 小时前
Linux ELF 文件
linux·运维·服务器
Escalating_xu1 小时前
【Makefile 进阶】从自动发现源文件、模式规则到目录分离与多模块递归构建
linux·开发语言