From: Nir Soffer <nirsof@gmail.com>
To: qemu-devel@nongnu.org
Cc: Hanna Reitz <hreitz@redhat.com>, Eric Blake <eblake@redhat.com>,
Markus Armbruster <armbru@redhat.com>,
qemu-block@nongnu.org, Fam Zheng <fam@euphon.net>,
Kevin Wolf <kwolf@redhat.com>, Nir Soffer <nirsof@gmail.com>
Subject: [PATCH 2/2] block/null: Add read-pattern option
Date: Mon, 28 Apr 2025 01:59:00 +0300 [thread overview]
Message-ID: <20250427225900.24316-3-nirsof@gmail.com> (raw)
In-Reply-To: <20250427225900.24316-1-nirsof@gmail.com>
When the `read-zeroes` is set, reads produce zeroes, and block status
return BDRV_BLOCK_ZERO, emulating a sparse image.
If we don't set `read-zeros` we report BDRV_BLOCK_DATA, but image data
is undefined; posix_memalign, _aligned_malloc, valloc, or memalign do
not promise to zero allocated memory.
When computing a blkhash of an image via qemu-nbd, we want to test 3
cases:
1. Sparse image: skip reading the entire image based on block status
result, and use a pre-computed zero block hash.
2. Image full of zeroes: read the entire image, detect block full of
zeroes and skip block hash computation.
3. Image full of data: read the entire image and compute a hash of all
blocks.
This change adds `read-pattern` option. If the option is set, reads
produce the specified pattern. With this option we can emulate an image
full of zeroes or full of non-zeroes.
Specifying both `read-zeroes` and `read-pattern != 0` is not useful
since `read-zeroes` implies a sparse image. In this case `read-zeroes`
wins and we ignore the pattern. Maybe we need to make the options mutual
exclusive.
The following examples shows how the new option can be used with blksum
(or nbdcopy --blkhash) to compute a blkhash of an image using the
null-co driver.
Sparse image - the very fast path:
% ./qemu-nbd -r -t -e 0 -f raw -k /tmp/sparse.sock \
"json:{'driver': 'raw', 'file': {'driver': 'null-co', 'size': '100g', 'read-zeroes': true}}" &
% time blksum 'nbd+unix:///?socket=/tmp/sparse.sock'
300ad1efddb063822fea65ae3174cd35320939d4d0b050613628c6e1e876f8f6 nbd+unix:///?socket=/tmp/sparse.sock
blksum 'nbd+unix:///?socket=/tmp/sparse.sock' 0.05s user 0.01s system 92% cpu 0.061 total
Image full of zeros - same hash, 268 times slower:
% ./qemu-nbd -r -t -e 0 -f raw -k /tmp/zero.sock \
"json:{'driver': 'raw', 'file': {'driver': 'null-co', 'size': '100g', 'read-pattern': 0}}" &
% time blksum 'nbd+unix:///?socket=/tmp/zero.sock'
300ad1efddb063822fea65ae3174cd35320939d4d0b050613628c6e1e876f8f6 nbd+unix:///?socket=/tmp/zero.sock
blksum 'nbd+unix:///?socket=/tmp/zero.sock' 7.45s user 22.57s system 183% cpu 16.347 total
Image full of data - difference hash, heavy cpu usage:
% ./qemu-nbd -r -t -e 0 -f raw -k /tmp/data.sock \
"json:{'driver': 'raw', 'file': {'driver': 'null-co', 'size': '100g', 'read-pattern': -1}}" &
% time blksum 'nbd+unix:///?socket=/tmp/data.sock'
2c122b3ed28c83ede3c08485659fa9b56ee54ba1751db74d8ba9aa13d9866432 nbd+unix:///?socket=/tmp/data.sock
blksum 'nbd+unix:///?socket=/tmp/data.sock' 46.05s user 14.15s system 448% cpu 13.414 total
Tested on top of
https://lists.gnu.org/archive/html/qemu-devel/2025-04/msg05096.html.
Signed-off-by: Nir Soffer <nirsof@gmail.com>
---
block/null.c | 17 +++++++++++++++++
qapi/block-core.json | 9 ++++++++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/block/null.c b/block/null.c
index 7ba87bd9a9..cbd7d1fdbd 100644
--- a/block/null.c
+++ b/block/null.c
@@ -22,11 +22,14 @@
#define NULL_OPT_LATENCY "latency-ns"
#define NULL_OPT_ZEROES "read-zeroes"
+#define NULL_OPT_PATTERN "read-pattern"
typedef struct {
int64_t length;
int64_t latency_ns;
bool read_zeroes;
+ bool has_read_pattern;
+ int read_pattern;
} BDRVNullState;
static QemuOptsList runtime_opts = {
@@ -49,6 +52,11 @@ static QemuOptsList runtime_opts = {
.type = QEMU_OPT_BOOL,
.help = "return zeroes when read",
},
+ {
+ .name = NULL_OPT_PATTERN,
+ .type = QEMU_OPT_NUMBER,
+ .help = "return pattern when read",
+ },
{ /* end of list */ }
},
};
@@ -95,6 +103,10 @@ static int null_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EINVAL;
}
s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false);
+ s->has_read_pattern = qemu_opt_find(opts, NULL_OPT_PATTERN) != NULL;
+ if (s->has_read_pattern) {
+ s->read_pattern = qemu_opt_get_number(opts, NULL_OPT_PATTERN, 0);
+ }
qemu_opts_del(opts);
bs->supported_write_flags = BDRV_REQ_FUA;
return ret;
@@ -125,6 +137,8 @@ static coroutine_fn int null_co_preadv(BlockDriverState *bs,
if (s->read_zeroes) {
qemu_iovec_memset(qiov, 0, 0, bytes);
+ } else if (s->has_read_pattern) {
+ qemu_iovec_memset(qiov, 0, s->read_pattern, bytes);
}
return null_co_common(bs);
@@ -199,6 +213,8 @@ static BlockAIOCB *null_aio_preadv(BlockDriverState *bs,
if (s->read_zeroes) {
qemu_iovec_memset(qiov, 0, 0, bytes);
+ } else if (s->has_read_pattern) {
+ qemu_iovec_memset(qiov, 0, s->read_pattern, bytes);
}
return null_aio_common(bs, cb, opaque);
@@ -272,6 +288,7 @@ null_co_get_allocated_file_size(BlockDriverState *bs)
static const char *const null_strong_runtime_opts[] = {
BLOCK_OPT_SIZE,
NULL_OPT_ZEROES,
+ NULL_OPT_PATTERN,
NULL
};
diff --git a/qapi/block-core.json b/qapi/block-core.json
index b1937780e1..7d576cccbb 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3297,10 +3297,17 @@
# false, the buffer is left unchanged.
# (default: false; since: 4.1)
#
+# @read-pattern: if set, reads from the device produce the specified
+# pattern; if unset, the buffer is left unchanged.
+# (since: 10.1)
+#
# Since: 2.9
##
{ 'struct': 'BlockdevOptionsNull',
- 'data': { '*size': 'int', '*latency-ns': 'uint64', '*read-zeroes': 'bool' } }
+ 'data': { '*size': 'int',
+ '*latency-ns': 'uint64',
+ '*read-zeroes': 'bool',
+ '*read-pattern': 'int' } }
##
# @BlockdevOptionsNVMe:
--
2.39.5 (Apple Git-154)
next prev parent reply other threads:[~2025-04-27 23:00 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-27 22:58 [PATCH 0/2] block/null: Add read-pattern Nir Soffer
2025-04-27 22:58 ` [PATCH 1/2] block/null: Report DATA if not reading zeroes Nir Soffer
2025-04-28 21:49 ` Eric Blake
2025-04-30 19:46 ` Nir Soffer
2025-04-27 22:59 ` Nir Soffer [this message]
2025-04-28 21:55 ` [PATCH 2/2] block/null: Add read-pattern option Eric Blake
2025-04-29 6:00 ` Markus Armbruster
2025-04-30 19:58 ` Nir Soffer
2025-04-30 19:54 ` Nir Soffer
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=20250427225900.24316-3-nirsof@gmail.com \
--to=nirsof@gmail.com \
--cc=armbru@redhat.com \
--cc=eblake@redhat.com \
--cc=fam@euphon.net \
--cc=hreitz@redhat.com \
--cc=kwolf@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/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.