1. 概念
NVM Express(NVMe),或称非易失性内存主机控制器接口规范(Non-Volatile Memory express),,是一个逻辑设备接口规范。他是与AHCI类似的、基于设备逻辑接口的总线传输协议规范(相当于通讯协议中的应用层),用于访问通过PCI-Express(PCIe)总线附加的非易失性内存介质,虽然理论上不一定要求 PCIe 总线协议。此规范目的在于充分利用PCI-E通道的低延时以及并行性,还有当代处理器、平台与应用的并行性,在可控制的存储成本下,极大的提升固态硬盘的读写性能,降低由于AHCI接口带来的高延时,彻底解放SATA时代固态硬盘的极致性能
(1)NVMe其实与AHCI一样都是逻辑设备接口标准(是接口标准,不是接口!不是接口!不是接口!因为很重要所以说三次),NVMe全称Non-Volatile Memory Express,非易失性存储器标准,是使用PCI-E通道的SSD一种规范,NVMe的设计之初就有充分利用到PCI-E SSD的低延时以及并行性,还有当代处理器、平台与应用的并行性。
(2)SSD的并行性可以充分被主机的硬件与软件充分利用,相比与现在的AHCI标准,NVMe标准可以带来多方面的性能提升。
2. NVME和PCIe
说到NVME就不得不提PCIe,PCIe模拟高速接口,可以理解为县道,省道,高速等道路,其实就是数据的物理通路,不同的接口速率上限不同,这里可以用SATA作为类比。
NVME是传输层协议和命令集,可以理解为小汽车,跑车,专业赛车等,该协议提供了命令的执行流程(寄存器配置)和命令集配置格式等。
NVMe是一种协议,是一组允许SSD使用PCIe总线的软硬件标准。可以这么说,NVMe是允许存储设备与服务器连接的语言,而PCIe是实际的物理连接。
3. NVME的优势
①NVMe是为SSD所生的。NVMe出现之前,SSD绝大多数走的是AHCI和SATA的协议,后者其实是为传统HDD服务的。与HDD相比,SSD具有更低的延时和更高的性能,AHCI已经不能跟上SSD性能发展的步伐了,已经成为制约SSD性能的瓶颈. SATA现在最高带宽就是600MB/s,nvme使得性能有数倍的提升;
②可降低延迟超过50%;
③NVMe可以把最大队列深度从32提升到64000,SSD的IOPS能力也会得到大幅提升;
④自动功耗状态切换和动态能耗管理功能大大降低功耗;
⑤NVMe标准的出现解决了不同PCIe SSD之间的驱动适用性问题。
4. NVME的硬件接口
5. NVME Command
NVMe Host(Server)和NVMe Controller(SSD)都是通过NVMe Command进行信息交互。NVMe Spec中定义了NVMe Command的格式,可以看到,NVMe Command占用64字节。其中Command的前4个字节规定如下。Command Identifier作为识别这个Command的标志,Opcode通常包含Command类型(如读写,identify等)
NVMe有两种命令,一种叫Admin Command,用以Host管理和控制SSD;另外一种就是I/O Command,用以Host和SSD之间数据的传输
Opcode在这两类的基础上进行划分。下图是部分管理Command的Opcode定义:
下图是 IO command的Opcode定义:
剩余的60个字节则根据Command类型定义。
6. NVME QUEUE
NVMe中有两个队列和一个寄存器:
①Submission Queue (SQ)
②Completion Queue(CQ)
③Doorbell Register (DB)
SQ和CQ位于Host的内存中,DB则位于SSD的控制器内部。
- 有两种SQ和CQ,一种是Admin,另外一种是I/O,前者放Admin命令,用以Host管理控制SSD,后者放置I/O命令,用以Host与SSD之间传输数据。
- 系统中只有一对Admin SQ/CQ,它们是一一对应的关系;I/O SQ/CQ却可以很多,多达65535(64K减去一个SQ/CQ)
- Host端每个Core可以有一个或者多个SQ,但只有一个CQ。给每个Core分配一对SQ/CQ好理解,为什么一个Core中还要多个SQ呢?一是性能需求,一个Core中有多线程,可以做到一个线程独享一个SQ;二是QoS需求,什么是QoS?Quality of Service,服务质量。实际系统中用多少个SQ,取决于系统配置和性能需求,可灵活设置I/O SQ个数
- 作为队列,每个SQ和CQ都有一定的深度:对Admin SQ/CQ来说,其深度可以是2-4096(4K);对I/O SQ/CQ,深度可以是2-65536(64K)。队列深度也是可以配置的。AHCI只有一个命令队列,且队列深度是固定的32
- Linux的NVMe驱动采用一个Core独占一个Queue(由Completion Queue和Submission Queue组成)的方式。这种设计避免了一个队列被多个Core竞争访问,大家都各自使用自己的Queue,互不干扰。
- 队列用来存放NVMe Command,NVMe Command是Host与SSD Controller交流的基本单元,应用的I/O请求也要转化成NVMe Command。
- 每条命令大小是64字节,每条命令完成状态是16字节
- 每个SQ或者CQ有两个DB: Head DB 和Tail DB
SSD作为一个PCIe Endpoint通过PCIe连着Root Complex (RC), 然后RC连接着CPU和内存。 RC就是CPU的代言人 SQ位于Host内存中,Host要发送命令时,先把准备好的命令放在SQ中,然后通知SSD来取;CQ也是位于Host内存中,一个命令执行完成,成功或失败,SSD总会往CQ中写入命令完成状态。 Host发送命令时,不是直接往SSD中发送命令的,而是把命令准备好放在自己的内存中,那怎么通知SSD来获取命令执行呢?Host就是通过写SSD端的DB寄存器来告知SSD的:
流程:
1.BIO封装成的Command会顺序存入Submission Queue中
2.对于Submission Queue来说,使用Tail表示最后操作的Command Index。每存入一个Command,Host就会更新Queue对应的Doorbell寄存器中的Tail值
3.NVMe没有规定Command存入队列的执行顺序,Controller可以一次取出多个Command进行批量处理,所以一个队列中的Command执行顺序是不固定的(可能导致先提交的请求后处理)
4.SSD执行命令
5.SSD Controller根据Doorbell的值,获取NVMe Command和对应数据(步骤3),待处理完成后(步骤4)将结果存入Completion Queue中
6.Controller通过中断的方式通知Host
7.将Completion Command从Completion Queue中取出,然后把队列的head值加1,并调用上层的Callback函数(完成BIO处理)
8.处理完Command后,往Completion Queue的Doorbell写入Head值,通知NVMe Controller操作完成。中断处理结束
DB的另外一个作用,就是通知作用:Host更新SQ Tail DB的同时,也是在告知SSD有新的命令需要处理;Host更新CQ Head DB的同时,也是在告知SSD,你返回的命令完成状态信息我已经处理,同时表示谢意。这里有一个对Host不公平的地方,Host对DB只能写,还仅限于写SQ Tail DB和CQ Head DB,不能读取DB
疑问:
1.SSD在取指的时候,是偷偷进行的,Host对此毫不知情。Host发了取指通知后,它并不清楚SSD什么时候去取命令,取了多少命令。怎么破?
答:
这是SSD往CQ中写入的命令完成状态信息(16字节)。是的,SSD往CQ中写入命令状态信息的同时,还把SQ Head DB的信息告知了Host!!这样,Host对SQ中Head和Tail的信息都有了,轻松玩转SQ
2.Controller写入Command后,只有中断触发通知Host,没有类似于Head/Tail机制告诉Host可以取哪些Completion Command?
答:一开始CQ中每条命令完成条目中的”P” bit初始化为0,SSD在往CQ中写入命令完成条目时,会把”P”写成1。记住一点,CQ是在Host端的内存中,Host可以检查CQ中的所有内容,当然包括”P”了。Host记住上次的Tail,然后往下一个一个检查”P”,就能得出新的Tail了。就是这样。