前言
NVMe Controller会将一些重要的信息(NVMe控制器的能力,状态,Admin SQ, CQ地址等)直接放在NVMe寄存器中,另一部分(跟SSD比较相关的)信息会放置在SSD内部,并最终通过Admin NVMe CMD去设置或者获取。
NVMe寄存器
NVMe寄存器地址映射在PCIe Bar寄存器中。NVMe寄存器的偏移表如下:
1. Controller Capabilities
[15:0] 表示Maximum Queue Entries Supported,代表这个Controller可以支持的Queue的深度多大。这里的值为0x03FF,表示此controller的IO SQ, CQ队列深度为1024。该字段表示控制器支持的最大单个队列大小。对于基于PCIe的NVMe实现,此值适用于主机创建的I/O提交队列和I/O完成队列。
[16] Contiguous Queues Required (CQR) (RO), 表示Contiguous Queues Required,表示host在create I/O SQ 和 CQ的時候,是否必須提供物理连续的内存。
[18:17] Arbitration Mechanism Supported (AMS) (RO), 仲裁机制支持(AMS) (RO), 表示控制器支持的可选仲裁机制。
[34:31] Doorbell Stride(RO), 每个提交队列和完成队列门铃寄存器的大小为32位。这个寄存器指示门铃寄存器之间的间隔。步幅被指定为(2 ^ (2 + DSTRD))字节。值0h表示4字节的步幅,每个寄存器之间没有保留空间。
[35] 表示控制器是否支持NVM子系统复位特性。如果控制器支持NVM子系统复位功能,则该位设置为“1”。如果控制器不支持NVM子系统复位特性,则该位清除为’ 0’。
[43:36] Command Sets Supported (RO), 该字段表示控制器支持的I/O命令集。如果一个位被设置为’ 1 ‘,则支持相应的I/O命令集。如果某个位被清除为’ 0 ',则不支持相应的I/O命令集。如果不支持I/O命令集,则将第44位设置为“1”。
[50:47] Memory Page Size Minimum (RO), 该字段表示控制器支持的最小主机内存页面大小。最小内存页大小是(2 ^ (12 + MPSMIN))。主机不能在CC.MPS中配置小于此值的内存页大小。
[54:51] Memory Page Size Maximum (RO), 该字段表示控制器支持的最大主机内存页面大小。最大内存页大小是(2 ^ (12 + MPSMAX))。主机不能在CC.MPS中配置大于此值的内存页大小。
2. Version
[7:0] Tertiary Version Number(TER)
[15:8] Minor Version Number (MNR) (RO)
[31:16] Major Version Number (MJR) (RO)
该寄存器表示控制器实现支持的NVM Express基本规范的主要、次要和第三个版本。
3. Controller Configuration
这个寄存器修改控制器的设置。主机软件需要将仲裁机制(CC.AMS)、内存页面大小(CC.MPS)和命令集(CC.CSS)设置为有效值,然后通过将CC.EN设置为“1”使能控制器。在初始化I/O完成队列入口大小(CC.IOCQES)和I/O提交队列入口大小(CC.IOSQES)之前尝试创建I/O队列应该导致控制器终止创建I/O完成队列命令或创建I/O提交队列命令,状态码为无效队列大小。
[0] Enable
[6:4] I/O Command Set Selected. 该字段指定选择的I/O命令集。主机软件只能选择一个支持的I/O命令集,如CAP.CSS所示。该字段只能在禁用控制器(CC.EN清除为’ 0 ‘)时更改。所选择的I/O命令集应用于所有I/O提交队列。如果“CSS”字段中第44位被设置为“1”,则111b表示只支持Admin命令集,不支持I/O命令集或I/O命令集特定的Admin命令。当只支持管理命令集时,在I/O提交队列上提交的任何命令和在Admin提交队列上提交的任何I/O命令集特定的管理命令都以无效命令操作码状态完成。如果命令集支持(CSS)字段中的第44位被清除为’ 0 ',则设置为3b’000.
[10:7] Memory Page Size (MPS) (RW). 该字段表示主机内存页面大小。内存页大小为(2 ^ (12 + MPS))。因此,最小主机内存页大小为4 KiB,最大主机内存页大小为128 MiB。主机软件设置的值应该是CAP.MPSMAX和CAP.MPSMIN字段所支持的值。该字段描述用于PRP表项大小的值。只有当EN被清除为“0”时,该字段才能被修改。
[13:11] Arbitration Mechanism Selected (AMS) (RW). 该字段选择要使用的仲裁机制。只有当EN被清除为“0”时,这个值才会被改变。主机软件应仅将此字段设置为CAP.AMS中指示的支持仲裁机制。如果该字段被设置为不支持的值,则行为未定义。
[15:14] Shutdown Notification (SHN) (RW)
[19:16] I/O Submission Queue Entry Size (IOSQES) (RW) .此字段定义用于所选I/O命令集的I/O提交队列条目大小。对于每个I/O命令集,该字段的所需值和最大值在图SSD中的Identify Controller数据结构中的SQES字段中指定。该值以字节为单位,指定为2的幂(2^n)。
[23:20] I/O Completion Queue Entry Size (IOCQES) (RW) .此字段定义用于所选I/O命令集的I/O提交队列条目大小。对于每个I/O命令集,该字段的所需值和最大值在图SSD中的Identify Controller数据结构中的CQES字段中指定。该值以字节为单位,指定为2的幂(2^n)。
4. Controller Status
[0] Ready (RDY) (RO). 在CC.EN设置为“1”后,当控制器准备接受提交队列尾部门铃写入时,此位被设置为“1”。一旦控制器准备重新启用,当CC.EN被清除为“0”时,该位将被清除为“0”。在CC.EN位设置为“1”之后,在此位设置为“1”之前,不会向控制器提交命令。不遵循这一要求将产生未定义的结果。主机软件在将CC.EN从先前的值“0”设置为“1”后,至少需要等待CAP.TO秒才能将该位设置为“1”。
5. Admin Queue Attributes
[11:0] Admin Submission Queue Size (ASQS) (RW). 定义条目中管理提交队列的大小。参考4.1.3节。当该字段被清除为0h时启用控制器将产生未定义的结果。Admin Submission Queue的最小大小是两个条目。Admin Submission Queue的最大大小是4,096个条目。这是一个基于0的值。这里设置为256。
[27:16] Admin Completion Queue Size (ACQS) (RW). 在条目中定义管理完成队列的大小。当该字段被清除为0h时启用控制器将产生未定义的结果。管理完成队列的最小大小是两个条目。Admin Completion Queue的最大大小是4,096个条目。这是一个基于0的值。
6. Admin Submission Queue Base Address
该字段为Admin Submission Queue指定64位物理地址。此地址应与内存页对齐(基于CC.MPS中的值)。所有Admin Command,包括创建I/O提交队列和I/O完成队列,都应提交到该队列。
7. Admin Completion Queue Base Address
该字段为Admin Completion Queue指定64位物理地址。此地址应与内存页对齐(基于CC.MPS中的值)。所有Admin Command,包括创建I/O提交队列和I/O完成队列,都应提交到该队列。
总结:NVMe寄存器中存放ASQ, ACQ, I/O SQ, I/O CQ中的队列深度,以及I/O SQ, I/O CQ中的Entry大小;ASQ ACQ中的Entry找不到在哪里设置,猜测可能是固定64字节和16字节;ASQ ACQ在主机内存中的位置;支持的NVMe 命令集;内存页面大小;复位信息;命令的仲裁机制;该控制器是否准备就绪;关机通知。
SSD内部数据结构——Identify Controller Data Structure (ICDS)
ICDS主要是描述Controller的一些信息和能力
struct nvme_id_ctrl {
__le16 vid;
__le16 ssvid;
char sn[20];
char mn[40];
char fr[8];
__u8 rab;
__u8 ieee[3];
__u8 cmic;
__u8 mdts;
__le16 cntlid;
__le32 ver;
__le32 rtd3r;
__le32 rtd3e;
__le32 oaes;
__le32 ctratt;
__u8 rsvd100[28];
__le16 crdt1;
__le16 crdt2;
__le16 crdt3;
__u8 rsvd134[122];
__le16 oacs;
__u8 acl;
__u8 aerl;
__u8 frmw;
__u8 lpa;
__u8 elpe;
__u8 npss;
__u8 avscc;
__u8 apsta;
__le16 wctemp;
__le16 cctemp;
__le16 mtfa;
__le32 hmpre;
__le32 hmmin;
__u8 tnvmcap[16];
__u8 unvmcap[16];
__le32 rpmbs;
__le16 edstt;
__u8 dsto;
__u8 fwug;
__le16 kas;
__le16 hctma;
__le16 mntmt;
__le16 mxtmt;
__le32 sanicap;
__le32 hmminds;
__le16 hmmaxd;
__u8 rsvd338[4];
__u8 anatt;
__u8 anacap;
__le32 anagrpmax;
__le32 nanagrpid;
__u8 rsvd352[160];
__u8 sqes;
__u8 cqes;
__le16 maxcmd;
__le32 nn;
__le16 oncs;
__le16 fuses;
__u8 fna;
__u8 vwc;
__le16 awun;
__le16 awupf;
__u8 nvscc;
__u8 nwpc;
__le16 acwu;
__u8 rsvd534[2];
__le32 sgls;
__le32 mnan;
__u8 rsvd544[224];
char subnqn[256];
__u8 rsvd1024[768];
__le32 ioccsz;
__le32 iorcsz;
__le16 icdoff;
__u8 ctrattr;
__u8 msdbd;
__u8 rsvd1804[244];
struct nvme_id_power_state psd[32];
__u8 vs[1024];
};
1. mdts字段
代表Maximum Data Transfer Size,表示一次NVMe IO最多能传输多少数据。
2. sqes字段
表示I/O SQ 的一个entry为多少bytes,表示为2^(sqes)
3. cqes字段
表示I/O CQ 的一个entry为多少bytes,表示为2^(cqes)
SSD内部数据结构——Identify Namespace Data Structure (INDS)
INDS是给HOST回报namespace中的一些信息及能力
struct nvme_id_ns {
__le64 nsze;
__le64 ncap;
__le64 nuse;
__u8 nsfeat;
__u8 nlbaf;
__u8 flbas;
__u8 mc;
__u8 dpc;
__u8 dps;
__u8 nmic;
__u8 rescap;
__u8 fpi;
__u8 dlfeat;
__le16 nawun;
__le16 nawupf;
__le16 nacwu;
__le16 nabsn;
__le16 nabo;
__le16 nabspf;
__le16 noiob;
__u8 nvmcap[16];
__le16 npwg;
__le16 npwa;
__le16 npdg;
__le16 npda;
__le16 nows;
__u8 rsvd74[18];
__le32 anagrpid;
__u8 rsvd96[3];
__u8 nsattr;
__le16 nvmsetid;
__le16 endgid;
__u8 nguid[16];
__u8 eui64[8];
struct nvme_lbaf lbaf[16];
__u8 rsvd192[192];
__u8 vs[3712];
};
1. nlbaf字段
表示此命名空间可以支持nlbaf中 LBA 格式
2. flbas
表示当前命名空间使用第flbas个 LBA格式
3. nvme_lbaf 中的 nsze字段
定义了命名空间在逻辑块中的总大小(LBA 0到n-1)。
4. nvme_lbaf 中的 ncap字段
字段定义了在任何时间点可以分配的逻辑块的最大数目。
5. nvme_lbaf 中的 nuse字段
字段定义命名空间中当前分配的逻辑块的数量。