Linux Tools for ZNS

Zoned namespace support was added to the Linux kernel with version 5.9. The initial driver release requires the namespace implement the Zone Append command in order to use with the kernel's block stack.

nvme-cli

Open source tooling for zns is provided by nvme-cli in the current master branch from version 1.12 and onward.

The ZNS specific commands all use the zns command line prefix. You can view further available commands by checking its help:

# 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

Identify ZNS Controller

The Zoned Namespace Command Set specification currently defines only one field in the command set's Identify Controller: the Zone Append Size Limit (ZASL), encoding the maximum command size for a Zone Append command. The example below returns '5', which corresponds to 128k bytes for maximum append:

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

Identify ZNS Namespace

Information specific to a Zoned Namespace can be found in this command set's Identify Namespace.

# 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)

More detailed information can be found with the '-H' (human readable) option:

# 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)

If the output is intended to be processed by another script, a more computer friendly json format can be requested with the '-o json' option:

# 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
    }
  ]
}

Reporting Zones

The 'report-zones' command can get information on individual zones, including their current zone state and write pointer. The following example retreives the first 10 zone descriptors.

# 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

Resetting the zone

To reset the write pointer and return the zone to the EMPTY state, the 'zone-reset' command can be used. The following example resets all zones with the '-a' option (WARNING: this effectively deletes the zone's data).

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

The 'report-zones' will now show all the zones are reset to the empty state:

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

Opening a zone

Explicitly opening a zone will make it ready for immediate write access and consumes an Open Resource. Opening the first zone:

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

Verifying its current state:

# 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

Closing a zone

Closing the zone releases the open resource and can be done on either explicitly or implicitly open zones. Closing the first zone:

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

Verifying its current state:

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

Finishing a zone

Finishing a zone sets its state to 'full'. Finishing the first zone:

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

Verifying its current state:

# 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

Offlining a zone makes the zone inaccessible. The data on the zone will no longer be accessible, and writes to the zone will not be possible until the zone is reset. Offlining the first zone:

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

Verifying its current state:

# 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

You can append data to specific zones. In this method, you only specify which zone to append data to, and the device will return the LBA where it stored the data. Here are a few examples below.

Append "hello world" to the first zone block (512 bytes in this example):

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

Read the data back from LBA 0 to verify it saved our data:

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

Now append more data and verify its contents:

# 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

Since we've appended two blocks to zone 0, we can check the current report zones to verify the current write pointer:

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