大普微NVMe FDP技术详解:从原理到实践
在企业级存储领域,随着数据写入量的激增,如何提升SSD的寿命并保持高性能始终是核心议题。NVMe规范提出的FDP(Flexible Data Placement,灵活数据放置) 技术,正是为了解决这一痛点而生。作为企业级SSD领域的深耕者,大普微对FDP技术进行了深度的研究与工程化落地,目前搭载自研芯片的最新一代Gen5 QLC SSD及H5/R6 TLC SSD已全面支持NVMe规范中的FDP特性。

•DapuStor QLC SSD
及TLC SSD均支持FDP功能
FDP 技术的出现,为企业级存储提供了一种标准化的机制来降低写放大,从而显著提升 SSD 的使用寿命(WAF 降低)并保持长期稳态性能(QoS 优化)。本文将从背景原理、协议规范、配置实践及开发接口等方面,深入剖析 FDP 技术。
01. 背景:写放大与 FDP 的诞生
写放大是 SSD 固有的现象。主机向SSD写入数据时,闪存内部在介质上的实际写入数据量会大于主机下发的写入数据量,这种现象被称为写放大。写放大会缩短 SSD 的寿命并降低其性能。
写放大的根源:垃圾回收
写放大主要源于 SSD 的 垃圾回收机制。 由于 NAND Flash 的物理特性,数据擦除是以一个数据块为单位的,这个数据块的大小大于一笔数据写入所占用的空间,所以被擦除的数据块上可能既有需要擦除的数据,也有仍然有效的数据。 因此,擦除一个数据块前要把其中的有效数据先读出来,写入到另外一个数据块,这种额外的搬移操作,就产生了写放大。
FDP 的解决思路
研究发现,如果能将不同生命周期的数据放置在 SSD的不同物理区域,就能显著减少写放大。
• 举例:将频繁更新(热数据)和不常更新(冷数据)的数据分开存放。
• 效果:同一物理块上的数据,其“由有效变无效”的状态切换将趋于同步。当擦除该块时,绝大多数数据已处于无效状态,需要搬移的有效数据变少,从而降低写放大。
02. NVMe 规范对 FDP 的支持
为了实现将不同类别的数据分别写入SSD上不同物理区域的目标,需要主机和SSD的互相配合:
(1)主机侧:按照数据的生命周期对数据分类。比如:经常更改的数据,写入硬盘后不经常更改的数据等等。
(2)SSD侧:划分出多个不同的物理区域,将不同的物理区域里放置不同生命周期的数据。
在NVMe FDP规范中,定义了以下概念,来帮助SSD管理数据写入的物理区域,并提供主机映射到这些物理区域的方法。
• Reclaim Unit (RU):数据写入的一个物理区域,由SSD内部管理的一组闪存物理块。
• Reclaim Unit Handle (RUH):指向RU(物理区域)的句柄。同一时刻一个RU只能被一个RUH访问。另一方面,RUH 在某个时刻究竟对应哪个RU,是SSD内部的实现,对主机屏蔽。这样有利于SSD内部资源调度,也实现了主机和SSD的工作解耦。
• Reclaim Group(RG):RUH的集合。通常一个SSD内只有一个RG。
• Placement Handle (PH):主机侧使用的句柄,用来指向RUH。在一个namespace内,PH和RUH之间一一对应。
基于以上定义的这些概念,主机每次在写数据时,根据当前写入的这笔数据的类别,指定一个Placement Handle。从而间接告诉SSD,数据写入到SSD内部的哪个物理区域里。如下图所示,主机在NVMe 的写命令中标记Placement Handle的值,SSD收到后进行解析,触发多级映射过程:Placement Handle -> Reclaim Handle -> Reclaim Unit。

从上图中可以看到,Reclaim Goup包含在Endurance Group里。通常一个 NVMe SSD只有一个Endurance Group。不过在一个Endurance Group里可创建多个Namespace。
配置FDP需要用到nvme-cli,版本2.3及以上。
03. 配置与使能 FDP
查看FDP的可用配置组
配置FDP时需要选取一组可用的配置值的组合(不是单独配置FDP特性的各个参数),可以通过如下命令获取可用配置组合。下面的示例展示了其中一个可用的组合。
nvme fdp configs /dev/nvme1 -e 1
FDP Attributes: 0x80
Vendor Specific Size: 0
Number of Reclaim Groups: 1
Number of Reclaim Unit Handles: 8
Number of Namespaces Supported: 128
Reclaim Unit Nominal Size: 9873653760
Estimated Reclaim Unit Time Limit: 345600
Reclaim Unit Handle List:
[0]: Persistently Isolated
[1]: Persistently Isolated
[2]: Persistently Isolated
[3]: Persistently Isolated
[4]: Persistently Isolated
[5]: Persistently Isolated
[6]: Persistently Isolated
[7]: Persistently Isolated
在这个配置组合中,配置的Reclaim Group 有1个,RUH有8个可用,Endurance Group里可以支持128个namespace。
生效范围与初始化
FDP的使能和关闭配置的影响范围是NVMe的一个Endurance Group,对于Endurance Group 内的多个Namespace 都生效。所以,在打开FDP功能前,首先确保盘上没有namespace。如果有,需先删除,再配置 FDP 使能。此后新建的 Namespace 将自动继承 FDP 属性。
使能FDP 的 nvme 命令示例如下:
nvme set-feature /dev/nvme1 -f 0x1d --value 1 -c 0x201 -s
-f 0x1d: 表示设置的是FDP特性,编码为0x1d。
-c 0x201: 表示NVMe set-feature 命令的 DWORD 12 的内容。对应NVMe规范中下表的定义,这里0x201对应的bit位代表使用第2个FDP配置组合(Flexible Data Placement Configuration Index, 编号从0开始),并使能(Flexible Data Placement Enable)

分配数据写入的物理区域
创建namespace,并且配置Placement Handle 和 Reclaim Unit Handle 的对应关系。命令示例如下:
nvme create-ns /dev/nvme1 -c 0x1bf1f72b0 -s 0x1bf1f72b0 -f 0 -d 0 -e 1 -m 0 -n 8 -p 2,5
-f 0:数据放置格式的索引。0 代表采用SSD提供的第一种可用方式,比如一个 LBA 指向一个 512数据块的方式。
-c -s 表示 namespace 的容量大小,0x开头的16进制数字代表有多少个数据块可用。每个数据块的大小由前面提到的-f 参数指定。
-n 2 表示会用到 2 个RUH。
-p 2,5 表示Placement Handle 和 RUH 的对应关系。即:placement handle 0, 1 分别对应RUH 2,5。注意参数中并没有 Placement Handle,只有RUH的ID列表,Placement Handle 的ID隐含的是从0,1,2.... 排列下去的。
配置 Directive 特性

从上图中看到,主机的写数据命令里带有一个DSPEC 字段,用来帮助SSD判断写入数据的位置。这个机制是由 NVMe 的Directive 特性支持。所以使用FDP功能需要同时配置Directive 特性。
Directive特性提供了主机和SSD之间交换配置信息的机制,从配置信息的类型看,分为4种类型:Identify, Streams, Data Placement, Vendor Specific。从配置信息的传输方向又分为2种:Directive Send 命令用于主机向SSD 传递信息,Directive Receive 命令用于SSD向主机传递信息。在FDP场景中,使用了Directive 特性中的 Data Placement 类型,以及Directive Send 命令从主机向SSD发命令,使能Data Placement。
打开Directive 功能的命令示例如下:
nvme dir-send /dev/nvme1n1 -n 1 -D 0 -O 1 -T 2 -e 1 -H
dir-send : 表示这是 Directive Send 命令。
-n 1 : namespace id 为1。
-T 2 : 对Directive 类型编号为2,即Data Placement 类型的Directive 做配置。
-e 1: 使能 -T 字段对应的 Directive,在这里是Data Placement 类型。
04. 按数据类别写入数据
初始化完成后,主机即可以在写入操作时,在NVMe 的write命令中指定Placement Handle,从而指示SSD实现数据按类别写入到不同的物理区域。
在 NVMe Command Set 规范中,通过 Write 命令 的特定字段来实现:
• 第 12 个 DWORD (23:20 bit):指定 Directive Type 为 Data Placement。
• 第 13 个 DWORD (31:16 bit):放置 DSPEC (Directive Specific) 字段,用于标记 Placement Handle。


根据NVMe Base Spec,通常一个SSD里只配置一个Reclaim Group,因此常用如下格式指定 Placement Handle:

05. 统计、监件控与事调试
统计信息获取
在 NVMe 规范中定义了 I/O Management Receive 命令,用于主机获取SSD的运行信息。这一机制在FDP应用场景中,通过设置 DWORD 10 字段中的07:00 bit位为 0x01 来指示返回 Reclaim Unit Handle Status,即 RUH 所指向的RU的统计信息。

根据NVMe command set spec,一个RUH所指向的RU的统计信息包括一系列字段,如下。
Placement Identifier,即主机使用的Placement Handle对应的ID。
Reclaim Unit Handle Identifier,即SSD使用的RUH所对应的ID。
Estimated Active Reclaim Unit Time Remaining (EARUTR),RUH当前指向的RU还可写入的剩余时间。在剩余时间用尽后,RUH会切换指向到其它RU。
Reclaim Unit Available Media Writes (RUAMW) ,RUH当前指向的RU可写入的逻辑块数量。
查看fdp统计信息的命令如下:
nvme fdp status /dev/nvme1n1
Placement Identifier 0; Reclaim Unit Handle Identifier 2
Estimated Active Reclaim Unit Time Remaining (EARUTR): 345600
Reclaim Unit Available Media Writes (RUAMW): 19284480
Placement Identifier 1; Reclaim Unit Handle Identifier 5
Estimated Active Reclaim Unit Time Remaining (EARUTR): 345600
Reclaim Unit Available Media Writes (RUAMW): 19284480
查看事件
NVMe FDP提供了事件机制,用于查看读写过程中的错误或者记录特殊操作,可借助这个机制调试程序。配置了FDP的事件监控后,可以通过NVMe 的log page(日志类型编号0x23 )来获取与FDP有关的事件报告。
要使用事件机制,首先要使能FDP事件,命令示例如下:
nvme fdp set-events/dev/nvme1 -n 1 -e 1 -p 0 -t 0,3
set-events: Success
其中 -p 0 代表placement handle是0。
-t 0,3 代表使能编号为0和3的事件。其中编号0的事件对应Reclaim Unit Not Fully Written,表示主机下发了命令主动改变了RUH所指向的RU。编号为3 的事件表示 Invalid Placement Identifier,即主机给SSD下发的写命令里传入了一个非法的Placement Handle。
查看FDP已经使能的事件类型的命令如下:
nvme get-feature /dev/nvme1n1 -f 0x1e -s 0/3 -c 0 -H
get-feature:0x1e (Flexible Direct Placement Events), Current value:0x00000002
Reclaim Unit Not Fully Written : Enabled
Invalid Placement Identifier : Enabled
配置了FDP的事件监控后,可以通过NVMe 的log page来获取与FDP有关的事件报告,在NVMe规范中有各种不同类型的日志,其中类型编号为0x23 的日志存放FDP事件。
通过nvme-cli 获取FDP日志的命令示例如下:
nvme fdp events /dev/nvme1n1 -e 1 -E -o json
{
"n":0,
"events":[]
}
-e 1: 表示endurance group id 是1。
-E: 表示与主机操作有关的日志。
例如,主机在写入数据时下发了错误的Placement Handle ID,会在nvme fdp events 命令中看到类似下面内容的返回值,其中type 3 代表Invalid Placement Identifier类型的错误,pid 代表主机下发的出错的 placement handle id是22。
{
"type":3,
"fdpef":*,
"pid":22,
"timestamp":*,
"nsid":*
}
06. 应用程序使用方法
应用程序主要通过两种方式利用FDP特性:
1. I/O Passthrough
如果通过Linux内核读写SSD,多数应用程序不是直接下发NVMe 的命令给SSD,而是通过读写内核提供的文件或者块设备来间接读写SSD,如下图所示。不过,因为NVMe 规范发展很快,提出的一些新技术,如FDP,Key-Value等,现有的文件和块接口还没有支持。但是,内核提供了io_uring 接口,使应用可以直接向NVMe 驱动发送命令。

io_uring 的使用方法可以参考 liburing 开源工程,https://github.com/axboe/liburing/tree/master。特别是其中的 test/io_uring_passthrough.c 文件,可以用来参考调用 I/O passthrough 的方法,并根据需要调整源代码以支持FDP。比如,在__test_io 函数中,按 NVMe 规范修改cmd->cdw12 和 cmd-cdw13 的内容,可以在写操作命令中增加Placement Handle 字段。注意测试时要使用字符设备名,如下图所示,选择 nvme list 命令显示的Generic 列对应的名字。

2. SPDK
自从23.05 版本以来,SPDK 开始支持FDP特性。通过查看开源工程下的 test/nvme/fdp/fdp.c 文件,用户可以了解使用SPDK调用FDP有关功能的函数接口和方法:
1. 获取FDP配置组。
2. 写入一笔数据。
3. 获取FDP统计。
4. 获取FDP事件。
可以将支持FDP特性的SSD绑定到SPDK,然后编译运行fdp.c,以观察调用FDP功能的结果,如下。
./fdp -r 'trtype:PCIe traddr:0000:c8:00.0'
07. 测试方法
在应用开发之前,用户可以使用fio测试FDP功能。fio 的版本选择3.34及以上。
测试时可以使用 fio 开源工程下的 examples/uring-cmd-fdp.fio 文件,根据实际盘片的FDP配置修改其中的字段。如下是uring-cmd-fdp.fio 的文件内容,其中 fdp=1 代表使用fdp功能,ioengine=io_uring_cmd 代表使用I/O passthrough 方式,fdp_pli 代表fio线程的写操作使用的Placement Handle,可以根据盘上配置修改。
[global]
filename=/dev/ng0n1
ioengine=io_uring_cmd
cmd_type=nvme
iodepth=32
bs=4K
fdp=1
time_based=1
runtime=1000
[write-heavy]
rw=randrw
rwmixwrite=90
fdp_pli=0,1,2,3
offset=0%
size=30%
[write-mid]
rw=randrw
rwmixwrite=30
fdp_pli=4,5
offset=30%
size=30%
[write-light]
rw=randrw
rwmixwrite=10
fdp_pli=6
offset=60%
size=30%
08. 大普微深耕NVMe前沿技术布局
在SSD存储容量迈向PB的时代,FDP有效解决了写放大痛点,提升了SSD在 AI 训练及大容量场景下的稳定性。从FDP到透明压缩等关键特性,我们坚持技术驱动,依托自研芯片的灵活架构,加速将NVMe协议中的创新标准转化为成熟的工程化能力。

