NVMe系统内存结构 - SGL

本文详细介绍了NVMe系统中的ScatterGatherList(SGL)数据结构,包括SGLSegment、SGLDescriptor的不同类型及其在数据传输中的作用,以及PRP与SGL的区别。通过实例展示了SGL在数据读取请求中的应用,强调了SGL在灵活性和地址描述方面的优势。
摘要由CSDN通过智能技术生成


本文属于《 NVMe协议基础系列教程》之一,欢迎查看其它文章。

1 SGL简介

Scatter Gather List(SGL)是一个数据结构,用以描述一段数据空间,这个空间可以是数据源所在的空间,也可以是数据目标空间。

SGL首先是个List,是个链表,由一个或者多个SGL Segment组成,而每个SGL Segment又由一个或者多个SGL Descriptor组成。

2 SGL Segment

在这里插入图片描述
SGL Segment是物理内存中一个连续区域中的Qword对齐数据结构,它描述了数据缓冲区的全部、部分或没有数据缓冲区,以及指向下一个SGL Segment(如果有的话)。

一个SGL Segment由一个或多个SGL descriptor组成。只有SGL Segment中的最后一个描述符,可以是SGL Segment descriptor或SGL Last Segment descriptor。

3 SGL Descriptor

SGL Descriptor是SGL最基本的单元,它描述了一段连续的物理内存空间:起始地址+空间大小。每个SGL Descriptor大小是16字节,其通用格式定义,如下图所示:
在这里插入图片描述

  • 第14:00字节:与Descriptor类型相关,不同的类型,有不同的定义。
  • 第15字节:SGL Identifier(SGL标识符)。
    Descriptor Type Specific(03:00)- 具体定义,与Descriptor类型相关;
    SGL Descriptor Type(07:04)- 表示Descriptor类型。

SGL Descriptor Type,有如下这些,具体类型值:
在这里插入图片描述
接下来,详细介绍,每一种具体Descriptor的定义。

3.1 SGL Data Block descriptor

在这里插入图片描述
SGL Data Block descriptor描述了一个数据块,定义如下表:

BytesDescription
7:0Address:指定了数据块的64位起始内存地址。
11:8Length:指定了数据块的字节长度。
若设置为00000000h,表示没有数据传输。指定没有数据传输的SGL Data Block descriptor是一个有效的descriptor。
如果Address字段的值,加上Length字段的值,大于1_00000000_00000000h,则应当作SGL Data Block descriptor发生错误来处理。
14:12保留
15SGL Identifier:该字段的定义如下所示。
Zero(03:00)- 值为0h,如果为非0值,则视为发生错误。
SGL Descriptor Type(07:04)- 值为0h,表示SGL Data Block descriptor类型。

一个全零的SGL descriptor,是一个Address字段为00000000_00000000h,Length字段为00000000h的SGL Data Block descriptor,可以用作空descriptor。

3.2 SGL Bit Bucket descriptor

在这里插入图片描述
SGL Bit Bucket descriptor用于忽略一部分源数据(仅Host从控制器读取数据时有效),定义如下表:

BytesDescription
7:0保留
11:8Length
a)如果SGL描述了一个目标数据缓冲区,那么Length字段指定了,不传输源数据的字节数(即丢弃的字节数)。当Length字段设置为00000000h时,表示源数据不被丢弃。指定不丢弃源数据的SGL Bit Bucket descriptor是有效的descriptor。
b)如果SGL描述了一个源数据缓冲区(即,从主机内存向控制器的写操作),那么Length字段将被忽略,并且源数据或目标数据中不会丢弃任何数据。指定不丢弃数据的SGL Bit Bucket descriptor不应被视为发生错误。
c)如果支持SGL Bit Bucket descriptor,则其在目标数据缓冲区中的长度,需要被包含在NVM命令集数据传输命令中,指定的NLB (Number of Logical Blocks)参数中。它们在源数据缓冲区中的长度不被包括在NLB参数中。
14:12保留
15SGL Identifier:该字段的定义如下所示。
Zero(03:00)- 值为0h,如果为非0值,则视为发生错误。
SGL Descriptor Type(07:04)- 值为1h,表示SGL Bit Bucket descriptor类型。

3.3 SGL Segment descriptor

在这里插入图片描述
SGL Segment descriptor用于描述下一个SGL Segment,而不是最后一个SGL segment,定义如下表:

BytesDescription
7:0Address:指定了下一个SGL Segment的起始64位内存地址,这是一个SGL Segment。
11:8Length:指定了下一个SGL Segment的长度,单位为字节。Length字段,应为非零值,且为16的倍数。
如果Address字段的值,加上Length字段的值,大于1_00000000_00000000h,则应当作SGL Segment descriptor发生错误来处理。
14:12保留
15SGL Identifier:该字段的定义如下所示。
Zero(03:00)- 值为0h,如果为非0值,则视为发生错误。
SGL Descriptor Type(07:04)- 值为2h,表示SGL Segment descriptor类型。

3.4 SGL Last Segment descriptor

在这里插入图片描述
SGL Last Segment descriptor,也叫SGL末段描述符
SGL Last Segment descriptor描述了下一个和最后一个SGL Segment,定义如下表:

BytesDescription
7:0Address:指定了下一个和最后一个SGL Segment的起始64位内存地址,这是一个SGL Segment。
11:8Length:指定了下一个和最后一个SGL Segment的长度,单位为字节。Length字段,应为非零值,且为16的倍数。
如果Address字段的值,加上Length字段的值,大于1_00000000_00000000h,则应当作SGL Last Segment descriptor发生错误来处理。
14:12保留
15SGL Identifier:该字段的定义如下所示。
Zero(03:00)- 值为0h,如果为非0值,则视为发生错误。
SGL Descriptor Type(07:04)- 值为3h,表示SGL Last Segment descriptor类型。

SGL Last Segment descriptor与SGL Segment descriptor,除了SGL Descriptor Type字段值不一样,其他字段完全没有区别。

SGL Last Segment descriptor,通常作为倒数第二个SGL段描述符使用。
为什么需要把倒数第二个SGL段描述符,单独的定义成一种类型呢?
我认为是,让SSD在解析SGL的时候,碰到SGL末段描述符,就知道链表快到头了,后面只有一个段了。

4 SGL组织结构

上面讲了很多,关于SGL Segment和SGL Descriptor定义,可能大家比较迷惑,SGL结构究竟是如何组织的,接下来,我们一探究竟。

对Admin命令来说,它只能用PRP告诉SSD内存物理地址;对I/O命令(NVM Command Set)来说,除了用PRP,Host还可以用SGL的方式,来告诉SSD数据在内存中写入或者读取的物理地址。

Host在命令中会告诉SSD采用何种方式。具体来说,如果命令当中DW0[15]是0,就是PRP方式,否则就是SGL方式。

在“Command Format — NVM Command Set”命令格式定义中,[39:24]SGL Entry 1(SGL1)字段,就是该命令的第一个SGL Segment(链表的头部),如下图所示:
在这里插入图片描述
SGL1可能为三种情况:SGL Data Block descriptor、SGL Last Segment descriptor、SGL Segment descriptor,接下来,我们详细介绍。

4.1 SGL1为SGL Data Block descriptor

SGL1也就是下图中的“First SGL Segment in SQ Entry”,如下所示:
在这里插入图片描述
SGL1是一个SGL Data Block descriptor,该descriptor直接指向内存数据块,它描述了需要传输的整个数据,因此没有其他SGL Segment了。

在本例中,该命令,通过SGL1表示了1块数据。
这也是最简单的情况。

4.2 SGL1为SGL Last Segment descriptor

在这里插入图片描述
SGL1是一个SGL Last Segment descriptor,因此它是倒数第二个SGL Segment,那么它指向的SGL Segment,即为最后一个SGL Segment(图中Last SGL Segment所示)。
最后一个SGL Segment,有4个SGL Data Block descriptor,均指向数据块。

在本例中,该命令,通过SGL1表示了4块数据。

4.3 SGL1为SGL Segment descriptor

在这里插入图片描述
按上图描述,可得出如下信息:

  • SGL1是一个SGL Segment descriptor,指向第2个SGL Segment;该descriptor中Length字段,应该为6个Descriptor长度(6*16=96字节)。
  • 在第2个SGL Segment中,有5个SGL Data Block descriptor与1个SGL Last Segment descriptor,前者指向数据块,后者再指向第3个SGL Segment(最后一个SGL Segment)。
  • 在第3个SGL Segment中,有4个SGL Data Block descriptor,也是指向数据块。

在本例中,该命令,通过SGL1表示了5+4=9块数据。

如果要继续往下链接SGL Segment的话,只需要把上例中,第2个SGL Segment中最后一个descriptor换成SGL Segment descriptor类型,并保证倒数第二个SGL Segment的最后一个descriptor为SGL Last Segment descriptor类型,就可以了。

通过上述的三个例子,想必大家,已经掌握了SGL的组织结构。

5 PRP与SGL的区别

无论是PRP还是SGL,本质都是描述内存中的一段数据空间,这段数据空间在物理上,可能是连续的,也可能是不连续的。主机在命令中设置好PRP或者SGL,告诉SSD数据源在内存的什么位置,或者从闪存上读取的数据,应该放到内存的什么位置。

大家也许跟我有同样的疑问,那就是,既然有PRP,为什么还需要SGL?
事实上,NVMe1.0时候的确只有PRP,SGL是NVMe1.1之后引入的。

SGL和PRP本质区别在哪?
下图道出了真相:一段数据空间,对PRP来说,它只能映射到一个个物理页;而对SGL来说,它可以映射到,任意大小的连续物理空间。
在这里插入图片描述
SGL和PRP本质区别就是:
PRP只能描述定长的内存空间(Address),而SGL可以描述任意大小的内存空间(Address + Length),SGL更加灵活。

在支持上的区别:

  • 对NVMe over PCIe(我们目前讲的都是NVMe跑在PCIe上),Admin命令只支持PRP,I/O命令可以支持PRP或者SGL;
  • 对NVMe over Fabrics,所有命令只支持SGL。

6 SGL数据读取请求示例

这个例子中,假设Host需要从SSD中读取13KB的数据,其中真正只需要11KB数据,这11KB的数据需要放到3个大小不同的内存中,分别是:3KB,4KB和4KB。
在这里插入图片描述
有三个SGL Segment描述了,逻辑块数据在主机内存中传输的位置。

  • 这三个SGL Segment,总共包含三个Data Block descriptor,长度分别为3kb、4kb和4kb。
  • Destination SGL Segment 1,包含一个长度为2kb的Bit Bucket descriptor,它表示不传输(即忽略)来自设备的2kb的逻辑块数据,也就是读取的时候跳过这2kb数据。
  • Destination SGL Segment 1,还包含一个Last Segment descriptor,表示该描述符所指向的Segment是最后一个SGL Segment。

参考文档:

  • 17
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: NVMe(Non-Volatile Memory Express)是一种基于PCIe总线的高性能存储接口标准。NVMe SCSI(Small Computer System Interface)是在NVMe基础上将SCSI命令映射到NVMe的一种实现方式。 NVMe SCSI是一种在NVMe存储设备上执行SCSI命令的协议。其中,NVMe SCSI Primary Commands - 5是一组用于执行传输数据、控制设备和查询设备信息的SCSI命令。 这组命令包括读取命令(Read Command)、写入命令(Write Command)、写入长命令(Write Long Command)、写入同步命令(Write Synchronization Command)和预取命令(Prefetch Command)。 读取命令用于从存储设备中读取数据。可以通过设置数据传输长度和传输起始位置来指定需要读取的数据范围。 写入命令用于向存储设备中写入数据。与读取命令类似,可以设置数据传输长度和传输起始位置来指定写入的数据范围。 写入长命令用于写入长数据。相比于写入命令,写入长命令支持更大的数据传输长度。 写入同步命令用于在写入数据之前先进行一个或多个同步确认。这样可以确保之前的所有写入操作都完成后再进行下一个写入操作。 预取命令用于指定设备预取机制的信息,以优化读取性能。 总之,NVMe SCSI Primary Commands - 5是一组在NVMe设备上执行SCSI命令的指令集,用于实现数据读取、写入、同步和预取等操作,提供了高性能和效率的存储访问方式。 ### 回答2: NVMe(Non-Volatile Memory Express)是一种高性能存储接口协议,它的SCSI(Small Computer System Interface)主要命令集中的第5个命令被称为NVMe SCSI Primary Command - 5。 具体而言,NVMe SCSI Primary Command - 5是用于完成与NVMe设备通信的主要命令之一。它允许主机与NVMe设备之间进行读写操作,以获取或修改存储在设备上的数据。 通过进行读取操作,NVMe SCSI Primary Command - 5命令可以传输存储在NVMe设备中的数据到主机中。这对于从存储设备中获取文件和信息非常有用。主机可以指定读取的起始地址和读取的数据长度,以确保获取正确的数据。 与读取操作相反,NVMe SCSI Primary Command - 5也支持写入操作。主机可以将数据写入NVMe设备,以便在存储中创建、修改或更新文件和信息。通过指定写入的起始地址和写入的数据长度,主机可以确保写入正确的位置和适当的数据。 总的来说,NVMe SCSI Primary Command - 5命令是与NVMe设备通信的关键命令之一。它通过读取和写入操作,允许主机与NVMe设备之间高效地传输数据。 ### 回答3: NVMe SCSI primary commands - 5,是指NVMe(Non-volatile Memory Express)SCSI(Small Computer System Interface)主要命令的第五个版本。 第五版的NVMe SCSI primary commands扩展了之前版本的功能,提供了更高的性能和更丰富的功能支持。这些命令是用于与NVMe设备进行通信和控制的指令集。 NVMe是一种针对固态存储器(SSD)的高速、低延迟、高吞吐量的连接协议。SCSI是一种通用的存储设备接口,用于连接计算机和外部存储设备。 NVMe SCSI主要命令-5包括了以下几个方面的功能: 1. 带宽管理:命令集提供了更灵活的带宽管理功能,可以根据需要灵活地分配和管理带宽资源,以最大程度地提高系统性能。 2. 多路径支持:命令集扩展了多路径支持的功能,使得在多条路径之间进行负载平衡和故障转移更加灵活和可靠。 3. 电源管理:命令集增加了更多的电源管理命令,以支持设备的低功耗状态和快速唤醒。 4. 安全性增强:命令集引入了更多的安全性增强功能,包括加密、数据完整性保护等,以保护存储数据的安全性。 5. 性能优化:命令集提供了更多的性能优化命令,包括请求队列管理、命令优先级管理等,以提高系统性能和响应速度。 总之,NVMe SCSI主要命令-5是为了更好地满足NVMe设备的高性能和高可靠性需求而设计的指令集。通过扩展功能和提供更多的控制能力,它可以提供更优秀的存储性能和多样化的应用场景支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百里杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值