最近,同事问了我一个问题,如下图:Write Zeroes Size Limit的minimum memory page size是多大?

我这一块不是很了解,因为nvme spec的pdf太长了。但是可以在spec的只言片语中找到它是数值。

The non-zero value is in units of the minimum memory page size (CAP.MPSMIN) and is reported as a power of two (2^n).

这句话出现在一些 size limit 字段的描述里,比如 WZSL:Write Zeroes Size Limit。

它看起来很简单,但里面藏着一个很容易被误解的问题:

CAP.MPSMIN 到底是多大? 它和 4KB、NAND page、LBA size 又是什么关系?

这篇文章就只讲清楚这一件事。


一、先看问题:WZSL 的单位是什么?

在 NVMe spec 里,WZSL 表示 Write Zeroes 命令推荐或者支持的最大数据大小限制。

但是它不是直接用 byte 表示,也不是直接用 LBA 数量表示。

spec 里说:

The non-zero value is in units of the minimum memory page size (CAP.MPSMIN) and is reported as a power of two (2^n).

翻成人话就是:

WZSL 如果不是 0,它的单位是 controller 支持的最小 memory page size,也就是 CAP.MPSMIN 对应的大小。 并且它不是直接给出数量,而是以 2 的幂次形式报告。

所以想理解 WZSL,第一步不是看 WZSL 本身,而是要先弄明白:

CAP.MPSMIN对应的minimum memory page size到底是多少?


二、CAP.MPSMIN 不是一个“页大小”,而是一个编码值

这里最容易犯的错误,是把 CAP.MPSMIN 当成一个直接的 size。

比如看到:

CAP.MPSMIN = 0

就以为它表示 0 byte,或者某种无效值。

这不对。

CAP.MPSMIN 是 NVMe controller capability register 里的一个字段,它表示 controller 支持的最小 host memory page size。

它的计算方式是:

minimum memory page size = 2^(12 + CAP.MPSMIN) bytes

也可以写成:

minimum memory page size = 4KB × 2^CAP.MPSMIN

或者写成程序员更熟悉的形式:

minimummemorypagesize = 4096 << CAPMPSMIN;

所以常见情况是:

CAP.MPSMIN
minimum memory page size
0
4 KB
1
8 KB
2
16 KB
3
32 KB
4
64 KB

也就是说:

CAP.MPSMIN = 0

并不是没有 page size,而是表示:

2^(12 + 0) = 2^12 = 4096 bytes = 4KB


三、公式里的 12 是从哪里来的?

这个问题的关键就在这个公式:

2^(12 + CAP.MPSMIN)

为什么不是:

2^CAP.MPSMIN

而是要加一个 12?

答案很简单:

因为 NVMe 把 4KB 作为 memory page size 的编码基准。 而 4KB = 4096 bytes = 2^12 bytes。

所以公式里的 12,本质上就是:

4KB 的指数表示

也就是说,NVMe spec 不是从 1 byte 开始编码 memory page size,而是从 4KB 开始编码。

因此:

CAP.MPSMIN = 0 => 4KB CAP.MPSMIN = 1 => 8KB CAP.MPSMIN = 2 => 16KB

它的含义是:

在 4KB 的基础上,再左移 CAP.MPSMIN 位

所以 12 不是 NAND 相关的常数,也不是 LBA 相关的常数,而是 NVMe 对 host memory page size 编码方式的基准。


四、spec 里哪里说了?

在 NVMe Base Spec 里,这个公式主要出现在两个地方。

第一个地方是 CAP:Controller Capabilities 寄存器。

CAP 里有两个相关字段:

MPSMIN:Memory Page Size Minimum MPSMAX:Memory Page Size Maximum

其中 MPSMIN 字段说明里写的是:

The minimum memory page size is 2^(12 + MPSMIN).

也就是:

controller 支持的最小 memory page size = 2^(12 + MPSMIN) bytes

第二个地方是 CC:Controller Configuration 寄存器。

CC 里有一个字段:

MPS:Memory Page Size

它表示 host 最终配置给 controller 使用的 memory page size。

CC.MPS 的说明里也写了类似公式:

The memory page size is 2^(12 + MPS).

也就是说:

CAP.MPSMIN / CAP.MPSMAX 表示 controller 支持的范围 CC.MPS 表示 host 实际选择的值

host 在配置 CC.MPS 的时候,不能随便写,必须落在 controller 支持的范围内:

CAP.MPSMIN <= CC.MPS <= CAP.MPSMAX

所以 CAP.MPSMIN 是 capability,CC.MPS 是 configuration。

这两个概念不能混在一起。


五、CAP.MPSMIN 不是 NAND page size

这里还有一个特别容易混淆的点:

CAP.MPSMIN 不是 NAND page size。

NAND page size 是 NAND 内部介质组织结构里的概念,比如一个 NAND page 可能是 16KB、18KB、32KB,甚至还包含 spare / OOB 区域。

而 NVMe 里的 CAP.MPSMIN,说的是:

host memory page size

它和这些东西有关:

PRP SQ / CQ memory DMA 地址边界 host memory buffer controller 和 host 之间的数据传输描述

它不是 NAND 内部的物理 page。

所以不能这样理解:

CAP.MPSMIN = NAND page size

也不能说:

SSD 内部 NAND page 是 16KB,所以 CAP.MPSMIN 就一定是 2

这两个是不同层面的概念。

NAND page 是介质层面的组织单位。

NVMe memory page size 是 host-controller 接口层面的内存页粒度。


六、回到 WZSL:它到底怎么换算?

现在再回头看 WZSL 这句话:

The non-zero value is in units of the minimum memory page size (CAP.MPSMIN) and is reported as a power of two (2^n).

假设某个 controller 的:

CAP.MPSMIN = 0

那么:

minimum memory page size = 4KB

如果 WZSL 字段报告的值是 n,那么它对应的数据大小就是:

2^n × 4KB

如果:

CAP.MPSMIN = 1

那么:

minimum memory page size = 8KB

这时候 WZSL 对应的数据大小就是:

2^n × 8KB

也就是说,WZSL 的换算要分两步:

第一步,先根据 CAP.MPSMIN 算出最小 memory page size:

minimum page size = 2^(12 + CAP.MPSMIN) bytes

第二步,再根据 WZSL 算出 size limit:

WZSL size limit = 2^WZSL × minimum memory page size

如果 CAP.MPSMIN 是 0,那么它就变成:

WZSL size limit = 2^WZSL × 4KB


七、为什么 NVMe 要这么设计?

从软件和硬件接口的角度看,这种设计有几个好处。

第一,它避免了直接用 byte 数描述大尺寸。

如果一个字段直接表示 byte 数,那么字段宽度可能需要很大。用 2 的幂次编码,可以用较少 bit 表示较大的范围。

第二,它符合内存页和 DMA 传输的习惯。

NVMe controller 和 host 之间的数据传输,本来就大量依赖 page granularity、alignment、PRP list 等机制。用 memory page size 作为单位,比直接用 byte 更贴近控制器和 host 的交互方式。

第三,它让不同平台可以有不同的 page size 支持范围。

有些平台最小 page size 是 4KB,有些可能支持更大的 page size。CAP.MPSMIN 和 CAP.MPSMAX 就是 controller 告诉 host:

我能接受的 memory page size 范围是这些。

然后 host 再通过 CC.MPS 选择一个实际使用的值。


八、工程上怎么确认 CAP.MPSMIN?

在 Linux 上,可以用 nvme-cli 看 controller register。

例如:

nvme show-regs /dev/nvme0

重点看 CAP 寄存器里的 MPSMIN 字段。

如果看到:

mpsmin : 0

那么 minimum memory page size 就是:

4KB

如果看到:

mpsmin : 1

那么就是:

8KB

不过在很多常见 NVMe SSD 上,CAP.MPSMIN 通常是 0,也就是 4KB。

但工程上不要默认写死,最好还是以实际 controller 的 CAP register 为准。


九、最后总结

CAP.MPSMIN 这个字段本身不复杂,但它容易被误解。

可以记住这几个结论:

CAP.MPSMIN 不是 byte 数 CAP.MPSMIN 不是 NAND page size CAP.MPSMIN 表示 controller 支持的最小 host memory page size

它的计算公式是:

minimum memory page size = 2^(12 + CAP.MPSMIN) bytes

公式里的 12 来自:

4KB = 4096 bytes = 2^12 bytes

也就是说,NVMe 的 memory page size 编码是以 4KB 为基准的。

所以:

CAP.MPSMIN = 0 => 4KB CAP.MPSMIN = 1 => 8KB CAP.MPSMIN = 2 => 16KB

再回到 WZSL:

WZSL 的单位是 CAP.MPSMIN 对应的 minimum memory page size

如果 CAP.MPSMIN = 0,那么 WZSL 的单位就是 4KB。

如果 CAP.MPSMIN = 1,那么 WZSL 的单位就是 8KB。

一个小字段背后,其实反映的是 NVMe 的一贯设计风格:

spec 里的 size 很少只是一个裸数字。 它通常要结合寄存器字段、编码基准、单位和上下文一起看。 读懂这些细节,才是真的读懂了 NVMe spec。


谢谢你读我的文章!