NVME概述

Non-Volatile Memory Express,非易失性内存主机控制器接口规范。

NVME像SATA和SAS一样,定义了硬件接口和传输协议。

几个关于存储的概念的对应关系:

尺寸外形 接口 协议
2.5寸或3.5寸:在SFF标准中定义 SATA接口 AHCI或ATA协议
M.2和PCIe:在PCI-SIG标准中定义 PCIe接口 NVME协议
SAS和FC接口:仅用于服务器和数据中心
  • 判断一个PCI设备是NVMe控制器:class code是1,subclass code是8。
  • 访问NVMe设备的寄存器:通过BAR0。
  • NVMe控制器执行命令的次序是由自己决定的。
  • 重置后只有一个提交队列,一个完成队列。这两个队列就是管理队列。驱动程序会在ASQ和ACQ寄存器设置它们的基址。
  • 管理队列可以处理管理器命令,如创建I/O队列、检索控制器和驱动的信息。
  • 管理队列的id是0。

BAR0里的寄存器

offset(字节) 大小(bit) 名字 作用
0x0 64 CAP 控制器capabilities
0x8 32 VS 版本
0xc 32 INTMS 中断掩码设置
0x10 32 INTMC 中断掩码清空
0x14 32 CC 控制器配置
0x18 32 保留
0x1c 32 CSTS 控制器状态
0x20 32 保留
0x24 32 AQA 关于管理队列的属性
0x28 64 ASQ 管理队列之提交队列
0x30 64 ACQ 管理队列之完成队列

NVMe基本原理

NVMe设备称为Controller(控制器),主机称为Host。主机和控制器之间通过共享内存队列实现交互。

NVMe队列

NVMe队列从功能上可分为2种:

  • 管理队列(Admin Queue):用于管理,仅一个
  • 命令队列(Command Queue):最多可以有65535个

NVMe队列从发送方向上分也可分为2种:

  • 主机向NVMe发送命令使用提交队列(Submission Queue)。

    NVMe控制器会执行提交队列上的命令。

    提交队列里一个条目有64字节,按双字排列:

    双字 内容
    0 命令(格式:0-7opcode, 8-9fused操作,10-13保留,14-15PRP或SGL选择,16-31命令ID)
    1 NSID(命名空间的ID)
    2,3 保留
    4,5 元数据指针
    6~9 数据指针。两个PRP。PRP是64位的物理地址,意思是数据是传进或传出内存。
    10~15 命令相关
  • NVMe设备向主机反馈命令的执行情况使用完成队列(Completion Queue)。

    当创建提交队列时就会指定完成队列。

    完成队列一个条目有16字节:

    比特 大小(bit) 内容
    0-31 32 命令相关
    32-63 32 保留
    64-79 16 提交队列的头指针
    80-95 16 提交队列的ID
    96-111 16 命令ID
    112 1 阶段位。当写条目的时候会切换。
    113-127 15 状态字段。0表示成功。

提交队列和完成队列的实体是一个内存区域,从结构上看是一个环形缓冲区。

NVMe命令处理流程

NVMe使用了门铃机制(Doorbell)。每个队列都有一个门铃指针。

  • 对于发送队列,门铃指针表示的是发送队列的尾指针。驱动程序把命令写入发送队列后,此队列的尾指针寄存器被更新,控制器端从而知道有新命令到来。
  • 当完成队列上有可用的命令,NVMe控制器通过中断机制(INTx, MSI或MSIx)通知主机端。主机端上的驱动程序会处理队列里的新条目,然后更新当前队列的头指针寄存器。
NVMe命令的格式
位置(字节) 内容
0~7 命令编号(代表一个具体的命令)+命名空间的编号(命令发到哪个命令空间)
8~15 保留
16~23 元数据指针
24~31 数据指针1
32~39 数据指针2
40~47 命令10+命令11
48~55 命令12+命令13
56~63 命令14+命令15

命令编号:

大小 名字 含义
2字节 CID
2比特 PSDT 存储数据在内存的组织形式
4比特 保留
2比特 FUSE 本命令是普通命令还是复合命令
1字节 OPC 操作码

命令

管理命令
作用 命令(双字0) NSID(双字1) 数据指针(双字6~9) 命令相关(双字10~15)
创建提交队列 opcode:0x01 队列的基址:双字6,7 双字10:低字队列ID,高字队列大小
双字11:低字flag,高字是对应完成队列的ID
创建完成队列 opcode:0x05 队列的基址:双字6,7 双字10:低字队列ID,高字队列大小
双字11:低字flag,高字是中断向量
identify opcode:0x06 如双字10 identify对象是命名空间,
则设置为命名空间的ID
输出的基址(单个页):双字6,7 双字10的低字节:identify的对象,0命名空间,1控制器,2命名空间列表
IO命令
作用 命令(双字0) NSID(双字1) 数据指针(双字6~9) 命令相关(双字10~15)
opcode:0x02 包含NSID 包含用于数据传递的PRP列表 双字10~11:起始LBA
双字12:低字是要传输的块数量
opcode:0x01 包含NSID 包含用于数据传递的PRP列表 双字10~11:起始LBA
双字12:低字是要传输的块数量

参考资料