NVMe设备命令大小限制
1 定义
单个NVMe命令是有一定大小限制的,host如果提交一个超过限制大小的命令,将会终止执行并得到一个值为0x02的状态码,代表Invalid Field in Command。
NVMe设备单个命令大小由Maximum Data Transfer Size (MDTS)限制,这个数字可以通过identify命令获取。
MDTS的单位是Minimum Memory Page Size(CAP.MPSMIN),它的定义是NVMe controller可以支持的host的最小内存页大小。每一个NVMe设备至少要支持4K的host页大小,因此从PCI Register中获得的MPSMIN的值定义为log后减12的结果。亦即:
MDTS的值也是log后的结果,因此实际的MDTS以字节为单位:
可以发现在NVMe设备初始化函数 nvme_dev_add 中有这样的表述:
int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;
if (ctrl->mdts)
dev->max_hw_sectors = 1 << (ctrl->mdts + shift - 9);
显然,这段语句具体阐释了这一运算过程。注意到,在算sector数的时候有一个减去9的操作,这是因为每个sector默认大小为512B。
2 应用
nvme-scsi.c 用于将一个标准scsi命令转化为NVMe命令。它的 nvme_trans_do_nvme_io 函数负责read/write命令的转换。考虑到一个scsi读写命令使用的缓冲区可能会发送超过上述大小,这个函数需要将之分割成小块并分别填充成单独的NVMe命令:
u32 max_blocks = queue_max_hw_sectors(ns->queue);
u32 num_cmds = nvme_trans_io_get_num_cmds(hdr, cdb_info, max_blocks);
for (i = 0; i < num_cmds; i++) {
...
}
显然ns->queue
中存了一些有意思的值。这些值是哪里来的呢?可以看到,在添加NVMe设备过程中调用的用于对namespace进行初始化的函数 nvme_alloc_ns 中有如下语句:
blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);
显然就是来自上面那次对dev->max_hw_sectors
的赋值了。