* [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests
@ 2026-10-14 17:00 Andrea Parri
2026-10-14 17:00 ` [PATCH blktests 1/4] nvme/rc: add zoned null_blk target helpers Andrea Parri
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Andrea Parri @ 2026-10-14 17:00 UTC (permalink / raw)
To: Shin'ichiro Kawasaki, linux-block
Cc: Andrea Parri, Christoph Hellwig, Keith Busch, Sagi Grimberg,
Chaitanya Kulkarni, Daniel Wagner, linux-nvme
Add regression tests for three nvmet bugs fixed by the kernel series
"nvmet: fix stale data exposure and ZNS validation" [1]:
nvme/071 "nvmet-fc: only send read data for successful commands"
nvme/072 "nvmet: zns: don't leak stale memory in zone management receive"
nvme/073 "nvmet: zns: reject a NUMD whose byte count wraps"
The nvme/072 fix is already applied to nvme-7.4; the other two are still
under review.
Patch 1 adds the zoned null_blk target setup, cleanup and zone report
status check that nvme/072 and nvme/073 share. Patches 2-4 add one test
each, with a small C helper for nvme/071 and nvme/072 where nvme-cli
cannot express the check.
All three tests run on every configured transport. On a kernel without
the fixes, nvme/072 and nvme/073 fail over loop, tcp, rdma and fc, and
nvme/071 fails over fc, the only affected transport. All three pass
with the fixes applied.
The tests take the next free numbers, nvme/071 to nvme/073. If other
nvme tests land first, I'm happy to renumber them and resend.
[1] https://lore.kernel.org/r/20261012141239.156408-1-parri.andrea@gmail.com
Andrea Parri (4):
nvme/rc: add zoned null_blk target helpers
nvme/071: test that nvmet sends no data for a failed read
nvme/072: test nvmet zone report initialization and length
nvme/073: test that nvmet rejects a zone report NUMD that wraps
src/.gitignore | 2 +
src/Makefile | 2 +
src/nvme-sentinel-read.c | 89 +++++++++++++++++++++
src/nvme-zone-report.c | 165 +++++++++++++++++++++++++++++++++++++++
tests/nvme/071 | 80 +++++++++++++++++++
tests/nvme/071.out | 2 +
tests/nvme/072 | 68 ++++++++++++++++
tests/nvme/072.out | 2 +
tests/nvme/073 | 46 +++++++++++
tests/nvme/073.out | 2 +
tests/nvme/rc | 61 +++++++++++++++
11 files changed, 519 insertions(+)
create mode 100644 src/nvme-sentinel-read.c
create mode 100644 src/nvme-zone-report.c
create mode 100755 tests/nvme/071
create mode 100644 tests/nvme/071.out
create mode 100755 tests/nvme/072
create mode 100644 tests/nvme/072.out
create mode 100755 tests/nvme/073
create mode 100644 tests/nvme/073.out
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH blktests 1/4] nvme/rc: add zoned null_blk target helpers
2026-10-14 17:00 [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Andrea Parri
@ 2026-10-14 17:00 ` Andrea Parri
2026-10-20 10:31 ` Shin'ichiro Kawasaki
2026-10-14 17:00 ` [PATCH blktests 2/4] nvme/071: test that nvmet sends no data for a failed read Andrea Parri
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Andrea Parri @ 2026-10-14 17:00 UTC (permalink / raw)
To: Shin'ichiro Kawasaki, linux-block
Cc: Andrea Parri, Christoph Hellwig, Keith Busch, Sagi Grimberg,
Chaitanya Kulkarni, Daniel Wagner, linux-nvme
Add helpers that export a zoned null_blk device as a namespace of the
default subsystem and connect to it, tear that setup down again, and
issue a partial Zone Management Receive and check its completion status.
The zone report tests added next share them.
Assisted-by: LLM
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
tests/nvme/rc | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/tests/nvme/rc b/tests/nvme/rc
index c415ca1234407..270fad19d61f1 100644
--- a/tests/nvme/rc
+++ b/tests/nvme/rc
@@ -408,6 +408,67 @@ _nvmet_passthru_target_cleanup() {
_remove_nvmet_host "${def_hostnqn}"
}
+# Export a zoned null_blk device as a namespace of the default subsystem and
+# connect to it. Callers must source common/null_blk.
+_nvmet_zoned_target_setup() {
+ if ! _init_null_blk nr_devices=0; then
+ return 1
+ fi
+ if ! _configure_null_blk nullb0 blocksize=512 size=1024 zoned=1 \
+ zone_size=1 power=1 >>"$FULL" 2>&1; then
+ echo "could not configure zoned null_blk device"
+ return 1
+ fi
+
+ _nvmet_target_setup --blkdev none
+ _create_nvmet_ns --blkdev /dev/nullb0 --uuid "$def_subsys_uuid" \
+ >/dev/null
+ _nvme_connect_subsys
+}
+
+_nvmet_zoned_target_cleanup() {
+ _nvme_disconnect_subsys
+ _nvmet_target_cleanup
+ _exit_null_blk
+}
+
+# Test cleanup handler to register with _nvmet_zoned_target_setup().
+_cleanup_nvmet_zoned_target() {
+ _cleanup_nvmet
+ _exit_null_blk
+}
+
+# Issue a partial zone report from zone 0 of namespace device $1 with a
+# $2-byte data buffer and NUMD $3, and check that it completes with status
+# code $4, or succeeds if $4 is 0.
+_check_zone_report() {
+ local dev=$1 data_len=$2 numd=$3 expected=$4 desc err rc status
+
+ desc="zone report with $data_len bytes and NUMD $numd"
+ err=$(nvme io-passthru "$dev" --namespace-id=1 --opcode=0x7a --read \
+ --data-len="$data_len" --cdw10=0 --cdw11=0 --cdw12="$numd" \
+ --cdw13=0x10000 2>&1 >/dev/null)
+ rc=$?
+ status=$(sed -n \
+ 's/^NVMe status:.*(\(0x[[:xdigit:]][[:xdigit:]]*\)).*/\1/p' \
+ <<<"$err")
+ printf '%s: exit=%d status=%s\n' "$desc" "$rc" "${status:-none}" \
+ >>"$FULL"
+
+ if ((expected == 0)); then
+ if ((rc != 0)); then
+ echo "$desc failed"
+ fi
+ elif ((rc == 0)); then
+ echo "$desc was not rejected"
+ elif [[ -z "$status" ]]; then
+ echo "$desc returned no NVMe status"
+ elif (((status & 0x7ff) != expected)); then
+ printf '%s returned status %s, expected %#x\n' "$desc" \
+ "$status" "$expected"
+ fi
+}
+
_discovery_genctr() {
local port=${1}
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH blktests 2/4] nvme/071: test that nvmet sends no data for a failed read
2026-10-14 17:00 [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Andrea Parri
2026-10-14 17:00 ` [PATCH blktests 1/4] nvme/rc: add zoned null_blk target helpers Andrea Parri
@ 2026-10-14 17:00 ` Andrea Parri
2026-10-20 10:35 ` Shin'ichiro Kawasaki
2026-10-14 17:00 ` [PATCH blktests 3/4] nvme/072: test nvmet zone report initialization and length Andrea Parri
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Andrea Parri @ 2026-10-14 17:00 UTC (permalink / raw)
To: Shin'ichiro Kawasaki, linux-block
Cc: Andrea Parri, Christoph Hellwig, Keith Busch, Sagi Grimberg,
Chaitanya Kulkarni, Daniel Wagner, linux-nvme
Issue a failing Identify into a buffer filled with a sentinel and verify
that the target transfers no data, with a successful Identify and a
namespace read as controls. The test runs on every configured transport;
only nvmet-fc failed it before the fix. Add a small helper because
nvme-cli cannot prefill the data buffer.
The test fails over fc on the unpatched kernel and passes with the fix
applied.
Assisted-by: LLM
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
src/.gitignore | 1 +
src/Makefile | 1 +
src/nvme-sentinel-read.c | 89 ++++++++++++++++++++++++++++++++++++++++
tests/nvme/071 | 80 ++++++++++++++++++++++++++++++++++++
tests/nvme/071.out | 2 +
5 files changed, 173 insertions(+)
create mode 100644 src/nvme-sentinel-read.c
create mode 100755 tests/nvme/071
create mode 100644 tests/nvme/071.out
diff --git a/src/.gitignore b/src/.gitignore
index e9869e172b20f..2429da9d9cf44 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -18,3 +18,4 @@
/ioctl-lbmd-query
/nvme-passthru-admin-uring
/nvme-delay-ioctl
+/nvme-sentinel-read
diff --git a/src/Makefile b/src/Makefile
index dd646940fa25e..78433f0762b4a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -24,6 +24,7 @@ C_TARGETS := \
mount_clear_sock \
nvme-delay-ioctl \
nvme-passthrough-meta \
+ nvme-sentinel-read \
ioctl-lbmd-query \
nbdsetsize \
openclose \
diff --git a/src/nvme-sentinel-read.c b/src/nvme-sentinel-read.c
new file mode 100644
index 0000000000000..dd36f283f46e2
--- /dev/null
+++ b/src/nvme-sentinel-read.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-3.0+
+// Copyright (C) 2026 Andrea Parri
+
+/*
+ * Issue an NVMe admin read command into a buffer filled with a sentinel and
+ * print the NVMe status and the number of bytes the command overwrote.
+ */
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/nvme_ioctl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#define SENTINEL 0xa5
+
+static int parse_u32(const char *s, uint32_t *val)
+{
+ unsigned long v;
+ char *end;
+
+ errno = 0;
+ v = strtoul(s, &end, 0);
+ if (errno || end == s || *end || v > UINT32_MAX)
+ return -1;
+ *val = v;
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct nvme_passthru_cmd cmd = { };
+ uint32_t opcode, cdw10, len, i, changed = 0;
+ unsigned char *buf;
+ void *p;
+ int fd, ret;
+
+ if (argc != 5 || parse_u32(argv[2], &opcode) || opcode > 0xff ||
+ parse_u32(argv[3], &cdw10) || parse_u32(argv[4], &len) || !len) {
+ fprintf(stderr, "usage: %s /dev/nvmeX opcode cdw10 data-len\n",
+ argv[0]);
+ return 2;
+ }
+
+ /*
+ * Page-align the buffer so that the kernel maps it for the transfer
+ * instead of bouncing it: a bounce buffer is zero-filled and copied
+ * back even when the command fails, which would hide the sentinel.
+ */
+ ret = posix_memalign(&p, 4096, len);
+ if (ret) {
+ fprintf(stderr, "posix_memalign: %s\n", strerror(ret));
+ return 2;
+ }
+ buf = p;
+ memset(buf, SENTINEL, len);
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[1]);
+ free(buf);
+ return 2;
+ }
+
+ cmd.opcode = opcode;
+ cmd.addr = (uintptr_t)buf;
+ cmd.data_len = len;
+ cmd.cdw10 = cdw10;
+
+ ret = ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ close(fd);
+ if (ret < 0) {
+ perror("NVME_IOCTL_ADMIN_CMD");
+ free(buf);
+ return 2;
+ }
+
+ for (i = 0; i < len; i++)
+ if (buf[i] != SENTINEL)
+ changed++;
+
+ printf("%#x %u\n", ret, changed);
+ free(buf);
+ return 0;
+}
diff --git a/tests/nvme/071 b/tests/nvme/071
new file mode 100755
index 0000000000000..5333bfac8db53
--- /dev/null
+++ b/tests/nvme/071
@@ -0,0 +1,80 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-3.0+
+# Copyright (C) 2026 Andrea Parri
+#
+# Check that the NVMe target transfers no read data for a failed command.
+# The host buffer is filled with a sentinel, so any data the target sends
+# shows up as changed bytes.
+#
+# Regression test for patch "nvmet-fc: only send read data for successful
+# commands".
+
+. tests/nvme/rc
+
+DESCRIPTION="verify nvmet does not transfer data for failed reads"
+QUICK=1
+
+requires() {
+ _nvme_requires
+ _have_loop
+ _require_kernel_nvme_target
+ _have_src_program nvme-sentinel-read
+}
+
+set_conditions() {
+ _set_nvme_trtype "$@"
+}
+
+# Issue an Identify with CNS $2 to controller $1 and print the NVMe status
+# and the number of changed bytes in the 4096-byte data buffer.
+_identify() {
+ src/nvme-sentinel-read "$1" 0x06 "$2" 4096 2>>"$FULL"
+}
+
+test() {
+ echo "Running ${TEST_NAME}"
+
+ local changed ctrl ns status
+
+ _setup_nvmet
+ # File backing: the namespace read below fails over loop with the
+ # default loop device backing, for reasons unrelated to this test.
+ _nvmet_target_setup --blkdev file
+ _nvme_connect_subsys
+
+ ctrl=$(_find_nvme_dev "$def_subsysnqn")
+ ns="/dev/${ctrl}n1"
+
+ # Identify Controller succeeds and must overwrite the sentinel.
+ if ! read -r status changed < <(_identify "/dev/$ctrl" 0x01); then
+ echo "successful Identify could not be issued"
+ else
+ echo "cns=0x01 status=$status changed=$changed" >>"$FULL"
+ if ((status != 0 || changed == 0)); then
+ echo "successful Identify returned status $status with $changed bytes"
+ fi
+ fi
+
+ # An unsupported CNS fails and must leave the sentinel intact.
+ if ! read -r status changed < <(_identify "/dev/$ctrl" 0xff); then
+ echo "failed Identify could not be issued"
+ else
+ echo "cns=0xff status=$status changed=$changed" >>"$FULL"
+ if (((status & 0x7ff) != 2)); then
+ echo "failed Identify returned status $status, expected 0x2"
+ fi
+ if ((changed != 0)); then
+ echo "failed Identify transferred data: $changed bytes changed"
+ fi
+ fi
+
+ if ! dd if="$ns" of=/dev/null bs=4096 count=256 iflag=direct \
+ status=none >>"$FULL" 2>&1; then
+ echo "namespace read failed"
+ fi
+
+ _nvme_disconnect_subsys
+ _nvmet_target_cleanup
+
+ echo "Test complete"
+}
diff --git a/tests/nvme/071.out b/tests/nvme/071.out
new file mode 100644
index 0000000000000..146809b91e0a3
--- /dev/null
+++ b/tests/nvme/071.out
@@ -0,0 +1,2 @@
+Running nvme/071
+Test complete
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH blktests 3/4] nvme/072: test nvmet zone report initialization and length
2026-10-14 17:00 [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Andrea Parri
2026-10-14 17:00 ` [PATCH blktests 1/4] nvme/rc: add zoned null_blk target helpers Andrea Parri
2026-10-14 17:00 ` [PATCH blktests 2/4] nvme/071: test that nvmet sends no data for a failed read Andrea Parri
@ 2026-10-14 17:00 ` Andrea Parri
2026-10-20 10:38 ` Shin'ichiro Kawasaki
2026-10-14 17:00 ` [PATCH blktests 4/4] nvme/073: test that nvmet rejects a zone report NUMD that wraps Andrea Parri
2026-10-20 10:41 ` [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Shin'ichiro Kawasaki
4 siblings, 1 reply; 11+ messages in thread
From: Andrea Parri @ 2026-10-14 17:00 UTC (permalink / raw)
To: Shin'ichiro Kawasaki, linux-block
Cc: Andrea Parri, Christoph Hellwig, Keith Busch, Sagi Grimberg,
Chaitanya Kulkarni, Daniel Wagner, linux-nvme
Export a zoned null_blk device through the NVMe target and verify that
Zone Management Receive zeroes reserved and unused report bytes and
rejects a transfer length inconsistent with NUMD. The test runs on every
configured transport, with RDMA restricted to 4 KiB page kernels so that
the preparatory write exceeds the default inline-data limit. Add a small
passthrough helper because nvme-cli does not expose the reserved report
bytes.
The test fails on the unpatched kernel and passes with the fix applied.
Assisted-by: LLM
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
src/.gitignore | 1 +
src/Makefile | 1 +
src/nvme-zone-report.c | 165 +++++++++++++++++++++++++++++++++++++++++
tests/nvme/072 | 68 +++++++++++++++++
tests/nvme/072.out | 2 +
5 files changed, 237 insertions(+)
create mode 100644 src/nvme-zone-report.c
create mode 100755 tests/nvme/072
create mode 100644 tests/nvme/072.out
diff --git a/src/.gitignore b/src/.gitignore
index 2429da9d9cf44..8119476416937 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -19,3 +19,4 @@
/nvme-passthru-admin-uring
/nvme-delay-ioctl
/nvme-sentinel-read
+/nvme-zone-report
diff --git a/src/Makefile b/src/Makefile
index 78433f0762b4a..7c4fd600ace1d 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -25,6 +25,7 @@ C_TARGETS := \
nvme-delay-ioctl \
nvme-passthrough-meta \
nvme-sentinel-read \
+ nvme-zone-report \
ioctl-lbmd-query \
nbdsetsize \
openclose \
diff --git a/src/nvme-zone-report.c b/src/nvme-zone-report.c
new file mode 100644
index 0000000000000..951b1d03d4a75
--- /dev/null
+++ b/src/nvme-zone-report.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-3.0+
+// Copyright (C) 2026 Andrea Parri
+
+/* Check that unused bytes in an NVMe zone report are zeroed. */
+#define _GNU_SOURCE
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/nvme_ioctl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#define REPORT_LEN 4096
+/*
+ * Larger than the default RDMA in-capsule data size on 4 KiB page kernels.
+ * nvme/072 skips RDMA on other page sizes, where this write could stay
+ * inline rather than use buffers allocated and freed per command.
+ */
+#define POISON_LEN 8192
+#define WRITE_OPCODE 0x01
+#define ZONE_REPORT_OPCODE 0x7a
+#define PARTIAL_REPORT (1U << 16)
+#define ZONE_REPORT_HEADER 64
+#define ZONE_DESC_LEN 64
+#define ZONE_START_OFFSET 16
+#define NR_ATTEMPTS 64
+
+static int poison_target(int fd, unsigned int iteration)
+{
+ unsigned char data[POISON_LEN];
+ struct nvme_passthru_cmd cmd = {
+ .opcode = WRITE_OPCODE,
+ .nsid = 1,
+ .addr = (uintptr_t)data,
+ .data_len = sizeof(data),
+ .cdw10 = iteration * (POISON_LEN / 512),
+ .cdw12 = POISON_LEN / 512 - 1,
+ };
+
+ memset(data, 0x5a, sizeof(data));
+ return ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
+}
+
+static uint64_t read_le64(const unsigned char *p)
+{
+ uint64_t value;
+
+ memcpy(&value, p, sizeof(value));
+ return le64toh(value);
+}
+
+int main(int argc, char **argv)
+{
+ unsigned char *report;
+ unsigned long long slba;
+ char *end;
+ void *p;
+ int fd, i, j, ret, rc = 0;
+
+ if (argc != 3) {
+ fprintf(stderr, "usage: %s /dev/nvmeXnY last-zone-slba\n",
+ argv[0]);
+ return 2;
+ }
+
+ errno = 0;
+ slba = strtoull(argv[2], &end, 0);
+ if (errno || *end) {
+ fprintf(stderr, "invalid SLBA: %s\n", argv[2]);
+ return 2;
+ }
+
+ /*
+ * Page-align the report so that the kernel maps it for the transfer
+ * instead of bouncing it: a bounce buffer is zero-filled, which would
+ * hide the sentinel and any bytes the target left unwritten.
+ */
+ ret = posix_memalign(&p, 4096, REPORT_LEN);
+ if (ret) {
+ fprintf(stderr, "posix_memalign: %s\n", strerror(ret));
+ return 2;
+ }
+ report = p;
+
+ fd = open(argv[1], O_RDWR);
+ if (fd < 0) {
+ perror(argv[1]);
+ free(report);
+ return 2;
+ }
+
+ for (i = 0; i < NR_ATTEMPTS; i++) {
+ struct nvme_passthru_cmd cmd = {
+ .opcode = ZONE_REPORT_OPCODE,
+ .nsid = 1,
+ .addr = (uintptr_t)report,
+ .data_len = REPORT_LEN,
+ .cdw10 = (uint32_t)slba,
+ .cdw11 = (uint32_t)(slba >> 32),
+ .cdw12 = REPORT_LEN / 4 - 1,
+ .cdw13 = PARTIAL_REPORT,
+ };
+ unsigned int nonzero = 0, stale = 0, poisoned = 0;
+ unsigned char sentinel = i & 1 ? 0xa5 : 0x3c;
+
+ ret = poison_target(fd, i);
+ if (ret) {
+ if (ret < 0)
+ perror("poison_target");
+ else
+ fprintf(stderr, "poison write status: %#x\n", ret);
+ rc = 2;
+ goto out;
+ }
+
+ memset(report, sentinel, REPORT_LEN);
+ ret = ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
+ if (ret) {
+ if (ret < 0)
+ perror("NVME_IOCTL_IO_CMD");
+ else
+ fprintf(stderr, "NVMe status: %#x\n", ret);
+ rc = 2;
+ goto out;
+ }
+
+ if (read_le64(report) != 1 ||
+ read_le64(report + ZONE_REPORT_HEADER +
+ ZONE_START_OFFSET) != slba) {
+ fprintf(stderr, "unexpected zone report header or SLBA\n");
+ rc = 1;
+ goto out;
+ }
+
+ for (j = 8; j < REPORT_LEN; j++) {
+ if (j >= ZONE_REPORT_HEADER &&
+ j < ZONE_REPORT_HEADER + ZONE_DESC_LEN)
+ continue;
+ if (!report[j])
+ continue;
+ nonzero++;
+ if (report[j] == sentinel)
+ stale++;
+ if (report[j] == 0x5a)
+ poisoned++;
+ }
+
+ fprintf(stderr, "attempt %d: %u nonzero bytes "
+ "(%u poison, %u sentinel)\n", i + 1, nonzero,
+ poisoned, stale);
+ if (nonzero) {
+ rc = 1;
+ goto out;
+ }
+ }
+
+out:
+ close(fd);
+ free(report);
+ return rc;
+}
diff --git a/tests/nvme/072 b/tests/nvme/072
new file mode 100755
index 0000000000000..c7bcf93ab4d20
--- /dev/null
+++ b/tests/nvme/072
@@ -0,0 +1,68 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-3.0+
+# Copyright (C) 2026 Andrea Parri
+#
+# Check that NVMe target zone reports are fully initialized and that their
+# transfer length matches NUMD.
+#
+# Regression test for patch "nvmet: zns: don't leak stale memory in zone
+# management receive".
+
+. tests/nvme/rc
+. common/null_blk
+
+DESCRIPTION="check NVMe target zone report initialization and length"
+QUICK=1
+
+requires() {
+ _nvme_requires
+ _have_loop
+ _require_kernel_nvme_target
+ _have_module null_blk
+ _have_kernel_options BLK_DEV_ZONED
+ _have_src_program nvme-zone-report
+
+ # RDMA defaults to PAGE_SIZE bytes of inline data. The helper's
+ # 8 KiB preparatory write must exceed that limit.
+ if [[ "$nvme_trtype" == "rdma" ]]; then
+ _page_size_equals 4096
+ fi
+}
+
+set_conditions() {
+ _set_nvme_trtype "$@"
+}
+
+test() {
+ echo "Running ${TEST_NAME}"
+
+ local block_size dev last_sector ret sectors slba zone_sectors
+
+ _setup_nvmet
+ _register_test_cleanup _cleanup_nvmet_zoned_target
+ if ! _nvmet_zoned_target_setup; then
+ return 1
+ fi
+ dev="/dev/$(_find_nvme_ns "$def_subsys_uuid")"
+
+ sectors=$(cat /sys/block/nullb0/size)
+ zone_sectors=$(cat /sys/block/nullb0/queue/chunk_sectors)
+ block_size=$(cat /sys/block/nullb0/queue/logical_block_size)
+ last_sector=$(((sectors - 1) / zone_sectors * zone_sectors))
+ slba=$((last_sector * 512 / block_size))
+
+ src/nvme-zone-report "$dev" "$slba" >>"$FULL" 2>&1
+ ret=$?
+ case $ret in
+ 0) ;;
+ 1) echo "zone report contained stale data" ;;
+ *) echo "zone report checker failed" ;;
+ esac
+
+ _check_zone_report "$dev" 4096 1023 0
+ _check_zone_report "$dev" 8192 1023 15
+
+ _nvmet_zoned_target_cleanup
+
+ echo "Test complete"
+}
diff --git a/tests/nvme/072.out b/tests/nvme/072.out
new file mode 100644
index 0000000000000..3f765b30a22b0
--- /dev/null
+++ b/tests/nvme/072.out
@@ -0,0 +1,2 @@
+Running nvme/072
+Test complete
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH blktests 4/4] nvme/073: test that nvmet rejects a zone report NUMD that wraps
2026-10-14 17:00 [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Andrea Parri
` (2 preceding siblings ...)
2026-10-14 17:00 ` [PATCH blktests 3/4] nvme/072: test nvmet zone report initialization and length Andrea Parri
@ 2026-10-14 17:00 ` Andrea Parri
2026-10-20 10:41 ` [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Shin'ichiro Kawasaki
4 siblings, 0 replies; 11+ messages in thread
From: Andrea Parri @ 2026-10-14 17:00 UTC (permalink / raw)
To: Shin'ichiro Kawasaki, linux-block
Cc: Andrea Parri, Christoph Hellwig, Keith Busch, Sagi Grimberg,
Chaitanya Kulkarni, Daniel Wagner, linux-nvme
Verify that Zone Management Receive rejects a NUMD whose byte count
wraps a 32-bit calculation, while accepting an ordinary request. The
test runs on every configured transport.
The test fails on the unpatched kernel and passes with the fix applied.
Assisted-by: LLM
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
tests/nvme/073 | 46 ++++++++++++++++++++++++++++++++++++++++++++++
tests/nvme/073.out | 2 ++
2 files changed, 48 insertions(+)
create mode 100755 tests/nvme/073
create mode 100644 tests/nvme/073.out
diff --git a/tests/nvme/073 b/tests/nvme/073
new file mode 100755
index 0000000000000..c6d3d29f4b906
--- /dev/null
+++ b/tests/nvme/073
@@ -0,0 +1,46 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-3.0+
+# Copyright (C) 2026 Andrea Parri
+#
+# Check that the NVMe target rejects a zone report NUMD whose byte count
+# overflows a 32-bit value.
+#
+# Regression test for patch "nvmet: zns: reject a NUMD whose byte count wraps".
+
+. tests/nvme/rc
+. common/null_blk
+
+DESCRIPTION="reject an NVMe target zone report NUMD that wraps"
+QUICK=1
+
+requires() {
+ _nvme_requires
+ _have_loop
+ _require_kernel_nvme_target
+ _have_module null_blk
+ _have_kernel_options BLK_DEV_ZONED
+}
+
+set_conditions() {
+ _set_nvme_trtype "$@"
+}
+
+test() {
+ echo "Running ${TEST_NAME}"
+
+ local dev
+
+ _setup_nvmet
+ _register_test_cleanup _cleanup_nvmet_zoned_target
+ if ! _nvmet_zoned_target_setup; then
+ return 1
+ fi
+ dev="/dev/$(_find_nvme_ns "$def_subsys_uuid")"
+
+ _check_zone_report "$dev" 64 15 0
+ _check_zone_report "$dev" 64 0x4000000f 2
+
+ _nvmet_zoned_target_cleanup
+
+ echo "Test complete"
+}
diff --git a/tests/nvme/073.out b/tests/nvme/073.out
new file mode 100644
index 0000000000000..ed6690e578688
--- /dev/null
+++ b/tests/nvme/073.out
@@ -0,0 +1,2 @@
+Running nvme/073
+Test complete
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH blktests 1/4] nvme/rc: add zoned null_blk target helpers
2026-10-14 17:00 ` [PATCH blktests 1/4] nvme/rc: add zoned null_blk target helpers Andrea Parri
@ 2026-10-20 10:31 ` Shin'ichiro Kawasaki
0 siblings, 0 replies; 11+ messages in thread
From: Shin'ichiro Kawasaki @ 2026-10-20 10:31 UTC (permalink / raw)
To: Andrea Parri
Cc: linux-block, Christoph Hellwig, Keith Busch, Sagi Grimberg,
Chaitanya Kulkarni, Daniel Wagner, linux-nvme
On Oct 14, 2026 / 19:00, Andrea Parri wrote:
> Add helpers that export a zoned null_blk device as a namespace of the
> default subsystem and connect to it, tear that setup down again, and
> issue a partial Zone Management Receive and check its completion status.
>
> The zone report tests added next share them.
>
> Assisted-by: LLM
> Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
> ---
> tests/nvme/rc | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 61 insertions(+)
>
> diff --git a/tests/nvme/rc b/tests/nvme/rc
> index c415ca1234407..270fad19d61f1 100644
> --- a/tests/nvme/rc
> +++ b/tests/nvme/rc
> @@ -408,6 +408,67 @@ _nvmet_passthru_target_cleanup() {
> _remove_nvmet_host "${def_hostnqn}"
> }
>
> +# Export a zoned null_blk device as a namespace of the default subsystem and
> +# connect to it. Callers must source common/null_blk.
> +_nvmet_zoned_target_setup() {
> + if ! _init_null_blk nr_devices=0; then
> + return 1
> + fi
I suggest to remove the three lines above. When null_blk module is loadable,
_configure_null_blk calls _init_null_blk. When null_blk module is built-in,
_init_null_blk just fails and should not be called.
> + if ! _configure_null_blk nullb0 blocksize=512 size=1024 zoned=1 \
I suggest to use nullb1 instead of nullb0. When null_blk module is built-in,
nullb0 device is already created and not usable for blktests.
> + zone_size=1 power=1 >>"$FULL" 2>&1; then
> + echo "could not configure zoned null_blk device"
> + return 1
> + fi
> +
> + _nvmet_target_setup --blkdev none
> + _create_nvmet_ns --blkdev /dev/nullb0 --uuid "$def_subsys_uuid" \
Again, I suggest s/nullb0/nullb1/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH blktests 2/4] nvme/071: test that nvmet sends no data for a failed read
2026-10-14 17:00 ` [PATCH blktests 2/4] nvme/071: test that nvmet sends no data for a failed read Andrea Parri
@ 2026-10-20 10:35 ` Shin'ichiro Kawasaki
2026-10-20 15:17 ` Andrea Parri
0 siblings, 1 reply; 11+ messages in thread
From: Shin'ichiro Kawasaki @ 2026-10-20 10:35 UTC (permalink / raw)
To: Andrea Parri
Cc: linux-block, Christoph Hellwig, Keith Busch, Sagi Grimberg,
Chaitanya Kulkarni, Daniel Wagner, linux-nvme
On Oct 14, 2026 / 19:00, Andrea Parri wrote:
> Issue a failing Identify into a buffer filled with a sentinel and verify
> that the target transfers no data, with a successful Identify and a
> namespace read as controls. The test runs on every configured transport;
> only nvmet-fc failed it before the fix. Add a small helper because
> nvme-cli cannot prefill the data buffer.
>
> The test fails over fc on the unpatched kernel and passes with the fix
> applied.
[...]
> diff --git a/tests/nvme/071 b/tests/nvme/071
> new file mode 100755
> index 0000000000000..5333bfac8db53
> --- /dev/null
> +++ b/tests/nvme/071
> @@ -0,0 +1,80 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-3.0+
> +# Copyright (C) 2026 Andrea Parri
> +#
> +# Check that the NVMe target transfers no read data for a failed command.
> +# The host buffer is filled with a sentinel, so any data the target sends
> +# shows up as changed bytes.
> +#
> +# Regression test for patch "nvmet-fc: only send read data for successful
> +# commands".
> +
> +. tests/nvme/rc
> +
> +DESCRIPTION="verify nvmet does not transfer data for failed reads"
> +QUICK=1
> +
> +requires() {
> + _nvme_requires
> + _have_loop
> + _require_kernel_nvme_target
> + _have_src_program nvme-sentinel-read
> +}
> +
> +set_conditions() {
> + _set_nvme_trtype "$@"
> +}
> +
> +# Issue an Identify with CNS $2 to controller $1 and print the NVMe status
> +# and the number of changed bytes in the 4096-byte data buffer.
> +_identify() {
In most cases, the function names that start with underscore are used for
helper functions in common scripts. This function is used only in this script
file, I suggest a function name that does not start with underscore. How about
issue_identify() ?
> + src/nvme-sentinel-read "$1" 0x06 "$2" 4096 2>>"$FULL"
> +}
> +
> +test() {
> + echo "Running ${TEST_NAME}"
> +
> + local changed ctrl ns status
> +
> + _setup_nvmet
> + # File backing: the namespace read below fails over loop with the
> + # default loop device backing, for reasons unrelated to this test.
> + _nvmet_target_setup --blkdev file
> + _nvme_connect_subsys
> +
> + ctrl=$(_find_nvme_dev "$def_subsysnqn")
> + ns="/dev/${ctrl}n1"
> +
> + # Identify Controller succeeds and must overwrite the sentinel.
> + if ! read -r status changed < <(_identify "/dev/$ctrl" 0x01); then
> + echo "successful Identify could not be issued"
> + else
> + echo "cns=0x01 status=$status changed=$changed" >>"$FULL"
> + if ((status != 0 || changed == 0)); then
> + echo "successful Identify returned status $status with $changed bytes"
> + fi
> + fi
> +
> + # An unsupported CNS fails and must leave the sentinel intact.
> + if ! read -r status changed < <(_identify "/dev/$ctrl" 0xff); then
> + echo "failed Identify could not be issued"
> + else
> + echo "cns=0xff status=$status changed=$changed" >>"$FULL"
> + if (((status & 0x7ff) != 2)); then
> + echo "failed Identify returned status $status, expected 0x2"
> + fi
> + if ((changed != 0)); then
> + echo "failed Identify transferred data: $changed bytes changed"
> + fi
> + fi
Do we really need this dd part below? As far as I understand, the core part of
this test case is Identify command part above.
> +
> + if ! dd if="$ns" of=/dev/null bs=4096 count=256 iflag=direct \
> + status=none >>"$FULL" 2>&1; then
> + echo "namespace read failed"
> + fi
> +
> + _nvme_disconnect_subsys
> + _nvmet_target_cleanup
> +
> + echo "Test complete"
> +}
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH blktests 3/4] nvme/072: test nvmet zone report initialization and length
2026-10-14 17:00 ` [PATCH blktests 3/4] nvme/072: test nvmet zone report initialization and length Andrea Parri
@ 2026-10-20 10:38 ` Shin'ichiro Kawasaki
0 siblings, 0 replies; 11+ messages in thread
From: Shin'ichiro Kawasaki @ 2026-10-20 10:38 UTC (permalink / raw)
To: Andrea Parri
Cc: linux-block, Christoph Hellwig, Keith Busch, Sagi Grimberg,
Chaitanya Kulkarni, Daniel Wagner, linux-nvme
On Oct 14, 2026 / 19:00, Andrea Parri wrote:
> Export a zoned null_blk device through the NVMe target and verify that
> Zone Management Receive zeroes reserved and unused report bytes and
> rejects a transfer length inconsistent with NUMD. The test runs on every
> configured transport, with RDMA restricted to 4 KiB page kernels so that
> the preparatory write exceeds the default inline-data limit. Add a small
> passthrough helper because nvme-cli does not expose the reserved report
> bytes.
>
> The test fails on the unpatched kernel and passes with the fix applied.
>
> Assisted-by: LLM
> Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
[...]
> diff --git a/tests/nvme/072 b/tests/nvme/072
> new file mode 100755
> index 0000000000000..c7bcf93ab4d20
> --- /dev/null
> +++ b/tests/nvme/072
> @@ -0,0 +1,68 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-3.0+
> +# Copyright (C) 2026 Andrea Parri
> +#
> +# Check that NVMe target zone reports are fully initialized and that their
> +# transfer length matches NUMD.
> +#
> +# Regression test for patch "nvmet: zns: don't leak stale memory in zone
> +# management receive".
> +
> +. tests/nvme/rc
> +. common/null_blk
> +
> +DESCRIPTION="check NVMe target zone report initialization and length"
> +QUICK=1
> +
> +requires() {
> + _nvme_requires
> + _have_loop
> + _require_kernel_nvme_target
> + _have_module null_blk
I suggest to call _have_null_blk instead of the line above. It will allow
built-in null_blk module.
> + _have_kernel_options BLK_DEV_ZONED
> + _have_src_program nvme-zone-report
> +
> + # RDMA defaults to PAGE_SIZE bytes of inline data. The helper's
> + # 8 KiB preparatory write must exceed that limit.
> + if [[ "$nvme_trtype" == "rdma" ]]; then
> + _page_size_equals 4096
> + fi
> +}
> +
> +set_conditions() {
> + _set_nvme_trtype "$@"
> +}
> +
> +test() {
> + echo "Running ${TEST_NAME}"
> +
> + local block_size dev last_sector ret sectors slba zone_sectors
> +
> + _setup_nvmet
> + _register_test_cleanup _cleanup_nvmet_zoned_target
> + if ! _nvmet_zoned_target_setup; then
> + return 1
> + fi
> + dev="/dev/$(_find_nvme_ns "$def_subsys_uuid")"
> +
> + sectors=$(cat /sys/block/nullb0/size)
> + zone_sectors=$(cat /sys/block/nullb0/queue/chunk_sectors)
> + block_size=$(cat /sys/block/nullb0/queue/logical_block_size)
Again, I suggest s/nullb0/nullb1/
> + last_sector=$(((sectors - 1) / zone_sectors * zone_sectors))
> + slba=$((last_sector * 512 / block_size))
> +
> + src/nvme-zone-report "$dev" "$slba" >>"$FULL" 2>&1
> + ret=$?
> + case $ret in
> + 0) ;;
> + 1) echo "zone report contained stale data" ;;
> + *) echo "zone report checker failed" ;;
> + esac
> +
> + _check_zone_report "$dev" 4096 1023 0
> + _check_zone_report "$dev" 8192 1023 15
> +
> + _nvmet_zoned_target_cleanup
> +
> + echo "Test complete"
> +}
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests
2026-10-14 17:00 [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Andrea Parri
` (3 preceding siblings ...)
2026-10-14 17:00 ` [PATCH blktests 4/4] nvme/073: test that nvmet rejects a zone report NUMD that wraps Andrea Parri
@ 2026-10-20 10:41 ` Shin'ichiro Kawasaki
2026-10-20 15:18 ` Andrea Parri
4 siblings, 1 reply; 11+ messages in thread
From: Shin'ichiro Kawasaki @ 2026-10-20 10:41 UTC (permalink / raw)
To: Andrea Parri
Cc: linux-block, Christoph Hellwig, Keith Busch, Sagi Grimberg,
Chaitanya Kulkarni, Daniel Wagner, linux-nvme
On Oct 14, 2026 / 19:00, Andrea Parri wrote:
> Add regression tests for three nvmet bugs fixed by the kernel series
> "nvmet: fix stale data exposure and ZNS validation" [1]:
>
> nvme/071 "nvmet-fc: only send read data for successful commands"
> nvme/072 "nvmet: zns: don't leak stale memory in zone management receive"
> nvme/073 "nvmet: zns: reject a NUMD whose byte count wraps"
>
> The nvme/072 fix is already applied to nvme-7.4; the other two are still
> under review.
>
> Patch 1 adds the zoned null_blk target setup, cleanup and zone report
> status check that nvme/072 and nvme/073 share. Patches 2-4 add one test
> each, with a small C helper for nvme/071 and nvme/072 where nvme-cli
> cannot express the check.
>
> All three tests run on every configured transport. On a kernel without
> the fixes, nvme/072 and nvme/073 fail over loop, tcp, rdma and fc, and
> nvme/071 fails over fc, the only affected transport. All three pass
> with the fixes applied.
>
> The tests take the next free numbers, nvme/071 to nvme/073. If other
> nvme tests land first, I'm happy to renumber them and resend.
>
> [1] https://lore.kernel.org/r/20261012141239.156408-1-parri.andrea@gmail.com
Andrea, thank you for this series. I ran the new test cases and they work as
expected. They all failed with the kernel v7.3, and they all passed with the
kernel fix patch series.
Overall, the patches in this series look good to me. I made some comments for
further improvement. Please check and see if they make sense for you.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH blktests 2/4] nvme/071: test that nvmet sends no data for a failed read
2026-10-20 10:35 ` Shin'ichiro Kawasaki
@ 2026-10-20 15:17 ` Andrea Parri
0 siblings, 0 replies; 11+ messages in thread
From: Andrea Parri @ 2026-10-20 15:17 UTC (permalink / raw)
To: Shin'ichiro Kawasaki
Cc: Andrea Parri, linux-block, Christoph Hellwig, Keith Busch,
Sagi Grimberg, Chaitanya Kulkarni, Daniel Wagner, linux-nvme
On Tue, Oct 20, 2026 at 07:35:53PM +0900, Shin'ichiro Kawasaki wrote:
> > +# Issue an Identify with CNS $2 to controller $1 and print the NVMe status
> > +# and the number of changed bytes in the 4096-byte data buffer.
> > +_identify() {
>
> In most cases, the function names that start with underscore are used for
> helper functions in common scripts. This function is used only in this script
> file, I suggest a function name that does not start with underscore. How about
> issue_identify() ?
Sure, issue_identify() it is.
> Do we really need this dd part below? As far as I understand, the core part of
> this test case is Identify command part above.
No, we don't. It was meant as a control that the fc fix still sends
data for successful reads, but the successful Identify above already
checks that: it must overwrite the whole 4096-byte buffer. I'll drop
the read, and with it the file backing, which was there only because
the read failed over the loop transport with the default backing.
Thanks,
Andrea
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests
2026-10-20 10:41 ` [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Shin'ichiro Kawasaki
@ 2026-10-20 15:18 ` Andrea Parri
0 siblings, 0 replies; 11+ messages in thread
From: Andrea Parri @ 2026-10-20 15:18 UTC (permalink / raw)
To: Shin'ichiro Kawasaki
Cc: Andrea Parri, linux-block, Christoph Hellwig, Keith Busch,
Sagi Grimberg, Chaitanya Kulkarni, Daniel Wagner, linux-nvme
On Tue, Oct 20, 2026 at 07:41:42PM +0900, Shin'ichiro Kawasaki wrote:
> Andrea, thank you for this series. I ran the new test cases and they work as
> expected. They all failed with the kernel v7.3, and they all passed with the
> kernel fix patch series.
>
> Overall, the patches in this series look good to me. I made some comments for
> further improvement. Please check and see if they make sense for you.
Thanks for testing and reviewing them. All of your comments make sense
to me, and I'll address them in v2: configure nullb1 without calling
_init_null_blk(), and use _have_null_blk, so that the tests also run
with a built-in null_blk; rename _identify() to issue_identify(); and
drop the namespace read from the Identify test.
nvme/071 has been taken in the meantime, so v2 will also renumber the
tests to nvme/072-074.
Thanks,
Andrea
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-20 15:19 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 17:00 [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Andrea Parri
2026-10-14 17:00 ` [PATCH blktests 1/4] nvme/rc: add zoned null_blk target helpers Andrea Parri
2026-10-20 10:31 ` Shin'ichiro Kawasaki
2026-10-14 17:00 ` [PATCH blktests 2/4] nvme/071: test that nvmet sends no data for a failed read Andrea Parri
2026-10-20 10:35 ` Shin'ichiro Kawasaki
2026-10-20 15:17 ` Andrea Parri
2026-10-14 17:00 ` [PATCH blktests 3/4] nvme/072: test nvmet zone report initialization and length Andrea Parri
2026-10-20 10:38 ` Shin'ichiro Kawasaki
2026-10-14 17:00 ` [PATCH blktests 4/4] nvme/073: test that nvmet rejects a zone report NUMD that wraps Andrea Parri
2026-10-20 10:41 ` [PATCH blktests 0/4] nvme: add nvmet zone report and FC regression tests Shin'ichiro Kawasaki
2026-10-20 15:18 ` Andrea Parri
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox