天天看点

Linux系统PCIe hotplug的现代化Threaded Interrupts运行时功耗管理Surprise Removal错误处理整合Moving BARs

Linux系统PCIe hotplug的现代化

  • Threaded Interrupts
  • 运行时功耗管理
  • Surprise Removal
  • 错误处理整合
  • Moving BARs

Linux系统支持PCI Express热拔插功能已经有十四个年头了。过去旧的代码当下正在转变以适应当代应用需求,比如数据中心的可热交换的Flash驱动器和笔记本上具备电源管理能力的雷电控制器。是时候对此做个总结了。

1992年初始版本的PCI规范并不支持运行时的板卡添加和移除。在20世纪90年代后期到21世纪初期,各种专有的热拔插控制器或者与厂商无关的标准热拔插控制器被构思出来,同时Linux通过位于drivers/pci/hotplug的驱动开始支持热拔插功能。然鹅,直到2002年Linux才开始支持PCI Express的热拔插功能,但是具体的形式随时间不断变化。最初,PCI Express热拔插主要是为服务器的热交换板卡或笔记本的ExpressCards设计的,如今,它已经广泛地应用于数据中心(数据中心NVMe Flash硬盘需要运行时拔插)和雷电接口。

Threaded Interrupts

Linux的PCIe热拔插驱动pciehp在2004年被Dely Sy引入。Kenji Kaneshige对其进行了第一次清理和修订,直到2011年才结束相关工作。在这之后,贡献者们的工作大多局限于完善驱动的缺陷,尤其是事件处理。

Threaded Interrupts是内核中主要的中断处理模式,是实时Linux的基石,但是不幸的,它们直到Kaneshige的修订完成后才被引入。因此,pciehp的硬中断处理器识别发生了什么事件,比如link-up或者link-down,并为每个事件安排工作项。这种方法的问题是当执行(work item)时,link状态可以再次发生变化。此外,如果link状态翻转比硬件处理器速度快时,可能导致检测到不均衡的link-up和link-down事件。最终,多个work item并存的可能性以及它们的交互方式使得很难判断事件处理代码的正确性。当前,PCI维护员Bjorn Helgaas称pciph的事件处理复杂且怪异。除非驱动的修订不再是一个选项时,那么驱动的基本反思是不可避免地。

在Linux 4.19版本中,我把pciehp转换为线程中断处理(threaded interrupt handling)。现在硬件中断处理器负责收集事件,中断线程负责处理事件。检测link变化是link up还是link down这项工作推迟到中断线程中事件处理函数处理,以避免处理陈旧的事件。新的方法可以快速处理事件序列(比如,link down后紧接着link up),可以容忍pcie slot启动过程中link状态发生翻转(比如电磁干扰引起的)。补丁集也修复了大量的bug和做了大量清理工作,因此,PCI Express驱动的可靠性和健壮性得到了显著的提升。对于Linux 4.20版本中安排的后续补丁将从pcihp和其他热拔插驱动中删除将近500行代码,进而使代码简化和标准化。

运行时功耗管理

Linux 4.19增加了运行时挂起PCIe热拔插端口的功能,这项功能对雷电控制器的掉电是必不可少,雷电控制器在操作系统中表现为一个PCIe上游端口和多个PCIe下游端口。在控制器掉电前,所有的PCIe端口运行时挂起。Linux从4.8版本开始就可以运行时挂起上游端口,但是在4.19版本前并不能挂起运行时挂起下游端口。

运行时挂起一个雷电PCIe端口本身并不会引起任何节能,端口将会对通过聚集IO Switch传输的PCIe数据包进行封装和解封,那么只要给Switch供电,那么就会消耗能量。然而,Linux的功耗管理模型要求所有的子设备在其父节点挂起前被挂起。通过运行时挂起雷电控制器的所有端口,它的父端口(根端口)允许挂起,这反过来通过ACPI平台方法触发掉电。控制器下电可以节约大约1.5W的功耗。

换句话说,运行时挂起雷电PCIe端口满足了Linux的层级化功耗管理模型。单个雷电PCIe端口在PCI电源状态D0(全功耗)或者D3hot(挂起)这两种状态消耗同样多的功耗,但是当所有端口运行时挂起时控制器作为整体是可以掉电的。在MacBook平台,雷电控制器掉电功能可能需要进一步打补丁解决,预计在4.21版本中出现。

当一个PCIe热拔插端口运行时挂起时热拔插事件处理发生一个有趣的细节:如果它的父端口也运行时挂起,那么该端口是不可访问的。因此,它不能带内发出中断信号,内核不能与之交互,或者甚至在父端口恢复运行时挂起前不能确定事件类型。当前有两种硬件方法可以解决这个问题。

第一个方法遵循PCIe规范:热拔插端口信号是一个电源管理事件(PME),它可以通过平台提供带外信号方式发生,比如一个通用IO引脚(在PCIE中的WAKE#信号)。PME唤醒雷电Host控制器下面所有层级,随之,热拔插端口可以被访问。这个方法被联想和Dell笔记本采用。它允许在连接设备的情况下让控制器掉电。Mika Westerberg已经在4.20版本中提交了支持该功能的补丁。

第二种方法是非标准的:雷电硬件直到哪个通道建立了连接,因此可以将聚合IO层发生的热拔插事件转换为一个overlaid PCIe层发生的热拔插事件。这样,当设备被添加或移除时,无论其自己还是其父端口是否处在D3hot状态,都会魔幻般从受影响的PCIe端口接收到一个中断。这种方法在Apple Macs(雷电1)中采用,只要设备已经连接的情况下雷电Host控制器需要保持供电。在4.19版本中已经添加了这种功能。

运行时电源管理当前对非-雷电热拔插端口是禁止的,因为当非雷电热拔插端口在D3hot状态时可能引起诸如不可屏蔽的中断(NMI)。厂商可以在命令行传递

pcie_port_pm=force

验证它们的热拔插端口是否支持运行时挂起,也许这个功能可能在晚些时候默认使能。

Surprise Removal

初始PCIe规范定义了标准用法模型,该模型定义了手动保留锁固定板卡位置和一个按钮用于请求从操作系统移除一个PCIe Slot。但是当前热拔插实现通常忽略这些元素和仅仅使用surprise removal这种方式。

当一个设备被拽出时,pciehp要求它的驱动解绑,然后bring down该槽位。但是直到这一切发生,对设备的读请求将会在17ms后溢出,然后返回一个全"1"的响应。这个超时降低了请求任务的速度,如果构造的响应对实际的数据是错误的,那么该任务可能崩溃或者陷入到无限循环中。因此,驱动需要从一个设备读取的数据的有效性,尤其是,检查全"1"但是不是一个有效响应的情况。一个惯用的方法是调用

pci_device_is_present()

,该函数读取厂商ID寄存器,检查该寄存器是否全"1"。然而,这也不是万能药;如果发生一个PCIe不可修复的错误,那么设备也可能以全"1"进行响应,但是如果错误可以恢复,那么也可以还原到有效响应。此外,对于不支持的请求或者位于桥地址窗口内内任一目标设备基址寄存器(BARs)外的读请求也可能返回全"1"响应。唯一能够权威且明确识别移除的实体是pciehp。

许多驱动甚至PCI核并不会对一个全"1"响应的每个读进行检查。效力于Facebook “Lighting"存储架构的工程师需要艰难地学习这个问题。Surprise移除一个NVMe硬盘实体阵列可能花费数秒钟时间,也会偶发性引起MCE(machine-check exceptions)。拔出过程如此缓慢以至于驱动可能在完成上一个拔出处理过程前,认为自己在跟一个新插入的硬盘进行交互。由Keith Busch在Linux 4.12版本提交的补丁中一个成果是让pciehp为surprise移除的设备设置标志位,在PCI核中在一些战略性的位置跳过该设备的访问。这足以将移除过程加速到毫秒级别。尤其是当设置标志位后,

pci_device_is_present()

返回false。之前,如果一个设备跟另一个设备快速交换,那么一旦新设备的厂商ID可读后对于已移除的设备会错误地返回True。

Benjamin Herrenschmidt's behest

中,由Busch提交的另一个补丁已经安排引入到4.20版本,目的是统一PCI设备错误状态的标志。错误状态可以用于识别设备是经历一个不可修复错误,但是有机会恢复后的临时性不可访问,还是永久性不可访问。驱动也会直接检查

struct pci_dev

结构体中

error_state

成员变量或者调用

pci_channel_offline()

来确定设备的可访问性。

然而,Helgass对标志使用表示了疑虑。其一,标志是异步设置的,因此,在设备被移除到标志被设置之间存在一个延迟。驱动开发者需要谨慎小心:即使根据标志位设备似乎在位,但是设备可能已经不在了。相反,如果设置了标志位,标志提供了明确指示,后来的设备访问是无效的,可以跳过。因此,设置标志位并不会使驱动开发者免于验证来自设备的响应,但是一旦设置标志位,它可以作为一个cache,避免了不明确的厂商ID检查。简而言之,问题只是得到了缓解,而没有完美解决。尽管一个完美的解决方案似乎是不可能的,但是我们不能获得用户的互斥锁来阻止用户召回设备,处于性能考量,我们不能在每次设备访问后都对设备在位变化进行检查。Austin Bolen指出新的PCIe扩展(root port programmed IO)允许失败设备访问的同步异常处理,这样看起来似乎是一个完美的解决方案。但是这个特征在未来一段时间是不可得的。

Helgaas对标志的第二个担心是标志可能使surprise拔插发生的bug更加难以发现。当设置标志后,这些bug变得更难以发现。

For example, a search for the advanced error recovery (AER) capability on device removal caused numerous configuration-space accesses and, before introduction of the flag, was noticeable through a significant slowdown upon surprise removal

。但是恰当的方式是:缓存AER能力的位置,而不是使用标志跳过配置访问糊弄问题。

错误处理整合

向线程中断的转变也减轻了整合pciehp处理PCIe不可修复错误的处理压力:当在热拔插端口或者子端口发生这样错误时,它可能引起一个link-down事件。但是有时候错误可能通过软件得以恢复,比如执行

secondary bus reset

。在这种情况下,pciehp通过使设备和驱动解绑,同时使slot掉电来应对link-down事件是不合时宜的。相反,它应该等待以确定错误是否可以恢复,如果可以恢复,那么忽略该link事件。为了达到这个目的,Busch和Sinan Kaya当前致力于开发补丁将pciehp中的AER与下游端口控制服务驱动联系在一起。

Moving BARs

PCIe设备

继续阅读