大普微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協議中的創新標準轉化為成熟的工程化能力。

