Zoned Storage-Tools and Librarues

本文详细介绍了Linux系统中用于管理分区存储的工具和库,包括lsblk、blkzone、libzbc、libnvme等。lsblk和blkzone命令用于查看设备和zone信息,libzbc和libnvme则是用户库,提供操作ZBC和NVMe设备的接口。此外,文章还提到了SCSI通用工具lsscsi和用于ZNS设备的nvme-cli,以及QEMU等模拟工具。


本章描述了支持zoned块设备的用户级应用程序和库。

Linux 系统实用程序

正如 util-linux 项目本身的文档所说,util-linux 是 Linux® 实用程序的随机集合。 util-linux 项目托管在 GitHub 。 该项目以 util-linux 名称打包在大多数发行版中,并默认安装。

util-linux 提供(以及其他实用程序)lsblkblkzone 命令行工具,用于列出分区zoned设备和获取zone的配置。 blkzone 工具还允许重置顺序区域的写指针。

这些实用程序对于 shell 脚本和解决用户应用程序中的zone管理问题特别有用。

lsbllk

  • lsblk 命令列出系统的所有块设备,无论块设备类型如何(这意味着是的,它还包括分区块设备)。 lsblk 的输出如下:
# lsblk
NAME     MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda        8:0    0 167.7G  0 disk
├─sda1     8:1    0     1G  0 part /boot
├─sda2     8:2    0 150.7G  0 part /
└─sda3     8:3    0    16G  0 part [SWAP]
sdb        8:16   0  12.8T  0 disk
sdc        8:32   0  12.8T  0 disk
sdd        8:48   0  13.7T  0 disk
  • 默认情况下,lsblk 不提供列出的块设备的区域模型的指示。 要发现此信息,请使用选项 -z
# lsblk -z
NAME     ZONED
sda      none
├─sda1   none
├─sda2   none
└─sda3   none
sdb      host-managed
sdc      host-managed
sdd      host-managed
  • 可以使用 -o 选项格式化 lsblk 的输出。 例如,以下命令将显示块设备名称、大小和区域模型。
# lsblk -o NAME,SIZE,ZONED
NAME       SIZE ZONED
sda      167.7G none
├─sda1       1G none
├─sda2   150.7G none
└─sda3      16G none
sdb       12.8T host-managed
sdc       12.8T host-managed
sdd       13.7T host-managed

blkzone

blkzone 命令行实用程序列出(报告)zoned块设备的区域并重置顺序区域的写指针。 blkzone 依赖于内核提供的 ZBD ioctl() 接口来执行“zone report”和“zone reset”操作。 blkzone 不会直接向设备发出 SCSI、ATA 或 NVMe 命令。

blkzone 命令用法如下图:

# blkzone --help

Usage:
 blkzone <command> [options] <device>

Run zone command on the given block device.

Commands:
 report       Report zone information about the given device
 capacity     Report zone capacity for the given device
 reset        Reset a range of zones.
 open         Open a range of zones.
 close        Close a range of zones.
 finish       Set a range of zones to Full.

Options:
 -o, --offset <sector>  start sector of zone to act (in 512-byte sectors)
 -l, --length <sectors> maximum sectors to act (in 512-byte sectors)
 -c, --count <number>   maximum number of zones
 -f, --force            enforce on block devices used by the system
 -v, --verbose          display more details

 -h, --help             display this help
 -V, --version          display version

Arguments:
 <sector> and <sectors> arguments may be followed by the suffixes for
   GiB, TiB, PiB, EiB, ZiB, and YiB (the "iB" is optional)

For more details see blkzone(8).
  • Zone Report
# blkzone report /dev/sdd
  start: 0x000000000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x000080000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x000100000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x000180000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x000200000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x000280000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x000300000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x000380000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x000400000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x000480000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  ...
  start: 0x010500000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x010580000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]
  start: 0x010600000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 1(em) [type: 2(SEQ_WRITE_REQUIRED)]
  start: 0x010680000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 1(em) [type: 2(SEQ_WRITE_REQUIRED)]
  start: 0x010700000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 1(em) [type: 2(SEQ_WRITE_REQUIRED)]
  start: 0x010780000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 1(em) [type: 2(SEQ_WRITE_REQUIRED)]
  start: 0x010800000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 1(em) [type: 2(SEQ_WRITE_REQUIRED)]

要将报告限制在某个区域范围内,请使用选项 --offset--count。 例如,要仅报告从扇区 274726912 开始的磁盘的第一个连续区域,请使用以下命令:

# blkzone report --offset 274726912 --count 1 /dev/sdd
start: 0x010600000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 1(em) [type: 2(SEQ_WRITE_REQUIRED)]
  • Device capacity
    如果一个zone的容量小于它的大小,blockdevlsblk 中列出的大小并不表示可以在分区块设备上存储多少数据。 设备的存储容量是所有zone容量的总和。

使用以下命令确定扇区中设备的存储容量:

# blkzone capacity /dev/nullb1
0x00c350000
  • Zone Reset
    可以使用reset操作使用 blkzone 重置顺序写入区域。 例如,要重置从扇区 274726912 开始的磁盘的第一个顺序区域,请使用以下命令:
# blkzone reset --offset 274726912 --count 1 /dev/sdd

如果使用重置操作指定的区域范围包括常规区域,则该命令将失败:

# blkzone reset /dev/sdd
blkzone: /dev/sdh: BLKRESETZONE ioctl failed: Remote I/O error

用户必须排除所有常规区域。 在上面的示例中,所有常规区域都位于扇区 0 和 274726912 之间的磁盘上。磁盘的其余部分由顺序写入区域组成。 这意味着以下命令将重置所有区域中的写指针:

# blkzone reset --offset 274726912 /dev/sdd

SCSI 通用实用程序

各种开源项目提供了支持直接操作 SCSI 设备的工具。 lsscsi 命令行工具和 sg3_utils 库和实用程序集合是最广泛用于 SCSI 设备操作的工具之一。 它们可作为大多数 Linux® 发行版的预编译包提供。

lsscsi 能够指示设备是否是 ZBC 主机管理的分区块设备。 sg3_utils 在 1.42 版本中增加了对 ZBC 的支持。

lsscsi

lsscsi 命令列出有关连接到 Linux 系统的 SCSI 设备的信息。 lsscsi 在大多数 Linux 发行版中都以包的形式提供。 例如,在 Fedora® Linux 中,可以使用以下命令安装 lssci:

# dnf install lsscsi
  • 识别Host Managed磁盘
    lsscsi 列出使用内核 SCSI 子系统管理的磁盘。 这始终包括直接连接到
    (1) 系统主板上的 SATA 端口或
    (2) 到 SATA PCIe 适配器的 SATA 磁盘。
# lsscsi
[2:0:0:0]    disk    ATA      INTEL SSDSC2CT18 335u  /dev/sda
[4:0:0:0]    zbc     ATA      HGST HSH721414AL T220  /dev/sdb
[10:0:1:0]   zbc     HGST     HSH721414AL52M0  a220  /dev/sdc
[10:0:3:0]   zbc     ATA      HGST HSH721415AL T220  /dev/sdd

默认输出的第二列表示设备类型。 对于主机托管磁盘,类型名称为 zbc。 对于普通磁盘,类型名称为 disk。 旧版本的 lsscsi 可能会直接列出设备类型的数值。 对于主机托管磁盘,显示 0x14。

将选项 -g 添加到 lssci 命令会返回与设备关联的 SCSI 通用节点文件路径。 这可以与 libzbc 或任何 sg3_utils 命令行工具结合使用。

# lsscsi -g
[2:0:0:0]    disk    ATA      INTEL SSDSC2CT18 335u  /dev/sda   /dev/sg0
[4:0:0:0]    zbc     ATA      HGST HSH721414AL T220  /dev/sdb   /dev/sg1
[10:0:1:0]   zbc     HGST     HSH721414AL52M0  a220  /dev/sdc   /dev/sg2
[10:0:3:0]   zbc     ATA      HGST HSH721415AL T220  /dev/sdd   /dev/sg3
  • 磁盘接口和传输
    输出的第三列是磁盘供应商 ID。 对于 ATA 磁盘,即使对于连接到 SAS 主机总线适配器 (HBA) 的 ATA 磁盘,它也始终是 ATA。 使用 -t 选项可以更精确地发现用于与磁盘通信的传输。
# lsscsi -t
[2:0:0:0]    disk    sata:55cd2e4000111f9b           /dev/sda
[4:0:0:0]    zbc     sata:5000cca25bc03731           /dev/sdb
[10:0:1:0]   zbc     sas:0x5000cca0000025c5          /dev/sdc
[10:0:3:0]   zbc     sas:0x300062b200f35d43          /dev/sdd

libzbc 用户库

libzbc 是一个用户库,提供操作 ZBC 和 ZAC 磁盘的功能。 libzbc 的命令实现符合 INCITS 技术委员会 T10(针对 ZBC)和 T13(针对 ZAC)定义的 ZBC 和 ZAC 标准的最新发布版本。

除了支持 ZBC 和 ZAC 磁盘之外,libzbc 还实现了一种模拟模式,该模式允许库模仿主机管理的分区磁盘的行为,使用常规文件或标准块设备作为后备存储。

libzbc 项目托管在 GitHub 上。 该项目的 README 文件提供了有关如何编译和安装 libzbc 库及其工具的信息。

概述

libzbc 提供了一个统一的应用程序编程接口 (API),它独立于zone模型和所用磁盘的接口。 在内部,四种不同类型的设备驱动程序用于处理依赖于设备接口的命令:

  • ZAC ATA 驱动程序 该驱动程序用于处理通过 SCSI 通用驱动程序(直接设备访问接口)将 ATA 命令直接传送到 ZAC 磁盘。
  • ZBC SCSI 驱动程序 此驱动程序主要处理针对 ZBC SCSI 磁盘的 SCSI 命令,但如果命令路径中存在功能性 SCSI 到 ATA (SAT) 命令转换层,也可用于控制 ZAC ATA 磁盘。 SAT 可以由内核 libata 子系统为连接到 SATA 适配器的 ATA 磁盘提供,也可以由 SAS 主机总线适配器 (HBA) 为连接到此类适配器的 SATA 磁盘提供。
  • Zoned Block Device Driver 该驱动程序使用内核 ZBD 接口来控制 ZBC 和 ZAC 磁盘。 仅当存在并启用内核分区块设备支持时,此驱动程序才可用。
  • 文件仿真驱动程序 该驱动程序使用常规文件或常规块设备作为后端存储来模拟主机管理的 ZBC 磁盘。 此驱动程序仅用于开发。 tcmu-runner 项目提供了更高级的 ZBC 磁盘仿真解决方案。

下图显示了这种结构。
在这里插入图片描述
libzbc 提供用于发现分区设备的区域配置和访问设备的功能。 对设备的访问可能会导致设备区域的条件、设备区域的属性和设备区域的状态(例如写指针在顺序区域中的位置)发生变化。 libzbc 不会在内部跟踪这些更改。 也就是说,libzbc 是无状态的。

为获取设备区域信息而提供的功能仅在执行时才会生成区域条件和状态的“快照”。 应用程序有责任实现对设备区域变化的跟踪(例如在完成对区域的写入请求后增加顺序区域的写入指针位置)。

库函数

从 5.0.0 版开始的所有 libzbc 函数都使用 512 字节的扇区单元来 (1) 报告区域信息和 (2) 作为设备访问的扇区寻址单元,而不管实际的设备逻辑块大小。 所有功能使用的单元中的这种统一可以通过隐藏设备之间逻辑块大小的潜在差异来简化应用程序开发。 但是,应用程序员必须始终小心地实现对设备的访问(读取或写入),这些访问与设备的逻辑块大小对齐。 此外,在主机管理的分区设备上,对顺序区域的写入操作必须与设备的物理块大小对齐。

libzbc提供的主要功能如下:

zbc_open():	Open a zoned device
zbc_close():	Close a zoned device
zbc_get_device_info():	Get device information
zbc_report_nr_zones():	Get the number of zones
zbc_report_zones():	Get zone information
zbc_list_zones():	Get zone information
zbc_zone_operation():	Execute a zone operation
zbc_open_zone():	Explicitly open a zone
zbc_close_zone():	Close an open zone
zbc_finish_zone():	Finish a zone
zbc_reset_zone():	Reset a zone write pointer
zbc_pread():	Read data from a zone
zbc_pwrite():	Write data to a zone
zbc_flush():	Flush data to disk

更多关于这些函数的使用和行为的详细信息可以在 libzbc 头文件的注释中找到。 此头文件默认安装为 /usr/include/libzbc/zbc.h。

libzbc 没有为多线程或多进程应用程序实现任何互斥机制。 这意味着应用程序有责任同步执行针对同一zone的冲突操作。 这种情况的一个典型例子是“多个线程对同一zone的并发写入操作”,如果应用程序没有写入顺序控制,这可能会导致写入错误

libzbc 还提供了以下功能,以方便应用程序的开发和测试:

zbc_set_log_level():	Set log level of the library functions
zbc_device_is_zoned():	Test if a device is a zoned block device
zbc_print_device_info():	Print to a file (stream) a device information
zbc_device_type_str():	Get a string description of a device type
zbc_device_model_str():	Get a string description of a device model
zbc_zone_type_str():	Get a string description of a zone type
zbc_zone_condition_str():	Get a string description of a zone condition
zbc_errno():	Return sense key and sense code of the last command executed
zbc_sk_str():	Get a string description of a sense key
zbc_asc_ascq_str():	Get a string description of a sense code

无论使用的磁盘类型如何,所有函数的行为方式都相同。 唯一的例外是 zbc_errno() 函数在使用分区块设备驱动程序时无法报告详细的错误信息。 原因是内核 I/O 堆栈无法将 SCSI/ATI 检测数据中为失败命令提供的详细信息向上传播到应用程序。

实用程序

libzbc 提供了几个命令行应用程序,用于通过调用库函数来操作分区磁盘。 提供的应用程序列表如下表所示:

  • gzbc: gzbc provides a graphical user interface showing zone information of a zoned device
  • zbc_info: Get information on a disk
  • zbc_report_zones: List the zones of a device
  • zbc_reset_zone: Reset the write pointer of sequential zones
  • zbc_open_zone: Explicitly open a sequential write zone
  • zbc_close_zone: Close an open sequential write zone
  • zbc_finish_zone: Transition a sequential write zone to the full condition
  • zbc_read_zone: Read a zone sectors
  • zbc_write_zone: Write a zone sectors

提供以下工具来创建和修改用作 libzbc 仿真模式的后端存储的常规文件或常规块设备

  • zbc_set_zones: Initialize (format) a regular file or regular disk to be used with libzbc emulation mode
  • zbc_set_write_ptr Change the write pointer position of the sequential zones of an emulated disk (intended for testing purposes only as this is not a valid operation for physical ZBC devices

所有实用程序在没有任何参数的情况下执行时都会输出帮助消息:

# zbc_report_zones
Usage: zbc_report_zones [options] <dev>
Options:
  -v          : Verbose mode
  -lba        : Use LBA size unit (default is 512B sectors)
  -start <offset> : Start offset of report. if "-lba" is used
                    <offset> is interpreted as an LBA. Otherwise,
                    it is interpreted as a 512B sector number.
                    Default is 0
  -n          : Get only the number of zones
  -nz <num>   : Get at most <num> zones
  -ro <opt>   : Specify reporting option: "all", "empty",
                    "imp_open", "exp_open", "closed", "full",
                    "rdonly", "offline", "rwp", "non_seq" or "not_wp".
                    Default is "all"

获取磁盘信息

zbc_info 显示有关磁盘的信息,包括常规块设备:

# zbc_info /dev/sg3
Device /dev/sg3:
    Vendor ID: ATA HGST HSH721415AL T220
    Zoned block device interface, Host-managed zone model
    29297213440 512-bytes sectors
    3662151680 logical blocks of 4096 B
    3662151680 physical blocks of 4096 B
    15000.173 GB capacity
    Read commands are unrestricted
    Maximum number of open sequential write required zones: 128
# zbc_info /dev/sg0
/dev/sg0 is not a zoned block device

区域信息

zbc_report_zones 说明了 libzbc 区域报告函数 zbc_report_zones()zbc_report_nr_zones()zbc_list_zones() 的使用。

# zbc_report_zones /dev/sg3
Device /dev/sg3:
    Vendor ID: ATA HGST HSH721415AL T220
    Zoned block device interface, Host-managed zone model
    29297213440 512-bytes sectors
    3662151680 logical blocks of 4096 B
    3662151680 physical blocks of 4096 B
    15000.173 GB capacity
    Read commands are unrestricted
    Maximum number of open sequential write required zones: 128
    55880 zones from 0, reporting option 0x00
55880 / 55880 zones:
Zone 00000: type 0x1 (Conventional), cond 0x0 (Not-write-pointer), sector 0, 524288 sectors
Zone 00001: type 0x1 (Conventional), cond 0x0 (Not-write-pointer), sector 524288, 524288 sectors
Zone 00002: type 0x1 (Conventional), cond 0x0 (Not-write-pointer), sector 1048576, 524288 sectors
...
Zone 00522: type 0x1 (Conventional), cond 0x0 (Not-write-pointer), sector 273678336, 524288 sectors
Zone 00523: type 0x1 (Conventional), cond 0x0 (Not-write-pointer), sector 274202624, 524288 sectors
Zone 00524: type 0x2 (Sequential-write-required), cond 0x1 (Empty), reset recommended 0, non_seq 0,
sector 274726912, 524288 sectors, wp 274726912
Zone 00525: type 0x2 (Sequential-write-required), cond 0x1 (Empty), reset recommended 0, non_seq 0,
sector 275251200, 524288 sectors, wp 275251200
...
Zone 55878: type 0x2 (Sequential-write-required), cond 0x1 (Empty), reset recommended 0, non_seq 0, sector 29296164864, 524288 sectors, wp 29296164864
Zone 55879: type 0x2 (Sequential-write-required), cond 0x1 (Empty), reset recommended 0, non_seq 0, sector 29296689152, 524288 sectors, wp 29296689152

zbc_report_zones 的输出可以通过使用选项 -ro(报告选项)来过滤哪些区域被报告,或者通过使用选项 -n 来限制输出到作为该选项的参数指定的区域数量。

# zbc_report_zones -ro not_wp -n /dev/sg3
Device /dev/sg3:
    Vendor ID: ATA HGST HSH721415AL T220
    Zoned block device interface, Host-managed zone model
    29297213440 512-bytes sectors
    3662151680 logical blocks of 4096 B
    3662151680 physical blocks of 4096 B
    15000.173 GB capacity
    Read commands are unrestricted
    Maximum number of open sequential write required zones: 128
    524 zones from 0, reporting option 0x3f

写入和重置区域

zbc_write_zone 将数据顺序写入区域。 如果该区域是顺序区域,则写入指针位置前进直到该区域已满。 默认情况下,zbc_write_zone 写入整个区域。 例如,使用上一个示例中使用的磁盘,可以使用以下命令以 512 kb 写入写入第一个顺序写入区域(区域编号 524),直到它已满:

# zbc_write_zone /dev/sg3 524 524288
Device /dev/sg3:
    Vendor ID: ATA HGST HSH721415AL T220
    SCSI ZBC device interface, Host-managed zone model
    29297213440 512-bytes sectors
    3662151680 logical blocks of 4096 B
    3662151680 physical blocks of 4096 B
    15000.173 GB capacity
    Read commands are unrestricted
    Maximum number of open sequential write required zones: 128
Target zone: Zone 524 / 55880, type 0x2 (Sequential-write-required), cond 0x1 (Empty), rwp 0, non_seq 0, sector 274726912, 524288 sectors, wp 274726912
Filling target zone 524, 524288 B I/Os
Wrote 268435456 B (512 I/Os) in 1.082 sec
  IOPS 472
  BW 247.952 MB/s

# zbc_report_zones -start 274726912 -nz 1 /dev/sg3
Device /dev/sg3:
    Vendor ID: ATA HGST HSH721415AL T220
    SCSI ZBC device interface, Host-managed zone model
    29297213440 512-bytes sectors
    3662151680 logical blocks of 4096 B
    3662151680 physical blocks of 4096 B
    15000.173 GB capacity
    Read commands are unrestricted
    Maximum number of open sequential write required zones: 128
    55356 zones from 274726912, reporting option 0x00
1 / 55356 zone:
Zone 00000: type 0x2 (Sequential-write-required), cond 0xe (Full), reset recommended 0, non_seq 0, sector 274726912, 524288 sectors, wp 2251799813685240

然后可以使用命令 zbc_reset_zone 重置写入的区域:

# zbc_reset_zone /dev/sg3 524
Device /dev/sg3:
    Vendor ID: ATA HGST HSH721415AL T220
    SCSI ZBC device interface, Host-managed zone model
    29297213440 512-bytes sectors
    3662151680 logical blocks of 4096 B
    3662151680 physical blocks of 4096 B
    15000.173 GB capacity
    Read commands are unrestricted
    Maximum number of open sequential write required zones: 128
reset zone 524/55880, sector 274726912...

# zbc_report_zones -start 274726912 -nz 1 /dev/sg7
Device /dev/sg7:
    Vendor ID: ATA HGST HSH721415AL T220
    SCSI ZBC device interface, Host-managed zone model
    29297213440 512-bytes sectors
    3662151680 logical blocks of 4096 B
    3662151680 physical blocks of 4096 B
    15000.173 GB capacity
    Read commands are unrestricted
    Maximum number of open sequential write required zones: 128
    55356 zones from 274726912, reporting option 0x00
1 / 55356 zone:
Zone 00000: type 0x2 (Sequential-write-required), cond 0x1 (Empty), reset recommended 0, non_seq 0, sector 274726912, 524288 sectors, wp 274726912

可以使用选项 -all 重置设备的所有顺序写入区域:

# zbc_reset_zone -all /dev/sg7
Device /dev/sg7:
    Vendor ID: ATA HGST HSH721415AL T220
    SCSI ZBC device interface, Host-managed zone model
    29297213440 512-bytes sectors
    3662151680 logical blocks of 4096 B
    3662151680 physical blocks of 4096 B
    15000.173 GB capacity
    Read commands are unrestricted
    Maximum number of open sequential write required zones: 128
Operating on all zones...

libnvme 用户库

libnvme 是一个开源用户库,提供与 nvme 设备交互的定义和函数。 nvme-cli 为用户从 shell 与 nvme 设备交互提供了方便的方法,而 libnvme 为其他程序提供了类似的访问。

概述

libnvme 提供了在 Linux 环境中发现和管理所有 nvme 设备的功能。 当 NVMe ZNS 规范获得批准时,libnvme 包含了 NVMe ZNS 规范提供的所有类型和命令的定义。

该库可用于构建 nvme 直通命令并通过 Linux nvme 驱动程序分发这些命令。 对于返回数据的命令,库提供结构和枚举来帮助解码有效负载。

库函数

libnvme 提供的所有 ZNS 函数都以 nvme_zns_ 名称为前缀。 以下是从 ZNS 规范定义的管理命令:

nvme_zns_identify_ns(): 检索 nvme_zns_id_ns 结构 
nv​​me_zns_identify_ctrl(): 检索 nvme_zns_id_ctrl 结构 
nv​​me_zns_get_log_changed_zones(): 检索 nvme_zns_changed_zone_log 结构

除了 admin 命令,ZNS 还定义了新的 IO 命令,libnvme 提供了以下 API 来发送它们:

nvme_zns_append(): 将数据附加到一个zone中 
nvme_zns_mgmt_send(): 请求对一个或所有区域执行操作 
nvme_zns_mgmt_recv(): 返回包含有关区域信息的数据

以下是 nvme_zns_mgmt_send() 函数可以执行的操作类型列表:

NVME_ZNS_ZSA_CLOSE: 将区域状态设置为关闭 
NVME_ZNS_ZSA_FINISH: 将区域状态设置为完整 
NVME_ZNS_ZSA_OPEN: 将区域状态设置为打开 
NVME_ZNS_ZSA_RESET: 将区域状态设置为空 
NVME_ZNS_ZSA_OFFLINE: 将区域状态设置为离线 
NVME_ZNS_ZSA_SET_DESC_EXT: 设置区域描述符扩展数据(如果可用)

库类型

ZNS 创建了新的常量类型和结构。 以下是 ZNS 规范中 libnvme 提供的结构:

nvme_zns_id_ns ZNS、; 特定命名空间标识,从 nvme_zns_identify_ns() 返回 
nvme_zns_id_ctrl ZNS: 特定控制器标识,从 nvme_zns_identify_ctrl() 返回 
nvme_zns_changed_zone_log: 指示一个或多个区域的区域描述符是否已更改的日志页面,从 nvme_zns_get_log_changed_zones() 返回 
nvme_zone_report: 提供从 ZNS 报告区域命令返回的结构,从 nvme_zns_mgmt_recv() 返回

libzbd 用户库

libzbd 是一个用户库,提供用于操作zoned块设备的功能。

libzbc 库不同,libzbd 不实现对分区块设备的直接命令访问。 libzbd 使用内核提供的基于 ioctl() 系统调用的zoned块设备接口。 这样做的直接后果是 libzbd 只允许访问正在运行的内核支持的zoned区块设备。 这包括物理设备(例如支持 ZBC 和 ZAC 标准的硬盘)和由各种设备驱动程序(例如 null_blk 和设备映射器驱动程序)实现的所有逻辑块设备。

libzbd 项目托管在 GitHub 上。 项目 README 文件提供了有关如何编译和安装 libzbd 库及其工具的信息。

概述

libzbd 提供用于发现和管理分区块设备中区域状态的功能。 可以使用标准 I/O 系统调用对设备进行读写访问。

当在分区块设备的区域上执行 libzbd 功能(和写入操作)时,这些功能和操作的执行可能会导致设备区域条件的变化和设备区域属性的变化。 这方面的一个例子是写指针在顺序区域中的位置。 libzbd 不会在内部跟踪这些更改。
换句话说,libzbd 是无状态的。

跟踪区域状况的变化是应用程序的责任。 这样的改变包括在对区域的写请求完成之后在顺序区域内增加的写指针位置。

库函数

所有 libzbd 函数都使用字节单位来测量与zone相关的信息。 此区域相关信息包括区域在设备上的起始位置、区域大小和区域写入指针位置。 分区块设备使用常规文件描述符编号标识,可以“按原样”与标准 I/O 系统调用一起使用。
然而,应用程序员必须始终小心实现读访问,以便它们与设备逻辑块大小对齐。 此外,在主机管理的分区块设备上,对顺序区域的写入操作必须与设备的物理块大小对齐。
libzbd提供的主要功能如下:

zbd_open(): 打开一个分区块设备 
zbd_close(): 关闭一个打开的分区块设备 
zbd_get_info(): 获取设备信息 
zbd_report_nr_zones(): 获取设备的区域数量 
zbd_report_zones(): 获取设备区域信息 
zbd_list_zones(): 获取设备区域信息 
zbd_zones_operation(): 对一个区域范围执行操作 
zbd_open_zones(): 显式打开一个区域范围 
zbd_close_zones(): 关闭一个区域范围 
zbd_reset_zones(): 重置一个区域范围的写指针 
zbd_finish_zones(): 完成一个区域范围

有关这些函数的使用和行为的更多详细信息可以在 libzbd 头文件的注释中找到。 此头文件默认安装为 /usr/include/libzbd/zbd.h。

libzbd 没有为多线程或多进程应用程序实现任何互斥机制。 这意味着应用程序有责任同步执行针对同一zone的冲突操作。 这种情况的一个典型示例是多个线程对同一区域执行并发写入操作,当应用程序没有写入顺序控制时,这可能会导致写入错误。

libzbd 提供以下功能以方便应用程序的开发和测试:

zbd_device_is_zoned(): 测试设备是否为分区块设备 
zbd_device_model_str(): 获取设备模型的字符串描述 
zbd_zone_type_str(): 获取区域类型的字符串描述 
zbd_zone_cond_str(): 获取区域条件的字符串描述 
zbd_set_log_level(): 设置库详细级别

实用程序

libzbd 提供了几个命令行应用程序,用于通过调用库函数来操作分区块设备。 libzbd 提供的应用列表如下表所示:

  • zbd 命令行实用程序,用于报告、打开、关闭、重置和完成设备的区域
  • gzbd 类似于 zbd 工具,但使用图形用户界面
  • gzbd-viewer 显示分区块设备的区域条件和状态的图形用户界面

tcmu-runner ZBC 磁盘仿真

tcmu-runner 是一个应用程序守护程序,它可以处理由内核 SCSI 目标子组件发送的 SCSI 命令的执行。 它使导出的 SCSI 逻辑单元 (LUN) 可以由常规文件或块设备支持。

QEMU

QEMU NVMe ZNS 设备仿真参见:

ZNS 的 Linux 工具

分区命名空间支持已添加到版本 5.9 的 Linux 内核中。 初始驱动程序版本需要命名空间来实现“区域追加”命令,以便与内核的块堆栈一起工作。

nvme-cli

zns 的开源工具由当前 master 分支(1.12 及更高版本)中的 nvme-cli 提供。 建议使用最新版本。

ZNS 特定的命令都使用 zns 命令行前缀。 通过运行 zns help 命令查看更多 zns 命令:

# nvme zns help
nvme-1.12
usage: nvme zns <command> [<device>] [<args>]

The '<device>' may be either an NVMe character device (ex: /dev/nvme0) or an
nvme block device (ex: /dev/nvme0n1).

Zoned Namespace Command Set

The following are all implemented sub-commands:
  id-ctrl             Retrieve ZNS controller identification
  id-ns               Retrieve ZNS namespace identification
  zone-mgmt-recv      Sends the zone management receive command
  zone-mgmt-send      Sends the zone management send command
  report-zones        Retrieve the Report Zones report
  close-zone          Closes one or more zones
  finish-zone         Finishes one or more zones
  open-zone           Opens one or more zones
  reset-zone          Resets one or more zones
  offline-zone        Offlines one or more zones
  set-zone-desc       Attaches zone descriptor extension data
  zone-append         Writes data and metadata (if applicable), appended to the end of the requested zone
  changed-zone-list   Retrieves the changed zone list log

识别 ZNS 控制器

分区命名空间命令集规范目前仅在命令集的识别控制器中定义了一个字段:Zone Append Size Limit (ZASL)。 区域追加大小限制 (ZASL) 对区域追加命令的最大命令大小进行编码。 下面的示例返回“5”,对应于 128k 字节(本示例中可以附加的最大大小):

# nvme zns id-ctrl /dev/nvme1n1
NVMe ZNS Identify Controller:
zasl    : 5

识别 ZNS 命名空间

针对于分区命名空间的信息可以在此命令集的识别命名空间中找到。

# nvme zns id-ns /dev/nvme1n1
ZNS Command Set Identify Namespace:
zoc     : 0
ozcs    : 1
mar     : 0xffffffff
mor     : 0xffffffff
rrl     : 0
frl     : 0
lbafe  0: zsze:0x100000 zdes:0 (in use)

使用“-H”(人类可读)选项查找更多详细信息:

# nvme zns id-ns /dev/nvme1n1  -H
ZNS Command Set Identify Namespace:
zoc     : 0   Zone Operation Characteristics
  [1:1] : 0     Zone Active Excursions: No
  [0:0] : 0     Variable Zone Capacity: No

ozcs    : 1   Optional Zoned Command Support
  [2:2] : 0x1   Read Across Zone Boundaries: Yes

mar     : 0xffffffff
mor     : 0xffffffff
rrl     : Not Reported
frl     : Not Reported
LBA Format Extension  0 : Zone Size: 0x100000 LBAs - Zone Descriptor Extension Size: 0 bytes (in use)

如果您想使用另一个脚本处理输出,您可以使用“-o json”选项请求更计算机友好的 json 格式:

# nvme zns id-ns /dev/nvme1n1  -o json
{
  "zoc" : 0,
  "ozcs" : 1,
  "mar" : 4294967295,
  "mor" : 4294967295,
  "rrl" : 0,
  "frl" : 0,
  "lbafe" : [
    {
      "zsze" : 1048576,
      "zdes" : 0
    }
  ]
}

报告zones信息

report-zones’ 命令提供有关各个zone的信息,包括它们当前的区域状态和写指针。 以下示例检索前 10 个区域描述符:

# nvme zns report-zones /dev/nvme1n1 -d 10
nr_zones: 373
SLBA: 0x0        WP: 0x2000     Cap: 0x100000   State: IMP_OPENED   Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x100000   WP: 0x100000   Cap: 0x100000   State: EMPTY        Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x200000   WP: 0xffffffffffffffff Cap: 0x100000   State: FULL         Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x300000   WP: 0xffffffffffffffff Cap: 0x100000   State: FULL         Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x400000   WP: 0xffffffffffffffff Cap: 0x100000   State: FULL         Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x500000   WP: 0xffffffffffffffff Cap: 0x100000   State: FULL         Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x600000   WP: 0xffffffffffffffff Cap: 0x100000   State: FULL         Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x700000   WP: 0xffffffffffffffff Cap: 0x100000   State: FULL         Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x800000   WP: 0x8c1000   Cap: 0x100000   State: IMP_OPENED   Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x900000   WP: 0x900000   Cap: 0x100000   State: EMPTY        Type: SEQWRITE_REQ   Attrs: 0x0

重置区域

要重置写指针并使区域返回 EMPTY 状态,请使用“zone-reset”命令。 以下示例重置所有区域(“-a”选项表示“all区域”)(警告:这实际上会删除区域的数据)。

# nvme zns reset-zone /dev/nvme1n1 -a
zns-reset-zone: Success, action:4 zone:0 nsid:1

打开一个区域

# nvme zns open-zone /dev/nvme1n1 
zns-open-zone: Success, action:3 zone:0 nsid:1

验证其当前状态:

# nvme zns report-zones /dev/nvme1n1 -d 2
nr_zones: 373
SLBA: 0x0        WP: 0x0        Cap: 0x100000   State: EXP_OPENED   Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x100000   WP: 0x100000   Cap: 0x100000   State: EMPTY        Type: SEQWRITE_REQ   Attrs: 0x0

关闭一个区域

关闭区域会释放打开的资源,并且可以在显式打开的区域和隐式打开的区域上完成。 运行以下命令关闭第一个区域:

# nvme zns close-zone /dev/nvme1n1 
zns-close-zone: Success, action:1 zone:0 nsid:1

验证其当前状态:

# nvme zns report-zones /dev/nvme1n1 -d 2
nr_zones: 373
SLBA: 0x0        WP: 0xffffffffffffffff Cap: 0x100000   State: FULL         Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x100000   WP: 0x100000   Cap: 0x100000   State: EMPTY        Type: SEQWRITE_REQ   Attrs: 0x0

Offlining a Zone

使区域脱机会使该区域无法访问。 脱机区域上的数据将无法再访问,并且在区域重置之前无法写入该区域。 运行此命令以使第一个区域脱机:

# nvme zns offline-zone /dev/nvme1n1
zns-offline-zone: Success, action:5 zone:0 nsid:1

验证其当前状态:

# nvme zns report-zones /dev/nvme1n1 -d 2
nr_zones: 373
SLBA: 0x0        WP: 0          Cap: 0x100000   State: OFFLINE      Type: SEQWRITE_REQ   Attrs: 0x0
SLBA: 0x100000   WP: 0x100000   Cap: 0x100000   State: EMPTY        Type: SEQWRITE_REQ   Attrs: 0x0

Zone Append

您可以将数据附加到特定区域。 在此方法中,您只需指定要将数据附加到哪个区域。 设备返回存储数据的 LBA。

以下是一些示例: 将“hello world”附加到第一个区域块(本示例中为 512 字节):

# echo "hello world" | nvme zns zone-append /dev/nvme1n1 -z 512
Success appended data to LBA 0

从 LBA 0 读回数据以验证它是否保存了我们的数据:

# nvme read /dev/nvme1n1 -z 512
hello world
read: Success

添加更多数据并验证其内容:

# echo "goodbye world" | nvme zns zone-append /dev/nvme1n1 -z 512
Success appended data to LBA 1

# nvme read /dev/nvme1n1 -z 512 -s 1
goodbye world
read: Success
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值