From: "Denis V. Lunev" <den@openvz.org>
To: svt-core@virtuozzo.com
Cc: andrey.drobyshev@virtuozzo.com, den@openvz.org
Subject: [PATCH hci-8.0 5/5] iotests/nbd-commands: cover the command flags and sparse replies #VSTOR-119829
Date: Mon, 31 Aug 2026 17:25:28 +0200 [thread overview]
Message-ID: <20260831152528.1350583-6-den@openvz.org> (raw)
In-Reply-To: <20260831152528.1350583-1-den@openvz.org>
From: Denis V. Lunev <den@openvz.org>
No test sends the NBD command flags, so the server paths behind them
go unexercised. Send them from the new client.
A structured read answers a hole with an offset and a length rather
than a cluster of zeroes. Lay out data, a hole and data again, and
read the three arrangements a hole can appear in, since a leading hole
and a hole between two extents take different turns through
nbd_co_send_sparse_read(). NBD_CMD_FLAG_DF asks for one chunk instead,
which is the same layout sent as real zeroes. Check the chunk
boundaries in both forms, and that the data still reads back.
NBD_CMD_FLAG_REQ_ONE caps the extent array at one entry, so the block
status reply covers only the first cluster of the three asked about.
That a reply may describe less than was requested is also how the
server keeps NBD_MAX_BLOCK_STATUS_EXTENTS from being exceeded on a
long fragmented range, and a client assuming full coverage believes
stale status.
NBD_CMD_FLAG_FAST_ZERO becomes BDRV_REQ_NO_FALLBACK. A cluster aligned
zero can be done by marking the cluster, while zeroing part of a
cluster over a backing file needs the read modify write the flag
forbids, so check both the success and the ENOTSUP.
NBD_CMD_FLAG_FUA is added to a write and a trim.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Eric Blake <eblake@redhat.com>
Message-ID: <20260827161002.310688-6-den@openvz.org>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
tests/qemu-iotests/tests/nbd-commands | 72 ++++++++++++++++++++++-
tests/qemu-iotests/tests/nbd-commands.out | 4 +-
2 files changed, 71 insertions(+), 5 deletions(-)
diff --git a/tests/qemu-iotests/tests/nbd-commands b/tests/qemu-iotests/tests/nbd-commands
index 09e811724b1..adfd4a495cb 100755
--- a/tests/qemu-iotests/tests/nbd-commands
+++ b/tests/qemu-iotests/tests/nbd-commands
@@ -25,6 +25,7 @@ nbd: ModuleType
DEPTH_LOCAL = 1
DEPTH_BACKING = 2
+CLUSTER = 65536
class TestNbdCommands(iotests.QMPTestCase):
@@ -90,7 +91,7 @@ class TestNbdCommands(iotests.QMPTestCase):
self.h.shutdown()
self.h = None
- def block_status(self, count=size, wanted=None):
+ def block_status(self, count=size, wanted=None, flags=0):
"""Map each meta context in the reply to its list of extents."""
reply = {}
@@ -99,9 +100,9 @@ class TestNbdCommands(iotests.QMPTestCase):
entries[1::2]))
if wanted is None:
- self.h.block_status(count, 0, cb)
+ self.h.block_status(count, 0, cb, flags)
else:
- self.h.block_status_filter(count, 0, wanted, cb)
+ self.h.block_status_filter(count, 0, wanted, cb, flags)
return reply
def top_extents(self):
@@ -158,6 +159,71 @@ class TestNbdCommands(iotests.QMPTestCase):
for wanted in (['base:allocation'], ['qemu:allocation-depth']):
self.assertEqual(sorted(self.block_status(wanted=wanted)), wanted)
+ def read_chunks(self, count, offset, flags=0):
+ chunks = []
+
+ def cb(subbuf, off, status, _err):
+ chunks.append((off, len(subbuf), status))
+
+ self.h.pread_structured(count, offset, cb, flags)
+ return chunks
+
+ def make_sparse(self):
+ """Lay out data, a hole and data again, one cluster each."""
+ self.h.pwrite(b'z' * CLUSTER, 0)
+ self.h.zero(CLUSTER, CLUSTER)
+ self.h.pwrite(b'z' * CLUSTER, 2 * CLUSTER)
+
+ def test_read_sparse_chunks(self):
+ self.make_sparse()
+
+ first = (0, CLUSTER, nbd.READ_DATA)
+ hole = (CLUSTER, CLUSTER, nbd.READ_HOLE)
+ second = (2 * CLUSTER, CLUSTER, nbd.READ_DATA)
+
+ # A hole is an offset and a length, not a cluster of zeroes,
+ # wherever it falls in the reply
+ self.assertEqual(self.read_chunks(2 * CLUSTER, 0), [first, hole])
+ self.assertEqual(self.read_chunks(2 * CLUSTER, CLUSTER),
+ [hole, second])
+ self.assertEqual(self.read_chunks(3 * CLUSTER, 0),
+ [first, hole, second])
+
+ self.assertEqual(self.h.pread(CLUSTER, CLUSTER), bytes(CLUSTER))
+ self.assertEqual(self.h.pread(CLUSTER, 0), b'z' * CLUSTER)
+
+ def test_read_dont_fragment(self):
+ self.make_sparse()
+
+ self.assertEqual(self.read_chunks(3 * CLUSTER, 0, nbd.CMD_FLAG_DF),
+ [(0, 3 * CLUSTER, nbd.READ_DATA)])
+
+ def test_block_status_req_one(self):
+ hole = nbd.STATE_HOLE | nbd.STATE_ZERO
+ self.make_sparse()
+
+ alloc = self.block_status(3 * CLUSTER)['base:allocation']
+ self.assertEqual(alloc, [(CLUSTER, 0), (CLUSTER, hole), (CLUSTER, 0)])
+
+ # One extent, so the reply covers less than was asked for and the
+ # client has to come back for the rest
+ alloc = self.block_status(3 * CLUSTER, flags=nbd.CMD_FLAG_REQ_ONE)
+ self.assertEqual(alloc['base:allocation'], [(CLUSTER, 0)])
+
+ def test_write_and_trim_fua(self):
+ self.h.pwrite(b'y' * 4096, 4096, nbd.CMD_FLAG_FUA)
+ self.assertEqual(self.h.pread(4096, 4096), b'y' * 4096)
+ self.h.trim(4096, 4096, nbd.CMD_FLAG_FUA)
+
+ def test_fast_zero(self):
+ self.h.zero(CLUSTER, CLUSTER, nbd.CMD_FLAG_FAST_ZERO)
+ self.assertEqual(self.h.pread(CLUSTER, CLUSTER), bytes(CLUSTER))
+
+ # Zeroing part of a cluster needs the fallback the flag forbids
+ with self.assertRaises(nbd.Error) as caught:
+ self.h.zero(4096, 4096, nbd.CMD_FLAG_FAST_ZERO)
+ self.assertEqual(caught.exception.errno, 'ENOTSUP')
+
def test_cache_past_end_of_export(self):
self.assertRaises(nbd.Error, self.h.cache, size + 1, 0)
diff --git a/tests/qemu-iotests/tests/nbd-commands.out b/tests/qemu-iotests/tests/nbd-commands.out
index 2f7d3902f23..281b69efeac 100644
--- a/tests/qemu-iotests/tests/nbd-commands.out
+++ b/tests/qemu-iotests/tests/nbd-commands.out
@@ -1,5 +1,5 @@
-.......
+............
----------------------------------------------------------------------
-Ran 7 tests
+Ran 12 tests
OK
--
2.53.0
next prev parent reply other threads:[~2026-08-31 15:26 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 15:25 [PATCH hci-8.0 0/5] nbd/server: accept a large NBD_CMD_CACHE #VSTOR-119829 Denis V. Lunev
2026-08-31 15:25 ` [PATCH hci-8.0 1/5] iotests: add coverage for NBD transmission commands #VSTOR-119829 Denis V. Lunev
2026-09-01 15:51 ` Andrey Drobyshev
2026-08-31 15:25 ` [PATCH hci-8.0 2/5] nbd/server: accept NBD_CMD_CACHE above the maximum payload size #VSTOR-119829 Denis V. Lunev
2026-08-31 15:25 ` [PATCH hci-8.0 3/5] iotests/nbd-commands: exercise the simple and structured reply modes #VSTOR-119829 Denis V. Lunev
2026-08-31 15:25 ` [PATCH hci-8.0 4/5] iotests/nbd-commands: cover NBD_CMD_BLOCK_STATUS with a payload #VSTOR-119829 Denis V. Lunev
2026-08-31 15:25 ` Denis V. Lunev [this message]
2026-09-01 15:50 ` [QEMU HCI-8.0 PATCH 6/5] VZ: iotests/nbd-commands: do not depend on cluster allocation order #VSTOR-119829 Andrey Drobyshev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260831152528.1350583-6-den@openvz.org \
--to=den@openvz.org \
--cc=andrey.drobyshev@virtuozzo.com \
--cc=svt-core@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.