本文总结 NVMe 热插拔情况下系统和 NVMe 设备会发生什么。

Update: 2022 / 11 / 7



NVMe | 热插拔

  • 总览
  • 起因
  • 实现
  • 硬件
  • pin 脚及信号
  • 寄存器
  • 准备工作
  • BIOS
  • 软件
  • 插入
  • 软件
  • 拔出
  • 软件
  • 参考链接



总览

参考这里 12345



起因

为什么要有热插拔 ?——

从历史上到目前,IT 系统设备( PC、服务器、存储等等)中,对于 RASReliabilityAvailabilityServiceability,即可靠性、可用性和可维护性)的要求变得越来越高。

那么系统怎么样才更可靠,可用性才更高?这是有专门的可靠性设计的,涉及太多的领域,我们不展开。常规的看,对于系统可能出现的问题和故障,需要做 故障检测故障隔离故障告警故障恢复 等等。

  1. 故障的检测可能会使用检测链路状况、器件状态等等。在 PCIe 领域,常见的是检测链路协商宽度、速率,检测 AER 寄存器等等。
  2. 故障的隔离主要的目的是让故障或即将故障失效的器件、部件从系统中隔离出去。比如把失效的 PCIe 卡从系统中移除,不再接管业务,避免故障扩散。
  3. 故障恢复的手段就更多样化了,有复位修复、上下电修复、备份冗余、故障时切换备机等等。比如把失效的 PCIe 设备重新上下电做修复,或者把业务切换到备用的卡上,或者更换故障 PCIe 卡,也就是我们常说的更换 FRUField Replace Unit 现场可更换单元)

网上很多的文章和书籍讲热插拔,都是讲的标准的热插拔,主要目的是为了现场快速更换 PCIe 设备。
热插拔的基本目的是要让 PCIe 设备按照规定的顺序、原则,从系统中移除或插入到系统中来,并能正常的工作,且不影响系统的正常运行。事实上,PCIe 热插拔 的关键目的就是为前面面所提到的系统 RAS 服务的,是提升系统 RAS 能力的非常重要的手段。

热插拔有三个重要的功能:

  • 在线替换发生故障的 PCIe 设备。不需要关闭、重启系统;
  • 热插拔器件,系统及其他功能服务继续运行,不受影响;
  • 热插拔 PCIe 设备的相关驱动/软件自动加载 / 卸载;

Spec 中对于热插拔是这样定义的:

功能

定义

Hot-Plug

Insertion and/or removal of a card into an active backplane or system board as defined in PCI Standard Hot-Plug Controller and Subsystem Specification, Revision. 1.0. No special card support is required.

Hot swap

Insertion and/or removal of a card into a passive backplane. The card must satisfy specific requirements to support Hot swap.

注意,这里有两个相关概念,前面我们讲的热插拔,其实是广义的“热插拔”。这里的 Hot-Plug ,为了方便,我们叫做 PCIe 热插拔,也就是我们现在及后面将要讨论的内容。

热插拔分为两种,一是 通知式热插拔,一个是 暴力热插拔,所谓 通知式热插拔 就是先通知 driver 卸载驱动,停止 I / O ,然后再拔出。所谓 暴力热插拔 就是带着 I / O 拔出

Hot Swap ,即热交换,也叫热切换,主要指的是 CPCICompact PCI,紧凑型 PCI )领域所使用的。关于 Hot SwapCPCI 有专门定义的一套规范,叫 《CompactPCI® Hot Swap Specification》,也有中文版本,有兴趣的可以自行研究。



实现

Spec 定义的热插拔是把一个 PCIe 卡(设备)从一个正在运行的背板或者系统中插入/或者移除。这个过程需要不影响系统的其他功能。插入的新的设备可以正确工作。

显然,这里面需要考虑的问题有硬件和软件两方面的事情。



硬件

pin 脚及信号

SFF-8639 也叫 U.2,物理结构可以兼容 SASSATASATA ExpressNVMe
SFF-8639 详细的引脚定义可以参考 PCI Express SFF-8639 Module specification

映泰BIOS热插拔_硬件架构


其中比较重要的 PCI sideband 信号有 PWRDISIfDet#PERST#DualPortEn

信号

作用

PWDIS

该信号 assert 时,disable SFF-8639 电路的供电。

IfDet#

长针信号,是 SFF-8639detect 信号,当 NVMe 盘插入拔插时,作为 presence 信号会被拉低拉高。

PERST#

PCIereset 信号,PCIe Spec 规定电源、时钟和 PESRT# 信号的时序。

DualPortEn

双端口盘(一般存储上会用双端口盘)该信号需要 assert,如果是单端口盘则该信号 de-assert

对于热插拔,Spec 对于所谓插槽(插卡)的 ONOFF 状态都做了比较清晰的定义,如下:

状态

定义

ON

• Power is applied to the slot.

• REFCLK is on.

• The link is active or in the standby (LOs or Ll ) low power state due to Active State Power Management.

• The PERST# signal is deasserted.


简而言之:电源打开、时钟开启、链路 link 、复位信号拉高。

OFF

• Power to the slot is turned off.

• REFCLK is off.

• The link is inactive. (Driver at the root of switch port is in Hi Z state)

• The PERST# signal is asserted.


简而言之:电源关闭、时钟关闭、链路断开、复位信号拉低生效。


寄存器

Spec 为标准热插拔控制器定义了一套标准的寄存器组,即 PCI Express Capbilities 结构里面包含的内容。

PCI Express Capbilities 结构的 Cap ID0x10,可以容易的根据 ID 找到这个结构。这个结构里面包含 Device CapabilityDevice Control / StatusLink Control / StatusSlot Capbility / Control/Status 等多个寄存器。

映泰BIOS热插拔_映泰BIOS热插拔_02


不是每个 PCIe 设备都完全具备这个图里的所有寄存器的。我们关注的热插拔,其实主要是Root ComplexPCIe Switch 的下行端口是有热插拔能力的。

在这些寄存器中,对于热插拔而言,我们重点关注 Slot 相关的几个寄存器:Slot CapabilitiesSlot StatusSlot Control。在讲这几个寄存器之前,我们首先要了解一下 Spec 定义的关于热插拔可能产生的事件(中断,可以通过设置 Slot Control 寄存器的 bit5 Hot-Plug Interrupt Enable 来使能):

事件

说明

插槽事件

插槽检测到的一些动作或状态变化


• 按钮按下事件

• 检测到电源失效( Power Fault

MRL 传感器变化事件

• 在位检测变化事件

命令完成事件

设置 Slot Control 的一些命令,Spec 要求都必须要芯片执行完成后,返回一个命令完成。并且要求这些命令要在 1 秒内完成,否则超时。

数据链路层状态变化事件

Data Link Layer State Changed,这个事件主要告诉驱动软件 Link 是否建立成功。比如,新热插入的 PCIe 设备上电成功,并 Link OK,上报此事件。根据协议,软件在获取到 link 成功后,需要等待 100 ms 执行后续的配置等操作。

下面,我们简单看看几个相关的寄存器:

  • Slot Capabilities:表征了这个槽位的能力,是否支持热插拔。
    比如,Bit0 Attention Button Present 表示了这个槽位的按钮是否存在。其他 bit 位根据命名基本上都能清楚的看出来是做什么用的,不再赘述。
    注意一下 Physical Slot Number 这个字段,这个字段表征当前槽位的槽位号,往往是 PCIe Switch / RC 的内部定义端口号。

映泰BIOS热插拔_热插拔_03

  • Slot Control Register:主要控制热插拔功能的寄存器。

映泰BIOS热插拔_映泰BIOS热插拔_04

  • Slot Status Register:主要表示热插拔状态的寄存器。

映泰BIOS热插拔_链路_05



硬件上看,很显然,一个新的设备插入系统,肯定是需要硬件上支持识别到这个插入动作的。因此,Spec 定义了一个 在位Present )的 pin 脚,硬件上用作判断 PCIe 卡是否插入。卡插入时,这个 pin 被拉低。当然,因为 PCIe 金手指的长度较长,插入卡时有可能前后高低差异。因此,需要有前后多个 Present Pin 来确保金手指完全插入。

映泰BIOS热插拔_映泰BIOS热插拔_06


需要注意的是,检测卡是否在位,除了使用 Present Pin 之外,也可以通过链路的负载检测等来完成。也就是所谓带内检测在位。或者一些特殊的场景比如 NT-NT 背靠背,就没有 Present 信号。NT-NT 的插入时,链路自动重新 Link

除了在位( Present ),硬件上还需要实现的是:电源控制,复位逻辑(可控的给出 PERST 信号复位新插入的设备)。另外,Spec 定义的热插拔是可控的、优雅的,不是突然地、暴力的。因此,热插拔的控制逻辑,需要一个按钮,来告诉系统我需要插入/拔出了。另外,人机界面上,一个 LED 指示灯来提示用户热插拔的工作状态也是友好的。

综上,热插拔所需要的的硬件基本元素如下:

Element

Purpose

Power Indicator

绿色的 LED 灯,表示电源 On / Off

Attention Indicator

黄色 LED 等。热插拔过程的一些状态表示。亮或是闪烁。

MRL

Manually-operated Retention Latch,锁止开关,用户固定卡。

MRL Sensor

检测锁止开关是否打开的光传感器。协议定义的 MRLMRL 传感器。

Electromechanical Interlock

机电联锁。一种确保不能随意移除卡的机制。

Attention Button

按钮。用于告诉系统 开始 / 取消 热插拔过程。

Slot numbering

热插拔槽位标号,这个取决于每个公司的自定义。

Power Controller

上下电控制部分。包括电源故障检测。

上面是 Spec 定义的一些元素,实际应用中,可以根据自己的实际项目需求来取舍。下图是一个支持热插拔的 PCIe Switch 的典型信号示意图:

映泰BIOS热插拔_链路_07



准备工作

BIOS
  1. 为每个端口预留资源

由于 PCIe 设备的枚举是遵从深度优先的原则,如果系统启动时没有带盘启动,BIOS 枚举时不会为 RP 或者 switch 下行口预留资源( bus 资源、memory 资源、prefetchable memory 资源)。后面再插入 Nvme 盘,由于端口资源不足,盘无法枚举进系统,除非软件重新调整资源后再枚举(这样会影响其他盘的 IO )。

如果是要做整机系统卖给客户,BIOS 必须要为支持热插拔的端口预留资源(按照系统支持的EndPoint 需要的最大资源预留)。如果仅仅是验证芯片功能,可以让系统带着 Nvme 盘或接口卡启动,确保在枚举时为支持热插拔的端口分配了资源。

  1. PCI Express 配置空间设置

由于 PCIe 配置空间有些 regHwInit 的,驱动是无法访问的,如果要支持 Nvme 盘的暴力热插拔,需要 BIOS 或者 Firmware 初始化一些 HwInitreg

  • Slot Implemented

如果 PCIe Capabilities 中的 Slot Implemented 没有实现,将会导致 Slot Capability 无法实现。因此,Slot Implemented 必须为置 1

映泰BIOS热插拔_硬件架构_08


  • Surprise Down Error Report Capable

Link Capabilities Reg 中的 Surprise Down Error Report Capable 必须要为 1,否则无法把 Surprise Down Error 转换成 DPC (如果要接 NVMe 盘的端口不支持 Surprise Down,则不需要修改该 bit

映泰BIOS热插拔_映泰BIOS热插拔_09

  • Data Link Layer Link Active Reporting Capable

Data Link Layer Link Active Reporting Capable 最好实现,这样可以通过带内 Link UpLink Down 方式辅助定位 NVMe 是否被拔出。

映泰BIOS热插拔_热插拔_10

  • Power Controller Present

Slot Capabilities Reg 中的 power controller present 最好能实现,这样软件可以控制槽位电源。
Hot-plug surprise 必须为 0(如果接 Nvme 盘的端口不支持 surprise down 的错误可以不关注该bit ),hot-plug surpriseDPC 实现之前传统的 surprise down handling 机制,其实有点掩耳盗铃的意思。由于在某些平台上,RC 收到 uncorrectable errormessage 会导致系统 crash,因此在支持热插拔时就增加了 hot-plug surprise bit ,当出现 surprise down error 时,不向 host 报告错误,从而阻止系统 crash。这个 bit 只能阻止 surprise down error 的上报,不能处理暴力拔出时未完成的 I / O 的导致的 CPU 等待超时问题,并且会影响 DPC 功能,属于历史遗留问题。

针对这一点 PCIe Spec 也有详细的描述:

映泰BIOS热插拔_链路_11

  • Hot-Plug Capable

Hot-Plug Capable 需要为 1,否则无法支持热插拔的各种中断。



软件

映泰BIOS热插拔_映泰BIOS热插拔_12


Slot 有对应的能力时,软件需要打开 Slot Ctrl capabilities 对应的 bit,并且根据系统要求设置 DPC Ctrl


映泰BIOS热插拔_映泰BIOS热插拔_13


插入

软件

NVMe 暴力插入的流程中软件层面流程图如下图所示:

映泰BIOS热插拔_映泰BIOS热插拔_14


其中 CPLD 模拟的 PCA9555 可以由真正的 PCA9555 芯片代替,使用 CPLD 模拟 PCA9555 完全是基于成本考虑。

  1. SSD 被插入,IfDet# 信号就被拉低,SFF8639 后的 I / O 端口改变,GPIO 中断到FWFW 通过 IIC 接口读取 SFF8639 后的 I / O 端口,然后和已经保存的 I / O 端口状态对比,发现某个 slotIfDet# 信号改变。
  2. FW 更改芯片配置空间的 presence statuspresence change status 寄存器。presence change change 中断( MSI 中断)到 CPUCPU 调用 hotplug service 注册的中断服务函数。
  3. 中断服务函数起线程或者工作队列读取配置控制的 presence change statuspresence statuspower statusFW 把配置空间的转换成 IIC 读取 SFF8639 对应的 IO 端口。
  4. 线程或者工作队列根据 presence change statuspresence statuspower status 来判断是否要热插拔,如果满足热插拔条件,则写配置空间的 power ctrlslot 上电,然后等待 PCIe 链路linkup
  5. FW 通过 IIC 写把配置空间的 power ctrl 写到 SFF8639 对应的 IIO 端口,NVMe SSD 被上电,#PERST 释放,PCIe 链路 link up
  6. 线程或者工作队列等待 PCIe 链路 link up 后,扫描 NVMe,调用 NVMe 注册的 probe
  7. NVMe driver 配置芯片返回 probe 流程,热插入完毕。

SFF-8639 spec 定义的在位信号和 PCIe 的定义是有冲突的。SFF-8639 规定 PRSNT#IfDet# 这两个带外信号都是 frist-to-mate and last-to-break 的,也就是说 SFF-8639 规定的PRSNT#IfDet#PCIe 带内信号要长,而 PCIe spec 规定的恰好相反。
下面是 PCIe SFF8639 Module Specification 的原话:

The SFF-8639 interface includes the PRSNT# and IfDet# signals, as an out of band presence detect mechanism, to detect the presence of the SFF-8639 module. Since PRSNT# and IfDet# are not in the last-to-mate and first-to-break group, another vendor-specific mechanism is required to provide warning of module removal。

由于历史原因( SAS 盘上是没有 button 按钮)导致的客户操作习惯(客户换盘都是暴力拔出的),Nvme 盘需要支持 暴力热插拔 而不是 通知式热插拔暴力热插拔 是指不提前通知驱动停I / O 的情况下,就把 PCIe 设备从系统中拔出,这样会导致存在未完成的 I / O

支持 暴力热插拔 要求 switch 或者 RP( 如果 SSD 接在 switch 下或 RP 下)可以协助处理这些未完成的请求,或者 host 有类似的机制,针对未完成的 Non posted 请求,返回 completion 包( PCIe 协议要求 Non posted 请求需要 completion 包),从而阻止 host 等待未完成的请求导致的超时(超时可以能导致系统 crash )。这就要用到 PCIe 3.1 协议新增的一个重要的 capabilityDPC

DPC 是什么? DPC 全称 Downstream Port Containment,这个 capabilityPCIe 3.1 协议针对root portswitch downstream port 新增的。其作用是当检查到下行口本身出现错误(什么等级的错误会触发 DPC 是可以配置的)或者下行口下面的设备上报错误 message(什么等级的error message 会触发 DPC 也是可以配的)可以关闭对应端口(让该端口的 LSTTM 进入disable 状态),把 PCIe traffic 拦截在该端口之下,从而阻止错误扩散。
针对未完成的 Non posted 的请求,root port 或者 switch downstream port 根据 DPC 的设置返回completion 包,completion 包的状态是 UR 或者 CA。对应 Non posted 的请求,在 completion 没有返回前,CPU 认为该指令是未完成的。
DPC 这种机制就为系统恢复错误提供了机会,因此,可以用来实现 Nvme 盘的暴力热插拔的需求。



拔出

先看卡的拔出过程。(注意,我们这里的插拔不是暴力的拔出,所谓暴力,是指不通知系统和软件,直接暴力的、突然的把 PCIe 卡从系统槽位中拔出。Spec 定义的热插拔是有严格的顺序要求和处理过程的。)



软件

NVMe 暴力拔出的流程中软件层面流程图如下图所示:

映泰BIOS热插拔_映泰BIOS热插拔_15

  1. Attention Button 按钮或者通过软件的命令,通知热插拔控制器开始进行热拔出操作。
  2. 闪烁 LED 灯,表示这张卡准备从系统中移除。五秒内没有其他异常,流程继续。五秒内可以取消此次操作。
  3. 系统把这个 PCIe 设备在系统中拥有的资源删除,如总线资源、Memory 资源等。确保再没有流程访问这种卡。
  4. 关闭这个卡所在槽位的电源。关闭 LED 灯,表示该卡可以从系统中拔出了。完成 Surprise Remove 流程。
  5. (可选操作)打开 MRL
  6. 用户移除这张卡。如果过程中有错误,则跳出移除过程,可以点 LED 红灯表示错误。需要人工干预处理。


软件方面的元素,其实简单来说就两个:

  • 热插拔驱动:系统的驱动程序,支持 PCIe 的热插拔过程、移除拔出卡的资源、重新分配插入卡的资源、控制上下电等等。
  • 支持热插拔的设备驱动:主要是需要支持插入时的初始化和移除时的资源释放。典型的是 linux 设备驱动的 probe / remove

参考链接


  1. HOT-PLUG(一) ↩︎
  2. HOT-PLUG(二) ↩︎
  3. HOT-PLUG(三) ↩︎
  4. HOT-PLUG(四) ↩︎