最近,同事问了我一个问题,如下图: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 = 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。
谢谢你读我的文章
!
Alipay
Wechat