上一篇介绍 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。

此文章是微信公众号付费文章,需查看请关注公众号“磊的收发室”查看。