文章目录
- NVMe
- 一、概述
- 1.1 什么是NVMe
- 1.2 高性能&低延迟
- 1.3 名词&术语
- 二、NVMe命令
- 2.1 命令通用格式
- 2.2 两类命令
- 2.2.1 Admin Command
- 2.2.2 IO Command(NVM Command)
- 2.3 SQ、CQ、DB
- 2.4 命令的执行过程
- 三、多命令队列与仲裁机制
- 3.1 NVMe多命令队列
- 3.2 多命令队列的仲裁机制
- 3.2.1 RR
- 3.2.2 带有优先权的RR
- 四、内存寻址
- 4.1 PRP方法
- 4.2 SGL方法
- 4.3 PRP 与 SGL 的比较
- 五、NVMe SSD Controller 物理架构(了解)
- 5.1 概念模型
- 5.2 子模块
- 5.3 NVMe主机接口控制器
一、概述
1.1 什么是NVMe
- Non-Volatile Memory Express(非易失性存储器标准)
- NVMe是面向PCIe SSD制定的标准接口协议,用于访问通过PCIe总线附加的SSD(可适用于各种支持PCIe总线的物理插槽上)
- 定义了NVMe协议的使用范围、指令集、寄存器配置规范等
- 具有良好的可拓展性、低延迟,低能耗,高性能等优点
PCle:Peripheral Component Interconnect Express,周边设备高速连接标准。是一种端对端的互连协议,提供了高速传输带宽的解决方案。其协议内容主要是物理层和数据链路层的。
另一个常用的相关协议是AHCI,AHCI主要是针对高延时的SATA接口的机械硬盘而设计的
1.2 高性能&低延迟
面向PCIe SSD产品的NVMe标准能有效降低控制器和软件接口部分的延迟,是因为:
- 能让SSD走PCI-E通道直连CPU,有效降低了数据延迟
- NVMe执行命令时则不需要读取寄存器
NVMe还能大大提高SSD的IOPS性能。理论上,IOPS=队列深度/ IO延迟,所以增加队列深度,就可以有效提升SSD的IOPS。
- 传统的ACHI标准下队列深度最多能达到32
- 在NVMe标准下,这一数值可以达到64000
此外NVMe还加入了自动功耗状态切换、动态能耗管理、免驱等功能,驱动适应性广,低功耗
1.3 名词&术语
1、Namespace
- Namespace是一定数量逻辑块(LB)的集合
- 其属性在Identify Controller中的数据结构中定义
逻辑块:NVMe定义的最小的读写单元,2KB、4KB……,用LBA来标识块地址
LBA range:表示物理上连续的逻辑块集合
2、Fused Operations
- 聚合操作。只能聚合两条命令,并且这两条命令在队列中应保持相邻顺序,还需要保证聚合操作的两条命令读写的原子性
- 只有NVM指令才有聚合操作
3、指令执行顺序
- 除了聚合操作,每一条SQ中的命令都是独立的
4、写单元的原子性
- 控制器需要支持写单元的原子性
- 但有时也能通过host配置Write Atomicity feature,减小原子性单元的大小,提高性能
5、元数据
- 数据的额外信息。可选的方式
6、仲裁机制
- 用来选择执行的命令的SQ的机制,三种仲裁方式:
- RR(每个队列优先级相同,轮转调度)
- 带权重的RR(队列有4种优先级,根据优先级调度)
- 自定义实现
7、Queue Pair
- 由SQ(提交队列)与CQ(完成队列)组成
- host通过SQ提交命令,NVMe Controller通过CQ提交完成命令
8、NVM 子系统
- 控制器
- NVM存储介质
- 控制器与NVM之间的接口
二、NVMe命令
2.1 命令通用格式
Host提交的命令均为16字节,具有相同的格式,某些字段根据命令的不同有不同的定义
字段 | 定义 |
Dword0 | CID、传输方式、聚合操作、操作码 |
1 | NID(Namespace ID) |
2 | 保留 |
3 | 保留 |
4、5 | 元数据指针(MPTR) |
6-9 | 数据指针(DPTR) |
10-15 | 根据命令指定 |
CQ命令均为4字节,也具有相同的格式,某些字段根据命令的不同有不同的定义
字段 | 定义 |
Dword0 | 根据命令指定 |
1 | 保留 |
2 | SQID、SQ头指针 |
3 | 状态域、P位、CID |
2.2 两类命令
主机提交的命令分为两类
2.2.1 Admin Command
- Admin指令只能提交到Admin Controller中(Admin CQ/SQ)
- 主要负责管理NVMe控制器,也包含对NVM的一些控制指令
- Admin Command通过Dword0中的8位操作码定义不同指令,每一种指令都对应有其完成命令
- 通过SQID(提交队列ID)+CID(命令ID)唯一标识完成的命令
字段
appmask | apptag | reftag | dsmgmt | slba | addr | metadata | rsvd | nblocks | control | Flags | Opcode |
操作码
操作码 | 指令 | 作用 |
00h | 删除I/O SQ | 释放SQ空间 |
01h | 创建 I/O SQ | 保存host分配给SQ的地址、队列优先权、队列大小 |
02h | 获取日志 | 返回所选日志页于缓冲区 |
04h | 删除 I/O CQ | 释放CQ空间 |
05h | 创建 I/O CQ | 保存host分配给CQ的地址、中断向量、队列大小等 |
06h | Identify | 返回关于controller与namespace能力和状态的数据结构(2k字节) |
08h | 撤销 | 用来撤销之前完成的指令,best-effort |
09h | 设置features | 根据FID设置相应的features |
0Ah | 获取 features | 根据FID返回队列数量、仲裁信息等 |
0Ch | 异步事件请求 | Controller向host报告运行信息(error or health) |
10h | 固件激活 | 验证下载的镜像,提交到Firmware Slot(1-7)中 |
11h | 固件镜像下载 | 下载固件镜像 |
2.2.2 IO Command(NVM Command)
- NVM 指令只能提交到I/O Controller中(IO CQ/SQ)
- 主要负责完成数据的传输
- IO Command通过Dword0中的8位操作码定义不同指令,每一种指令都对应有其完成命令
- 通过SQID(提交队列ID)+CID(命令ID)唯一标识完成的命令
字段
rsvd11 | numd | offset | lid | prp2 | prp1 | rsvd1 | command_id | flags | Opcode |
操作码 | 指令 | 作用 |
00h | Flush | 将数据(和元数据)提交到NVM中,所有命令都要执行 |
01h | Write | 将数据(和元数据)写入NVM中 |
02h | Read | 读NVM中的数据(和元数据) |
04h | Wirte Uncorrectable | 标记无效数据块 |
05h | Compare | 比较从NVM端读出的数据和比较数据缓冲区的数据 |
09h | Dataset Management | 标识一定范围数据的特点,eg,频繁读、频繁写(提升性能) |
2.3 SQ、CQ、DB
NVMe的三种队列命令
- Submission Queue(SQ)提交队列(在内存中)
- Host要发送命令时,先把准备好的命令放在SQ中(Host并不直接往SSD中发送命令,而是把命令准备好放在自己的内存中,写DB通知SSD来取)
- Completion Queue(CQ)完成队列(在内存中)
- 一个命令执行完成,成功或失败,SSD总会往CQ中写入命令完成状态、
- SSD写完后,Host会处理CQ,查看指令完成状态
- Doorbell Register (DB)门铃注册(在SSD控制器内部)
- Host通过写DB告诉SSD去SQ读指令
- Host通过写DB回复SSD命令已经处理完毕,可以前来检阅
2.4 命令的执行过程
命令由host提交到内存中的SQ队列中,更新TDBxSQ后,NVMe控制器通过DMA的方式将SQ中的命令(怎么取,如何取,取多少,因设计而异)取到控制器缓冲区,执行命令;执行完成后,根据执行状态,组装完成命令,仍然通过DMA的方式将完成命令写入内存CQ的队列中;NVMe控制器通过MSI-X中断方式通知host已完成命令;最后,host处理CQ命令,更新控制器中HDBxCQ,标识着命令真正完成
通过三种队列命令配合,完成处理流程
- ① Host写命令到SQ;
- ② Host写DB(通知SSD取指令);
- ③ SSD收到通知,于是从SQ中取指令;
- ④ SSD执行指令;
- ⑤ 指令执行完成,SSD往CQ中写指令执行结果;
- ⑥ SSD通过MSI-X中断方式通知host已完成命令;
- ⑦ Host处理CQ,查看指令完成状态;
- ⑧ Host通过DB回复SSD,命令真正完成
MSI-X中断:NVMe协议中支持的中断方式有4种,pin-based、Single MSI、Multi-message MSI和MSI-X,协议推荐采用MSI-X中断方式,能够支持更多的中断向量(2K)。
MSI-X允许每一个CQ发送自己的中断信息(相比于发一条中断信息提醒全部CQ队列有很大的优势)。在产生MSI-X中断信息前,需要检查该中断在相应寄存器种不被屏蔽
三、多命令队列与仲裁机制
3.1 NVMe多命令队列
- NVMe采用了多命令队列,每个命令可变数据长度
- NVMe协议支持命令间的乱序执行,也支持命令内数据块的乱序传输,同时支持命令队列间的可变权重处理
- QID来标识唯一ID,16bit,由host分配
- host可以修改队列优先级(如果支持的话),共四级:U、H、M、L
NVMe与SATA协议对比
3.2 多命令队列的仲裁机制
3.2.1 RR
Admin SQ与I/O SQ优先级相同,控制器每次可以选择一个队列中的多个命令,轮转调度
3.2.2 带有优先权的RR
有3个严格的优先权,Priority1 > Priority2 > Priority3,在这三个优先级队列中,高优先级的队列中如果有命令,则优先执行(非抢占式)
- Priority1:Admin
- Priority2:Urgent
- Priority3:IO SQ
四、内存寻址
Host告诉Controller数据源在内存的什么位置,或者从闪存上读取的数据应该放到内存的什么位置,需要内存寻址
- NVMe把Host的内存分为页的集合,页的大小在CC寄存器中配置
- NVMe采取PRP和SGL两种寻址方式
- Admin命令的数据地址只能采取PRP的方式
- I/O命令的数据地址既可以采取PRP的方式,又可以采取SGL的方式
- Host在命令中会告诉Controller采用何种方式。具体来说,如果命令当中DW0[15:14]是0,就是PRP的方式,否则就是SGL的方式
4.1 PRP方法
PRP Entry是一个64位(63 ~ 0编号)的内存物理地址指针,结构如下:
- 最后两位为0,指四字节对齐
- Offset(n:2)位表示页内内偏移
PRP寻址有两种方式
- 直接用PRP指针寻址:直接由PRP指向内存页
- 通过PRP List寻址:PRP指向PRP List的地址,PRP List存有真正的数据地址
4.2 SGL方法
SGL是另外一种索引内存的数据结构。SSGL字段由若干个SGL描述符组成,所以SGL描述符是SGL数据结构的基本单位
目前定义的SGL描述符有6种,在一个SGL字段中并不需要同时使用
- SGL 数据描述符,用来索引数据块地址,host内存;
- SGL 垃圾数据描述符,用来索引无用数据;
- SGL 段描述符,用来索引下一个SGL段;
- SGL 最后一个段描述符,用来索引最后一个SGL段;
- keyed SGL 数据描述符;
- Transport SGL 数据描述符;
- 在上面SGL例子中,共有3个SGL段,用到了4种SGL描述符
- Host需要往SSD中读取13KB(Data Block + Bit Bucket)的数据,其中真正只需要11KB数据(Data Block),这11KB的数据需要放到3个大小不同的内存中,分别是:A(3KB),B(4KB)和 C(4KB)
4.3 PRP 与 SGL 的比较
无论是PRP还是SGL,本质都是描述内存中的一段数据空间。
Host在命令中设置好PRP或者SGL,告诉Controller数据源在内存的什么位置,或者从闪存上读取的数据应该放到内存的什么位置。
SGL和PRP本质的区别在于:
- 一段数据空间,对PRP来说,它只能映射到一个个物理页
- 对SGL来说,它可以映射到任意大小的连续物理空间,具有更大的灵活性,也能够描述更大的数据空间
五、NVMe SSD Controller 物理架构(了解)
5.1 概念模型
整体来看,NVMe SSD可以分为三部分
- host端的驱动
- PCIe+NVMe实现的控制器
- FTL+NAND Flash的存储介质
5.2 子模块
1、主机接口控制器
- 主机接口负责进行主机与固态盘之间的通信和数据传输,接受和解析I/O请求,并维护一条或者多条请求队列
- 主机接口控制器是对外连接的模块,它的两大工作 —— 接受和解析IO、维护请求队列是NVMe的实现难点,也是最最主要的设计点,我围绕NVMe的设计实现也基本是针对主机接口控制器而言的
2、多核处理器
- 固态盘的管理需要处理诸多复杂的任务,比如主机接口协议、调度算法、FTL算法和缓存算法等,因此需要强有力的多核处理器来提高这些任务的处理效率,从而降低软件延时
3、缓存芯片
- 固态盘内置有缓存芯片,一般是DRAM,用于缓存用户数据和软件算法的元数据
- 缓存既能加快数据访问的速度,提高固态盘的性能,也能够减少对闪存的写入,延长固态盘的寿命
- 缓存用户数据的部分称之为数据缓存,缓存地址映射表的部分称之为映射缓存
- 为了防止突然掉电导致RAM中的数据丢失,固态盘一般会内置备用电容,并采用适当的数据保护技术,用于保证在突然掉电的情况下,将RAM中关键的脏数据刷回闪存
4、中央控制器
- 中央控制器是整个固态盘控制器的核心,负责配置固态盘的工作模式,管理各个模块之间的通信和数据流。
- 中央控制器内置有小容量的高速SRAM缓存,用于临时缓存数据
5、纠错码引擎
- 纠错码引擎对要写入闪存的数据进行编码,所增加的纠错码冗余会被写到闪存页的额外存储区中
- 当需要从闪存中读取数据时,纠错码引擎会对数据和它的纠错码冗余进行解码,如果发生的比特错误数在纠错能力范围内,数据中的错误就会被纠正,从而得到正确的数据;否则,如果没有其它的数据恢复手段,存储的数据就会丢失
6、通道控制器
- 为了提高性能,固态盘将数量众多的闪存芯片安置在多个通道上,每个通道上的多个芯片共享一条I/O总线。
- 每个通道包含一个独立的通道控制器,主要负责与中央控制器和本通道上的闪存芯片进行通信,并维护多条操作闪存的命令队列(比如为每个芯片维护一条单独的队列,外加一条总的高优先级队列),将命令发往目标芯片进行执行
7、闪存芯片
- 闪存芯片上既存储用户数据,也存储需要持久化的元数据,比如地址映射表
- 固态盘提供的物理存储容量会比用户可见的容量要多(一般多7% ~ 28%),多余部分称之为过量供应(Over-provisioning, OP)空间,主要用于提高软件算法(比如垃圾回收操作)的效率和补偿因闪存坏块产生的容量损失
- 有的固态盘还会在闪存芯片之间组建RAID5,以加强存储的可靠性
RAID5:将校验数据条带化存储在多个盘中
8、DMA引擎
- DMA引擎负责控制在两个RAM之间进行快速的数据传输,比如在中央控制器的SRAM和缓存芯片之间
5.3 NVMe主机接口控制器
NMVe主机接口控制器的逻辑将是我主要的设计内容,一般来说NMVe主机接口控制器包括以下几个部分:
- Admin队列处理模块
- IO命令队列处理模块
- IO Completion队列处理模块
- IO写数据处理模块
- IO读数据处理模块
- 中断处理模块