上一篇介绍 L2P 时,我们讲过一个很现实的问题。
假设 Firmware 以 4 KiB 为一个映射单位,每条 L2P Entry 占用 4 Byte,那么每管理 1 TiB 的逻辑地址空间,大约就需要 1 GiB 内存保存完整 L2P。
如果是一块 16 TiB 的 SSD,完整 L2P 大约需要 16 GiB。
随着 SSD 容量不断增加,完整 L2P 对内存的需求也会越来越高。
那么问题来了:
如果完整 L2P 无法全部放进内存,SSD 还能找到数据吗?
当然可以。
一种经典思路,是把完整 L2P 保存到 NAND 中,内存只保留当前正在使用的一小部分映射。
DFTL 就是这种思路中非常经典的一种设计。
DFTL 的全称是 Demand-based Flash Translation Layer,可以理解为:
按照实际访问需求,从 NAND 中加载页级映射。
听起来似乎并不复杂。
完整 L2P 放不进内存,那就把它放进 NAND。需要哪条映射时,再从 NAND 中读出来。
但只要继续往下想一步,就会发现事情并没有这么简单。
主机想读取一份用户数据,Firmware 首先需要查询 L2P。
可现在,L2P 自己也保存在 NAND 中。
那么 Firmware 怎么知道,目标 L2P Entry 究竟位于 NAND 的哪个位置?
换句话说:
查找用户数据之前,Firmware 要先查找映射。
但在查找映射之前,它又要先知道映射存放在哪里。
DFTL 真正要解决的,就是这个问题。
一、先把本文使用的几个地址说清楚
在继续介绍 DFTL 之前,先把 LBA、LPN、PPN 和 PPA 之间的关系重新理清楚。
可以先把寻找用户数据的主线记成:
LBA → LPN → PPN → PPA
其中,LBA 和 LPN 描述的是逻辑地址。
PPN 和 PPA 描述的是物理地址。
LBA:主机发给 SSD 的逻辑地址
LBA 的全称是 Logical Block Address。
主机向 SSD 发起读写命令时,使用的就是 LBA。
需要注意的是,LBA 是地址编号,不能简单地说“一个 LBA 的大小是 4 KiB”。
更准确的说法是:
本文假设设备采用 4 KiB 的 LBA Format,也就是每个 LBA 对应 4 KiB 数据。
例如,主机要求读取:
LBA 5000
主机只知道这个逻辑地址,并不知道数据实际存放在哪颗 NAND、哪个 Die、哪个 Block 和 Page 中。
LPN:Firmware 内部使用的逻辑映射单元编号
LPN 的全称是 Logical Page Number。
Firmware 会按照自己的映射粒度,把主机看到的逻辑地址空间划分成一个个逻辑映射单元。
这些映射单元的编号,就是 LPN。
本文假设:
每个 LBA 对应 4 KiB 数据。
L2P 的映射粒度也是 4 KiB。
因此,在本文的简化模型中:
LBA 5000 对应 LPN 5000。
如果设备采用的是 512 Byte LBA,而 Firmware 的映射粒度仍然是 4 KiB,那么就需要连续 8 个 LBA 才能组成一个 LPN。
所以,LBA 和 LPN 并不天然一一对应,只是在本文的参数下恰好一一对应。
PPN:LPN 对应的物理映射单元编号
PPN 的全称是 Physical Page Number。
在 DFTL 原论文中,逻辑 Page 和物理 Page 大小一致,因此一个 LPN 可以直接映射到一张物理 NAND Page。
但是本文继续沿用上一篇的参数:
L2P 映射粒度为 4 KiB。
一张 NAND Page 的数据区大小为 16 KiB。
这意味着一张 16 KiB NAND Page 可以容纳四个 4 KiB 的映射单元。
因此,为了保持文章前后一致,本文虽然继续使用 PPN 这个名称,但它更准确地表示:
一个 4 KiB 物理映射单元的编号。
例如:
LPN 5000 → PPN 660
表示的是:
LPN 5000 对应的 4 KiB 用户数据,目前存放在物理映射单元 PPN 660 中。
PPN 660 最终还要进一步对应到某张 16 KiB NAND Page,以及这张 Page 内部的具体位置。
本文只讨论 DFTL 的地址翻译,不继续展开四个 4 KiB 数据如何拼装进一张 16 KiB NAND Page,也不展开 Page Program 时的数据聚合过程。
PPA:Firmware 最终使用的 NAND 物理地址
PPA 的全称是 Physical Page Address。
一条 PPA 最终需要让 Firmware 确定数据所在的:
Channel → CE → Die → Plane → Block → Page → Page 内位置
因此,可以把 PPN 和 PPA 的关系简单理解为:
PPN 是便于管理的物理映射单元编号。
PPA 是这个编号最终对应的具体 NAND 物理位置。
后文为了聚焦 DFTL 的映射流程,会继续使用 PPN,不再反复展开到具体 PPA。
还需要说明的是,本文假设每条 L2P Entry 占用 4 Byte,只是为了方便计算。
真实产品中的 Entry 宽度还会受到物理容量、Over-Provisioning、坏块替换空间、地址编码方式以及其他元数据的影响,并不一定刚好是 4 Byte。
论文中主要讨论的是控制器 SRAM。为了便于结合现代 SSD 理解,本文把 SRAM、DRAM 等用于保存映射的易失性存储统一称为“内存”。
二、DFTL 没有让完整 L2P 消失
先说结论。
DFTL 并没有减少逻辑地址到物理地址之间的映射数量。
假设 Firmware 以 4 KiB 为一个映射单位。
对于一块 1 TiB 的 SSD,它需要管理的逻辑映射单元数量为:
1 TiB ÷ 4 KiB
= 2⁴⁰ Byte ÷ 2¹² Byte
= 2²⁸
= 268,435,456
也就是说,一块 1 TiB 的 SSD,大约需要管理 2.68 亿个 LPN。
每个 LPN 都需要一条对应的 L2P Entry,因此完整 L2P 中同样需要保存大约 2.68 亿条映射。
如果每条 Entry 占用 4 Byte,那么完整 L2P 的大小为:
268,435,456 × 4 Byte
= 1,073,741,824 Byte
= 1 GiB
这就是为什么每管理 1 TiB 的逻辑地址空间,大约需要 1 GiB 内存保存完整 L2P。
如果是一块 16 TiB 的 SSD,逻辑映射单元和 L2P 空间都会扩大 16 倍:
大约需要管理 42.9 亿个 LPN。
完整 L2P 大约需要 16 GiB。
DFTL 所做的,并不是删除这些映射,而是改变它们的存放位置。
在完整 L2P 常驻内存的设计中,映射关系可以被理解成一个巨大的数组:
LPN 0 → PPN 105
LPN 1 → PPN 830
LPN 2 → PPN 416 ……
主机给出 LPN,Firmware 直接以内存数组下标访问对应的 Entry,就能获得 PPN。
DFTL 则把这张巨大的数组拆成很多份,写入 NAND。
每一份映射被组织成一张 Translation Page。
所以,DFTL 的核心变化可以概括为:
完整 L2P 依然存在。
只是它不再完整地常驻内存,而是被拆成许多 Translation Page,保存到 NAND 中。
内存里只留下两类信息:
当前正在使用的一小部分 L2P Entry;
一张用来寻找 Translation Page 的目录。
前者叫作 CMT,后者叫作 GTD。
三、Data Page 与 Translation Page
DFTL 引入以后,NAND Page 有了两种不同的用途。
第一类是 Data Page。
Data Page 保存真正的用户数据。
第二类是 Translation Page。
Translation Page 不保存用户文件内容,而是保存很多条 LPN 到 PPN 的映射关系。
例如,一张 Translation Page 中可能保存:
LPN 4096 → PPN 500
LPN 4097 → PPN 731
LPN 4098 → PPN 284
……
LPN 5000 → PPN 660
……
LPN 8191 → PPN 902
把所有 Translation Page 组合起来,就是完整的 L2P。
论文把主要保存 Data Page 的 Block 称为 Data Block,把主要保存 Translation Page 的 Block 称为 Translation Block。
需要注意的是,这是一种用途上的划分。
它不是说 NAND 在出厂时就被永久切成左半边存映射、右半边存数据。
如图所示,更准确的理解是:
Firmware 从 NAND 空间中分配一小部分 Block 保存 Translation Page;
剩下的大部分 Block 用于保存用户数据。

在本文的简化参数下,每条 4 Byte 映射管理 4 KiB 用户数据,因此纯映射数据的理论空间占比约为:
4 Byte ÷ 4 KiB ≈ 0.1%
这个比例不包括版本、校验、冗余副本以及其他管理开销。
因此,Translation Block 的数量通常远少于 Data Block。
映射为什么不直接放进 OOB?
NAND Page 除了 Data Area,通常还存在一小块 OOB 区域。
OOB 可以保存 ECC、Page 状态和逻辑地址等信息。
既然如此,为什么 DFTL 不把 L2P 全部放在 OOB 中?
原因是 OOB 容量很小。
如果映射分散在大量 Page 的 OOB 中,查找和恢复映射时需要读取很多物理 Page。
DFTL 论文最终选择把映射集中保存在 Page 的 Data Area 中。
这样一张 Translation Page 就能连续保存大量 Mapping Entry,查找一组映射只需要读取一张 Page。
Data Page 保存用户数据。
Translation Page 保存寻找用户数据所需要的映射。
四、一张 Translation Page 能保存多少条映射?
一条 L2P Entry 只占用 4 Byte,但 NAND 的读取和写入单位显然不是 4 Byte。
Firmware 不可能只为读取一条 4 Byte 映射,就让 NAND 单独返回 4 Byte。
因此,DFTL 会把许多条连续的 L2P Entry 聚合在一起,保存到同一张 Translation Page 中。
需要说明的是,下面仍然沿用 DFTL 的结构和术语,但不照搬论文当年的 2 KiB Page 参数,而是按照本文的参数重新推演:
Translation Page 数据区大小为 16 KiB;
每条 L2P Entry 占用 4 Byte;
每条 L2P Entry 对应 4 KiB 逻辑数据。
在暂时不考虑额外管理信息占用的情况下,一张 Translation Page 能保存:
16 KiB ÷ 4 Byte = 4096 条 L2P Entry
一张 Translation Page 可以保存 4096 条连续映射。
它能够覆盖的逻辑地址空间为:
4096 × 4 KiB = 16 MiB
于是,完整 L2P 可以按照下面的方式拆开:
第 0 组:LPN 0~4095
第 1 组:LPN 4096~8191
第 2 组:LPN 8192~12287
第 3 组:LPN 12288~16383
既然完整 L2P 被拆成了很多组,Firmware 就需要给每一组一个逻辑编号。
这个编号叫作 MVPN:
Mapping Virtual Page Number
可以把 MVPN 简单理解成:
这是第几组 L2P Entry。
因此:
MVPN 0 保存 LPN 0~4095 的映射;
MVPN 1 保存 LPN 4096~8191 的映射;
MVPN 2 保存 LPN 8192~12287 的映射。
假设主机要访问 LPN 5000。
因为:
5000 = 1 × 4096 + 904
所以 Firmware 可以得到:
MVPN = 1 Offset = 904
这表示:
LPN 5000 的映射,位于逻辑 Translation Page 1 中的第 904 个 Entry。
Alipay
Wechat