Re: [PATCH v6 14/14] python: use vm.cmd() instead of vm.qmp() where appropriate

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:50PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> In many cases we just want an effect of qmp command and want to raise on
> failure. Use vm.cmd() method which does exactly this.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  tests/avocado/vnc.py  |  16 +-
>  tests/qemu-iotests/030| 168 +++---
>  tests/qemu-iotests/040| 172 +++
>  tests/qemu-iotests/041| 483 --
>  tests/qemu-iotests/045|  15 +-
>  tests/qemu-iotests/055|  62 +--
>  tests/qemu-iotests/056|  77 ++-
>  tests/qemu-iotests/093|  42 +-
>  tests/qemu-iotests/118| 225 
>  tests/qemu-iotests/124| 102 ++--
>  tests/qemu-iotests/129|  14 +-
>  tests/qemu-iotests/132|   5 +-
>  tests/qemu-iotests/139|  45 +-
>  tests/qemu-iotests/147|  31 +-
>  tests/qemu-iotests/151|  57 +--
>  tests/qemu-iotests/152|  10 +-
>  tests/qemu-iotests/155|  55 +-
>  tests/qemu-iotests/165|   8 +-
>  tests/qemu-iotests/196|   3 +-
>  tests/qemu-iotests/205|   6 +-
>  tests/qemu-iotests/218| 105 ++--
>  tests/qemu-iotests/245| 245 -
>  tests/qemu-iotests/264|  31 +-
>  tests/qemu-iotests/281|  21 +-
>  tests/qemu-iotests/295|  15 +-
>  tests/qemu-iotests/296|  15 +-
>  tests/qemu-iotests/298|  13 +-
>  tests/qemu-iotests/300|  54 +-
>  tests/qemu-iotests/iotests.py |   9 +-
>  .../tests/export-incoming-iothread|   6 +-
>  .../qemu-iotests/tests/graph-changes-while-io |   6 +-
>  tests/qemu-iotests/tests/image-fleecing   |   3 +-
>  .../tests/migrate-bitmaps-postcopy-test   |  31 +-
>  tests/qemu-iotests/tests/migrate-bitmaps-test |  47 +-
>  .../qemu-iotests/tests/migrate-during-backup  |  41 +-
>  .../qemu-iotests/tests/migration-permissions  |   9 +-
>  .../tests/mirror-ready-cancel-error   |  74 ++-
>  tests/qemu-iotests/tests/mirror-top-perms |  16 +-
>  tests/qemu-iotests/tests/nbd-multiconn|  12 +-
>  tests/qemu-iotests/tests/reopen-file  |   3 +-
>  .../qemu-iotests/tests/stream-error-on-reset  |   6 +-
>  41 files changed, 951 insertions(+), 1407 deletions(-)

Big but mechanical.  It would be worth amending the commit message to
describe how you found all these spots (in case someone backporting
this patch has to redo the work over a different subset of files based
on what has changed since the two trees diverged).

> 
> diff --git a/tests/avocado/vnc.py b/tests/avocado/vnc.py
> index aeeefc70be..862c8996a8 100644
> --- a/tests/avocado/vnc.py
> +++ b/tests/avocado/vnc.py
> @@ -88,9 +88,8 @@ def test_change_password(self):
>  self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password=on')
>  self.vm.launch()
>  self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled'])
> -set_password_response = self.vm.qmp('change-vnc-password',
> -password='new_password')
> -self.assertEqual(set_password_response['return'], {})
> +self.vm.cmd('change-vnc-password',
> +password='new_password')

Indeed a nicer idiom, where you are able to use it (whether by
self.assertEqual or by self.assert_qmp).

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 13/14] tests/vm/basevm.py: use cmd() instead of qmp()

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:49PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> We don't expect failure here and need 'result' object. cmd() is better
> in this case.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  tests/vm/basevm.py | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py
> index a97e23b0ce..8aef4cff96 100644
> --- a/tests/vm/basevm.py
> +++ b/tests/vm/basevm.py
> @@ -312,8 +312,8 @@ def boot(self, img, extra_args=[]):
>  self._guest = guest
>  # Init console so we can start consuming the chars.
>  self.console_init()
> -usernet_info = guest.qmp("human-monitor-command",
> - command_line="info usernet").get("return")
> +usernet_info = guest.cmd("human-monitor-command",
> + command_line="info usernet")
>  self.ssh_port = get_info_usernet_hostfwd_port(usernet_info)

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 12/14] iotests.py: pause_job(): drop return value

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> The returned value is unused. It's simple to check by command
> 
>  git grep -B 3 '\.pause_job('
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  tests/qemu-iotests/iotests.py | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 11/14] iotests: drop some extra ** in qmp() call

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:47PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> qmp() method supports passing dict (if it doesn't need substituting
> '_' to '-' in keys). So, drop some extra '**' operators.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  tests/qemu-iotests/040|  4 +-
>  tests/qemu-iotests/041| 14 +++---
>  tests/qemu-iotests/129|  2 +-
>  tests/qemu-iotests/147|  2 +-
>  tests/qemu-iotests/155|  2 +-
>  tests/qemu-iotests/264| 12 ++---
>  tests/qemu-iotests/295|  5 +-
>  tests/qemu-iotests/296| 15 +++---
>  tests/qemu-iotests/tests/migrate-bitmaps-test |  4 +-
>  .../tests/mirror-ready-cancel-error   | 50 +--
>  10 files changed, 54 insertions(+), 56 deletions(-)
>

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 10/14] iotests: drop some occasional semicolons

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:46PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  tests/qemu-iotests/041 | 2 +-
>  tests/qemu-iotests/196 | 2 +-
>  2 files changed, 2 insertions(+), 2 deletions(-)

Maybe in the subject s/occasional/extra/

Reviewed-by: Eric Blake 

> 
> diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
> index 4d7a829b65..550e4dc391 100755
> --- a/tests/qemu-iotests/041
> +++ b/tests/qemu-iotests/041
> @@ -1086,7 +1086,7 @@ class TestRepairQuorum(iotests.QMPTestCase):
>  def test_after_a_quorum_snapshot(self):
>  result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
>   snapshot_file=quorum_snapshot_file,
> - snapshot_node_name="snap1");
> + snapshot_node_name="snap1")
>  self.assert_qmp(result, 'return', {})

I'm a bit surprised the linters don't pick up on this.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 09/14] iotests: refactor some common qmp result checks into generic pattern

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:45PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> To simplify further conversion.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  tests/qemu-iotests/040 | 3 ++-
>  tests/qemu-iotests/147 | 3 ++-
>  tests/qemu-iotests/155 | 4 ++--
>  tests/qemu-iotests/218 | 4 ++--
>  tests/qemu-iotests/296 | 3 ++-
>  5 files changed, 10 insertions(+), 7 deletions(-)
>

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 08/14] iotests: add some missed checks of qmp result

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:44PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  tests/qemu-iotests/041| 1 +
>  tests/qemu-iotests/151| 1 +
>  tests/qemu-iotests/152| 2 ++
>  tests/qemu-iotests/tests/migrate-bitmaps-test | 2 ++
>  4 files changed, 6 insertions(+)
> 

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v2 03/21] preallocate: Don't poll during permission updates

2023-10-05 Thread Vladimir Sementsov-Ogievskiy

On 11.09.23 12:46, Kevin Wolf wrote:

When the permission related BlockDriver callbacks are called, we are in
the middle of an operation traversing the block graph. Polling in such a
place is a very bad idea because the graph could change in unexpected
ways. In the future, callers will also hold the graph lock, which is
likely to turn polling into a deadlock.

So we need to get rid of calls to functions like bdrv_getlength() or
bdrv_truncate() there as these functions poll internally. They are
currently used so that when no parent has write/resize permissions on
the image any more, the preallocate filter drops the extra preallocated
area in the image file and gives up write/resize permissions itself.

In order to achieve this without polling in .bdrv_check_perm, don't
immediately truncate the image, but only schedule a BH to do so. The
filter keeps the write/resize permissions a bit longer now until the BH
has executed.

There is one case in which delaying doesn't work: Reopening the image
read-only. In this case, bs->file will likely be reopened read-only,
too, so keeping write permissions a bit longer on it doesn't work. But
we can already cover this case in preallocate_reopen_prepare() and not
rely on the permission updates for it.


Hmm, now I found one more "future" case.

I now try to rebase my "[PATCH v7 0/7] blockdev-replace" 
https://patchew.org/QEMU/20230421114102.884457-1-vsement...@yandex-team.ru/

And it breaks after this commit.

By accident, blockdev-replace series uses exactly "preallocate" filter to test 
insertion/removing of filters. And removing is broken now.

Removing is done as follows:

1. We have filter inserted: disk0 -file-> filter -file-> file0

2. blockdev-replace, replaces file child of disk0, so we should get the picture*: 
disk0 -file-> file0 <-file- filter

3. blockdev-del filter


But step [2] fails, as now preallocate filter doesn't drop permissions during 
the operation (postponing this for a while) and the picture* is impossible. 
Permission check fails.

Hmmm... Any idea how blockdev-replace and preallocate filter should work :) ? 
Maybe, doing truncation in .drain_begin() will help? Will try



Signed-off-by: Kevin Wolf 
Reviewed-by: Eric Blake 
Reviewed-by: Stefan Hajnoczi 
---



--
Best regards,
Vladimir




Re: [PATCH v6 07/14] iotests: QemuStorageDaemon: add cmd() method like in QEMUMachine.

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:43PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> Add similar method for consistency.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  tests/qemu-iotests/iotests.py | 7 ++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 06/14] python/machine.py: upgrade vm.cmd() method

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:42PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> The method is not popular in iotests, we prefer use vm.qmp() and then
> check success by hand.. But that's not optimal. To simplify movement to

extra '.'

> vm.cmd() let's support same interface improvements like in vm.qmp().
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  python/qemu/machine/machine.py | 12 +++-
>  1 file changed, 11 insertions(+), 1 deletion(-)

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 05/14] python/qemu: rename command() to cmd()

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:41PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> Use a shorter name. We are going to move in iotests from qmp() to
> command() where possible. But command() is longer than qmp() and don't
> look better. Let's rename.
> 
> You can simply grep for '\.command(' and for 'def command(' to check
> that everything is updated (command() in tests/docker/docker.py is
> unrelated).
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> Reviewed-by: Daniel P. Berrangé 
> ---
>  docs/devel/testing.rst|  10 +-

There's a risk that this will need rebasing on top of any other test
changes being made in parallel; hopefully we can get this series in
sooner rather than later.

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 04/14] python: rename QEMUMonitorProtocol.cmd() to cmd_raw()

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:40PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> Having cmd() and command() methods in one class doesn't look good.
> Rename cmd() to cmd_raw(), to show its meaning better.
> 
> We also want to rename command() to cmd() in future, so this commit is
> a necessary step.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  python/qemu/machine/machine.py | 2 +-
>  python/qemu/qmp/legacy.py  | 4 ++--
>  tests/qemu-iotests/iotests.py  | 2 +-
>  3 files changed, 4 insertions(+), 4 deletions(-)

Reviewed-by: Eric Blake 

I was surprised how few users there are, but agree that this opens up
the door to let our common usage be the least typing.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 03/14] scripts/cpu-x86-uarch-abi.py: use .command() instead of .cmd()

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:39PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> Here we don't expect a failure. In case on failure we'll crash on

s/case on/case of/

> trying to access ['return']. Let's better use .command() that clearly
> raise on failure.

Maybe: s/Let's better /Better is to/; s/raise/raises/

> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  scripts/cpu-x86-uarch-abi.py | 12 ++--
>  1 file changed, 6 insertions(+), 6 deletions(-)
>

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




[PULL 15/15] nbd/server: Add FLAG_PAYLOAD support to CMD_BLOCK_STATUS

2023-10-05 Thread Eric Blake
Allow a client to request a subset of negotiated meta contexts.  For
example, a client may ask to use a single connection to learn about
both block status and dirty bitmaps, but where the dirty bitmap
queries only need to be performed on a subset of the disk; forcing the
server to compute that information on block status queries in the rest
of the disk is wasted effort (both at the server, and on the amount of
traffic sent over the wire to be parsed and ignored by the client).

Qemu as an NBD client never requests to use more than one meta
context, so it has no need to use block status payloads.  Testing this
instead requires support from libnbd, which CAN access multiple meta
contexts in parallel from a single NBD connection; an interop test
submitted to the libnbd project at the same time as this patch
demonstrates the feature working, as well as testing some corner cases
(for example, when the payload length is longer than the export
length), although other corner cases (like passing the same id
duplicated) requires a protocol fuzzer because libnbd is not wired up
to break the protocol that badly.

This also includes tweaks to 'qemu-nbd --list' to show when a server
is advertising the capability, and to the testsuite to reflect the
addition to that output.

Of note: qemu will always advertise the new feature bit during
NBD_OPT_INFO if extended headers have alreay been negotiated
(regardless of whether any NBD_OPT_SET_META_CONTEXT negotiation has
occurred); but for NBD_OPT_GO, qemu only advertises the feature if
block status is also enabled (that is, if the client does not
negotiate any contexts, then NBD_CMD_BLOCK_STATUS cannot be used, so
the feature is not advertised).

Signed-off-by: Eric Blake 
Message-ID: <20230925192229.3186470-26-ebl...@redhat.com>
[eblake: fix logic to reject unnegotiated contexts]
Signed-off-by: Eric Blake 
---
 docs/interop/nbd.txt  |   2 +-
 nbd/server.c  | 117 --
 qemu-nbd.c|   1 +
 nbd/trace-events  |   1 +
 tests/qemu-iotests/223.out|  12 +-
 tests/qemu-iotests/307.out|  10 +-
 .../tests/nbd-qemu-allocation.out |   2 +-
 7 files changed, 125 insertions(+), 20 deletions(-)

diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt
index 9aae5e1f294..18efb251de9 100644
--- a/docs/interop/nbd.txt
+++ b/docs/interop/nbd.txt
@@ -69,4 +69,4 @@ NBD_CMD_BLOCK_STATUS for "qemu:dirty-bitmap:", NBD_CMD_CACHE
 NBD_CMD_FLAG_FAST_ZERO
 * 5.2: NBD_CMD_BLOCK_STATUS for "qemu:allocation-depth"
 * 7.1: NBD_FLAG_CAN_MULTI_CONN for shareable writable exports
-* 8.2: NBD_OPT_EXTENDED_HEADERS
+* 8.2: NBD_OPT_EXTENDED_HEADERS, NBD_FLAG_BLOCK_STATUS_PAYLOAD
diff --git a/nbd/server.c b/nbd/server.c
index 2dce9c3ad6a..859c163d19f 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -512,6 +512,9 @@ static int nbd_negotiate_handle_export_name(NBDClient 
*client, bool no_zeroes,
 if (client->mode >= NBD_MODE_STRUCTURED) {
 myflags |= NBD_FLAG_SEND_DF;
 }
+if (client->mode >= NBD_MODE_EXTENDED && client->contexts.count) {
+myflags |= NBD_FLAG_BLOCK_STAT_PAYLOAD;
+}
 trace_nbd_negotiate_new_style_size_flags(client->exp->size, myflags);
 stq_be_p(buf, client->exp->size);
 stw_be_p(buf + 8, myflags);
@@ -699,6 +702,10 @@ static int nbd_negotiate_handle_info(NBDClient *client, 
Error **errp)
 if (client->mode >= NBD_MODE_STRUCTURED) {
 myflags |= NBD_FLAG_SEND_DF;
 }
+if (client->mode >= NBD_MODE_EXTENDED &&
+(client->contexts.count || client->opt == NBD_OPT_INFO)) {
+myflags |= NBD_FLAG_BLOCK_STAT_PAYLOAD;
+}
 trace_nbd_negotiate_new_style_size_flags(exp->size, myflags);
 stq_be_p(buf, exp->size);
 stw_be_p(buf + 8, myflags);
@@ -2420,6 +2427,90 @@ static int coroutine_fn nbd_co_send_bitmap(NBDClient 
*client,
 return nbd_co_send_extents(client, request, ea, last, context_id, errp);
 }

+/*
+ * nbd_co_block_status_payload_read
+ * Called when a client wants a subset of negotiated contexts via a
+ * BLOCK_STATUS payload.  Check the payload for valid length and
+ * contents.  On success, return 0 with request updated to effective
+ * length.  If request was invalid but all payload consumed, return 0
+ * with request->len and request->contexts->count set to 0 (which will
+ * trigger an appropriate NBD_EINVAL response later on).  Return
+ * negative errno if the payload was not fully consumed.
+ */
+static int
+nbd_co_block_status_payload_read(NBDClient *client, NBDRequest *request,
+ Error **errp)
+{
+uint64_t payload_len = request->len;
+g_autofree char *buf = NULL;
+size_t count, i, nr_bitmaps;
+uint32_t id;
+
+if (payload_len > NBD_MAX_BUFFER_SIZE) {
+error_setg(errp, "len (%" PRIu64 ") is larger than max len (%u)",
+   request->len, 

[PULL 06/15] nbd/server: Prepare to send extended header replies

2023-10-05 Thread Eric Blake
Although extended mode is not yet enabled, once we do turn it on, we
need to reply with extended headers to all messages.  Update the low
level entry points necessary so that all other callers automatically
get the right header based on the current mode.

Signed-off-by: Eric Blake 
Reviewed-by: Vladimir Sementsov-Ogievskiy 
Message-ID: <20230925192229.3186470-17-ebl...@redhat.com>
---
 nbd/server.c | 30 ++
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/nbd/server.c b/nbd/server.c
index 501749d62b5..910c48c6467 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1938,8 +1938,6 @@ static inline void set_be_chunk(NBDClient *client, struct 
iovec *iov,
 size_t niov, uint16_t flags, uint16_t type,
 NBDRequest *request)
 {
-/* TODO - handle structured vs. extended replies */
-NBDStructuredReplyChunk *chunk = iov->iov_base;
 size_t i, length = 0;

 for (i = 1; i < niov; i++) {
@@ -1947,12 +1945,26 @@ static inline void set_be_chunk(NBDClient *client, 
struct iovec *iov,
 }
 assert(length <= NBD_MAX_BUFFER_SIZE + sizeof(NBDStructuredReadData));

-iov[0].iov_len = sizeof(*chunk);
-stl_be_p(>magic, NBD_STRUCTURED_REPLY_MAGIC);
-stw_be_p(>flags, flags);
-stw_be_p(>type, type);
-stq_be_p(>cookie, request->cookie);
-stl_be_p(>length, length);
+if (client->mode >= NBD_MODE_EXTENDED) {
+NBDExtendedReplyChunk *chunk = iov->iov_base;
+
+iov[0].iov_len = sizeof(*chunk);
+stl_be_p(>magic, NBD_EXTENDED_REPLY_MAGIC);
+stw_be_p(>flags, flags);
+stw_be_p(>type, type);
+stq_be_p(>cookie, request->cookie);
+stq_be_p(>offset, request->from);
+stq_be_p(>length, length);
+} else {
+NBDStructuredReplyChunk *chunk = iov->iov_base;
+
+iov[0].iov_len = sizeof(*chunk);
+stl_be_p(>magic, NBD_STRUCTURED_REPLY_MAGIC);
+stw_be_p(>flags, flags);
+stw_be_p(>type, type);
+stq_be_p(>cookie, request->cookie);
+stl_be_p(>length, length);
+}
 }

 static int coroutine_fn nbd_co_send_chunk_done(NBDClient *client,
@@ -2512,6 +2524,8 @@ static coroutine_fn int nbd_send_generic_reply(NBDClient 
*client,
 {
 if (client->mode >= NBD_MODE_STRUCTURED && ret < 0) {
 return nbd_co_send_chunk_error(client, request, -ret, error_msg, errp);
+} else if (client->mode >= NBD_MODE_EXTENDED) {
+return nbd_co_send_chunk_done(client, request, errp);
 } else {
 return nbd_co_send_simple_reply(client, request, ret < 0 ? -ret : 0,
 NULL, 0, errp);
-- 
2.41.0




[PULL 05/15] nbd/server: Prepare to receive extended header requests

2023-10-05 Thread Eric Blake
Although extended mode is not yet enabled, once we do turn it on, we
need to accept extended requests for all messages.  Previous patches
have already taken care of supporting 64-bit lengths, now we just need
to read it off the wire.

Note that this implementation will block indefinitely on a buggy
client that sends a non-extended payload (that is, we try to read a
full packet before we ever check the magic number, but a client that
mistakenly sends a simple request after negotiating extended headers
doesn't send us enough bytes), but it's no different from any other
client that stops talking to us partway through a packet and thus not
worth coding around.

Signed-off-by: Eric Blake 
Reviewed-by: Vladimir Sementsov-Ogievskiy 
Message-ID: <20230925192229.3186470-16-ebl...@redhat.com>
---
 nbd/nbd-internal.h |  5 -
 nbd/server.c   | 43 ++-
 2 files changed, 34 insertions(+), 14 deletions(-)

diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index 133b1d94b50..dfa02f77ee4 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -34,8 +34,11 @@
  * https://github.com/yoe/nbd/blob/master/doc/proto.md
  */

-/* Size of all NBD_OPT_*, without payload */
+/* Size of all compact NBD_CMD_*, without payload */
 #define NBD_REQUEST_SIZE(4 + 2 + 2 + 8 + 8 + 4)
+/* Size of all extended NBD_CMD_*, without payload */
+#define NBD_EXTENDED_REQUEST_SIZE   (4 + 2 + 2 + 8 + 8 + 8)
+
 /* Size of all NBD_REP_* sent in answer to most NBD_OPT_*, without payload */
 #define NBD_REPLY_SIZE  (4 + 4 + 8)
 /* Size of reply to NBD_OPT_EXPORT_NAME */
diff --git a/nbd/server.c b/nbd/server.c
index 56e9b41828e..501749d62b5 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1411,11 +1411,13 @@ nbd_read_eof(NBDClient *client, void *buffer, size_t 
size, Error **errp)
 static int coroutine_fn nbd_receive_request(NBDClient *client, NBDRequest 
*request,
 Error **errp)
 {
-uint8_t buf[NBD_REQUEST_SIZE];
-uint32_t magic;
+uint8_t buf[NBD_EXTENDED_REQUEST_SIZE];
+uint32_t magic, expect;
 int ret;
+size_t size = client->mode >= NBD_MODE_EXTENDED ?
+NBD_EXTENDED_REQUEST_SIZE : NBD_REQUEST_SIZE;

-ret = nbd_read_eof(client, buf, sizeof(buf), errp);
+ret = nbd_read_eof(client, buf, size, errp);
 if (ret < 0) {
 return ret;
 }
@@ -1423,13 +1425,21 @@ static int coroutine_fn nbd_receive_request(NBDClient 
*client, NBDRequest *reque
 return -EIO;
 }

-/* Request
-   [ 0 ..  3]   magic   (NBD_REQUEST_MAGIC)
-   [ 4 ..  5]   flags   (NBD_CMD_FLAG_FUA, ...)
-   [ 6 ..  7]   type(NBD_CMD_READ, ...)
-   [ 8 .. 15]   cookie
-   [16 .. 23]   from
-   [24 .. 27]   len
+/*
+ * Compact request
+ *  [ 0 ..  3]   magic   (NBD_REQUEST_MAGIC)
+ *  [ 4 ..  5]   flags   (NBD_CMD_FLAG_FUA, ...)
+ *  [ 6 ..  7]   type(NBD_CMD_READ, ...)
+ *  [ 8 .. 15]   cookie
+ *  [16 .. 23]   from
+ *  [24 .. 27]   len
+ * Extended request
+ *  [ 0 ..  3]   magic   (NBD_EXTENDED_REQUEST_MAGIC)
+ *  [ 4 ..  5]   flags   (NBD_CMD_FLAG_FUA, NBD_CMD_FLAG_PAYLOAD_LEN, ...)
+ *  [ 6 ..  7]   type(NBD_CMD_READ, ...)
+ *  [ 8 .. 15]   cookie
+ *  [16 .. 23]   from
+ *  [24 .. 31]   len
  */

 magic = ldl_be_p(buf);
@@ -1437,13 +1447,20 @@ static int coroutine_fn nbd_receive_request(NBDClient 
*client, NBDRequest *reque
 request->type   = lduw_be_p(buf + 6);
 request->cookie = ldq_be_p(buf + 8);
 request->from   = ldq_be_p(buf + 16);
-request->len= (uint32_t)ldl_be_p(buf + 24); /* widen 32 to 64 bits */
+if (client->mode >= NBD_MODE_EXTENDED) {
+request->len = ldq_be_p(buf + 24);
+expect = NBD_EXTENDED_REQUEST_MAGIC;
+} else {
+request->len = (uint32_t)ldl_be_p(buf + 24); /* widen 32 to 64 bits */
+expect = NBD_REQUEST_MAGIC;
+}

 trace_nbd_receive_request(magic, request->flags, request->type,
   request->from, request->len);

-if (magic != NBD_REQUEST_MAGIC) {
-error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", magic);
+if (magic != expect) {
+error_setg(errp, "invalid magic (got 0x%" PRIx32 ", expected 0x%"
+   PRIx32 ")", magic, expect);
 return -EINVAL;
 }
 return 0;
-- 
2.41.0




[PULL 11/15] nbd/client: Accept 64-bit block status chunks

2023-10-05 Thread Eric Blake
Once extended mode is enabled, we need to accept 64-bit status replies
(even for replies that don't exceed a 32-bit length).  It is easier to
normalize narrow replies into wide format so that the rest of our code
only has to handle one width.  Although a server is non-compliant if
it sends a 64-bit reply in compact mode, or a 32-bit reply in extended
mode, it is still easy enough to tolerate these mismatches.

In normal execution, we are only requesting "base:allocation" which
never exceeds 32 bits for flag values. But during testing with
x-dirty-bitmap, we can force qemu to connect to some other context
that might have 64-bit status bit; however, we ignore those upper bits
(other than mapping qemu:allocation-depth into something that
'qemu-img map --output=json' can expose), and since that only affects
testing, we really don't bother with checking whether more than the
two least-significant bits are set.

Signed-off-by: Eric Blake 
Reviewed-by: Vladimir Sementsov-Ogievskiy 
Message-ID: <20230925192229.3186470-22-ebl...@redhat.com>
---
 block/nbd.c| 49 --
 block/trace-events |  1 +
 2 files changed, 35 insertions(+), 15 deletions(-)

diff --git a/block/nbd.c b/block/nbd.c
index 76461430411..52ebc8b2f53 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -615,13 +615,17 @@ static int nbd_parse_offset_hole_payload(BDRVNBDState *s,
  */
 static int nbd_parse_blockstatus_payload(BDRVNBDState *s,
  NBDStructuredReplyChunk *chunk,
- uint8_t *payload, uint64_t 
orig_length,
- NBDExtent32 *extent, Error **errp)
+ uint8_t *payload, bool wide,
+ uint64_t orig_length,
+ NBDExtent64 *extent, Error **errp)
 {
 uint32_t context_id;
+uint32_t count;
+size_t ext_len = wide ? sizeof(*extent) : sizeof(NBDExtent32);
+size_t pay_len = sizeof(context_id) + wide * sizeof(count) + ext_len;

 /* The server succeeded, so it must have sent [at least] one extent */
-if (chunk->length < sizeof(context_id) + sizeof(*extent)) {
+if (chunk->length < pay_len) {
 error_setg(errp, "Protocol error: invalid payload for "
  "NBD_REPLY_TYPE_BLOCK_STATUS");
 return -EINVAL;
@@ -636,8 +640,15 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s,
 return -EINVAL;
 }

-extent->length = payload_advance32();
-extent->flags = payload_advance32();
+if (wide) {
+count = payload_advance32();
+extent->length = payload_advance64();
+extent->flags = payload_advance64();
+} else {
+count = 0;
+extent->length = payload_advance32();
+extent->flags = payload_advance32();
+}

 if (extent->length == 0) {
 error_setg(errp, "Protocol error: server sent status chunk with "
@@ -658,7 +669,7 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s,
  * (always a safe status, even if it loses information).
  */
 if (s->info.min_block && !QEMU_IS_ALIGNED(extent->length,
-   s->info.min_block)) {
+  s->info.min_block)) {
 trace_nbd_parse_blockstatus_compliance("extent length is unaligned");
 if (extent->length > s->info.min_block) {
 extent->length = QEMU_ALIGN_DOWN(extent->length,
@@ -672,13 +683,15 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s,
 /*
  * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have
  * sent us any more than one extent, nor should it have included
- * status beyond our request in that extent. However, it's easy
- * enough to ignore the server's noncompliance without killing the
+ * status beyond our request in that extent. Furthermore, a wide
+ * server should have replied with an accurate count (we left
+ * count at 0 for a narrow server).  However, it's easy enough to
+ * ignore the server's noncompliance without killing the
  * connection; just ignore trailing extents, and clamp things to
  * the length of our request.
  */
-if (chunk->length > sizeof(context_id) + sizeof(*extent)) {
-trace_nbd_parse_blockstatus_compliance("more than one extent");
+if (count != wide || chunk->length > pay_len) {
+trace_nbd_parse_blockstatus_compliance("unexpected extent count");
 }
 if (extent->length > orig_length) {
 extent->length = orig_length;
@@ -1124,7 +1137,7 @@ nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t 
cookie,

 static int coroutine_fn
 nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie,
- uint64_t length, NBDExtent32 *extent,
+ uint64_t length, NBDExtent64 *extent,
  

[PULL 09/15] nbd/client: Plumb errp through nbd_receive_replies

2023-10-05 Thread Eric Blake
Instead of ignoring the low-level error just to refabricate our own
message to pass to the caller, we can just plumb the caller's errp
down to the low level.

Signed-off-by: Eric Blake 
Message-ID: <20230925192229.3186470-20-ebl...@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy 
---
 block/nbd.c | 18 --
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/block/nbd.c b/block/nbd.c
index 4a7f37da1c6..22d3cb11ac8 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -416,7 +416,8 @@ static void coroutine_fn GRAPH_RDLOCK 
nbd_reconnect_attempt(BDRVNBDState *s)
 reconnect_delay_timer_del(s);
 }

-static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie)
+static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie,
+Error **errp)
 {
 int ret;
 uint64_t ind = COOKIE_TO_INDEX(cookie), ind2;
@@ -457,20 +458,25 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState 
*s, uint64_t cookie)

 /* We are under mutex and cookie is 0. We have to do the dirty work. */
 assert(s->reply.cookie == 0);
-ret = nbd_receive_reply(s->bs, s->ioc, >reply, NULL);
-if (ret <= 0) {
-ret = ret ? ret : -EIO;
+ret = nbd_receive_reply(s->bs, s->ioc, >reply, errp);
+if (ret == 0) {
+ret = -EIO;
+error_setg(errp, "server dropped connection");
+}
+if (ret < 0) {
 nbd_channel_error(s, ret);
 return ret;
 }
 if (nbd_reply_is_structured(>reply) &&
 s->info.mode < NBD_MODE_STRUCTURED) {
 nbd_channel_error(s, -EINVAL);
+error_setg(errp, "unexpected structured reply");
 return -EINVAL;
 }
 ind2 = COOKIE_TO_INDEX(s->reply.cookie);
 if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) {
 nbd_channel_error(s, -EINVAL);
+error_setg(errp, "unexpected cookie value");
 return -EINVAL;
 }
 if (s->reply.cookie == cookie) {
@@ -842,9 +848,9 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
 }
 *request_ret = 0;

-ret = nbd_receive_replies(s, cookie);
+ret = nbd_receive_replies(s, cookie, errp);
 if (ret < 0) {
-error_setg(errp, "Connection closed");
+error_prepend(errp, "Connection closed: ");
 return -EIO;
 }
 assert(s->ioc);
-- 
2.41.0




[PULL 12/15] nbd/client: Request extended headers during negotiation

2023-10-05 Thread Eric Blake
All the pieces are in place for a client to finally request extended
headers.  Note that we must not request extended headers when qemu-nbd
is used to connect to the kernel module (as nbd.ko does not expect
them, but expects us to do the negotiation in userspace before handing
the socket over to the kernel), but there is no harm in all other
clients requesting them.

Extended headers are not essential to the information collected during
'qemu-nbd --list', but probing for it gives us one more piece of
information in that output.  Update the iotests affected by the new
line of output.

Signed-off-by: Eric Blake 
Reviewed-by: Vladimir Sementsov-Ogievskiy 
Message-ID: <20230925192229.3186470-23-ebl...@redhat.com>
---
 nbd/client-connection.c   |  2 +-
 nbd/client.c  | 20 ++-
 qemu-nbd.c|  3 +++
 tests/qemu-iotests/223.out|  6 ++
 tests/qemu-iotests/233.out|  4 
 tests/qemu-iotests/241.out|  3 +++
 tests/qemu-iotests/307.out|  5 +
 .../tests/nbd-qemu-allocation.out |  1 +
 8 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/nbd/client-connection.c b/nbd/client-connection.c
index aa0201b7107..f9da67c87e3 100644
--- a/nbd/client-connection.c
+++ b/nbd/client-connection.c
@@ -93,7 +93,7 @@ NBDClientConnection *nbd_client_connection_new(const 
SocketAddress *saddr,
 .do_negotiation = do_negotiation,

 .initial_info.request_sizes = true,
-.initial_info.mode = NBD_MODE_STRUCTURED,
+.initial_info.mode = NBD_MODE_EXTENDED,
 .initial_info.base_allocation = true,
 .initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap),
 .initial_info.name = g_strdup(export_name ?: "")
diff --git a/nbd/client.c b/nbd/client.c
index a2f253062aa..29ffc609a4b 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -953,15 +953,23 @@ static int nbd_start_negotiate(QIOChannel *ioc, 
QCryptoTLSCreds *tlscreds,
 if (fixedNewStyle) {
 int result = 0;

+if (max_mode >= NBD_MODE_EXTENDED) {
+result = nbd_request_simple_option(ioc,
+   NBD_OPT_EXTENDED_HEADERS,
+   false, errp);
+if (result) {
+return result < 0 ? -EINVAL : NBD_MODE_EXTENDED;
+}
+}
 if (max_mode >= NBD_MODE_STRUCTURED) {
 result = nbd_request_simple_option(ioc,
NBD_OPT_STRUCTURED_REPLY,
false, errp);
-if (result < 0) {
-return -EINVAL;
+if (result) {
+return result < 0 ? -EINVAL : NBD_MODE_STRUCTURED;
 }
 }
-return result ? NBD_MODE_STRUCTURED : NBD_MODE_SIMPLE;
+return NBD_MODE_SIMPLE;
 } else {
 return NBD_MODE_EXPORT_NAME;
 }
@@ -1034,6 +1042,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, 
QCryptoTLSCreds *tlscreds,
 }

 switch (info->mode) {
+case NBD_MODE_EXTENDED:
 case NBD_MODE_STRUCTURED:
 if (base_allocation) {
 result = nbd_negotiate_simple_meta_context(ioc, info, errp);
@@ -1144,7 +1153,7 @@ int nbd_receive_export_list(QIOChannel *ioc, 
QCryptoTLSCreds *tlscreds,

 *info = NULL;
 result = nbd_start_negotiate(ioc, tlscreds, hostname, ,
- NBD_MODE_STRUCTURED, NULL, errp);
+ NBD_MODE_EXTENDED, NULL, errp);
 if (tlscreds && sioc) {
 ioc = sioc;
 }
@@ -1155,6 +1164,7 @@ int nbd_receive_export_list(QIOChannel *ioc, 
QCryptoTLSCreds *tlscreds,
 switch ((NBDMode)result) {
 case NBD_MODE_SIMPLE:
 case NBD_MODE_STRUCTURED:
+case NBD_MODE_EXTENDED:
 /* newstyle - use NBD_OPT_LIST to populate array, then try
  * NBD_OPT_INFO on each array member. If structured replies
  * are enabled, also try NBD_OPT_LIST_META_CONTEXT. */
@@ -1191,7 +1201,7 @@ int nbd_receive_export_list(QIOChannel *ioc, 
QCryptoTLSCreds *tlscreds,
 break;
 }

-if (result == NBD_MODE_STRUCTURED &&
+if (result >= NBD_MODE_STRUCTURED &&
 nbd_list_meta_contexts(ioc, [i], errp) < 0) {
 goto out;
 }
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 54faa87a0c0..1a39bb8fac0 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -235,6 +235,9 @@ static int qemu_nbd_client_list(SocketAddress *saddr, 
QCryptoTLSCreds *tls,
 printf("  opt block: %u\n", list[i].opt_block);
 printf("  max block: %u\n", list[i].max_block);
 }
+printf("  transaction size: %s\n",
+   list[i].mode >= 

[PULL 14/15] nbd/server: Prepare for per-request filtering of BLOCK_STATUS

2023-10-05 Thread Eric Blake
The next commit will add support for the optional extension
NBD_CMD_FLAG_PAYLOAD during NBD_CMD_BLOCK_STATUS, where the client can
request that the server only return a subset of negotiated contexts,
rather than all contexts.  To make that task easier, this patch
populates the list of contexts to return on a per-command basis (for
now, identical to the full set of negotiated contexts).

Signed-off-by: Eric Blake 
Message-ID: <20230925192229.3186470-25-ebl...@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy 
---
 include/block/nbd.h |  1 +
 nbd/server.c| 22 +-
 2 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/include/block/nbd.h b/include/block/nbd.h
index 2006497f987..4e7bd6342f9 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -77,6 +77,7 @@ typedef struct NBDRequest {
 uint16_t flags; /* NBD_CMD_FLAG_* */
 uint16_t type;  /* NBD_CMD_* */
 NBDMode mode;   /* Determines which network representation to use */
+NBDMetaContexts *contexts; /* Used by NBD_CMD_BLOCK_STATUS */
 } NBDRequest;

 typedef struct NBDSimpleReply {
diff --git a/nbd/server.c b/nbd/server.c
index 2719992db7b..2dce9c3ad6a 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -2505,6 +2505,7 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
 break;

 case NBD_CMD_BLOCK_STATUS:
+request->contexts = >contexts;
 valid_flags |= NBD_CMD_FLAG_REQ_ONE;
 break;

@@ -2748,17 +2749,18 @@ static coroutine_fn int nbd_handle_request(NBDClient 
*client,
   "discard failed", errp);

 case NBD_CMD_BLOCK_STATUS:
+assert(request->contexts);
 if (!request->len) {
 return nbd_send_generic_reply(client, request, -EINVAL,
   "need non-zero length", errp);
 }
 assert(client->mode >= NBD_MODE_EXTENDED ||
request->len <= UINT32_MAX);
-if (client->contexts.count) {
+if (request->contexts->count) {
 bool dont_fragment = request->flags & NBD_CMD_FLAG_REQ_ONE;
-int contexts_remaining = client->contexts.count;
+int contexts_remaining = request->contexts->count;

-if (client->contexts.base_allocation) {
+if (request->contexts->base_allocation) {
 ret = nbd_co_send_block_status(client, request,
exp->common.blk,
request->from,
@@ -2771,7 +2773,7 @@ static coroutine_fn int nbd_handle_request(NBDClient 
*client,
 }
 }

-if (client->contexts.allocation_depth) {
+if (request->contexts->allocation_depth) {
 ret = nbd_co_send_block_status(client, request,
exp->common.blk,
request->from, request->len,
@@ -2784,8 +2786,9 @@ static coroutine_fn int nbd_handle_request(NBDClient 
*client,
 }
 }

+assert(request->contexts->exp == client->exp);
 for (i = 0; i < client->exp->nr_export_bitmaps; i++) {
-if (!client->contexts.bitmaps[i]) {
+if (!request->contexts->bitmaps[i]) {
 continue;
 }
 ret = nbd_co_send_bitmap(client, request,
@@ -2801,6 +2804,10 @@ static coroutine_fn int nbd_handle_request(NBDClient 
*client,
 assert(!contexts_remaining);

 return 0;
+} else if (client->contexts.count) {
+return nbd_send_generic_reply(client, request, -EINVAL,
+  "CMD_BLOCK_STATUS payload not valid",
+  errp);
 } else {
 return nbd_send_generic_reply(client, request, -EINVAL,
   "CMD_BLOCK_STATUS not negotiated",
@@ -2879,6 +2886,11 @@ static coroutine_fn void nbd_trip(void *opaque)
 } else {
 ret = nbd_handle_request(client, , req->data, _err);
 }
+if (request.contexts && request.contexts != >contexts) {
+assert(request.type == NBD_CMD_BLOCK_STATUS);
+g_free(request.contexts->bitmaps);
+g_free(request.contexts);
+}
 if (ret < 0) {
 error_prepend(_err, "Failed to send reply: ");
 goto disconnect;
-- 
2.41.0




[PULL 07/15] nbd/server: Support 64-bit block status

2023-10-05 Thread Eric Blake
The NBD spec states that if the client negotiates extended headers,
the server must avoid NBD_REPLY_TYPE_BLOCK_STATUS and instead use
NBD_REPLY_TYPE_BLOCK_STATUS_EXT which supports 64-bit lengths, even if
the reply does not need more than 32 bits.  As of this patch,
client->mode is still never NBD_MODE_EXTENDED, so the code added here
does not take effect until the next patch enables negotiation.

For now, all metacontexts that we know how to export never populate
more than 32 bits of information, so we don't have to worry about
NBD_REP_ERR_EXT_HEADER_REQD or filtering during handshake, and we
always send all zeroes for the upper 32 bits of status during
NBD_CMD_BLOCK_STATUS.

Note that we previously had some interesting size-juggling on call
chains, such as:

nbd_co_send_block_status(uint32_t length)
-> blockstatus_to_extents(uint32_t bytes)
  -> bdrv_block_status_above(bytes, _t num)
  -> nbd_extent_array_add(uint64_t num)
-> store num in 32-bit length

But we were lucky that it never overflowed: bdrv_block_status_above
never sets num larger than bytes, and we had previously been capping
'bytes' at 32 bits (since the protocol does not allow sending a larger
request without extended headers).  This patch adds some assertions
that ensure we continue to avoid overflowing 32 bits for a narrow
client, while fully utilizing 64-bits all the way through when the
client understands that.  Even in 64-bit math, overflow is not an
issue, because all lengths are coming from the block layer, and we
know that the block layer does not support images larger than off_t
(if lengths were coming from the network, the story would be
different).

Signed-off-by: Eric Blake 
Reviewed-by: Vladimir Sementsov-Ogievskiy 
Message-ID: <20230925192229.3186470-18-ebl...@redhat.com>
---
 nbd/server.c | 108 ++-
 1 file changed, 82 insertions(+), 26 deletions(-)

diff --git a/nbd/server.c b/nbd/server.c
index 910c48c6467..183efe27bf8 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -2103,20 +2103,24 @@ static int coroutine_fn 
nbd_co_send_sparse_read(NBDClient *client,
 }

 typedef struct NBDExtentArray {
-NBDExtent32 *extents;
+NBDExtent64 *extents;
 unsigned int nb_alloc;
 unsigned int count;
 uint64_t total_length;
+bool extended;
 bool can_add;
 bool converted_to_be;
 } NBDExtentArray;

-static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc)
+static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc,
+NBDMode mode)
 {
 NBDExtentArray *ea = g_new0(NBDExtentArray, 1);

+assert(mode >= NBD_MODE_STRUCTURED);
 ea->nb_alloc = nb_alloc;
-ea->extents = g_new(NBDExtent32, nb_alloc);
+ea->extents = g_new(NBDExtent64, nb_alloc);
+ea->extended = mode >= NBD_MODE_EXTENDED;
 ea->can_add = true;

 return ea;
@@ -2135,15 +2139,36 @@ static void 
nbd_extent_array_convert_to_be(NBDExtentArray *ea)
 int i;

 assert(!ea->converted_to_be);
+assert(ea->extended);
 ea->can_add = false;
 ea->converted_to_be = true;

 for (i = 0; i < ea->count; i++) {
-ea->extents[i].flags = cpu_to_be32(ea->extents[i].flags);
-ea->extents[i].length = cpu_to_be32(ea->extents[i].length);
+ea->extents[i].length = cpu_to_be64(ea->extents[i].length);
+ea->extents[i].flags = cpu_to_be64(ea->extents[i].flags);
 }
 }

+/* Further modifications of the array after conversion are abandoned */
+static NBDExtent32 *nbd_extent_array_convert_to_narrow(NBDExtentArray *ea)
+{
+int i;
+NBDExtent32 *extents = g_new(NBDExtent32, ea->count);
+
+assert(!ea->converted_to_be);
+assert(!ea->extended);
+ea->can_add = false;
+ea->converted_to_be = true;
+
+for (i = 0; i < ea->count; i++) {
+assert((ea->extents[i].length | ea->extents[i].flags) <= UINT32_MAX);
+extents[i].length = cpu_to_be32(ea->extents[i].length);
+extents[i].flags = cpu_to_be32(ea->extents[i].flags);
+}
+
+return extents;
+}
+
 /*
  * Add extent to NBDExtentArray. If extent can't be added (no available space),
  * return -1.
@@ -2154,19 +2179,27 @@ static void 
nbd_extent_array_convert_to_be(NBDExtentArray *ea)
  * would result in an incorrect range reported to the client)
  */
 static int nbd_extent_array_add(NBDExtentArray *ea,
-uint32_t length, uint32_t flags)
+uint64_t length, uint32_t flags)
 {
 assert(ea->can_add);

 if (!length) {
 return 0;
 }
+if (!ea->extended) {
+assert(length <= UINT32_MAX);
+}

 /* Extend previous extent if flags are the same */
 if (ea->count > 0 && flags == ea->extents[ea->count - 1].flags) {
-uint64_t sum = (uint64_t)length + ea->extents[ea->count - 1].length;
+uint64_t sum = length + ea->extents[ea->count - 1].length;

-if (sum <= UINT32_MAX) {
+/*
+ * sum 

[PULL 13/15] nbd/server: Refactor list of negotiated meta contexts

2023-10-05 Thread Eric Blake
Peform several minor refactorings of how the list of negotiated meta
contexts is managed, to make upcoming patches easier: Promote the
internal type NBDExportMetaContexts to the public opaque type
NBDMetaContexts, and mark exp const.  Use a shorter member name in
NBDClient.  Hoist calls to nbd_check_meta_context() earlier in their
callers, as the number of negotiated contexts may impact the flags
exposed in regards to an export, which in turn requires a new
parameter.  Drop a redundant parameter to nbd_negotiate_meta_queries.
No semantic change intended on the success path; on the failure path,
dropping context in nbd_check_meta_export even when reporting an error
is safer.

Signed-off-by: Eric Blake 
Reviewed-by: Vladimir Sementsov-Ogievskiy 
Message-ID: <20230925192229.3186470-24-ebl...@redhat.com>
---
 include/block/nbd.h |  1 +
 nbd/server.c| 55 -
 2 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/include/block/nbd.h b/include/block/nbd.h
index ba8724f5336..2006497f987 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -29,6 +29,7 @@
 typedef struct NBDExport NBDExport;
 typedef struct NBDClient NBDClient;
 typedef struct NBDClientConnection NBDClientConnection;
+typedef struct NBDMetaContexts NBDMetaContexts;

 extern const BlockExportDriver blk_exp_nbd;

diff --git a/nbd/server.c b/nbd/server.c
index d3eed6535bf..2719992db7b 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -105,11 +105,13 @@ struct NBDExport {

 static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);

-/* NBDExportMetaContexts represents a list of contexts to be exported,
+/*
+ * NBDMetaContexts represents a list of meta contexts in use,
  * as selected by NBD_OPT_SET_META_CONTEXT. Also used for
- * NBD_OPT_LIST_META_CONTEXT. */
-typedef struct NBDExportMetaContexts {
-NBDExport *exp;
+ * NBD_OPT_LIST_META_CONTEXT.
+ */
+struct NBDMetaContexts {
+const NBDExport *exp; /* associated export */
 size_t count; /* number of negotiated contexts */
 bool base_allocation; /* export base:allocation context (block status) */
 bool allocation_depth; /* export qemu:allocation-depth */
@@ -117,7 +119,7 @@ typedef struct NBDExportMetaContexts {
 * export qemu:dirty-bitmap:,
 * sized by exp->nr_export_bitmaps
 */
-} NBDExportMetaContexts;
+};

 struct NBDClient {
 int refcount;
@@ -144,7 +146,7 @@ struct NBDClient {
 uint32_t check_align; /* If non-zero, check for aligned client requests */

 NBDMode mode;
-NBDExportMetaContexts export_meta;
+NBDMetaContexts contexts; /* Negotiated meta contexts */

 uint32_t opt; /* Current option being negotiated */
 uint32_t optlen; /* remaining length of data in ioc for the option being
@@ -455,10 +457,10 @@ static int nbd_negotiate_handle_list(NBDClient *client, 
Error **errp)
 return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
 }

-static void nbd_check_meta_export(NBDClient *client)
+static void nbd_check_meta_export(NBDClient *client, NBDExport *exp)
 {
-if (client->exp != client->export_meta.exp) {
-client->export_meta.count = 0;
+if (exp != client->contexts.exp) {
+client->contexts.count = 0;
 }
 }

@@ -504,6 +506,7 @@ static int nbd_negotiate_handle_export_name(NBDClient 
*client, bool no_zeroes,
 error_setg(errp, "export not found");
 return -EINVAL;
 }
+nbd_check_meta_export(client, client->exp);

 myflags = client->exp->nbdflags;
 if (client->mode >= NBD_MODE_STRUCTURED) {
@@ -521,7 +524,6 @@ static int nbd_negotiate_handle_export_name(NBDClient 
*client, bool no_zeroes,

 QTAILQ_INSERT_TAIL(>exp->clients, client, next);
 blk_exp_ref(>exp->common);
-nbd_check_meta_export(client);

 return 0;
 }
@@ -641,6 +643,9 @@ static int nbd_negotiate_handle_info(NBDClient *client, 
Error **errp)
   errp, "export '%s' not present",
   sane_name);
 }
+if (client->opt == NBD_OPT_GO) {
+nbd_check_meta_export(client, exp);
+}

 /* Don't bother sending NBD_INFO_NAME unless client requested it */
 if (sendname) {
@@ -729,7 +734,6 @@ static int nbd_negotiate_handle_info(NBDClient *client, 
Error **errp)
 client->check_align = check_align;
 QTAILQ_INSERT_TAIL(>exp->clients, client, next);
 blk_exp_ref(>exp->common);
-nbd_check_meta_export(client);
 rc = 1;
 }
 return rc;
@@ -852,7 +856,7 @@ static bool nbd_strshift(const char **str, const char 
*prefix)
  * Handle queries to 'base' namespace. For now, only the base:allocation
  * context is available.  Return true if @query has been handled.
  */
-static bool nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta,
+static bool nbd_meta_base_query(NBDClient *client, NBDMetaContexts *meta,
 

[PULL 04/15] nbd/server: Support a request payload

2023-10-05 Thread Eric Blake
Upcoming additions to support NBD 64-bit effect lengths allow for the
possibility to distinguish between payload length (capped at 32M) and
effect length (64 bits, although we generally assume 63 bits because
of off_t limitations).  Without that extension, only the NBD_CMD_WRITE
request has a payload; but with the extension, it makes sense to allow
at least NBD_CMD_BLOCK_STATUS to have both a payload and effect length
in a future patch (where the payload is a limited-size struct that in
turn gives the real effect length as well as a subset of known ids for
which status is requested).  Other future NBD commands may also have a
request payload, so the 64-bit extension introduces a new
NBD_CMD_FLAG_PAYLOAD_LEN that distinguishes between whether the header
length is a payload length or an effect length, rather than
hard-coding the decision based on the command.

According to the spec, a client should never send a command with a
payload without the negotiation phase proving such extension is
available.  So in the unlikely event the bit is set or cleared
incorrectly, the client is already at fault; if the client then
provides the payload, we can gracefully consume it off the wire and
fail the command with NBD_EINVAL (subsequent checks for magic numbers
ensure we are still in sync), while if the client fails to send
payload we block waiting for it (basically deadlocking our connection
to the bad client, but not negatively impacting our ability to service
other clients, so not a security risk).  Note that we do not support
the payload version of BLOCK_STATUS yet.

This patch also fixes a latent bug introduced in b2578459: once
request->len can be 64 bits, assigning it to a 32-bit payload_len can
cause wraparound to 0 which then sets req->complete prematurely;
thankfully, the bug was not possible back then (it takes this and
later patches to even allow request->len larger than 32 bits; and
since previously the only 'payload_len = request->len' assignment was
in NBD_CMD_WRITE which also sets check_length, which in turn rejects
lengths larger than 32M before relying on any possibly-truncated value
stored in payload_len).

Signed-off-by: Eric Blake 
Message-ID: <20230925192229.3186470-15-ebl...@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy 
[eblake: enhance comment on handling client error, fix type bug]
Signed-off-by: Eric Blake 
---
 nbd/server.c | 42 +-
 nbd/trace-events |  1 +
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/nbd/server.c b/nbd/server.c
index 7a6f95071f8..56e9b41828e 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -2322,10 +2322,12 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
Error **errp)
 {
 NBDClient *client = req->client;
+bool extended_with_payload;
 bool check_length = false;
 bool check_rofs = false;
 bool allocate_buffer = false;
-unsigned payload_len = 0;
+bool payload_okay = false;
+uint64_t payload_len = 0;
 int valid_flags = NBD_CMD_FLAG_FUA;
 int ret;

@@ -2338,6 +2340,13 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,

 trace_nbd_co_receive_request_decode_type(request->cookie, request->type,
  nbd_cmd_lookup(request->type));
+extended_with_payload = client->mode >= NBD_MODE_EXTENDED &&
+request->flags & NBD_CMD_FLAG_PAYLOAD_LEN;
+if (extended_with_payload) {
+payload_len = request->len;
+check_length = true;
+}
+
 switch (request->type) {
 case NBD_CMD_DISC:
 /* Special case: we're going to disconnect without a reply,
@@ -2354,6 +2363,15 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
 break;

 case NBD_CMD_WRITE:
+if (client->mode >= NBD_MODE_EXTENDED) {
+if (!extended_with_payload) {
+/* The client is noncompliant. Trace it, but proceed. */
+trace_nbd_co_receive_ext_payload_compliance(request->from,
+request->len);
+}
+valid_flags |= NBD_CMD_FLAG_PAYLOAD_LEN;
+}
+payload_okay = true;
 payload_len = request->len;
 check_length = true;
 allocate_buffer = true;
@@ -2395,6 +2413,16 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
request->len, NBD_MAX_BUFFER_SIZE);
 return -EINVAL;
 }
+if (payload_len && !payload_okay) {
+/*
+ * For now, we don't support payloads on other commands; but
+ * we can keep the connection alive by ignoring the payload.
+ * We will fail the command later with NBD_EINVAL for the use
+ * of an unsupported flag (and not for access beyond bounds).
+ */
+assert(request->type != NBD_CMD_WRITE);
+request->len = 0;
+}
 if 

[PULL 10/15] nbd/client: Initial support for extended headers

2023-10-05 Thread Eric Blake
Update the client code to be able to send an extended request, and
parse an extended header from the server.  Note that since we reject
any structured reply with a too-large payload, we can always normalize
a valid header back into the compact form, so that the caller need not
deal with two branches of a union.  Still, until a later patch lets
the client negotiate extended headers, the code added here should not
be reached.  Note that because of the different magic numbers, it is
just as easy to trace and then tolerate a non-compliant server sending
the wrong header reply as it would be to insist that the server is
compliant.

Signed-off-by: Eric Blake 
Message-ID: <20230925192229.3186470-21-ebl...@redhat.com>
[eblake: fix trace format]
Reviewed-by: Vladimir Sementsov-Ogievskiy 
---
 include/block/nbd.h |   3 +-
 block/nbd.c |   2 +-
 nbd/client.c| 104 +---
 nbd/trace-events|   3 +-
 4 files changed, 74 insertions(+), 38 deletions(-)

diff --git a/include/block/nbd.h b/include/block/nbd.h
index 8a765e78dfb..ba8724f5336 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -389,7 +389,8 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo 
*info,
  Error **errp);
 int nbd_send_request(QIOChannel *ioc, NBDRequest *request);
 int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc,
-   NBDReply *reply, Error **errp);
+   NBDReply *reply, NBDMode mode,
+   Error **errp);
 int nbd_client(int fd);
 int nbd_disconnect(int fd);
 int nbd_errno_to_system_errno(int err);
diff --git a/block/nbd.c b/block/nbd.c
index 22d3cb11ac8..76461430411 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -458,7 +458,7 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState 
*s, uint64_t cookie,

 /* We are under mutex and cookie is 0. We have to do the dirty work. */
 assert(s->reply.cookie == 0);
-ret = nbd_receive_reply(s->bs, s->ioc, >reply, errp);
+ret = nbd_receive_reply(s->bs, s->ioc, >reply, s->info.mode, errp);
 if (ret == 0) {
 ret = -EIO;
 error_setg(errp, "server dropped connection");
diff --git a/nbd/client.c b/nbd/client.c
index cecb0f04377..a2f253062aa 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -1346,22 +1346,29 @@ int nbd_disconnect(int fd)

 int nbd_send_request(QIOChannel *ioc, NBDRequest *request)
 {
-uint8_t buf[NBD_REQUEST_SIZE];
+uint8_t buf[NBD_EXTENDED_REQUEST_SIZE];
+size_t len;

-assert(request->mode <= NBD_MODE_STRUCTURED); /* TODO handle extended */
-assert(request->len <= UINT32_MAX);
 trace_nbd_send_request(request->from, request->len, request->cookie,
request->flags, request->type,
nbd_cmd_lookup(request->type));

-stl_be_p(buf, NBD_REQUEST_MAGIC);
 stw_be_p(buf + 4, request->flags);
 stw_be_p(buf + 6, request->type);
 stq_be_p(buf + 8, request->cookie);
 stq_be_p(buf + 16, request->from);
-stl_be_p(buf + 24, request->len);
+if (request->mode >= NBD_MODE_EXTENDED) {
+stl_be_p(buf, NBD_EXTENDED_REQUEST_MAGIC);
+stq_be_p(buf + 24, request->len);
+len = NBD_EXTENDED_REQUEST_SIZE;
+} else {
+assert(request->len <= UINT32_MAX);
+stl_be_p(buf, NBD_REQUEST_MAGIC);
+stl_be_p(buf + 24, request->len);
+len = NBD_REQUEST_SIZE;
+}

-return nbd_write(ioc, buf, sizeof(buf), NULL);
+return nbd_write(ioc, buf, len, NULL);
 }

 /* nbd_receive_simple_reply
@@ -1388,30 +1395,36 @@ static int nbd_receive_simple_reply(QIOChannel *ioc, 
NBDSimpleReply *reply,
 return 0;
 }

-/* nbd_receive_structured_reply_chunk
+/* nbd_receive_reply_chunk_header
  * Read structured reply chunk except magic field (which should be already
- * read).
+ * read).  Normalize into the compact form.
  * Payload is not read.
  */
-static int nbd_receive_structured_reply_chunk(QIOChannel *ioc,
-  NBDStructuredReplyChunk *chunk,
-  Error **errp)
+static int nbd_receive_reply_chunk_header(QIOChannel *ioc, NBDReply *chunk,
+  Error **errp)
 {
 int ret;
+size_t len;
+uint64_t payload_len;

-assert(chunk->magic == NBD_STRUCTURED_REPLY_MAGIC);
+if (chunk->magic == NBD_STRUCTURED_REPLY_MAGIC) {
+len = sizeof(chunk->structured);
+} else {
+assert(chunk->magic == NBD_EXTENDED_REPLY_MAGIC);
+len = sizeof(chunk->extended);
+}

 ret = nbd_read(ioc, (uint8_t *)chunk + sizeof(chunk->magic),
-   sizeof(*chunk) - sizeof(chunk->magic), "structured chunk",
+   len - sizeof(chunk->magic), "structured chunk",
errp);
 if (ret < 0) {
 return ret;
 }

-chunk->flags = 

[PULL 08/15] nbd/server: Enable initial support for extended headers

2023-10-05 Thread Eric Blake
Time to start supporting clients that request extended headers.  Now
we can finally reach the code added across several previous patches.

Even though the NBD spec has been altered to allow us to accept
NBD_CMD_READ larger than the max payload size (provided our response
is a hole or broken up over more than one data chunk), we are not
planning to take advantage of that, and continue to cap NBD_CMD_READ
to 32M regardless of header size.

For NBD_CMD_WRITE_ZEROES and NBD_CMD_TRIM, the block layer already
supports 64-bit operations without any effort on our part.  For
NBD_CMD_BLOCK_STATUS, the client's length is a hint, and the previous
patch took care of implementing the required
NBD_REPLY_TYPE_BLOCK_STATUS_EXT.

We do not yet support clients that want to do request payload
filtering of NBD_CMD_BLOCK_STATUS; that will be added in later
patches, but is not essential for qemu as a client since qemu only
requests the single context base:allocation.

Signed-off-by: Eric Blake 
Reviewed-by: Vladimir Sementsov-Ogievskiy 
Message-ID: <20230925192229.3186470-19-ebl...@redhat.com>
---
 docs/interop/nbd.txt |  1 +
 nbd/server.c | 21 +
 2 files changed, 22 insertions(+)

diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt
index f5ca25174a6..9aae5e1f294 100644
--- a/docs/interop/nbd.txt
+++ b/docs/interop/nbd.txt
@@ -69,3 +69,4 @@ NBD_CMD_BLOCK_STATUS for "qemu:dirty-bitmap:", NBD_CMD_CACHE
 NBD_CMD_FLAG_FAST_ZERO
 * 5.2: NBD_CMD_BLOCK_STATUS for "qemu:allocation-depth"
 * 7.1: NBD_FLAG_CAN_MULTI_CONN for shareable writable exports
+* 8.2: NBD_OPT_EXTENDED_HEADERS
diff --git a/nbd/server.c b/nbd/server.c
index 183efe27bf8..d3eed6535bf 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -482,6 +482,10 @@ static int nbd_negotiate_handle_export_name(NBDClient 
*client, bool no_zeroes,
 [10 .. 133]   reserved (0) [unless no_zeroes]
  */
 trace_nbd_negotiate_handle_export_name();
+if (client->mode >= NBD_MODE_EXTENDED) {
+error_setg(errp, "Extended headers already negotiated");
+return -EINVAL;
+}
 if (client->optlen > NBD_MAX_STRING_SIZE) {
 error_setg(errp, "Bad length received");
 return -EINVAL;
@@ -1264,6 +1268,10 @@ static int nbd_negotiate_options(NBDClient *client, 
Error **errp)
 case NBD_OPT_STRUCTURED_REPLY:
 if (length) {
 ret = nbd_reject_length(client, false, errp);
+} else if (client->mode >= NBD_MODE_EXTENDED) {
+ret = nbd_negotiate_send_rep_err(
+client, NBD_REP_ERR_EXT_HEADER_REQD, errp,
+"extended headers already negotiated");
 } else if (client->mode >= NBD_MODE_STRUCTURED) {
 ret = nbd_negotiate_send_rep_err(
 client, NBD_REP_ERR_INVALID, errp,
@@ -1280,6 +1288,19 @@ static int nbd_negotiate_options(NBDClient *client, 
Error **errp)
  errp);
 break;

+case NBD_OPT_EXTENDED_HEADERS:
+if (length) {
+ret = nbd_reject_length(client, false, errp);
+} else if (client->mode >= NBD_MODE_EXTENDED) {
+ret = nbd_negotiate_send_rep_err(
+client, NBD_REP_ERR_INVALID, errp,
+"extended headers already negotiated");
+} else {
+ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
+client->mode = NBD_MODE_EXTENDED;
+}
+break;
+
 default:
 ret = nbd_opt_drop(client, NBD_REP_ERR_UNSUP, errp,
"Unsupported option %" PRIu32 " (%s)",
-- 
2.41.0




Re: [Libguestfs] [PATCH v7 01/12] nbd/server: Support a request payload

2023-10-05 Thread Vladimir Sementsov-Ogievskiy

On 05.10.23 18:38, Eric Blake wrote:

On Mon, Sep 25, 2023 at 02:22:31PM -0500, Eric Blake wrote:

Upcoming additions to support NBD 64-bit effect lengths allow for the
possibility to distinguish between payload length (capped at 32M) and
effect length (64 bits, although we generally assume 63 bits because
of off_t limitations).

[...]


+++ b/nbd/server.c
@@ -2322,9 +2322,11 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
 Error **errp)
  {
  NBDClient *client = req->client;
+bool extended_with_payload;
  bool check_length = false;
  bool check_rofs = false;
  bool allocate_buffer = false;
+bool payload_okay = false;
  unsigned payload_len = 0;


Pre-existing type mismatch caught as a result of Vladimir's review of
12/12, but:


  int valid_flags = NBD_CMD_FLAG_FUA;
  int ret;
@@ -2338,6 +2340,13 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,

  trace_nbd_co_receive_request_decode_type(request->cookie, request->type,
   nbd_cmd_lookup(request->type));
+extended_with_payload = client->mode >= NBD_MODE_EXTENDED &&
+request->flags & NBD_CMD_FLAG_PAYLOAD_LEN;
+if (extended_with_payload) {
+payload_len = request->len;


this can assign a 64-bit number into a 32-bit variable, which can
truncate to 0,...


+check_length = true;
+}
+
  switch (request->type) {
  case NBD_CMD_DISC:
  /* Special case: we're going to disconnect without a reply,
@@ -2354,6 +2363,15 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
  break;

  case NBD_CMD_WRITE:
+if (client->mode >= NBD_MODE_EXTENDED) {
+if (!extended_with_payload) {
+/* The client is noncompliant. Trace it, but proceed. */
+trace_nbd_co_receive_ext_payload_compliance(request->from,
+request->len);
+}
+valid_flags |= NBD_CMD_FLAG_PAYLOAD_LEN;
+}
+payload_okay = true;
  payload_len = request->len;


...the pre-existing code is safe only as long as request->len cannot
exceed 32 bytes (which it can't do until later in this series actually
enables extended requests).  Switching the type now is prudent...


  check_length = true;
  allocate_buffer = true;
@@ -2395,6 +2413,14 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
 request->len, NBD_MAX_BUFFER_SIZE);
  return -EINVAL;
  }
+if (payload_len && !payload_okay) {
+/*
+ * For now, we don't support payloads on other commands; but
+ * we can keep the connection alive by ignoring the payload.
+ */
+assert(request->type != NBD_CMD_WRITE);
+request->len = 0;


...otherwise, this check is bypassed for a request size of exactly 4G
if check_length is false and thus the previous conditional for
request->len vs. NBD_MAX_BUFFER_SIZE didn't trigger (prior to this
patch, payload_len was only set for CND_WRITE which also set
check_length).  Thus, I'm squashing in:

diff --git i/nbd/server.c w/nbd/server.c
index 5258064e5ac..1cb66e86a89 100644
--- i/nbd/server.c
+++ w/nbd/server.c
@@ -2327,7 +2327,7 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
  bool check_rofs = false;
  bool allocate_buffer = false;
  bool payload_okay = false;
-unsigned payload_len = 0;
+uint64_t payload_len = 0;
  int valid_flags = NBD_CMD_FLAG_FUA;
  int ret;




OK, agree

--
Best regards,
Vladimir




Re: [PATCH v3 02/16] hw/ide/ahci: Clean up local variable shadowing

2023-10-05 Thread John Snow
On Wed, Oct 4, 2023 at 8:00 AM Philippe Mathieu-Daudé  wrote:
>
> Fix:
>
>   hw/ide/ahci.c:1577:23: error: declaration shadows a local variable 
> [-Werror,-Wshadow]
> IDEState *s = >port.ifs[j];
>   ^
>   hw/ide/ahci.c:1569:29: note: previous declaration is here
> void ahci_uninit(AHCIState *s)
> ^
>
> Signed-off-by: Philippe Mathieu-Daudé 

Reviewed-by: John Snow 

> ---
>  hw/ide/ahci.c | 4 +---
>  1 file changed, 1 insertion(+), 3 deletions(-)
>
> diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
> index d0a774bc17..fcc5476e9e 100644
> --- a/hw/ide/ahci.c
> +++ b/hw/ide/ahci.c
> @@ -1622,9 +1622,7 @@ void ahci_uninit(AHCIState *s)
>  AHCIDevice *ad = >dev[i];
>
>  for (j = 0; j < 2; j++) {
> -IDEState *s = >port.ifs[j];
> -
> -ide_exit(s);
> +ide_exit(>port.ifs[j]);
>  }
>  object_unparent(OBJECT(>port));
>  }
> --
> 2.41.0
>




Re: [Libguestfs] [PATCH v7 01/12] nbd/server: Support a request payload

2023-10-05 Thread Eric Blake
On Mon, Sep 25, 2023 at 02:22:31PM -0500, Eric Blake wrote:
> Upcoming additions to support NBD 64-bit effect lengths allow for the
> possibility to distinguish between payload length (capped at 32M) and
> effect length (64 bits, although we generally assume 63 bits because
> of off_t limitations).
[...]

> +++ b/nbd/server.c
> @@ -2322,9 +2322,11 @@ static int coroutine_fn 
> nbd_co_receive_request(NBDRequestData *req,
> Error **errp)
>  {
>  NBDClient *client = req->client;
> +bool extended_with_payload;
>  bool check_length = false;
>  bool check_rofs = false;
>  bool allocate_buffer = false;
> +bool payload_okay = false;
>  unsigned payload_len = 0;

Pre-existing type mismatch caught as a result of Vladimir's review of
12/12, but:

>  int valid_flags = NBD_CMD_FLAG_FUA;
>  int ret;
> @@ -2338,6 +2340,13 @@ static int coroutine_fn 
> nbd_co_receive_request(NBDRequestData *req,
> 
>  trace_nbd_co_receive_request_decode_type(request->cookie, request->type,
>   nbd_cmd_lookup(request->type));
> +extended_with_payload = client->mode >= NBD_MODE_EXTENDED &&
> +request->flags & NBD_CMD_FLAG_PAYLOAD_LEN;
> +if (extended_with_payload) {
> +payload_len = request->len;

this can assign a 64-bit number into a 32-bit variable, which can
truncate to 0,...

> +check_length = true;
> +}
> +
>  switch (request->type) {
>  case NBD_CMD_DISC:
>  /* Special case: we're going to disconnect without a reply,
> @@ -2354,6 +2363,15 @@ static int coroutine_fn 
> nbd_co_receive_request(NBDRequestData *req,
>  break;
> 
>  case NBD_CMD_WRITE:
> +if (client->mode >= NBD_MODE_EXTENDED) {
> +if (!extended_with_payload) {
> +/* The client is noncompliant. Trace it, but proceed. */
> +trace_nbd_co_receive_ext_payload_compliance(request->from,
> +request->len);
> +}
> +valid_flags |= NBD_CMD_FLAG_PAYLOAD_LEN;
> +}
> +payload_okay = true;
>  payload_len = request->len;

...the pre-existing code is safe only as long as request->len cannot
exceed 32 bytes (which it can't do until later in this series actually
enables extended requests).  Switching the type now is prudent...

>  check_length = true;
>  allocate_buffer = true;
> @@ -2395,6 +2413,14 @@ static int coroutine_fn 
> nbd_co_receive_request(NBDRequestData *req,
> request->len, NBD_MAX_BUFFER_SIZE);
>  return -EINVAL;
>  }
> +if (payload_len && !payload_okay) {
> +/*
> + * For now, we don't support payloads on other commands; but
> + * we can keep the connection alive by ignoring the payload.
> + */
> +assert(request->type != NBD_CMD_WRITE);
> +request->len = 0;

...otherwise, this check is bypassed for a request size of exactly 4G
if check_length is false and thus the previous conditional for
request->len vs. NBD_MAX_BUFFER_SIZE didn't trigger (prior to this
patch, payload_len was only set for CND_WRITE which also set
check_length).  Thus, I'm squashing in:

diff --git i/nbd/server.c w/nbd/server.c
index 5258064e5ac..1cb66e86a89 100644
--- i/nbd/server.c
+++ w/nbd/server.c
@@ -2327,7 +2327,7 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
 bool check_rofs = false;
 bool allocate_buffer = false;
 bool payload_okay = false;
-unsigned payload_len = 0;
+uint64_t payload_len = 0;
 int valid_flags = NBD_CMD_FLAG_FUA;
 int ret;



-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [Libguestfs] [PATCH v7 12/12] nbd/server: Add FLAG_PAYLOAD support to CMD_BLOCK_STATUS

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 05:33:26PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > +static int
> > +nbd_co_block_status_payload_read(NBDClient *client, NBDRequest *request,
> > + Error **errp)
> > +{
> > +int payload_len = request->len;
> 
> payload_len should be uint64_t
> 
> > +g_autofree char *buf = NULL;
> > +size_t count, i, nr_bitmaps;
> > +uint32_t id;
> > +
> 
> otherwise, we may do something unexpected here, when reqeuest->len is too big 
> for int:
> 
> > +if (payload_len > NBD_MAX_BUFFER_SIZE) {
> > +error_setg(errp, "len (%" PRIu64 ") is larger than max len (%u)",
> > +   request->len, NBD_MAX_BUFFER_SIZE);
> > +return -EINVAL;
> > +}

Oh, it looks like I introduced that same type mismatch in commit
8db7e2d6 as well, although it appears to have a latent effect until
this series enables the ability for request->length to actually exceed
32 bits.  I'll reply on 1/12 with another squash I'm making there.

> > +
> > +assert(client->contexts.exp == client->exp);
> > +nr_bitmaps = client->exp->nr_export_bitmaps;
> > +request->contexts = g_new0(NBDMetaContexts, 1);
> > +request->contexts->exp = client->exp;
> > +
> > +if (payload_len % sizeof(uint32_t) ||
> > +payload_len < sizeof(NBDBlockStatusPayload) ||
> > +payload_len > (sizeof(NBDBlockStatusPayload) +
> > +   sizeof(id) * client->contexts.count)) {
> > +goto skip;
> > +}
> 
> [..]
> 
> >* connection right away, -EAGAIN to indicate we were interrupted and the
> > @@ -2505,7 +2593,18 @@ static int coroutine_fn 
> > nbd_co_receive_request(NBDRequestData *req,
> >   break;
> > 
> >   case NBD_CMD_BLOCK_STATUS:
> > -request->contexts = >contexts;
> > +if (extended_with_payload) {
> > +ret = nbd_co_block_status_payload_read(client, request, errp);
> > +if (ret < 0) {
> > +return ret;
> > +}
> > +/* payload now consumed */
> > +check_length = extended_with_payload = false;
> 
> why set extended_with_payload to false? it's a bit misleading. And you don't 
> do this for WRITE request.

Indeed; it doesn't make any different to later in the function.  Will drop.

> 
> > +payload_len = 0;
> > +valid_flags |= NBD_CMD_FLAG_PAYLOAD_LEN;
> > +} else {
> > +request->contexts = >contexts;
> > +}
> >   valid_flags |= NBD_CMD_FLAG_REQ_ONE;
> >   break;
> > 
> 
> [..]
> 
> 
> with payload_len changed to uint64_t, your squash-in applied and s/>/>=/ 
> fixed:
> Reviewed-by: Vladimir Sementsov-Ogievskiy 

Thanks for the careful review.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PULL 0/9] Python patches

2023-10-05 Thread John Snow
On Thu, Oct 5, 2023, 9:00 AM Stefan Hajnoczi  wrote:

> On Thu, 5 Oct 2023 at 00:49, Philippe Mathieu-Daudé 
> wrote:
> >
> > Hi John,
> >
> > On 4/10/23 21:46, John Snow wrote:
> > > The following changes since commit
> da1034094d375afe9e3d8ec8980550ea0f06f7e0:
> > >
> > >Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into
> staging (2023-10-03 07:43:44 -0400)
> > >
> > > are available in the Git repository at:
> > >
> > >https://gitlab.com/jsnow/qemu.git tags/python-pull-request
> > >
> > > for you to fetch changes up to
> 4d7a663cbe8343e884b88e44bd88d37dd0a470e5:
> > >
> > >Python: test Python 3.12 (2023-10-04 15:19:00 -0400)
> > >
> > > 
> > > Python pullreq
> > >
> > > Buffering improvements for qemu machine, minor changes to support the
> > > newly released Python 3.12
> > >
> > > 
> > >
> > > John Snow (9):
> > >Python/iotests: Add type hint for nbd module
> > >python/machine: move socket setup out of _base_args property
> > >python/machine: close sock_pair in cleanup path
> > >python/console_socket: accept existing FD in initializer
> > >python/machine: use socketpair() for console connections
> > >python/machine: use socketpair() for qtest connection
> > >python/machine: remove unused sock_dir argument
> > >python/qmp: remove Server.wait_closed() call for Python 3.12
> > >Python: test Python 3.12
> >
> > Is that a pull request or a patch series to be reviewed?
>
> Strange, some of the patches have Reviewed-by tags but others do not.
> I could not find a "Add type hint for nbd module" patch on the mailing
> list before this pull request, so I guess it hasn't been reviewed.
>
> I'll hold off from merging this series for now.
>
> John: Please make sure all patches have been on the mailing list for
> review before sending a pull request.
>
> Stefan
>

Okey Dokey.

--js


Re: [PULL 1/9] Python/iotests: Add type hint for nbd module

2023-10-05 Thread John Snow
On Thu, Oct 5, 2023, 10:05 AM Eric Blake  wrote:

> On Wed, Oct 04, 2023 at 03:46:05PM -0400, John Snow wrote:
> > The test bails gracefully if this module isn't installed, but linters
> > need a little help understanding that. It's enough to just declare the
> > type in this case.
> >
> > (Fixes pylint complaining about use of an uninitialized variable because
> > it isn't wise enough to understand the notrun call is noreturn.)
> >
> > Signed-off-by: John Snow 
> > ---
> >  tests/qemu-iotests/tests/nbd-multiconn | 4 +++-
> >  1 file changed, 3 insertions(+), 1 deletion(-)
>
> Since there's questions about this pull request seeming to be the
> first time this patch has appeared on list, I'll go ahead and review
> it here on the assumption that a v2 pull request is warranted.
>

Sorry... Tried to "sneak" in some real trivial stuff, but didn't mean to
try and pull a fast one. I figured it'd get reviewed and then we'd merge. I
can see this sentiment is not a well expected one 


> >
> > diff --git a/tests/qemu-iotests/tests/nbd-multiconn
> b/tests/qemu-iotests/tests/nbd-multiconn
> > index 478a1eaba27..7e686a786ea 100755
> > --- a/tests/qemu-iotests/tests/nbd-multiconn
> > +++ b/tests/qemu-iotests/tests/nbd-multiconn
> > @@ -20,6 +20,8 @@
> >
> >  import os
> >  from contextlib import contextmanager
> > +from types import ModuleType
> > +
> >  import iotests
> >  from iotests import qemu_img_create, qemu_io
> >
> > @@ -28,7 +30,7 @@ disk = os.path.join(iotests.test_dir, 'disk')
> >  size = '4M'
> >  nbd_sock = os.path.join(iotests.sock_dir, 'nbd_sock')
> >  nbd_uri = 'nbd+unix:///{}?socket=' + nbd_sock
> > -
> > +nbd: ModuleType
>
> Is it possible to put this closer to the code actually using 'nbd', as
> in this region?
>
> | if __name__ == '__main__':
> | try:
> | # Easier to use libnbd than to try and set up parallel
> | # 'qemu-nbd --list' or 'qemu-io' processes, but not all systems
> | # have libnbd installed.
> | import nbd  # type: ignore
>
> but then again, open_nbd() right after your current location utilizes
> the variable, so I guess not.  I trust your judgment on silencing the
> linters, so whether or not you move it (if moving is even possible),
>

It might be possible to move things around to be more localized, but this
was the smallest possible diffstat.

It's not really about the type, the declaration also helps pylint not
complain the "potentially" illegal use. (type: ignore isn't sufficient.)

The alternative is, I think, using some try...except around the import up
at the top, and using a HAVE_NBDLIB variable that we use to exit early
instead. I think there's no real benefit to that much churn, and this gets
the job done with less.

It might be possible to teach pylint that notrun is a NORETURN function,
but I didn't explore that avenue.


> Reviewed-by: Eric Blake 
>

Thanks, and apologies for the fastball.


> --
> Eric Blake, Principal Software Engineer
> Red Hat, Inc.
> Virtualization:  qemu.org | libguestfs.org
>
>


Re: How to tame CI?

2023-10-05 Thread Juan Quintela
Vladimir Sementsov-Ogievskiy  wrote:
> On 26.07.23 16:32, Thomas Huth wrote:
>> On 26/07/2023 15.00, Peter Maydell wrote:
>>> On Wed, 26 Jul 2023 at 13:06, Juan Quintela  wrote:
 To make things easier, this is the part that show how it breaks (this is
 the gcov test):

 357/423 qemu:block / io-qcow2-copy-before-write    
 ERROR   6.38s   exit status 1
>>> PYTHON=/builds/juan.quintela/qemu/build/pyvenv/bin/python3
 MALLOC_PERTURB_=44
 /builds/juan.quintela/qemu/build/pyvenv/bin/python3
 /builds/juan.quintela/qemu/build/../tests/qemu-iotests/check -tap
 -qcow2 copy-before-write --source-dir
 /builds/juan.quintela/qemu/tests/qemu-iotests --build-dir
 /builds/juan.quintela/qemu/build/tests/qemu-iotests
 ― ✀  
 ―
 stderr:
 --- 
 /builds/juan.quintela/qemu/tests/qemu-iotests/tests/copy-before-write.out
 +++ 
 /builds/juan.quintela/qemu/build/scratch/qcow2-file-copy-before-write/copy-before-write.out.bad
 @@ -1,5 +1,21 @@
 -
 +...F
 +==
 +FAIL: test_timeout_break_snapshot (__main__.TestCbwError)
 +--
 +Traceback (most recent call last):
 +  File 
 "/builds/juan.quintela/qemu/tests/qemu-iotests/tests/copy-before-write", 
 line 210, in test_timeout_break_snapshot
 +    self.assertEqual(log, """\
 +AssertionError: 'wrot[195 chars]read 1048576/1048576 bytes at
 offset 0\n1 MiB,[46 chars]c)\n' != 'wrot[195 chars]read failed:
 Permission denied\n'
 +  wrote 524288/524288 bytes at offset 0
 +  512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 +  wrote 524288/524288 bytes at offset 524288
 +  512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 ++ read failed: Permission denied
 +- read 1048576/1048576 bytes at offset 0
 +- 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 +
>>>
>>> This iotest failing is an intermittent that I've seen running
>>> pullreqs on master. I tend to see it on the s390 host. I
>>> suspect a race condition somewhere where it fails if the host
>>> is heavily loaded.
>> It's obviously a failure in an iotest, so let's CC: the
>> corresponding people (done now).
>> 
>
> Sorry for long delay.
>
> Does it still fail?
>
> In the test we expect that copy-before-write operation fails (because
> of throttling and timeout), and therefore snapshot is broken and next
> read from snapshot should fail.
>
> But most probably the copy-before-write operation succeeded in this
> case for some reason.. I don't think that throttling and timeouts in
> block layer can guarantee some determinism.. But usually it works.
>
> we use throttling with bps-write = 300 * 1024, i.e. 300KB per second. and 
> cbw-timeout is set to 1 second.
>
> Then we do write 512K,
>
> then the comment say:
> # We need second write to trigger throttling
>
> and we write another 512K.
>
> first 512K are written, and we should wait 512/300 = 1.7 seconds since
> _start_ of that write before issuing the second one.. But if write was
> slow we may have to wait less than a second from finish of the first
> write start the second one. Then timeout will not fire.
>
> 
>
> I see two possible ways to fix that:
>
> 1. decrease bps-write a bit. For example to 200 BPS.
>
> 2. rework the test to use null-co instead of real images. This way we will 
> not suffer from unstable IO duration.
>
>
> So, is the problem still fire sometimes?  

For me it is random.  When it happens, it do it forever.
And then it stops, and don't happens for a while.

It is not happening for me now.

Later, Juan.




Re: [PATCH v7 12/12] nbd/server: Add FLAG_PAYLOAD support to CMD_BLOCK_STATUS

2023-10-05 Thread Vladimir Sementsov-Ogievskiy

On 25.09.23 22:22, Eric Blake wrote:

Allow a client to request a subset of negotiated meta contexts.  For
example, a client may ask to use a single connection to learn about
both block status and dirty bitmaps, but where the dirty bitmap
queries only need to be performed on a subset of the disk; forcing the
server to compute that information on block status queries in the rest
of the disk is wasted effort (both at the server, and on the amount of
traffic sent over the wire to be parsed and ignored by the client).

Qemu as an NBD client never requests to use more than one meta
context, so it has no need to use block status payloads.  Testing this
instead requires support from libnbd, which CAN access multiple meta
contexts in parallel from a single NBD connection; an interop test
submitted to the libnbd project at the same time as this patch
demonstrates the feature working, as well as testing some corner cases
(for example, when the payload length is longer than the export
length), although other corner cases (like passing the same id
duplicated) requires a protocol fuzzer because libnbd is not wired up
to break the protocol that badly.

This also includes tweaks to 'qemu-nbd --list' to show when a server
is advertising the capability, and to the testsuite to reflect the
addition to that output.

Of note: qemu will always advertise the new feature bit during
NBD_OPT_INFO if extended headers have alreay been negotiated
(regardless of whether any NBD_OPT_SET_META_CONTEXT negotiation has
occurred); but for NBD_OPT_GO, qemu only advertises the feature if
block status is also enabled (that is, if the client does not
negotiate any contexts, then NBD_CMD_BLOCK_STATUS cannot be used, so
the feature is not advertised).

Signed-off-by: Eric Blake 
---

v5: factor out 'id - NBD_MTA_ID_DIRTY_BITMAP' [Vladimir], rework logic
on zero-length requests to be clearer [Vladimir], rebase to earlier
changes


[..]


+/*
+ * nbd_co_block_status_payload_read
+ * Called when a client wants a subset of negotiated contexts via a
+ * BLOCK_STATUS payload.  Check the payload for valid length and
+ * contents.  On success, return 0 with request updated to effective
+ * length.  If request was invalid but all payload consumed, return 0
+ * with request->len and request->contexts->count set to 0 (which will
+ * trigger an appropriate NBD_EINVAL response later on).  Return
+ * negative errno if the payload was not fully consumed.
+ */
+static int
+nbd_co_block_status_payload_read(NBDClient *client, NBDRequest *request,
+ Error **errp)
+{
+int payload_len = request->len;


payload_len should be uint64_t


+g_autofree char *buf = NULL;
+size_t count, i, nr_bitmaps;
+uint32_t id;
+


otherwise, we may do something unexpected here, when reqeuest->len is too big 
for int:


+if (payload_len > NBD_MAX_BUFFER_SIZE) {
+error_setg(errp, "len (%" PRIu64 ") is larger than max len (%u)",
+   request->len, NBD_MAX_BUFFER_SIZE);
+return -EINVAL;
+}
+
+assert(client->contexts.exp == client->exp);
+nr_bitmaps = client->exp->nr_export_bitmaps;
+request->contexts = g_new0(NBDMetaContexts, 1);
+request->contexts->exp = client->exp;
+
+if (payload_len % sizeof(uint32_t) ||
+payload_len < sizeof(NBDBlockStatusPayload) ||
+payload_len > (sizeof(NBDBlockStatusPayload) +
+   sizeof(id) * client->contexts.count)) {
+goto skip;
+}


[..]


   * connection right away, -EAGAIN to indicate we were interrupted and the
@@ -2505,7 +2593,18 @@ static int coroutine_fn 
nbd_co_receive_request(NBDRequestData *req,
  break;

  case NBD_CMD_BLOCK_STATUS:
-request->contexts = >contexts;
+if (extended_with_payload) {
+ret = nbd_co_block_status_payload_read(client, request, errp);
+if (ret < 0) {
+return ret;
+}
+/* payload now consumed */
+check_length = extended_with_payload = false;


why set extended_with_payload to false? it's a bit misleading. And you don't do 
this for WRITE request.


+payload_len = 0;
+valid_flags |= NBD_CMD_FLAG_PAYLOAD_LEN;
+} else {
+request->contexts = >contexts;
+}
  valid_flags |= NBD_CMD_FLAG_REQ_ONE;
  break;



[..]


with payload_len changed to uint64_t, your squash-in applied and s/>/>=/ fixed:
Reviewed-by: Vladimir Sementsov-Ogievskiy 


--
Best regards,
Vladimir




Re: [Libguestfs] [PATCH v7 12/12] nbd/server: Add FLAG_PAYLOAD support to CMD_BLOCK_STATUS

2023-10-05 Thread Vladimir Sementsov-Ogievskiy

On 05.10.23 16:49, Eric Blake wrote:

On Wed, Oct 04, 2023 at 04:55:02PM -0500, Eric Blake wrote:

+static int
+nbd_co_block_status_payload_read(NBDClient *client, NBDRequest *request,
+ Error **errp)


[..]



+for (i = 0; i < count; i++) {
+id = ldl_be_p(buf + sizeof(NBDBlockStatusPayload) + sizeof(id) * i);
+if (id == NBD_META_ID_BASE_ALLOCATION) {
+if (request->contexts->base_allocation) {
+goto skip;
+}


should we also check that base_allocation is negotiated?


Oh, good point.  Without that check, the client can pass in random id
numbers that it never negotiated.  I've queued 1-11 and will probably
send a pull request for those this week, while respinning this patch
to fix the remaining issues you pointed out.


I'm squashing in the following. If you can review it today, I'll
include it in my pull request this afternoon; if not, we still have
time before soft freeze to get it in the next batch.

diff --git i/nbd/server.c w/nbd/server.c
index 30816b42386..62654579cbc 100644
--- i/nbd/server.c
+++ w/nbd/server.c
@@ -2478,19 +2478,22 @@ nbd_co_block_status_payload_read(NBDClient *client, 
NBDRequest *request,
  for (i = 0; i < count; i++) {
  id = ldl_be_p(buf + sizeof(NBDBlockStatusPayload) + sizeof(id) * i);
  if (id == NBD_META_ID_BASE_ALLOCATION) {
-if (request->contexts->base_allocation) {
+if (!client->contexts.base_allocation ||
+request->contexts->base_allocation) {
  goto skip;
  }
  request->contexts->base_allocation = true;
  } else if (id == NBD_META_ID_ALLOCATION_DEPTH) {
-if (request->contexts->allocation_depth) {
+if (!client->contexts.allocation_depth ||
+request->contexts->allocation_depth) {
  goto skip;
  }
  request->contexts->allocation_depth = true;
  } else {
-int idx = id - NBD_META_ID_DIRTY_BITMAP;
+unsigned idx = id - NBD_META_ID_DIRTY_BITMAP;

-if (idx > nr_bitmaps || request->contexts->bitmaps[idx]) {
+if (idx > nr_bitmaps || !client->contexts.bitmaps[idx] ||


Oops, I didn't notice: s/>/>=/, as nr_bitmaps is length of array.


+request->contexts->bitmaps[idx]) {
  goto skip;
  }
  request->contexts->bitmaps[idx] = true;
diff --git i/nbd/trace-events w/nbd/trace-events
index 3cf2d00e458..00ae3216a11 100644
--- i/nbd/trace-events
+++ w/nbd/trace-events
@@ -70,7 +70,7 @@ nbd_co_send_chunk_read(uint64_t cookie, uint64_t offset, void 
*data, uint64_t si
  nbd_co_send_chunk_read_hole(uint64_t cookie, uint64_t offset, uint64_t size) "Send structured read 
hole reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", len = %" PRIu64
  nbd_co_send_extents(uint64_t cookie, unsigned int extents, uint32_t id, uint64_t length, int last) 
"Send block status reply: cookie = %" PRIu64 ", extents = %u, context = %d (extents cover 
%" PRIu64 " bytes, last chunk = %d)"
  nbd_co_send_chunk_error(uint64_t cookie, int err, const char *errname, const char *msg) 
"Send structured error reply: cookie = %" PRIu64 ", error = %d (%s), msg = '%s'"
-nbd_co_receive_block_status_payload_compliance(uint64_t from, int len) "client sent unusable 
block status payload: from=0x%" PRIx64 ", len=0x%x"
+nbd_co_receive_block_status_payload_compliance(uint64_t from, uint64_t len) "client sent 
unusable block status payload: from=0x%" PRIx64 ", len=0x%" PRIx64
  nbd_co_receive_request_decode_type(uint64_t cookie, uint16_t type, const char *name) "Decoding type: 
cookie = %" PRIu64 ", type = %" PRIu16 " (%s)"
  nbd_co_receive_request_payload_received(uint64_t cookie, uint64_t len) "Payload received: 
cookie = %" PRIu64 ", len = %" PRIu64
  nbd_co_receive_ext_payload_compliance(uint64_t from, uint64_t len) "client sent 
non-compliant write without payload flag: from=0x%" PRIx64 ", len=0x%" PRIx64





--
Best regards,
Vladimir




Re: [PATCH v6 02/14] qmp_shell.py: _fill_completion() use .command() instead of .cmd()

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:38PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> We just want to ignore failure, so we don't need low level .cmd(). This
> helps further renaming .command() to .cmd().
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  python/qemu/qmp/qmp_shell.py | 20 ++--
>  1 file changed, 14 insertions(+), 6 deletions(-)
> 

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH v6 01/14] python/qemu/qmp/legacy: cmd(): drop cmd_id unused argument

2023-10-05 Thread Eric Blake
On Thu, Oct 05, 2023 at 04:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> The argument is unused, let's drop it for now, as we are going to
> refactor the interface and don't want to refactor unused things.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  python/qemu/qmp/legacy.py | 6 +-
>  1 file changed, 1 insertion(+), 5 deletions(-)

Reviewed-by: Eric Blake 

Although I welcome John's opinion as to how long we have to maintain
legacy.py at all, given that its name implies there is a better thing
we should have converted to already.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PULL 1/9] Python/iotests: Add type hint for nbd module

2023-10-05 Thread Eric Blake
On Wed, Oct 04, 2023 at 03:46:05PM -0400, John Snow wrote:
> The test bails gracefully if this module isn't installed, but linters
> need a little help understanding that. It's enough to just declare the
> type in this case.
> 
> (Fixes pylint complaining about use of an uninitialized variable because
> it isn't wise enough to understand the notrun call is noreturn.)
> 
> Signed-off-by: John Snow 
> ---
>  tests/qemu-iotests/tests/nbd-multiconn | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)

Since there's questions about this pull request seeming to be the
first time this patch has appeared on list, I'll go ahead and review
it here on the assumption that a v2 pull request is warranted.

> 
> diff --git a/tests/qemu-iotests/tests/nbd-multiconn 
> b/tests/qemu-iotests/tests/nbd-multiconn
> index 478a1eaba27..7e686a786ea 100755
> --- a/tests/qemu-iotests/tests/nbd-multiconn
> +++ b/tests/qemu-iotests/tests/nbd-multiconn
> @@ -20,6 +20,8 @@
>  
>  import os
>  from contextlib import contextmanager
> +from types import ModuleType
> +
>  import iotests
>  from iotests import qemu_img_create, qemu_io
>  
> @@ -28,7 +30,7 @@ disk = os.path.join(iotests.test_dir, 'disk')
>  size = '4M'
>  nbd_sock = os.path.join(iotests.sock_dir, 'nbd_sock')
>  nbd_uri = 'nbd+unix:///{}?socket=' + nbd_sock
> -
> +nbd: ModuleType

Is it possible to put this closer to the code actually using 'nbd', as
in this region?

| if __name__ == '__main__':
| try:
| # Easier to use libnbd than to try and set up parallel
| # 'qemu-nbd --list' or 'qemu-io' processes, but not all systems
| # have libnbd installed.
| import nbd  # type: ignore

but then again, open_nbd() right after your current location utilizes
the variable, so I guess not.  I trust your judgment on silencing the
linters, so whether or not you move it (if moving is even possible),

Reviewed-by: Eric Blake 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




[PATCH v6 05/14] python/qemu: rename command() to cmd()

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
Use a shorter name. We are going to move in iotests from qmp() to
command() where possible. But command() is longer than qmp() and don't
look better. Let's rename.

You can simply grep for '\.command(' and for 'def command(' to check
that everything is updated (command() in tests/docker/docker.py is
unrelated).

Signed-off-by: Vladimir Sementsov-Ogievskiy 
Reviewed-by: Daniel P. Berrangé 
---
 docs/devel/testing.rst|  10 +-
 python/qemu/machine/machine.py|   8 +-
 python/qemu/qmp/legacy.py |   2 +-
 python/qemu/qmp/qmp_shell.py  |   2 +-
 python/qemu/utils/qemu_ga_client.py   |   2 +-
 python/qemu/utils/qom.py  |   8 +-
 python/qemu/utils/qom_common.py   |   2 +-
 python/qemu/utils/qom_fuse.py |   6 +-
 scripts/cpu-x86-uarch-abi.py  |   8 +-
 scripts/device-crash-test |   8 +-
 scripts/render_block_graph.py |   8 +-
 tests/avocado/avocado_qemu/__init__.py|   4 +-
 tests/avocado/cpu_queries.py  |   5 +-
 tests/avocado/hotplug_cpu.py  |  10 +-
 tests/avocado/info_usernet.py |   4 +-
 tests/avocado/machine_arm_integratorcp.py |   6 +-
 tests/avocado/machine_m68k_nextcube.py|   4 +-
 tests/avocado/machine_mips_malta.py   |   6 +-
 tests/avocado/machine_s390_ccw_virtio.py  |  28 ++--
 tests/avocado/migration.py|  10 +-
 tests/avocado/pc_cpu_hotplug_props.py |   2 +-
 tests/avocado/version.py  |   4 +-
 tests/avocado/virtio_check_params.py  |   6 +-
 tests/avocado/virtio_version.py   |   5 +-
 tests/avocado/x86_cpu_model_versions.py   |  13 +-
 tests/migration/guestperf/engine.py   | 150 +++---
 tests/qemu-iotests/256|  34 ++---
 tests/qemu-iotests/257|  36 +++---
 28 files changed, 198 insertions(+), 193 deletions(-)

diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst
index 5d1fc0aa95..21525e9aae 100644
--- a/docs/devel/testing.rst
+++ b/docs/devel/testing.rst
@@ -1014,8 +1014,8 @@ class.  Here's a simple usage example:
   """
   def test_qmp_human_info_version(self):
   self.vm.launch()
-  res = self.vm.command('human-monitor-command',
-command_line='info version')
+  res = self.vm.cmd('human-monitor-command',
+command_line='info version')
   self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)')
 
 To execute your test, run:
@@ -1065,15 +1065,15 @@ and hypothetical example follows:
   first_machine.launch()
   second_machine.launch()
 
-  first_res = first_machine.command(
+  first_res = first_machine.cmd(
   'human-monitor-command',
   command_line='info version')
 
-  second_res = second_machine.command(
+  second_res = second_machine.cmd(
   'human-monitor-command',
   command_line='info version')
 
-  third_res = self.get_vm(name='third_machine').command(
+  third_res = self.get_vm(name='third_machine').cmd(
   'human-monitor-command',
   command_line='info version')
 
diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py
index dd1a79cb37..c4e80544bd 100644
--- a/python/qemu/machine/machine.py
+++ b/python/qemu/machine/machine.py
@@ -697,16 +697,16 @@ def qmp(self, cmd: str,
 self._quit_issued = True
 return ret
 
-def command(self, cmd: str,
-conv_keys: bool = True,
-**args: Any) -> QMPReturnValue:
+def cmd(self, cmd: str,
+conv_keys: bool = True,
+**args: Any) -> QMPReturnValue:
 """
 Invoke a QMP command.
 On success return the response dict.
 On failure raise an exception.
 """
 qmp_args = self._qmp_args(conv_keys, args)
-ret = self._qmp.command(cmd, **qmp_args)
+ret = self._qmp.cmd(cmd, **qmp_args)
 if cmd == 'quit':
 self._quit_issued = True
 return ret
diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py
index e5fa1ce9c4..22a2b5616e 100644
--- a/python/qemu/qmp/legacy.py
+++ b/python/qemu/qmp/legacy.py
@@ -207,7 +207,7 @@ def cmd_raw(self, name: str,
 qmp_cmd['arguments'] = args
 return self.cmd_obj(qmp_cmd)
 
-def command(self, cmd: str, **kwds: object) -> QMPReturnValue:
+def cmd(self, cmd: str, **kwds: object) -> QMPReturnValue:
 """
 Build and send a QMP command to the monitor, report errors if any
 """
diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py
index 988d79c01b..98e684e9e8 100644
--- a/python/qemu/qmp/qmp_shell.py
+++ b/python/qemu/qmp/qmp_shell.py
@@ -202,7 +202,7 @@ def close(self) -> None:
 
 def _fill_completion(self) -> None:
 try:
-cmds = 

[PATCH v6 04/14] python: rename QEMUMonitorProtocol.cmd() to cmd_raw()

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
Having cmd() and command() methods in one class doesn't look good.
Rename cmd() to cmd_raw(), to show its meaning better.

We also want to rename command() to cmd() in future, so this commit is
a necessary step.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 python/qemu/machine/machine.py | 2 +-
 python/qemu/qmp/legacy.py  | 4 ++--
 tests/qemu-iotests/iotests.py  | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py
index 35d5a672db..dd1a79cb37 100644
--- a/python/qemu/machine/machine.py
+++ b/python/qemu/machine/machine.py
@@ -692,7 +692,7 @@ def qmp(self, cmd: str,
 conv_keys = True
 
 qmp_args = self._qmp_args(conv_keys, args)
-ret = self._qmp.cmd(cmd, args=qmp_args)
+ret = self._qmp.cmd_raw(cmd, args=qmp_args)
 if cmd == 'quit' and 'error' not in ret and 'return' in ret:
 self._quit_issued = True
 return ret
diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py
index fe115e301c..e5fa1ce9c4 100644
--- a/python/qemu/qmp/legacy.py
+++ b/python/qemu/qmp/legacy.py
@@ -194,8 +194,8 @@ def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage:
 )
 )
 
-def cmd(self, name: str,
-args: Optional[Dict[str, object]] = None) -> QMPMessage:
+def cmd_raw(self, name: str,
+args: Optional[Dict[str, object]] = None) -> QMPMessage:
 """
 Build a QMP command and send it to the QMP Monitor.
 
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index ef66fbd62b..8ffd9fb660 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -460,7 +460,7 @@ def __init__(self, *args: str, instance_id: str = 'a', qmp: 
bool = False):
 def qmp(self, cmd: str, args: Optional[Dict[str, object]] = None) \
 -> QMPMessage:
 assert self._qmp is not None
-return self._qmp.cmd(cmd, args)
+return self._qmp.cmd_raw(cmd, args)
 
 def get_qmp(self) -> QEMUMonitorProtocol:
 assert self._qmp is not None
-- 
2.34.1




[PATCH v6 01/14] python/qemu/qmp/legacy: cmd(): drop cmd_id unused argument

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
The argument is unused, let's drop it for now, as we are going to
refactor the interface and don't want to refactor unused things.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 python/qemu/qmp/legacy.py | 6 +-
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py
index e1e9383978..fe115e301c 100644
--- a/python/qemu/qmp/legacy.py
+++ b/python/qemu/qmp/legacy.py
@@ -195,20 +195,16 @@ def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage:
 )
 
 def cmd(self, name: str,
-args: Optional[Dict[str, object]] = None,
-cmd_id: Optional[object] = None) -> QMPMessage:
+args: Optional[Dict[str, object]] = None) -> QMPMessage:
 """
 Build a QMP command and send it to the QMP Monitor.
 
 :param name: command name (string)
 :param args: command arguments (dict)
-:param cmd_id: command id (dict, list, string or int)
 """
 qmp_cmd: QMPMessage = {'execute': name}
 if args:
 qmp_cmd['arguments'] = args
-if cmd_id:
-qmp_cmd['id'] = cmd_id
 return self.cmd_obj(qmp_cmd)
 
 def command(self, cmd: str, **kwds: object) -> QMPReturnValue:
-- 
2.34.1




[PATCH v6 13/14] tests/vm/basevm.py: use cmd() instead of qmp()

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
We don't expect failure here and need 'result' object. cmd() is better
in this case.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 tests/vm/basevm.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py
index a97e23b0ce..8aef4cff96 100644
--- a/tests/vm/basevm.py
+++ b/tests/vm/basevm.py
@@ -312,8 +312,8 @@ def boot(self, img, extra_args=[]):
 self._guest = guest
 # Init console so we can start consuming the chars.
 self.console_init()
-usernet_info = guest.qmp("human-monitor-command",
- command_line="info usernet").get("return")
+usernet_info = guest.cmd("human-monitor-command",
+ command_line="info usernet")
 self.ssh_port = get_info_usernet_hostfwd_port(usernet_info)
 if not self.ssh_port:
 raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \
-- 
2.34.1




[PATCH v6 09/14] iotests: refactor some common qmp result checks into generic pattern

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
To simplify further conversion.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 tests/qemu-iotests/040 | 3 ++-
 tests/qemu-iotests/147 | 3 ++-
 tests/qemu-iotests/155 | 4 ++--
 tests/qemu-iotests/218 | 4 ++--
 tests/qemu-iotests/296 | 3 ++-
 5 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index 5601a4873c..e61e7f2433 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -62,9 +62,10 @@ class ImageCommitTestCase(iotests.QMPTestCase):
 self.assert_no_active_block_jobs()
 if node_names:
 result = self.vm.qmp('block-commit', device='drive0', 
top_node=top, base_node=base)
+self.assert_qmp(result, 'return', {})
 else:
 result = self.vm.qmp('block-commit', device='drive0', top=top, 
base=base)
-self.assert_qmp(result, 'return', {})
+self.assert_qmp(result, 'return', {})
 self.wait_for_complete(need_ready)
 
 def run_default_commit_test(self):
diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147
index 47dfa62e6b..770b73e2f4 100755
--- a/tests/qemu-iotests/147
+++ b/tests/qemu-iotests/147
@@ -159,10 +159,11 @@ class BuiltinNBD(NBDBlockdevAddBase):
 
 if export_name is None:
 result = self.server.qmp('nbd-server-add', device='nbd-export')
+self.assert_qmp(result, 'return', {})
 else:
 result = self.server.qmp('nbd-server-add', device='nbd-export',
  name=export_name)
-self.assert_qmp(result, 'return', {})
+self.assert_qmp(result, 'return', {})
 
 if export_name2 is not None:
 result = self.server.qmp('nbd-server-add', device='nbd-export',
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
index eadda52615..d3e1b7401e 100755
--- a/tests/qemu-iotests/155
+++ b/tests/qemu-iotests/155
@@ -181,6 +181,7 @@ class MirrorBaseClass(BaseClass):
 result = self.vm.qmp(self.cmd, job_id='mirror-job', 
device='source',
  sync=sync, target='target',
  auto_finalize=False)
+self.assert_qmp(result, 'return', {})
 else:
 if self.existing:
 mode = 'existing'
@@ -190,8 +191,7 @@ class MirrorBaseClass(BaseClass):
  sync=sync, target=target_img,
  format=iotests.imgfmt, mode=mode,
  node_name='target', auto_finalize=False)
-
-self.assert_qmp(result, 'return', {})
+self.assert_qmp(result, 'return', {})
 
 self.vm.run_job('mirror-job', auto_finalize=False,
 pre_finalize=self.openBacking, auto_dismiss=True)
diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218
index 6320c4cb56..5e74c55b6a 100755
--- a/tests/qemu-iotests/218
+++ b/tests/qemu-iotests/218
@@ -61,14 +61,14 @@ def start_mirror(vm, speed=None, buf_size=None):
  sync='full',
  speed=speed,
  buf_size=buf_size)
+assert ret['return'] == {}
 else:
 ret = vm.qmp('blockdev-mirror',
  job_id='mirror',
  device='source',
  target='target',
  sync='full')
-
-assert ret['return'] == {}
+assert ret['return'] == {}
 
 
 log('')
diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296
index 0d21b740a7..19a674c5ae 100755
--- a/tests/qemu-iotests/296
+++ b/tests/qemu-iotests/296
@@ -133,9 +133,10 @@ class EncryptionSetupTestCase(iotests.QMPTestCase):
 
 if reOpen:
 result = vm.qmp(command, options=[opts])
+self.assert_qmp(result, 'return', {})
 else:
 result = vm.qmp(command, **opts)
-self.assert_qmp(result, 'return', {})
+self.assert_qmp(result, 'return', {})
 
 
 ###
-- 
2.34.1




[PATCH v6 07/14] iotests: QemuStorageDaemon: add cmd() method like in QEMUMachine.

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
Add similar method for consistency.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 tests/qemu-iotests/iotests.py | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 8ffd9fb660..6cc50f0b50 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -38,7 +38,7 @@
 from contextlib import contextmanager
 
 from qemu.machine import qtest
-from qemu.qmp.legacy import QMPMessage, QEMUMonitorProtocol
+from qemu.qmp.legacy import QMPMessage, QMPReturnValue, QEMUMonitorProtocol
 from qemu.utils import VerboseProcessError
 
 # Use this logger for logging messages directly from the iotests module
@@ -466,6 +466,11 @@ def get_qmp(self) -> QEMUMonitorProtocol:
 assert self._qmp is not None
 return self._qmp
 
+def cmd(self, cmd: str, args: Optional[Dict[str, object]] = None) \
+-> QMPReturnValue:
+assert self._qmp is not None
+return self._qmp.cmd(cmd, **(args or {}))
+
 def stop(self, kill_signal=15):
 self._p.send_signal(kill_signal)
 self._p.wait()
-- 
2.34.1




[PATCH v6 08/14] iotests: add some missed checks of qmp result

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 tests/qemu-iotests/041| 1 +
 tests/qemu-iotests/151| 1 +
 tests/qemu-iotests/152| 2 ++
 tests/qemu-iotests/tests/migrate-bitmaps-test | 2 ++
 4 files changed, 6 insertions(+)

diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index 8429958bf0..4d7a829b65 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -1087,6 +1087,7 @@ class TestRepairQuorum(iotests.QMPTestCase):
 result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
  snapshot_file=quorum_snapshot_file,
  snapshot_node_name="snap1");
+self.assert_qmp(result, 'return', {})
 
 result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
  sync='full', node_name='repair0', replaces="img1",
diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151
index b4d1bc2553..668d0c1e9c 100755
--- a/tests/qemu-iotests/151
+++ b/tests/qemu-iotests/151
@@ -159,6 +159,7 @@ class TestActiveMirror(iotests.QMPTestCase):
  sync='full',
  copy_mode='write-blocking',
  speed=1)
+self.assert_qmp(result, 'return', {})
 
 self.vm.hmp_qemu_io('source', 'break write_aio A')
 self.vm.hmp_qemu_io('source', 'aio_write 0 1M')  # 1
diff --git a/tests/qemu-iotests/152 b/tests/qemu-iotests/152
index 4e179c340f..b73a0d08a2 100755
--- a/tests/qemu-iotests/152
+++ b/tests/qemu-iotests/152
@@ -43,6 +43,7 @@ class TestUnaligned(iotests.QMPTestCase):
 def test_unaligned(self):
 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
  granularity=65536, target=target_img)
+self.assert_qmp(result, 'return', {})
 self.complete_and_wait()
 self.vm.shutdown()
 self.assertEqual(iotests.image_size(test_img), 
iotests.image_size(target_img),
@@ -51,6 +52,7 @@ class TestUnaligned(iotests.QMPTestCase):
 def test_unaligned_with_update(self):
 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
  granularity=65536, target=target_img)
+self.assert_qmp(result, 'return', {})
 self.wait_ready()
 self.vm.hmp_qemu_io('drive0', 'write 0 512')
 self.complete_and_wait(wait_ready=False)
diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test 
b/tests/qemu-iotests/tests/migrate-bitmaps-test
index 59f3357580..8668caae1e 100755
--- a/tests/qemu-iotests/tests/migrate-bitmaps-test
+++ b/tests/qemu-iotests/tests/migrate-bitmaps-test
@@ -101,6 +101,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
 sha256 = get_bitmap_hash(self.vm_a)
 
 result = self.vm_a.qmp('migrate', uri=mig_cmd)
+self.assert_qmp(result, 'return', {})
 while True:
 event = self.vm_a.event_wait('MIGRATION')
 if event['data']['status'] == 'completed':
@@ -176,6 +177,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
 self.assert_qmp(result, 'return', {})
 
 result = self.vm_a.qmp('migrate', uri=mig_cmd)
+self.assert_qmp(result, 'return', {})
 while True:
 event = self.vm_a.event_wait('MIGRATION')
 if event['data']['status'] == 'completed':
-- 
2.34.1




[PATCH v6 12/14] iotests.py: pause_job(): drop return value

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
The returned value is unused. It's simple to check by command

 git grep -B 3 '\.pause_job('

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 tests/qemu-iotests/iotests.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 6cc50f0b50..467faca43c 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -1338,8 +1338,7 @@ def pause_job(self, job_id='job0', wait=True):
 result = self.vm.qmp('block-job-pause', device=job_id)
 self.assert_qmp(result, 'return', {})
 if wait:
-return self.pause_wait(job_id)
-return result
+self.pause_wait(job_id)
 
 def case_skip(self, reason):
 '''Skip this test case'''
-- 
2.34.1




[PATCH v6 06/14] python/machine.py: upgrade vm.cmd() method

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
The method is not popular in iotests, we prefer use vm.qmp() and then
check success by hand.. But that's not optimal. To simplify movement to
vm.cmd() let's support same interface improvements like in vm.qmp().

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 python/qemu/machine/machine.py | 12 +++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py
index c4e80544bd..352e15b074 100644
--- a/python/qemu/machine/machine.py
+++ b/python/qemu/machine/machine.py
@@ -698,13 +698,23 @@ def qmp(self, cmd: str,
 return ret
 
 def cmd(self, cmd: str,
-conv_keys: bool = True,
+args_dict: Optional[Dict[str, object]] = None,
+conv_keys: Optional[bool] = None,
 **args: Any) -> QMPReturnValue:
 """
 Invoke a QMP command.
 On success return the response dict.
 On failure raise an exception.
 """
+if args_dict is not None:
+assert not args
+assert conv_keys is None
+args = args_dict
+conv_keys = False
+
+if conv_keys is None:
+conv_keys = True
+
 qmp_args = self._qmp_args(conv_keys, args)
 ret = self._qmp.cmd(cmd, **qmp_args)
 if cmd == 'quit':
-- 
2.34.1




[PATCH v6 11/14] iotests: drop some extra ** in qmp() call

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
qmp() method supports passing dict (if it doesn't need substituting
'_' to '-' in keys). So, drop some extra '**' operators.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 tests/qemu-iotests/040|  4 +-
 tests/qemu-iotests/041| 14 +++---
 tests/qemu-iotests/129|  2 +-
 tests/qemu-iotests/147|  2 +-
 tests/qemu-iotests/155|  2 +-
 tests/qemu-iotests/264| 12 ++---
 tests/qemu-iotests/295|  5 +-
 tests/qemu-iotests/296| 15 +++---
 tests/qemu-iotests/tests/migrate-bitmaps-test |  4 +-
 .../tests/mirror-ready-cancel-error   | 50 +--
 10 files changed, 54 insertions(+), 56 deletions(-)

diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index e61e7f2433..4b8bf09a5d 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -774,7 +774,7 @@ class TestCommitWithFilters(iotests.QMPTestCase):
 result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg')
 self.assert_qmp(result, 'return', {})
 
-result = self.vm.qmp('blockdev-add', **{
+result = self.vm.qmp('blockdev-add', {
 'node-name': 'top-filter',
 'driver': 'throttle',
 'throttle-group': 'tg',
@@ -935,7 +935,7 @@ class TestCommitWithOverriddenBacking(iotests.QMPTestCase):
 self.vm.launch()
 
 # Use base_b instead of base_a as the backing of top
-result = self.vm.qmp('blockdev-add', **{
+result = self.vm.qmp('blockdev-add', {
 'node-name': 'top',
 'driver': iotests.imgfmt,
 'file': {
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index 550e4dc391..3aef42aec8 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -236,7 +236,7 @@ class TestSingleBlockdev(TestSingleDrive):
 args = {'driver': iotests.imgfmt,
 'node-name': self.qmp_target,
 'file': { 'filename': target_img, 'driver': 'file' } }
-result = self.vm.qmp("blockdev-add", **args)
+result = self.vm.qmp("blockdev-add", args)
 self.assert_qmp(result, 'return', {})
 
 def test_mirror_to_self(self):
@@ -963,7 +963,7 @@ class TestRepairQuorum(iotests.QMPTestCase):
 #assemble the quorum block device from the individual files
 args = { "driver": "quorum", "node-name": "quorum0",
  "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] }
-result = self.vm.qmp("blockdev-add", **args)
+result = self.vm.qmp("blockdev-add", args)
 self.assert_qmp(result, 'return', {})
 
 
@@ -1278,7 +1278,7 @@ class TestReplaces(iotests.QMPTestCase):
 """
 Check that we can replace filter nodes.
 """
-result = self.vm.qmp('blockdev-add', **{
+result = self.vm.qmp('blockdev-add', {
  'driver': 'copy-on-read',
  'node-name': 'filter0',
  'file': {
@@ -1319,7 +1319,7 @@ class TestFilters(iotests.QMPTestCase):
 self.vm = iotests.VM().add_device('virtio-scsi,id=vio-scsi')
 self.vm.launch()
 
-result = self.vm.qmp('blockdev-add', **{
+result = self.vm.qmp('blockdev-add', {
 'node-name': 'target',
 'driver': iotests.imgfmt,
 'file': {
@@ -1355,7 +1355,7 @@ class TestFilters(iotests.QMPTestCase):
 os.remove(backing_img)
 
 def test_cor(self):
-result = self.vm.qmp('blockdev-add', **{
+result = self.vm.qmp('blockdev-add', {
 'node-name': 'filter',
 'driver': 'copy-on-read',
 'file': self.filterless_chain
@@ -1384,7 +1384,7 @@ class TestFilters(iotests.QMPTestCase):
 assert target_map[1]['depth'] == 0
 
 def test_implicit_mirror_filter(self):
-result = self.vm.qmp('blockdev-add', **self.filterless_chain)
+result = self.vm.qmp('blockdev-add', self.filterless_chain)
 self.assert_qmp(result, 'return', {})
 
 # We need this so we can query from above the mirror node
@@ -1418,7 +1418,7 @@ class TestFilters(iotests.QMPTestCase):
 def test_explicit_mirror_filter(self):
 # Same test as above, but this time we give the mirror filter
 # a node-name so it will not be invisible
-result = self.vm.qmp('blockdev-add', **self.filterless_chain)
+result = self.vm.qmp('blockdev-add', self.filterless_chain)
 self.assert_qmp(result, 'return', {})
 
 # We need this so we can query from above the mirror node
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
index 

[PATCH v6 03/14] scripts/cpu-x86-uarch-abi.py: use .command() instead of .cmd()

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
Here we don't expect a failure. In case on failure we'll crash on
trying to access ['return']. Let's better use .command() that clearly
raise on failure.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 scripts/cpu-x86-uarch-abi.py | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/scripts/cpu-x86-uarch-abi.py b/scripts/cpu-x86-uarch-abi.py
index 82ff07582f..893afd1b35 100644
--- a/scripts/cpu-x86-uarch-abi.py
+++ b/scripts/cpu-x86-uarch-abi.py
@@ -69,7 +69,7 @@
 shell = QEMUMonitorProtocol(sock)
 shell.connect()
 
-models = shell.cmd("query-cpu-definitions")
+models = shell.command("query-cpu-definitions")
 
 # These QMP props don't correspond to CPUID fatures
 # so ignore them
@@ -85,7 +85,7 @@
 
 names = []
 
-for model in models["return"]:
+for model in models:
 if "alias-of" in model:
 continue
 names.append(model["name"])
@@ -93,12 +93,12 @@
 models = {}
 
 for name in sorted(names):
-cpu = shell.cmd("query-cpu-model-expansion",
- { "type": "static",
-   "model": { "name": name }})
+cpu = shell.command("query-cpu-model-expansion",
+{ "type": "static",
+  "model": { "name": name }})
 
 got = {}
-for (feature, present) in cpu["return"]["model"]["props"].items():
+for (feature, present) in cpu["model"]["props"].items():
 if present and feature not in skip:
 got[feature] = True
 
-- 
2.34.1




[PATCH v6 02/14] qmp_shell.py: _fill_completion() use .command() instead of .cmd()

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
We just want to ignore failure, so we don't need low level .cmd(). This
helps further renaming .command() to .cmd().

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 python/qemu/qmp/qmp_shell.py | 20 ++--
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py
index 619ab42ced..988d79c01b 100644
--- a/python/qemu/qmp/qmp_shell.py
+++ b/python/qemu/qmp/qmp_shell.py
@@ -91,14 +91,21 @@
 import sys
 from typing import (
 IO,
+Dict,
 Iterator,
 List,
 NoReturn,
 Optional,
 Sequence,
+cast,
 )
 
-from qemu.qmp import ConnectError, QMPError, SocketAddrT
+from qemu.qmp import (
+ConnectError,
+ExecuteError,
+QMPError,
+SocketAddrT,
+)
 from qemu.qmp.legacy import (
 QEMUMonitorProtocol,
 QMPBadPortError,
@@ -194,11 +201,12 @@ def close(self) -> None:
 super().close()
 
 def _fill_completion(self) -> None:
-cmds = self.cmd('query-commands')
-if 'error' in cmds:
-return
-for cmd in cmds['return']:
-self._completer.append(cmd['name'])
+try:
+cmds = cast(List[Dict[str, str]], self.command('query-commands'))
+for cmd in cmds:
+self._completer.append(cmd['name'])
+except ExecuteError:
+pass
 
 def _completer_setup(self) -> None:
 self._completer = QMPCompleter()
-- 
2.34.1




[PATCH v6 10/14] iotests: drop some occasional semicolons

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 tests/qemu-iotests/041 | 2 +-
 tests/qemu-iotests/196 | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index 4d7a829b65..550e4dc391 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -1086,7 +1086,7 @@ class TestRepairQuorum(iotests.QMPTestCase):
 def test_after_a_quorum_snapshot(self):
 result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
  snapshot_file=quorum_snapshot_file,
- snapshot_node_name="snap1");
+ snapshot_node_name="snap1")
 self.assert_qmp(result, 'return', {})
 
 result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
diff --git a/tests/qemu-iotests/196 b/tests/qemu-iotests/196
index 76509a5ad1..27c1629be3 100755
--- a/tests/qemu-iotests/196
+++ b/tests/qemu-iotests/196
@@ -46,7 +46,7 @@ class TestInvalidateAutoclear(iotests.QMPTestCase):
 
 def test_migration(self):
 result = self.vm_a.qmp('migrate', uri='exec:cat>' + migfile)
-self.assert_qmp(result, 'return', {});
+self.assert_qmp(result, 'return', {})
 self.assertNotEqual(self.vm_a.event_wait("STOP"), None)
 
 with open(disk, 'r+b') as f:
-- 
2.34.1




[PATCH v6 00/14] iotests: use vm.cmd()

2023-10-05 Thread Vladimir Sementsov-Ogievskiy
Hi all!

Let's get rid of pattern

result = self.vm.qmp(...)
self.assert_qmp(result, 'return', {})

And switch to just

self.vm.cmd(...)

v6: rebase on master

Vladimir Sementsov-Ogievskiy (14):
  python/qemu/qmp/legacy: cmd(): drop cmd_id unused argument
  qmp_shell.py: _fill_completion() use .command() instead of .cmd()
  scripts/cpu-x86-uarch-abi.py: use .command() instead of .cmd()
  python: rename QEMUMonitorProtocol.cmd() to cmd_raw()
  python/qemu: rename command() to cmd()
  python/machine.py: upgrade vm.cmd() method
  iotests: QemuStorageDaemon: add cmd() method like in QEMUMachine.
  iotests: add some missed checks of qmp result
  iotests: refactor some common qmp result checks into generic pattern
  iotests: drop some occasional semicolons
  iotests: drop some extra ** in qmp() call
  iotests.py: pause_job(): drop return value
  tests/vm/basevm.py: use cmd() instead of qmp()
  python: use vm.cmd() instead of vm.qmp() where appropriate

 docs/devel/testing.rst|  10 +-
 python/qemu/machine/machine.py|  20 +-
 python/qemu/qmp/legacy.py |  10 +-
 python/qemu/qmp/qmp_shell.py  |  20 +-
 python/qemu/utils/qemu_ga_client.py   |   2 +-
 python/qemu/utils/qom.py  |   8 +-
 python/qemu/utils/qom_common.py   |   2 +-
 python/qemu/utils/qom_fuse.py |   6 +-
 scripts/cpu-x86-uarch-abi.py  |   8 +-
 scripts/device-crash-test |   8 +-
 scripts/render_block_graph.py |   8 +-
 tests/avocado/avocado_qemu/__init__.py|   4 +-
 tests/avocado/cpu_queries.py  |   5 +-
 tests/avocado/hotplug_cpu.py  |  10 +-
 tests/avocado/info_usernet.py |   4 +-
 tests/avocado/machine_arm_integratorcp.py |   6 +-
 tests/avocado/machine_m68k_nextcube.py|   4 +-
 tests/avocado/machine_mips_malta.py   |   6 +-
 tests/avocado/machine_s390_ccw_virtio.py  |  28 +-
 tests/avocado/migration.py|  10 +-
 tests/avocado/pc_cpu_hotplug_props.py |   2 +-
 tests/avocado/version.py  |   4 +-
 tests/avocado/virtio_check_params.py  |   6 +-
 tests/avocado/virtio_version.py   |   5 +-
 tests/avocado/vnc.py  |  16 +-
 tests/avocado/x86_cpu_model_versions.py   |  13 +-
 tests/migration/guestperf/engine.py   | 150 +++---
 tests/qemu-iotests/030| 168 +++---
 tests/qemu-iotests/040| 171 +++
 tests/qemu-iotests/041| 482 --
 tests/qemu-iotests/045|  15 +-
 tests/qemu-iotests/055|  62 +--
 tests/qemu-iotests/056|  77 ++-
 tests/qemu-iotests/093|  42 +-
 tests/qemu-iotests/118| 225 
 tests/qemu-iotests/124| 102 ++--
 tests/qemu-iotests/129|  14 +-
 tests/qemu-iotests/132|   5 +-
 tests/qemu-iotests/139|  45 +-
 tests/qemu-iotests/147|  30 +-
 tests/qemu-iotests/151|  56 +-
 tests/qemu-iotests/152|   8 +-
 tests/qemu-iotests/155|  55 +-
 tests/qemu-iotests/165|   8 +-
 tests/qemu-iotests/196|   3 +-
 tests/qemu-iotests/205|   6 +-
 tests/qemu-iotests/218| 105 ++--
 tests/qemu-iotests/245| 245 -
 tests/qemu-iotests/256|  34 +-
 tests/qemu-iotests/257|  36 +-
 tests/qemu-iotests/264|  31 +-
 tests/qemu-iotests/281|  21 +-
 tests/qemu-iotests/295|  16 +-
 tests/qemu-iotests/296|  21 +-
 tests/qemu-iotests/298|  13 +-
 tests/qemu-iotests/300|  54 +-
 tests/qemu-iotests/iotests.py |  21 +-
 .../tests/export-incoming-iothread|   6 +-
 .../qemu-iotests/tests/graph-changes-while-io |   6 +-
 tests/qemu-iotests/tests/image-fleecing   |   3 +-
 .../tests/migrate-bitmaps-postcopy-test   |  31 +-
 tests/qemu-iotests/tests/migrate-bitmaps-test |  45 +-
 .../qemu-iotests/tests/migrate-during-backup  |  41 +-
 .../qemu-iotests/tests/migration-permissions  |   9 +-
 .../tests/mirror-ready-cancel-error   |  74 ++-
 tests/qemu-iotests/tests/mirror-top-perms |  16 +-
 tests/qemu-iotests/tests/nbd-multiconn|  12 +-
 tests/qemu-iotests/tests/reopen-file  |   3 +-
 .../qemu-iotests/tests/stream-error-on-reset  |   6 +-
 tests/vm/basevm.py|   4 +-
 70 files changed, 1188 insertions(+), 1614 deletions(-)

Re: [Libguestfs] [PATCH v7 12/12] nbd/server: Add FLAG_PAYLOAD support to CMD_BLOCK_STATUS

2023-10-05 Thread Eric Blake
On Wed, Oct 04, 2023 at 04:55:02PM -0500, Eric Blake wrote:
> > > +static int
> > > +nbd_co_block_status_payload_read(NBDClient *client, NBDRequest *request,
> > > + Error **errp)
> > 
> > [..]

> > > +for (i = 0; i < count; i++) {
> > > +id = ldl_be_p(buf + sizeof(NBDBlockStatusPayload) + sizeof(id) * 
> > > i);
> > > +if (id == NBD_META_ID_BASE_ALLOCATION) {
> > > +if (request->contexts->base_allocation) {
> > > +goto skip;
> > > +}
> > 
> > should we also check that base_allocation is negotiated?
> 
> Oh, good point.  Without that check, the client can pass in random id
> numbers that it never negotiated.  I've queued 1-11 and will probably
> send a pull request for those this week, while respinning this patch
> to fix the remaining issues you pointed out.

I'm squashing in the following. If you can review it today, I'll
include it in my pull request this afternoon; if not, we still have
time before soft freeze to get it in the next batch.

diff --git i/nbd/server.c w/nbd/server.c
index 30816b42386..62654579cbc 100644
--- i/nbd/server.c
+++ w/nbd/server.c
@@ -2478,19 +2478,22 @@ nbd_co_block_status_payload_read(NBDClient *client, 
NBDRequest *request,
 for (i = 0; i < count; i++) {
 id = ldl_be_p(buf + sizeof(NBDBlockStatusPayload) + sizeof(id) * i);
 if (id == NBD_META_ID_BASE_ALLOCATION) {
-if (request->contexts->base_allocation) {
+if (!client->contexts.base_allocation ||
+request->contexts->base_allocation) {
 goto skip;
 }
 request->contexts->base_allocation = true;
 } else if (id == NBD_META_ID_ALLOCATION_DEPTH) {
-if (request->contexts->allocation_depth) {
+if (!client->contexts.allocation_depth ||
+request->contexts->allocation_depth) {
 goto skip;
 }
 request->contexts->allocation_depth = true;
 } else {
-int idx = id - NBD_META_ID_DIRTY_BITMAP;
+unsigned idx = id - NBD_META_ID_DIRTY_BITMAP;

-if (idx > nr_bitmaps || request->contexts->bitmaps[idx]) {
+if (idx > nr_bitmaps || !client->contexts.bitmaps[idx] ||
+request->contexts->bitmaps[idx]) {
 goto skip;
 }
 request->contexts->bitmaps[idx] = true;
diff --git i/nbd/trace-events w/nbd/trace-events
index 3cf2d00e458..00ae3216a11 100644
--- i/nbd/trace-events
+++ w/nbd/trace-events
@@ -70,7 +70,7 @@ nbd_co_send_chunk_read(uint64_t cookie, uint64_t offset, void 
*data, uint64_t si
 nbd_co_send_chunk_read_hole(uint64_t cookie, uint64_t offset, uint64_t size) 
"Send structured read hole reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", 
len = %" PRIu64
 nbd_co_send_extents(uint64_t cookie, unsigned int extents, uint32_t id, 
uint64_t length, int last) "Send block status reply: cookie = %" PRIu64 ", 
extents = %u, context = %d (extents cover %" PRIu64 " bytes, last chunk = %d)"
 nbd_co_send_chunk_error(uint64_t cookie, int err, const char *errname, const 
char *msg) "Send structured error reply: cookie = %" PRIu64 ", error = %d (%s), 
msg = '%s'"
-nbd_co_receive_block_status_payload_compliance(uint64_t from, int len) "client 
sent unusable block status payload: from=0x%" PRIx64 ", len=0x%x"
+nbd_co_receive_block_status_payload_compliance(uint64_t from, uint64_t len) 
"client sent unusable block status payload: from=0x%" PRIx64 ", len=0x%" PRIx64
 nbd_co_receive_request_decode_type(uint64_t cookie, uint16_t type, const char 
*name) "Decoding type: cookie = %" PRIu64 ", type = %" PRIu16 " (%s)"
 nbd_co_receive_request_payload_received(uint64_t cookie, uint64_t len) 
"Payload received: cookie = %" PRIu64 ", len = %" PRIu64
 nbd_co_receive_ext_payload_compliance(uint64_t from, uint64_t len) "client 
sent non-compliant write without payload flag: from=0x%" PRIx64 ", len=0x%" 
PRIx64



-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PULL 0/9] Python patches

2023-10-05 Thread Stefan Hajnoczi
On Thu, 5 Oct 2023 at 00:49, Philippe Mathieu-Daudé  wrote:
>
> Hi John,
>
> On 4/10/23 21:46, John Snow wrote:
> > The following changes since commit da1034094d375afe9e3d8ec8980550ea0f06f7e0:
> >
> >Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging 
> > (2023-10-03 07:43:44 -0400)
> >
> > are available in the Git repository at:
> >
> >https://gitlab.com/jsnow/qemu.git tags/python-pull-request
> >
> > for you to fetch changes up to 4d7a663cbe8343e884b88e44bd88d37dd0a470e5:
> >
> >Python: test Python 3.12 (2023-10-04 15:19:00 -0400)
> >
> > 
> > Python pullreq
> >
> > Buffering improvements for qemu machine, minor changes to support the
> > newly released Python 3.12
> >
> > 
> >
> > John Snow (9):
> >Python/iotests: Add type hint for nbd module
> >python/machine: move socket setup out of _base_args property
> >python/machine: close sock_pair in cleanup path
> >python/console_socket: accept existing FD in initializer
> >python/machine: use socketpair() for console connections
> >python/machine: use socketpair() for qtest connection
> >python/machine: remove unused sock_dir argument
> >python/qmp: remove Server.wait_closed() call for Python 3.12
> >Python: test Python 3.12
>
> Is that a pull request or a patch series to be reviewed?

Strange, some of the patches have Reviewed-by tags but others do not.
I could not find a "Add type hint for nbd module" patch on the mailing
list before this pull request, so I guess it hasn't been reviewed.

I'll hold off from merging this series for now.

John: Please make sure all patches have been on the mailing list for
review before sending a pull request.

Stefan



Re: [PATCH v3 16/16] trace/control: Clean up global variable shadowing

2023-10-05 Thread Stefan Hajnoczi
On Wed, Oct 04, 2023 at 02:00:19PM +0200, Philippe Mathieu-Daudé wrote:
> Fix:
> 
>   trace/control.c:288:34: error: declaration shadows a variable in the global 
> scope [-Werror,-Wshadow]
>   void trace_opt_parse(const char *optarg)
>^
>   
> /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/getopt.h:77:14:
>  note: previous declaration is here
>   extern char *optarg;/* getopt(3) external variables */
>^
> 
> Signed-off-by: Philippe Mathieu-Daudé 
> ---
>  trace/control.h | 4 ++--
>  trace/control.c | 4 ++--
>  2 files changed, 4 insertions(+), 4 deletions(-)

Reviewed-by: Stefan Hajnoczi 


signature.asc
Description: PGP signature


Re: [PULL 0/1] Block patches

2023-10-05 Thread Stefan Hajnoczi
Applied, thanks.

Please update the changelog at https://wiki.qemu.org/ChangeLog/8.2 for any 
user-visible changes.


signature.asc
Description: PGP signature


Re: How to tame CI?

2023-10-05 Thread Vladimir Sementsov-Ogievskiy

On 26.07.23 16:32, Thomas Huth wrote:

On 26/07/2023 15.00, Peter Maydell wrote:

On Wed, 26 Jul 2023 at 13:06, Juan Quintela  wrote:

To make things easier, this is the part that show how it breaks (this is
the gcov test):

357/423 qemu:block / io-qcow2-copy-before-write    
ERROR   6.38s   exit status 1

PYTHON=/builds/juan.quintela/qemu/build/pyvenv/bin/python3 MALLOC_PERTURB_=44 
/builds/juan.quintela/qemu/build/pyvenv/bin/python3 
/builds/juan.quintela/qemu/build/../tests/qemu-iotests/check -tap -qcow2 
copy-before-write --source-dir /builds/juan.quintela/qemu/tests/qemu-iotests 
--build-dir /builds/juan.quintela/qemu/build/tests/qemu-iotests

― ✀  ―
stderr:
--- /builds/juan.quintela/qemu/tests/qemu-iotests/tests/copy-before-write.out
+++ 
/builds/juan.quintela/qemu/build/scratch/qcow2-file-copy-before-write/copy-before-write.out.bad
@@ -1,5 +1,21 @@
-
+...F
+==
+FAIL: test_timeout_break_snapshot (__main__.TestCbwError)
+--
+Traceback (most recent call last):
+  File 
"/builds/juan.quintela/qemu/tests/qemu-iotests/tests/copy-before-write", line 
210, in test_timeout_break_snapshot
+    self.assertEqual(log, """\
+AssertionError: 'wrot[195 chars]read 1048576/1048576 bytes at offset 0\n1 
MiB,[46 chars]c)\n' != 'wrot[195 chars]read failed: Permission denied\n'
+  wrote 524288/524288 bytes at offset 0
+  512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+  wrote 524288/524288 bytes at offset 524288
+  512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++ read failed: Permission denied
+- read 1048576/1048576 bytes at offset 0
+- 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+


This iotest failing is an intermittent that I've seen running
pullreqs on master. I tend to see it on the s390 host. I
suspect a race condition somewhere where it fails if the host
is heavily loaded.


It's obviously a failure in an iotest, so let's CC: the corresponding people 
(done now).



Sorry for long delay.

Does it still fail?

In the test we expect that copy-before-write operation fails (because of 
throttling and timeout), and therefore snapshot is broken and next read from 
snapshot should fail.

But most probably the copy-before-write operation succeeded in this case for 
some reason.. I don't think that throttling and timeouts in block layer can 
guarantee some determinism.. But usually it works.

we use throttling with bps-write = 300 * 1024, i.e. 300KB per second. and 
cbw-timeout is set to 1 second.

Then we do write 512K,

then the comment say:
# We need second write to trigger throttling

and we write another 512K.

first 512K are written, and we should wait 512/300 = 1.7 seconds since _start_ 
of that write before issuing the second one.. But if write was slow we may have 
to wait less than a second from finish of the first write start the second one. 
Then timeout will not fire.



I see two possible ways to fix that:

1. decrease bps-write a bit. For example to 200 BPS.

2. rework the test to use null-co instead of real images. This way we will not 
suffer from unstable IO duration.


So, is the problem still fire sometimes?

--
Best regards,
Vladimir




Re: [RFC PATCH v2 21/22] qapi: Inline and remove QERR_UNSUPPORTED definition

2023-10-05 Thread Markus Armbruster
Please ignore this copy, it has the recipients messed up.




Re: [RFC PATCH v2 21/22] qapi: Inline and remove QERR_UNSUPPORTED definition

2023-10-05 Thread Markus Armbruster
Philippe Mathieu-Daudé  writes:

> Address the comment added in commit 4629ed1e98
> ("qerror: Finally unused, clean up"), from 2015:
>
>   /*
>* These macros will go away, please don't use
>* in new code, and do not add new ones!
>*/
>
> Mechanical transformation using:
>
>   $ sed -i -e 's/QERR_UNSUPPORTED/"this feature or command is not currently 
> supported"/' \
> $(git grep -wl QERR_UNSUPPORTED)
>
> then manually removing the definition in include/qapi/qmp/qerror.h.
>
> Signed-off-by: Philippe Mathieu-Daudé 
> ---
> RFC: Not sure what is the best way to change the comment
>  in qga/qapi-schema.json...
> ---
>  qga/qapi-schema.json  |  5 +++--
>  include/qapi/qmp/qerror.h |  3 ---
>  qga/commands-bsd.c|  8 +++
>  qga/commands-posix.c  | 46 +++
>  qga/commands-win32.c  | 22 +--
>  5 files changed, 41 insertions(+), 43 deletions(-)
>
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index b720dd4379..51683f4dc2 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -6,8 +6,9 @@
   ##
   # = General note concerning the use of guest agent interfaces
>  #
>  # "unsupported" is a higher-level error than the errors that
>  # individual commands might document.  The caller should always be
> -# prepared to receive QERR_UNSUPPORTED, even if the given command
> -# doesn't specify it, or doesn't document any failure mode at all.
> +# prepared to receive the "this feature or command is not currently 
> supported"
> +# message, even if the given command doesn't specify it, or doesn't document
> +# any failure mode at all.
>  ##
>  
>  ##

The comment is problematic before the patch.  It's a doc comment,
i.e. it goes into the "QEMU Guest Agent Protocol Reference" manual,
where QERR_UNSUPPORTED is meaningless.  Back when the comment was added
(commit 9481ecd737b "qga schema: document generic QERR_UNSUPPORTED"), it
was still internal documentation, where QERR_UNSUPPORTED made sense.  It
became external documentation four years later (commit 56e8bdd46a8
"build-sys: add qapi doc generation targets")

I'm not sure how useful the comment is.

I guess we could instead simply point out that clients should always be
prepared for errors, even when the command doesn't document any, simply
because commands need not exist in all versions or builds of qemu-ga.

> diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
> index 840831cc6a..7606f4525d 100644
> --- a/include/qapi/qmp/qerror.h
> +++ b/include/qapi/qmp/qerror.h
> @@ -17,7 +17,4 @@
>   * add new ones!
>   */
>  
> -#define QERR_UNSUPPORTED \
> -"this feature or command is not currently supported"
> -
>  #endif /* QERROR_H */
> diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c
> index 17bddda1cf..11536f148f 100644
> --- a/qga/commands-bsd.c
> +++ b/qga/commands-bsd.c
> @@ -152,25 +152,25 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp)
>  
>  GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
>  {
> -error_setg(errp, QERR_UNSUPPORTED);
> +error_setg(errp, "this feature or command is not currently supported");
>  return NULL;
>  }
>  
>  GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
>  {
> -error_setg(errp, QERR_UNSUPPORTED);
> +error_setg(errp, "this feature or command is not currently supported");
>  return NULL;
>  }
>  
>  GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
>  {
> -error_setg(errp, QERR_UNSUPPORTED);
> +error_setg(errp, "this feature or command is not currently supported");
>  return NULL;
>  }
>  
>  GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
>  {
> -error_setg(errp, QERR_UNSUPPORTED);
> +error_setg(errp, "this feature or command is not currently supported");
>  return NULL;
>  }

These are all commands that are not supported by this build of qemu-ga.
We could use the opportunity to improve the error message: scratch
"feature or ".  Or maybe change the message to "this command is not
supported in this version of qemu-ga".

More of the same below, marked [*].

Taking a step back...  Do we really need to make this a failure of its
own?  Why not fail exactly as if the command didn't exist?  Why would a
client ever care for the difference between "command doesn't exist in
this build of qemu-ga (but it does in other builds of this version of
qemu-ga)" and "command doesn't exist in this build of qemu-ga (and it
won't in any build of this version of qemu-ga)"?

If clients don't care, we could instead unregister these commands,
i.e. undo qmp_register_command().  The command will then fail exactly
like any other unknown command.  We still need to provide the functions
to make the linker happy (unless we play with weak symbols), but they
can simply abort().

Michael and/or Konstantin, do you have comments as maintainers?  A
preference even?

Hmm, there's yet another mechanism to disable commands:
qmp_disable_command() & friends.  Looks 

Re: [RFC PATCH v2 21/22] qapi: Inline and remove QERR_UNSUPPORTED definition

2023-10-05 Thread Markus Armbruster
Philippe Mathieu-Daudé  writes:

> Address the comment added in commit 4629ed1e98
> ("qerror: Finally unused, clean up"), from 2015:
>
>   /*
>* These macros will go away, please don't use
>* in new code, and do not add new ones!
>*/
>
> Mechanical transformation using:
>
>   $ sed -i -e 's/QERR_UNSUPPORTED/"this feature or command is not currently 
> supported"/' \
> $(git grep -wl QERR_UNSUPPORTED)
>
> then manually removing the definition in include/qapi/qmp/qerror.h.
>
> Signed-off-by: Philippe Mathieu-Daudé 
> ---
> RFC: Not sure what is the best way to change the comment
>  in qga/qapi-schema.json...
> ---
>  qga/qapi-schema.json  |  5 +++--
>  include/qapi/qmp/qerror.h |  3 ---
>  qga/commands-bsd.c|  8 +++
>  qga/commands-posix.c  | 46 +++
>  qga/commands-win32.c  | 22 +--
>  5 files changed, 41 insertions(+), 43 deletions(-)
>
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index b720dd4379..51683f4dc2 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -6,8 +6,9 @@
   ##
   # = General note concerning the use of guest agent interfaces
>  #
>  # "unsupported" is a higher-level error than the errors that
>  # individual commands might document.  The caller should always be
> -# prepared to receive QERR_UNSUPPORTED, even if the given command
> -# doesn't specify it, or doesn't document any failure mode at all.
> +# prepared to receive the "this feature or command is not currently 
> supported"
> +# message, even if the given command doesn't specify it, or doesn't document
> +# any failure mode at all.
>  ##
>  
>  ##

The comment is problematic before the patch.  It's a doc comment,
i.e. it goes into the "QEMU Guest Agent Protocol Reference" manual,
where QERR_UNSUPPORTED is meaningless.  Back when the comment was added
(commit 9481ecd737b "qga schema: document generic QERR_UNSUPPORTED"), it
was still internal documentation, where QERR_UNSUPPORTED made sense.  It
became external documentation four years later (commit 56e8bdd46a8
"build-sys: add qapi doc generation targets")

I'm not sure how useful the comment is.

I guess we could instead simply point out that clients should always be
prepared for errors, even when the command doesn't document any, simply
because commands need not exist in all versions or builds of qemu-ga.

> diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
> index 840831cc6a..7606f4525d 100644
> --- a/include/qapi/qmp/qerror.h
> +++ b/include/qapi/qmp/qerror.h
> @@ -17,7 +17,4 @@
>   * add new ones!
>   */
>  
> -#define QERR_UNSUPPORTED \
> -"this feature or command is not currently supported"
> -
>  #endif /* QERROR_H */
> diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c
> index 17bddda1cf..11536f148f 100644
> --- a/qga/commands-bsd.c
> +++ b/qga/commands-bsd.c
> @@ -152,25 +152,25 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp)
>  
>  GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
>  {
> -error_setg(errp, QERR_UNSUPPORTED);
> +error_setg(errp, "this feature or command is not currently supported");
>  return NULL;
>  }
>  
>  GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
>  {
> -error_setg(errp, QERR_UNSUPPORTED);
> +error_setg(errp, "this feature or command is not currently supported");
>  return NULL;
>  }
>  
>  GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
>  {
> -error_setg(errp, QERR_UNSUPPORTED);
> +error_setg(errp, "this feature or command is not currently supported");
>  return NULL;
>  }
>  
>  GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
>  {
> -error_setg(errp, QERR_UNSUPPORTED);
> +error_setg(errp, "this feature or command is not currently supported");
>  return NULL;
>  }

These are all commands that are not supported by this build of qemu-ga.
We could use the opportunity to improve the error message: scratch
"feature or ".  Or maybe change the message to "this command is not
supported in this version of qemu-ga".

More of the same below, marked [*].

Taking a step back...  Do we really need to make this a failure of its
own?  Why not fail exactly as if the command didn't exist?  Why would a
client ever care for the difference between "command doesn't exist in
this build of qemu-ga (but it does in other builds of this version of
qemu-ga)" and "command doesn't exist in this build of qemu-ga (and it
won't in any build of this version of qemu-ga)"?

If clients don't care, we could instead unregister these commands,
i.e. undo qmp_register_command().  The command will then fail exactly
like any other unknown command.  We still need to provide the functions
to make the linker happy (unless we play with weak symbols), but they
can simply abort().

Michael and/or Konstantin, do you have comments as maintainers?  A
preference even?

Hmm, there's yet another mechanism to disable commands:
qmp_disable_command() & friends.  Looks 

Re: [PATCH v2 1/2] hw/ide: reset: cancel async DMA operation before resetting state

2023-10-05 Thread Simon Rowe
On Wednesday, 6 September 2023 Fiona Ebner  wrote:

> If there is a pending DMA operation during ide_bus_reset(), the fact
> that the IDEState is already reset before the operation is canceled
> can be problematic. In particular, ide_dma_cb() might be called and
> then use the reset IDEState which contains the signature after the
> reset. When used to construct the IO operation this leads to
> ide_get_sector() returning 0 and nsector being 1. This is particularly
> bad, because a write command will thus destroy the first sector which
> often contains a partition table or similar.

Tested-by: simon.r...@nutanix.com



Re: [PATCH v3 14/16] softmmu/vl: Clean up global variable shadowing

2023-10-05 Thread Philippe Mathieu-Daudé

On 5/10/23 10:59, Markus Armbruster wrote:

Philippe Mathieu-Daudé  writes:


Fix:

   softmmu/vl.c:1069:44: error: declaration shadows a variable in the global 
scope [-Werror,-Wshadow]
   static void parse_display_qapi(const char *optarg)
  ^
   softmmu/vl.c:1224:39: error: declaration shadows a variable in the global 
scope [-Werror,-Wshadow]
   static void monitor_parse(const char *optarg, const char *mode, bool pretty)
 ^
   softmmu/vl.c:1634:17: error: declaration shadows a variable in the global 
scope [-Werror,-Wshadow]
   const char *optarg = qdict_get_try_str(qdict, "type");
   ^
   softmmu/vl.c:1784:45: error: declaration shadows a variable in the global 
scope [-Werror,-Wshadow]
   static void object_option_parse(const char *optarg)
   ^
   
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/getopt.h:77:14: 
note: previous declaration is here
   extern char *optarg;/* getopt(3) external variables */
^

Signed-off-by: Philippe Mathieu-Daudé 


How much we care about the shadowing is unclear, but that doesn't matter
if the patches make sense even if we pretend global @optarg doesn't
exist.  Let's check that.


---
  softmmu/vl.c | 26 +-
  1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/softmmu/vl.c b/softmmu/vl.c
index 98e071e63b..ae1ff9887d 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -1066,12 +1066,12 @@ static void select_vgahw(const MachineClass 
*machine_class, const char *p)
  }
  }
  
-static void parse_display_qapi(const char *optarg)

+static void parse_display_qapi(const char *optstr)
  {
  DisplayOptions *opts;
  Visitor *v;
  
-v = qobject_input_visitor_new_str(optarg, "type", _fatal);

+v = qobject_input_visitor_new_str(optstr, "type", _fatal);
  
  visit_type_DisplayOptions(v, NULL, , _fatal);

  QAPI_CLONE_MEMBERS(DisplayOptions, , opts);


The actual argument is a string that is either JSON or KEY=VALUE,...
The fact that it's always an option argument now (actually the value of
global @optarg) is irrelevant here.

parse_display_qapi() passes its parameter to
qobject_input_visitor_new_str() parameter @str, which passes it to
qobject_from_json() parameter @string if JSON, or else to keyval_parse()
parameter @params.

I'd rename @optarg to @str here, like you do in the next hunk, to not
suggest a connection to CLI.  Not a demand.


OK.



-static void object_option_parse(const char *optarg)
+static void object_option_parse(const char *optstr)
  {
  QemuOpts *opts;
  const char *type;
  Visitor *v;
  
-if (optarg[0] == '{') {

-QObject *obj = qobject_from_json(optarg, _fatal);
+if (optstr[0] == '{') {
+QObject *obj = qobject_from_json(optstr, _fatal);
  
  v = qobject_input_visitor_new(obj);

  qobject_unref(obj);
  } else {
  opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
-   optarg, true);
+   optstr, true);
  if (!opts) {
  exit(1);
  }


Same argument as for parse_display_qapi(), and same suggestion.

If this goes though my tree, I can implement my two suggestions, if you
agree.


Sure, thank you!


Reviewed-by: Markus Armbruster 






[PATCH v2] hw/ide/ahci: fix legacy software reset

2023-10-05 Thread Niklas Cassel
From: Niklas Cassel 

Legacy software contains a standard mechanism for generating a reset to a
Serial ATA device - setting the SRST (software reset) bit in the Device
Control register.

Serial ATA has a more robust mechanism called COMRESET, also referred to
as port reset. A port reset is the preferred mechanism for error
recovery and should be used in place of software reset.

Commit e2a5d9b3d9c3 ("hw/ide/ahci: simplify and document PxCI handling")
improved the handling of PxCI, such that PxCI gets cleared after handling
a non-NCQ, or NCQ command (instead of incorrectly clearing PxCI after
receiving an arbitrary FIS).

However, simply clearing PxCI after a non-NCQ, or NCQ command, is not
enough, we also need to clear PxCI when receiving a SRST in the Device
Control register.

This fixes an issue for FreeBSD where the device would fail to reset.
The problem was not noticed in Linux, because Linux uses a COMRESET
instead of a legacy software reset by default.

Fixes: e2a5d9b3d9c3 ("hw/ide/ahci: simplify and document PxCI handling")
Reported-by: Marcin Juszkiewicz 
Signed-off-by: Niklas Cassel 
---
Changes since v1: write the D2H FIS before clearing PxCI.

 hw/ide/ahci.c | 16 
 1 file changed, 16 insertions(+)

diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index babdd7b458..7269dabbdb 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1254,10 +1254,26 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
 case STATE_RUN:
 if (cmd_fis[15] & ATA_SRST) {
 s->dev[port].port_state = STATE_RESET;
+/*
+ * When setting SRST in the first H2D FIS in the reset 
sequence,
+ * the device does not send a D2H FIS. Host software thus has 
to
+ * set the "Clear Busy upon R_OK" bit such that PxCI (and BUSY)
+ * gets cleared. See AHCI 1.3.1, section 10.4.1 Software Reset.
+ */
+if (opts & AHCI_CMD_CLR_BUSY) {
+ahci_clear_cmd_issue(ad, slot);
+}
 }
 break;
 case STATE_RESET:
 if (!(cmd_fis[15] & ATA_SRST)) {
+/*
+ * When clearing SRST in the second H2D FIS in the reset
+ * sequence, the device will send a D2H FIS. See SATA 3.5a 
Gold,
+ * section 11.4 Software reset protocol.
+ */
+ahci_clear_cmd_issue(ad, slot);
+ahci_write_fis_d2h(ad, false);
 ahci_reset_port(s, port);
 }
 break;
-- 
2.41.0




[PATCH] hw/ide/ahci: fix legacy software reset

2023-10-05 Thread Niklas Cassel
From: Niklas Cassel 

Legacy software contains a standard mechanism for generating a reset to a
Serial ATA device - setting the SRST (software reset) bit in the Device
Control register.

Serial ATA has a more robust mechanism called COMRESET, also referred to
as port reset. A port reset is the preferred mechanism for error
recovery and should be used in place of software reset.

Commit e2a5d9b3d9c3 ("hw/ide/ahci: simplify and document PxCI handling")
improved the handling of PxCI, such that PxCI gets cleared after handling
a non-NCQ, or NCQ command (instead of incorrectly clearing PxCI after
receiving an arbitrary FIS).

However, simply clearing PxCI after a non-NCQ, or NCQ command, is not
enough, we also need to clear PxCI when receiving a SRST in the Device
Control register.

This fixes an issue for FreeBSD where the device would fail to reset.
The problem was not noticed in Linux, because Linux uses a COMRESET
instead of a legacy software reset by default.

Fixes: e2a5d9b3d9c3 ("hw/ide/ahci: simplify and document PxCI handling")
Reported-by: Marcin Juszkiewicz 
Signed-off-by: Niklas Cassel 
---
 hw/ide/ahci.c | 16 
 1 file changed, 16 insertions(+)

diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index babdd7b458..3a8b97c325 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1254,10 +1254,26 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
 case STATE_RUN:
 if (cmd_fis[15] & ATA_SRST) {
 s->dev[port].port_state = STATE_RESET;
+/*
+ * When setting SRST in the first H2D FIS in the reset 
sequence,
+ * the device does not send a D2H FIS. Host software thus has 
to
+ * set the "Clear Busy upon R_OK" bit such that PxCI (and BUSY)
+ * gets cleared. See AHCI 1.3.1, section 10.4.1 Software Reset.
+ */
+if (opts & AHCI_CMD_CLR_BUSY) {
+ahci_clear_cmd_issue(ad, slot);
+}
 }
 break;
 case STATE_RESET:
 if (!(cmd_fis[15] & ATA_SRST)) {
+/*
+ * When clearing SRST in the second H2D FIS in the reset
+ * sequence, the device will send a D2H FIS. See SATA 3.5a 
Gold,
+ * section 11.4 Software reset protocol.
+ */
+ahci_write_fis_d2h(ad, false);
+ahci_clear_cmd_issue(ad, slot);
 ahci_reset_port(s, port);
 }
 break;
-- 
2.41.0




Re: [PATCH v2 00/22] qapi: Kill 'qapi/qmp/qerror.h' for good

2023-10-05 Thread Markus Armbruster
Philippe Mathieu-Daudé  writes:

> Since v1:
> - Fixed checkpatch warnings (Juan)
> - Added R-b tags
> - New patch for 'vcpu_dirty_limit'
>
> Hi,
>
> This is kind of a selfish series. I'm really tired to grep
> and read this comment from 2015 in qapi/qmp/qerror.h:
>   /*
>* These macros will go away, please don't use
>* in new code, and do not add new ones!
>*/
>
> Besides, these definitions are still added in recent code
> (see for example commit 09f9ec9913 from June 2023). So
> let's finish with this 8 years old technical debt.

Gee, a late birthday present for me!  Thank you!

> Overall it took me 3h: 1h to find the correct Coccinelle
> doc about Python use and read it again [*], then 1h to
> adapt the script for each patch, rest is testing and
> writing comments, so the scripts used could be used as
> reference later.

By the time you're done, it'll likely be 6h or more...




Re: [PATCH v3 14/16] softmmu/vl: Clean up global variable shadowing

2023-10-05 Thread Markus Armbruster
Philippe Mathieu-Daudé  writes:

> Fix:
>
>   softmmu/vl.c:1069:44: error: declaration shadows a variable in the global 
> scope [-Werror,-Wshadow]
>   static void parse_display_qapi(const char *optarg)
>  ^
>   softmmu/vl.c:1224:39: error: declaration shadows a variable in the global 
> scope [-Werror,-Wshadow]
>   static void monitor_parse(const char *optarg, const char *mode, bool pretty)
> ^
>   softmmu/vl.c:1634:17: error: declaration shadows a variable in the global 
> scope [-Werror,-Wshadow]
>   const char *optarg = qdict_get_try_str(qdict, "type");
>   ^
>   softmmu/vl.c:1784:45: error: declaration shadows a variable in the global 
> scope [-Werror,-Wshadow]
>   static void object_option_parse(const char *optarg)
>   ^
>   
> /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/getopt.h:77:14:
>  note: previous declaration is here
>   extern char *optarg;/* getopt(3) external variables */
>^
>
> Signed-off-by: Philippe Mathieu-Daudé 

How much we care about the shadowing is unclear, but that doesn't matter
if the patches make sense even if we pretend global @optarg doesn't
exist.  Let's check that.

> ---
>  softmmu/vl.c | 26 +-
>  1 file changed, 13 insertions(+), 13 deletions(-)
>
> diff --git a/softmmu/vl.c b/softmmu/vl.c
> index 98e071e63b..ae1ff9887d 100644
> --- a/softmmu/vl.c
> +++ b/softmmu/vl.c
> @@ -1066,12 +1066,12 @@ static void select_vgahw(const MachineClass 
> *machine_class, const char *p)
>  }
>  }
>  
> -static void parse_display_qapi(const char *optarg)
> +static void parse_display_qapi(const char *optstr)
>  {
>  DisplayOptions *opts;
>  Visitor *v;
>  
> -v = qobject_input_visitor_new_str(optarg, "type", _fatal);
> +v = qobject_input_visitor_new_str(optstr, "type", _fatal);
>  
>  visit_type_DisplayOptions(v, NULL, , _fatal);
>  QAPI_CLONE_MEMBERS(DisplayOptions, , opts);

The actual argument is a string that is either JSON or KEY=VALUE,...
The fact that it's always an option argument now (actually the value of
global @optarg) is irrelevant here.

parse_display_qapi() passes its parameter to
qobject_input_visitor_new_str() parameter @str, which passes it to
qobject_from_json() parameter @string if JSON, or else to keyval_parse()
parameter @params.

I'd rename @optarg to @str here, like you do in the next hunk, to not
suggest a connection to CLI.  Not a demand.

> @@ -1221,21 +1221,21 @@ static int mon_init_func(void *opaque, QemuOpts 
> *opts, Error **errp)
>  return monitor_init_opts(opts, errp);
>  }
>  
> -static void monitor_parse(const char *optarg, const char *mode, bool pretty)
> +static void monitor_parse(const char *str, const char *mode, bool pretty)
>  {
>  static int monitor_device_index = 0;
>  QemuOpts *opts;
>  const char *p;
>  char label[32];
>  
> -if (strstart(optarg, "chardev:", )) {
> +if (strstart(str, "chardev:", )) {
>  snprintf(label, sizeof(label), "%s", p);
>  } else {
>  snprintf(label, sizeof(label), "compat_monitor%d",
>   monitor_device_index);
> -opts = qemu_chr_parse_compat(label, optarg, true);
> +opts = qemu_chr_parse_compat(label, str, true);
>  if (!opts) {
> -error_report("parse error: %s", optarg);
> +error_report("parse error: %s", str);
>  exit(1);
>  }
>  }

The actual argument is either @optarg or a string literal, but that's
again irrelevant here.

> @@ -1631,13 +1631,13 @@ static const QEMUOption *lookup_opt(int argc, char 
> **argv,
>  
>  static MachineClass *select_machine(QDict *qdict, Error **errp)
>  {
> -const char *optarg = qdict_get_try_str(qdict, "type");
> +const char *machine_type = qdict_get_try_str(qdict, "type");
>  GSList *machines = object_class_get_list(TYPE_MACHINE, false);
>  MachineClass *machine_class;
>  Error *local_err = NULL;
>  
> -if (optarg) {
> -machine_class = find_machine(optarg, machines);
> +if (machine_type) {
> +machine_class = find_machine(machine_type, machines);
>  qdict_del(qdict, "type");
>  if (!machine_class) {
>  error_setg(_err, "unsupported machine type");

Obvious improvement.

> @@ -1781,20 +1781,20 @@ static void object_option_add_visitor(Visitor *v)
>  QTAILQ_INSERT_TAIL(_opts, opt, next);
>  }
>  
> -static void object_option_parse(const char *optarg)
> +static void object_option_parse(const char *optstr)
>  {
>  QemuOpts *opts;
>  const char *type;
>  Visitor *v;
>  
> -if (optarg[0] == '{') {
> -QObject *obj = qobject_from_json(optarg, _fatal);
> +if (optstr[0] == '{') {
> +QObject *obj = qobject_from_json(optstr, _fatal);
>  
>  v = qobject_input_visitor_new(obj);
>  

Re: [RFC] migration/block-dirty-bitmap: make loading bitmap for device with iothread future-proof

2023-10-05 Thread Fiona Ebner
Am 04.10.23 um 18:13 schrieb Vladimir Sementsov-Ogievskiy:
>> @@ -833,7 +836,10 @@ static int dirty_bitmap_load_start(QEMUFile *f,
>> DBMLoadState *s)
>>     bdrv_disable_dirty_bitmap(s->bitmap);
>>   if (flags & DIRTY_BITMAP_MIG_START_FLAG_ENABLED) {
>> +    AioContext *ctx = bdrv_get_aio_context(s->bs);
>> +    aio_context_acquire(ctx);
>>   bdrv_dirty_bitmap_create_successor(s->bitmap, _err);
>> +    aio_context_release(ctx);
> 
> Would not this deadlock in current code? When we have the only one aio
> context and therefore we are already in it?
> 

Yes, I noticed that myself a bit later ;)

Am 01.08.23 um 09:57 schrieb Fiona Ebner:
> And the patch itself also got an issue. AFAICT, when
> qcow2_co_load_vmstate() is called, we already have acquired the context
> for the drive we load the snapshot from, and since
> polling/AIO_WAIT_WHILE requires that the caller has acquired the context
> exactly once, we'd need to distinguish if the dirty bitmap is for that
> drive (can't acquire the context a second time) or a different drive
> (need to acquire the context for the first time).

Quoted from a reply in this thread
https://lists.nongnu.org/archive/html/qemu-devel/2023-08/msg7.html

Am 04.10.23 um 18:13 schrieb Vladimir Sementsov-Ogievskiy:
> If as Juan said, we rework incoming migration coroutine to be a separate
> thread, this patch becomes more correct, I think..

Yes, it would become an issue when the function is called from a
non-coroutine context.

> If keep coroutine, I think, we should check are we already in that aio
> context, and if so we should not acquire it.

In coroutine context, we don't need to acquire it, but it shouldn't hurt
either and this approach should work for non-coroutine context too. The
question is if such conditional lock-taking is acceptable (do we already
have something similar somewhere?) or if it can be avoided somehow like
it was preferred in another one of my patches:

Am 05.05.23 um 16:59 schrieb Peter Xu:
> On Fri, May 05, 2023 at 03:46:52PM +0200, Fiona Ebner wrote:
>> To fix it, ensure that the BQL is held during setup. To avoid changing
>> the behavior for migration too, introduce conditionals for the setup
>> callbacks that need the BQL and only take the lock if it's not already
>> held.
> 
> The major complexity of this patch is the "conditionally taking" part.

Quoted from
https://lists.nongnu.org/archive/html/qemu-devel/2023-05/msg01514.html




Re: [PATCH v2 11/22] qapi: Inline QERR_INVALID_PARAMETER_VALUE definition (constant value)

2023-10-05 Thread Juan Quintela
Philippe Mathieu-Daudé  wrote:
> vcpu_dirty_limit
>
> Address the comment added in commit 4629ed1e98
> ("qerror: Finally unused, clean up"), from 2015:
>
>   /*
>* These macros will go away, please don't use
>* in new code, and do not add new ones!
>*/
>
> Mechanical transformation using the following
> coccinelle semantic patch:
>
> @match@
> expression errp;
> constant param;
> constant value;
> @@
>  error_setg(errp, QERR_INVALID_PARAMETER_VALUE, param, value);
>
> @script:python strformat depends on match@
> param << match.param;
> value << match.value;
> fixedfmt; // new var
> @@
> fixedfmt = "\"Parameter '%s' expects %s\"" % (param[1:-1], value[1:-1])
> coccinelle.fixedfmt = cocci.make_ident(fixedfmt)
>
> @replace@
> expression match.errp;
> constant match.param;
> constant match.value;
> identifier strformat.fixedfmt;
> @@
> -error_setg(errp, QERR_INVALID_PARAMETER_VALUE, param, value);
> +error_setg(errp, fixedfmt);
>
> Then manually splitting lines over 90 characters.
>
> Reviewed-by: Juan Quintela 
> Signed-off-by: Philippe Mathieu-Daudé 

Reviewed-by: Juan Quintela 




Re: [PATCH v2 10/22] qapi: Correct error message for 'vcpu_dirty_limit' parameter

2023-10-05 Thread Juan Quintela
Philippe Mathieu-Daudé  wrote:
> QERR_INVALID_PARAMETER_VALUE is defined as:
>
>   #define QERR_INVALID_PARAMETER_VALUE \
>   "Parameter '%s' expects %s"
>
> The current error is formatted as:
>
>   "Parameter 'vcpu_dirty_limit' expects is invalid, it must greater then 1 
> MB/s"
>
> Replace by:
>
>   "Parameter 'vcpu_dirty_limit' is invalid, it must greater then 1 MB/s"
>
> Signed-off-by: Philippe Mathieu-Daudé 

Reviewed-by: Juan Quintela