注意:本文翻译仅为学习交流用,版权归原文作者所有。
原文出处。
这个这个章节讲NVM Express中的数据结构。
一个entry的提交者使用当前项的尾指针指向下一个空闲的队列槽位。在放置新entry后,提交者将尾指针向后移动,如果尾指针超过了队列大小,则将尾部entry滚动至0。提交者放置新entry直至队列满为止。
entry的消费者使用当前头entry指针表示下一个准备消耗的entry。在消耗一个entry后,头指针向下一个entry移动。同提交者…
提交队列和关联的完成队列的创建和销毁要求是有序执行的。主机软件应该先创建完成队列,再创建与之关联的提交队列。提交队列能在完成队列被创建完毕后任何时间创建。主机软件应该先删除提交队列,再删除完成队列。为中止提交到提交队列中的命令,主机软件应该下发删除IO提交队列的命令。
主机软件写提交队列Tail Doorbell和完成队列Head Doorbell,来向控制器通知新值。如果主机软件向提交队列Tail Doorbell或者完成队列Head Doorbell写无效值,并且异步事件请求命令未完成,那么一个异步事件将被下发到Admin完成队列,状态码为Invalid Doorbell Write Value.与之关联的队列应该被删除和重新创建。
主机软件检查完成队列项的PhaseTag§判断是否有命令完成状态放入完成队列。完成队列尾指针对主机是透明的,仅在控制器内部使用。控制器使用完成队列项中的SQHD(提交队列头指针)告诉主机提交队列头指针的值,在SQHD指向的提交队列项被消耗时,并不表示像是否被执行或者已经完成。
当主机软件向提交队列Tail Doorbell写新值时,表示有新的命令放置在提交队列Tail pointer指向的地方。提交队列Tail Doorbell被写时,表示主机软件提供了一个至多个提交队列项。
完成队列项被下发时,SQ head pointer向前移动,SQ项被控制器消耗。完成队列项可能指示一个至多个提交队列项被消耗。
————————————————————————
局部总结:
————————————————————————
Head entry Pointer == Tail entry Pointer
Head entry Pointer + 1 == Tail entry Pointer
IO SQ/CQ队列最大值是64Ki个slots,Admin SQ/CQ队列最大值是4Ki个slots。
队列的ID是两字节整型,在队列创建时被确定。
加权robin采取优先级分四档:Urgent, High, Medium, Low.
每个命令大小为64字节。
提交队列项64字节,由以下几项组成:1) Command Dword 0, 2) NS Id, 3) Meta Pointer, 4) PRP Entry1/2, SGL Entry1, Meta SGL Segment Pointer.
具体的CDW0(4字节)格式可见SPEC中的图104。
Bits | Description |
---|---|
31:16 | Command Identifier (CID): This field specifies a unique identifier for the command when combined with the Submission Queue identifier. 命令ID,在提交队列创建时候CID |
15:14 | PRP or SGL for Data Transfer (PSDT):指定是否使用PRPs或者SGLs用于描述传输的数据,PRPs用于AdminCMD NVMe over PCIe, SGLs用于Admin/IO cmd NVMe over Fabrics, - 00b: PRPs用于传输; - 01b: SGLs用于传输,并且Meta Pointer(MPTR)包含单个连续物理buffer的地址,buffer字节对齐; - 10b: SGLs用于传输,并且Meta Pointer(MPTR)包含SGL Segment的地址,这个SGL Segment是4字节对齐的; - 11b: 保留 |
09:08 | Fused Operation(FUSE): … |
07:00 | Opcode(OPC): 指定命令的操作码 |
Admin/NVM Command Set的大小是64字节,将来如果定义其他IO Command有可能有其他命令size/format.
SGLs不会被用在Admin CMD NVMe over PCIe中。
Bytes | Description |
---|---|
03:00 | Command Dword 0 (CDW0) |
07:04 | NSID: 这个命令应用的NS |
15:08 | 保留 |
23:16 | Metadata Pointer(MPTR):只有命令有meta数据并且不跟逻辑块数据交织在一起时有效,在Format NVM cmd中被用。CDW0.PSDT调节MPTR。 |
39:24 | Data Pointer(DPTR): |
CDW0.PSDT=00b:
PRPx每个占1字节
PRP2:
a) 如果数据传输不跨内存页,则保留
b) 如果数据传输超过一个内存页,则记录第二个内存页的基址。
c) PRP List Pointer跨多个内存页。
PRP1: 包含PRP项或者PRP List Pointer。
SGL占2字节
SGL1:
Bytes | Desc |
---|---|
43:40 | Command Dword 10 |
47:44 | Command Dword 11 |
51:48 | Command Dword 12 |
55:52 | Command Dword 13 |
59:56 | Command Dword 14 |
63:60 | Command Dword 15 |
CDWx:在具体Admin/NVM CMD Set中定义具体的含义。
除了在Admin/NVM Cmd定义的变量,Admin/NVM Vendor Spec cmds支持Data/Meta Transfer数量(以Dwords为单位)。
CWD10: NO. of Dwords Data Transfer(NDT),数据传输的数量。
CWD11: NO. of Dwords Meta Transfer(NDM),元数据传输数量。
PRP(物理区域页)entry是指向一个物理内存页的指针。PRP作为一个内存和控制器之间数据传递的catter/gather机制。PRP entry固定大小(8个字节)以在控制器和主机之间高效数据传输。
物理内存页大小在主机软件中的CC.MPS
配置。
PRP entry由Page Base Addr + Offset
组成(8字节)。
Bits | Desc |
---|---|
63:00 | Page Base Address and Offset(PBAO): 描述64-bit物理页内存地址,低n位数(n:0)描述页内偏移 |
PRP List(物理区域页列表)是一组PRP entry,存放在一个page里,描述连续内存。如果传输的数据量需要多个PRP List内存页描述,则最后一个PRP entry是一个指向下一个PRP List的指针。
第一个PRP Entry可能是非零内存页偏移,但是第二个PRP Entry需要是4字节对齐。
SGL是内存地址空间的数据结构,用于描述数据缓存(data buffer)。控制器在Identify Controller
中指针SGL的类型。data buffer要么是源buffer要么是目标buffer。一个SGL包含多个SGL Segment。
SGL segment是4字节对齐的数据结构,存放在连续的物理内存中,描述全部、部分、无 数据缓存和下一个SGL segment。一个SGL segment由一组至少一个SGL描述符组成。只有SGL Segment中的最后一个描述符可能是SGL Segment描述符或者SGL Last Segment desc.
!控制器支持字节、双字节对齐和Data Blocks粒度。
Figure12: SGL Desc Type
Code | Desc |
---|---|
0h | SGL Data Block descriptor |
1h | SGL Bit Bucket descriptor |
2h | SGL Segment descriptor |
3h | SGL Last Segment descriptor |
4h | Keyed SGL Data Block descriptor |
5h | Transport SGL Data Block descriptor |
6h to Eh | 保留 |
Fh | 厂商自定义 |
NS支持meta时,meta作为逻辑块的一部分(i.e., extended logical block),meta也可能独立于data传输。meta与关联的逻辑块要保持原子写。
MR是在NS格式化时指定的。
在NS被格式化用于End-to-End data protection时,meta的前8个字节或者后8个字节用于存放保护信息。
完成队列的一个entry至少16个字节。
如果CQ entry被多个写创建,那么Phase Tag应该被最后一个写 更新。
CQE前16个字节布局:
DW 2:
Bits | Desc |
---|---|
31:16 | SQID:标记命令下发到哪个提交队列 |
15:00 | SQ Head Pointer: 当前提交队列的头指针位置 |
DW 3:
Bits | Desc |
---|---|
31:17 | Status Field(SF): 命令完成的状态 |
16 | Phase Tag§: 标记CQE是否是新的 |
15:00 | Command ID(CID): 正在完成的命令的ID,提交队列的最大深度是64Ki |
…
CMB是一个区域,这个区域能进行常规的对控制器的read/write。
通过设置CAP.CMBS=1
控制器支持CMB
CMB PCIe地址范围能用于下发对CMB的外部内存读/写请求。CMB的PCIe基地址能通过PCI BAR(CMBLOC.BIR
)和偏移(CMDLOC.OFST
)定义。CMB大小由CMBSZ.SZ
设置。
控制器能够使用主机提供的地址去读写CMB。
尽管PCIe地址范围与CMB控制器的地址范围可能不同,但是两个范围尺寸是相同的,并且每个范围的偏移是一一对应的。主机通过CMBMSC
配置控制器地址范围。
Host通过CMBMSC.CMSE
赋能CMB control内存空间,Host将地址映射到CMB的controller地址范围,controller能直接向CMB下发内存读/写请求。
Host需要配置CMB的controller地址范围,避免与用于DMA的地址范围重叠。
——————
注:
Host内存中的提交队列要求控制器执行PCIe读Host内存,以取回队列项。控制器内存中的提交队列赋能Host软件直接写SQE到控制器内部内存空间,避免从控制器读到内存。这个方法能减少命令执行时延,改善PCIe fabric 拓扑结构。PRP Lists和SGLs要求从PCIe fabrics中取回,当然也能直接将PRP/SGL写到CMB中。对于小数量的写,将data和meta直接写到CMB比从控制器取回Host内存,更有优势。
在控制器内存中的队列,与在Host内存中的队列,行为相同。不同的是,内存地址是位于控制器自己的内存,而不是Host的内存。Admin或者IO队列能存储在CMB中。
PMR基本原理与CMB相同,PMR是PM区域,能被PCIe 读/写。外部内存读写PCIe地址范围能被定向到PMR。
NVM Set是一个集合,在逻辑上(有可能在物理上)独立于其他NVM Set的NVM集合。一个NS是在NVM Set里面,不会跨多个NVM Set.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a6Tud9HO-1613464013150)(./imgs/fig133.png)]
…
…
从提交队列和完成队列中取entry有三种仲裁方式:1) Round Robin, 2) Werighted Round Robin with Urgent Priority Class Arbit. 3) Vendor Spec Arbit.