Signed-off-by: Ma Haocong <mahaocong@didichuxing.com>
Signed-off-by: John Snow <jsnow@redhat.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/mirror.c | 98 +++++++++++++++++++++++++-------
blockdev.c | 39 ++++++++++++-
5 files changed, 145 insertions(+), 29 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
-index 98fc66eabf..9d73875bd6 100644
+index 85b781bc21..0821214138 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -50,7 +50,7 @@ typedef struct MirrorBlockJob {
BdrvDirtyBitmap *dirty_bitmap;
BdrvDirtyBitmapIter *dbi;
uint8_t *buf;
-@@ -690,7 +692,8 @@ static int mirror_exit_common(Job *job)
+@@ -697,7 +699,8 @@ static int mirror_exit_common(Job *job)
bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing,
&error_abort);
if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs);
if (bdrv_cow_bs(unfiltered_target) != backing) {
-@@ -795,6 +798,16 @@ static void mirror_abort(Job *job)
+@@ -802,6 +805,16 @@ static void mirror_abort(Job *job)
assert(ret == 0);
}
static void coroutine_fn mirror_throttle(MirrorBlockJob *s)
{
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
-@@ -976,7 +989,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
+@@ -983,7 +996,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
mirror_free_init(s);
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
ret = mirror_dirty_init(s);
if (ret < 0 || job_is_cancelled(&s->common.job)) {
goto immediate_exit;
-@@ -1209,6 +1223,7 @@ static const BlockJobDriver mirror_job_driver = {
+@@ -1216,6 +1230,7 @@ static const BlockJobDriver mirror_job_driver = {
.run = mirror_run,
.prepare = mirror_prepare,
.abort = mirror_abort,
.pause = mirror_pause,
.complete = mirror_complete,
.cancel = mirror_cancel,
-@@ -1225,6 +1240,7 @@ static const BlockJobDriver commit_active_job_driver = {
+@@ -1232,6 +1247,7 @@ static const BlockJobDriver commit_active_job_driver = {
.run = mirror_run,
.prepare = mirror_prepare,
.abort = mirror_abort,
.pause = mirror_pause,
.complete = mirror_complete,
},
-@@ -1587,7 +1603,10 @@ static BlockJob *mirror_start_job(
+@@ -1594,7 +1610,10 @@ static BlockJob *mirror_start_job(
BlockCompletionFunc *cb,
void *opaque,
const BlockJobDriver *driver,
bool auto_complete, const char *filter_node_name,
bool is_mirror, MirrorCopyMode copy_mode,
Error **errp)
-@@ -1599,10 +1618,39 @@ static BlockJob *mirror_start_job(
+@@ -1606,10 +1625,39 @@ static BlockJob *mirror_start_job(
uint64_t target_perms, target_shared_perms;
int ret;
- if (granularity == 0) {
-- granularity = bdrv_get_default_bitmap_granularity(target);
+ if (sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
+ error_setg(errp, "Sync mode '%s' not supported",
+ MirrorSyncMode_str(sync_mode));
+ "sync mode '%s' is not compatible with bitmaps",
+ MirrorSyncMode_str(sync_mode));
+ return NULL;
- }
-
++ }
++
+ if (bitmap) {
+ if (granularity) {
+ error_setg(errp, "granularity (%d)"
+ }
+ granularity = bdrv_dirty_bitmap_granularity(bitmap);
+ } else if (granularity == 0) {
-+ granularity = bdrv_get_default_bitmap_granularity(target);
-+ }
+ granularity = bdrv_get_default_bitmap_granularity(target);
+ }
+-
assert(is_power_of_2(granularity));
if (buf_size < 0) {
-@@ -1740,7 +1788,9 @@ static BlockJob *mirror_start_job(
+@@ -1747,7 +1795,9 @@ static BlockJob *mirror_start_job(
s->replaces = g_strdup(replaces);
s->on_source_error = on_source_error;
s->on_target_error = on_target_error;
s->backing_mode = backing_mode;
s->zero_target = zero_target;
s->copy_mode = copy_mode;
-@@ -1761,6 +1811,18 @@ static BlockJob *mirror_start_job(
+@@ -1768,6 +1818,18 @@ static BlockJob *mirror_start_job(
bdrv_disable_dirty_bitmap(s->dirty_bitmap);
}
ret = block_job_add_bdrv(&s->common, "source", bs, 0,
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE |
BLK_PERM_CONSISTENT_READ,
-@@ -1838,6 +1900,9 @@ fail:
+@@ -1845,6 +1907,9 @@ fail:
if (s->dirty_bitmap) {
bdrv_release_dirty_bitmap(s->dirty_bitmap);
}
job_early_fail(&s->common.job);
}
-@@ -1855,29 +1920,23 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
+@@ -1862,29 +1927,23 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, const char *replaces,
int creation_flags, int64_t speed,
uint32_t granularity, int64_t buf_size,
}
BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
-@@ -1902,7 +1961,8 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
+@@ -1909,7 +1968,8 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
job_id, bs, creation_flags, base, NULL, speed, 0, 0,
MIRROR_LEAVE_BACKING_CHAIN, false,
on_error, on_error, true, cb, opaque,
has_granularity, granularity,
has_buf_size, buf_size,
diff --git a/include/block/block_int.h b/include/block/block_int.h
-index f1a54db0f8..3e625a4261 100644
+index c31cbd034a..11442893d0 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
-@@ -1247,7 +1247,9 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
+@@ -1254,7 +1254,9 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, const char *replaces,
int creation_flags, int64_t speed,
uint32_t granularity, int64_t buf_size,
successfully created mirror.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/mirror.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
-index 9d73875bd6..8148df1f80 100644
+index 0821214138..c688726fae 100644
--- a/block/mirror.c
+++ b/block/mirror.c
-@@ -667,8 +667,6 @@ static int mirror_exit_common(Job *job)
+@@ -674,8 +674,6 @@ static int mirror_exit_common(Job *job)
bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
}
/* Make sure that the source BDS doesn't go away during bdrv_replace_node,
* before we can call bdrv_drained_end */
bdrv_ref(src);
-@@ -776,6 +774,18 @@ static int mirror_exit_common(Job *job)
+@@ -783,6 +781,18 @@ static int mirror_exit_common(Job *job)
blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
bs_opaque->job = NULL;
bdrv_drained_end(src);
-@@ -1628,10 +1638,6 @@ static BlockJob *mirror_start_job(
+@@ -1635,10 +1645,6 @@ static BlockJob *mirror_start_job(
" sync mode",
MirrorSyncMode_str(sync_mode));
return NULL;
}
} else if (bitmap) {
error_setg(errp,
-@@ -1648,6 +1654,12 @@ static BlockJob *mirror_start_job(
+@@ -1655,6 +1661,12 @@ static BlockJob *mirror_start_job(
return NULL;
}
granularity = bdrv_dirty_bitmap_granularity(bitmap);
of modes.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
blockdev.c | 3 +++
1 file changed, 3 insertions(+)
beforehand.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/mirror.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
-index 8148df1f80..46e3d0860b 100644
+index c688726fae..a7f829f766 100644
--- a/block/mirror.c
+++ b/block/mirror.c
-@@ -780,8 +780,8 @@ static int mirror_exit_common(Job *job)
+@@ -787,8 +787,8 @@ static int mirror_exit_common(Job *job)
job->ret == 0 && ret == 0)) {
/* Success; synchronize copy back to sync. */
bdrv_clear_dirty_bitmap(s->sync_bitmap, NULL);
}
}
bdrv_release_dirty_bitmap(s->dirty_bitmap);
-@@ -1828,11 +1828,8 @@ static BlockJob *mirror_start_job(
+@@ -1835,11 +1835,8 @@ static BlockJob *mirror_start_job(
}
if (s->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
honor provenance.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
tests/qemu-iotests/384 | 547 +++++++
tests/qemu-iotests/384.out | 2846 ++++++++++++++++++++++++++++++++++++
uniform w.r.t. backup block jobs.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/mirror.c | 28 +++------------
blockdev.c | 29 +++++++++++++++
3 files changed, 70 insertions(+), 59 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
-index 46e3d0860b..33477ade1b 100644
+index a7f829f766..6a126d18c8 100644
--- a/block/mirror.c
+++ b/block/mirror.c
-@@ -1628,31 +1628,13 @@ static BlockJob *mirror_start_job(
+@@ -1635,31 +1635,13 @@ static BlockJob *mirror_start_job(
uint64_t target_perms, target_shared_perms;
int ret;
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Stefan Reiter <s.reiter@proxmox.com>
+Date: Mon, 23 Aug 2021 11:28:32 +0200
+Subject: [PATCH] monitor/qmp: fix race with clients disconnecting early
+
+The following sequence can produce a race condition that results in
+responses meant for different clients being sent to the wrong one:
+
+(QMP, no OOB)
+1) client A connects
+2) client A sends 'qmp_capabilities'
+3) 'qmp_dispatch' runs in coroutine, schedules out to
+ 'do_qmp_dispatch_bh' and yields
+4) client A disconnects (i.e. aborts, crashes, etc...)
+5) client B connects
+6) 'do_qmp_dispatch_bh' runs 'qmp_capabilities' and wakes calling coroutine
+7) capabilities are now set and 'mon->commands' is set to '&qmp_commands'
+8) 'qmp_dispatch' returns to 'monitor_qmp_dispatch'
+9) success message is sent to client B *without it ever having sent
+ 'qmp_capabilities' itself*
+9a) even if client B ignores it, it will now presumably send it's own
+ greeting, which will error because caps are already set
+
+The fix proposed here uses an atomic, sequential connection number
+stored in the MonitorQMP struct, which is incremented everytime a new
+client connects. Since it is not changed on CHR_EVENT_CLOSED, the
+behaviour of allowing a client to disconnect only one side of the
+connection is retained.
+
+The connection_nr needs to be exposed outside of the monitor subsystem,
+since qmp_dispatch lives in qapi code. It needs to be checked twice,
+once for actually running the command in the BH (fixes 7), and once for
+sending back a response (fixes 9).
+
+This satisfies my local reproducer - using multiple clients constantly
+looping to open a connection, send the greeting, then exiting no longer
+crashes other, normally behaving clients with unrelated responses.
+
+Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+---
+ include/monitor/monitor.h | 1 +
+ monitor/monitor-internal.h | 7 +++++++
+ monitor/monitor.c | 15 +++++++++++++++
+ monitor/qmp.c | 15 ++++++++++++++-
+ qapi/qmp-dispatch.c | 21 +++++++++++++++++----
+ stubs/monitor-core.c | 5 +++++
+ 6 files changed, 59 insertions(+), 5 deletions(-)
+
+diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
+index 1a8a369b50..2c8a558c67 100644
+--- a/include/monitor/monitor.h
++++ b/include/monitor/monitor.h
+@@ -16,6 +16,7 @@ extern QemuOptsList qemu_mon_opts;
+ Monitor *monitor_cur(void);
+ Monitor *monitor_set_cur(Coroutine *co, Monitor *mon);
+ bool monitor_cur_is_qmp(void);
++int monitor_get_connection_nr(const Monitor *mon);
+
+ void monitor_init_globals(void);
+ void monitor_init_globals_core(void);
+diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
+index 9c3a09cb01..a92be8c3f7 100644
+--- a/monitor/monitor-internal.h
++++ b/monitor/monitor-internal.h
+@@ -144,6 +144,13 @@ typedef struct {
+ QemuMutex qmp_queue_lock;
+ /* Input queue that holds all the parsed QMP requests */
+ GQueue *qmp_requests;
++
++ /*
++ * A sequential number that gets incremented on every new CHR_EVENT_OPENED.
++ * Used to avoid leftover responses in BHs from being sent to the wrong
++ * client. Access with atomics.
++ */
++ int connection_nr;
+ } MonitorQMP;
+
+ /**
+diff --git a/monitor/monitor.c b/monitor/monitor.c
+index 46a171bca6..5ccdd2424b 100644
+--- a/monitor/monitor.c
++++ b/monitor/monitor.c
+@@ -135,6 +135,21 @@ bool monitor_cur_is_qmp(void)
+ return cur_mon && monitor_is_qmp(cur_mon);
+ }
+
++/**
++ * If @mon is a QMP monitor, return the connection_nr, otherwise -1.
++ */
++int monitor_get_connection_nr(const Monitor *mon)
++{
++ MonitorQMP *qmp_mon;
++
++ if (!monitor_is_qmp(mon)) {
++ return -1;
++ }
++
++ qmp_mon = container_of(mon, MonitorQMP, common);
++ return qatomic_read(&qmp_mon->connection_nr);
++}
++
+ /**
+ * Is @mon is using readline?
+ * Note: not all HMP monitors use readline, e.g., gdbserver has a
+diff --git a/monitor/qmp.c b/monitor/qmp.c
+index 092c527b6f..6b8cfcf6d8 100644
+--- a/monitor/qmp.c
++++ b/monitor/qmp.c
+@@ -141,6 +141,8 @@ static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req)
+ QDict *rsp;
+ QDict *error;
+
++ int conn_nr_before = qatomic_read(&mon->connection_nr);
++
+ rsp = qmp_dispatch(mon->commands, req, qmp_oob_enabled(mon),
+ &mon->common);
+
+@@ -156,7 +158,17 @@ static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req)
+ }
+ }
+
+- monitor_qmp_respond(mon, rsp);
++ /*
++ * qmp_dispatch might have yielded and waited for a BH, in which case there
++ * is a chance a new client connected in the meantime - if this happened,
++ * the command will not have been executed, but we also need to ensure that
++ * we don't send back a corresponding response on a line that no longer
++ * belongs to this request.
++ */
++ if (conn_nr_before == qatomic_read(&mon->connection_nr)) {
++ monitor_qmp_respond(mon, rsp);
++ }
++
+ qobject_unref(rsp);
+ }
+
+@@ -444,6 +456,7 @@ static void monitor_qmp_event(void *opaque, QEMUChrEvent event)
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
++ qatomic_inc_fetch(&mon->connection_nr);
+ mon->commands = &qmp_cap_negotiation_commands;
+ monitor_qmp_caps_reset(mon);
+ data = qmp_greeting(mon);
+diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
+index 59600210ce..95602446eb 100644
+--- a/qapi/qmp-dispatch.c
++++ b/qapi/qmp-dispatch.c
+@@ -120,16 +120,28 @@ typedef struct QmpDispatchBH {
+ QObject **ret;
+ Error **errp;
+ Coroutine *co;
++ int conn_nr;
+ } QmpDispatchBH;
+
+ static void do_qmp_dispatch_bh(void *opaque)
+ {
+ QmpDispatchBH *data = opaque;
+
+- assert(monitor_cur() == NULL);
+- monitor_set_cur(qemu_coroutine_self(), data->cur_mon);
+- data->cmd->fn(data->args, data->ret, data->errp);
+- monitor_set_cur(qemu_coroutine_self(), NULL);
++ /*
++ * A QMP monitor tracks it's client with a connection number, if this
++ * changes during the scheduling delay of this BH, we must not execute the
++ * command. Otherwise a badly placed 'qmp_capabilities' might affect the
++ * connection state of a client it was never meant for.
++ */
++ if (data->conn_nr == monitor_get_connection_nr(data->cur_mon)) {
++ assert(monitor_cur() == NULL);
++ monitor_set_cur(qemu_coroutine_self(), data->cur_mon);
++ data->cmd->fn(data->args, data->ret, data->errp);
++ monitor_set_cur(qemu_coroutine_self(), NULL);
++ } else {
++ error_setg(data->errp, "active monitor connection changed");
++ }
++
+ aio_co_wake(data->co);
+ }
+
+@@ -243,6 +255,7 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
+ .ret = &ret,
+ .errp = &err,
+ .co = qemu_coroutine_self(),
++ .conn_nr = monitor_get_connection_nr(cur_mon),
+ };
+ aio_bh_schedule_oneshot(qemu_get_aio_context(), do_qmp_dispatch_bh,
+ &data);
+diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c
+index d058a2a00d..3290b58120 100644
+--- a/stubs/monitor-core.c
++++ b/stubs/monitor-core.c
+@@ -13,6 +13,11 @@ Monitor *monitor_set_cur(Coroutine *co, Monitor *mon)
+ return NULL;
+ }
+
++int monitor_get_connection_nr(const Monitor *mon)
++{
++ return -1;
++}
++
+ void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
+ {
+ }
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Michael Tokarev <mjt@tls.msk.ru>
-Date: Wed, 1 Sep 2021 16:16:24 +0300
-Subject: [PATCH] qemu-sockets: fix unix socket path copy (again)
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Commit 4cfd970ec188558daa6214f26203fe553fb1e01f added an
-assert which ensures the path within an address of a unix
-socket returned from the kernel is at least one byte and
-does not exceed sun_path buffer. Both of this constraints
-are wrong:
-
-A unix socket can be unnamed, in this case the path is
-completely empty (not even \0)
-
-And some implementations (notable linux) can add extra
-trailing byte (\0) _after_ the sun_path buffer if we
-passed buffer larger than it (and we do).
-
-So remove the assertion (since it causes real-life breakage)
-but at the same time fix the usage of sun_path. Namely,
-we should not access sun_path[0] if kernel did not return
-it at all (this is the case for unnamed sockets),
-and use the returned salen when copyig actual path as an
-upper constraint for the amount of bytes to copy - this
-will ensure we wont exceed the information provided by
-the kernel, regardless whenever there is a trailing \0
-or not. This also helps with unnamed sockets.
-
-Note the case of abstract socket, the sun_path is actually
-a blob and can contain \0 characters, - it should not be
-passed to g_strndup and the like, it should be accessed by
-memcpy-like functions.
-
-Fixes: 4cfd970ec188558daa6214f26203fe553fb1e01f
-Fixes: http://bugs.debian.org/993145
-Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
-Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
-Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
-CC: qemu-stable@nongnu.org
----
- util/qemu-sockets.c | 13 +++++--------
- 1 file changed, 5 insertions(+), 8 deletions(-)
-
-diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
-index f2f3676d1f..c5043999e9 100644
---- a/util/qemu-sockets.c
-+++ b/util/qemu-sockets.c
-@@ -1345,25 +1345,22 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa,
- SocketAddress *addr;
- struct sockaddr_un *su = (struct sockaddr_un *)sa;
-
-- assert(salen >= sizeof(su->sun_family) + 1 &&
-- salen <= sizeof(struct sockaddr_un));
--
- addr = g_new0(SocketAddress, 1);
- addr->type = SOCKET_ADDRESS_TYPE_UNIX;
-+ salen -= offsetof(struct sockaddr_un, sun_path);
- #ifdef CONFIG_LINUX
-- if (!su->sun_path[0]) {
-+ if (salen > 0 && !su->sun_path[0]) {
- /* Linux abstract socket */
-- addr->u.q_unix.path = g_strndup(su->sun_path + 1,
-- salen - sizeof(su->sun_family) - 1);
-+ addr->u.q_unix.path = g_strndup(su->sun_path + 1, salen - 1);
- addr->u.q_unix.has_abstract = true;
- addr->u.q_unix.abstract = true;
- addr->u.q_unix.has_tight = true;
-- addr->u.q_unix.tight = salen < sizeof(*su);
-+ addr->u.q_unix.tight = salen < sizeof(su->sun_path);
- return addr;
- }
- #endif
-
-- addr->u.q_unix.path = g_strndup(su->sun_path, sizeof(su->sun_path));
-+ addr->u.q_unix.path = g_strndup(su->sun_path, salen);
- return addr;
- }
- #endif /* WIN32 */
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Stefan Reiter <s.reiter@proxmox.com>
+Date: Wed, 1 Sep 2021 16:51:04 +0200
+Subject: [PATCH] monitor/hmp: add support for flag argument with value
+
+Adds support for the "-xS" parameter type, where "-x" denotes a flag
+name and the "S" suffix indicates that this flag is supposed to take an
+arbitrary string parameter.
+
+These parameters are always optional, the entry in the qdict will be
+omitted if the flag is not given.
+
+Reviewed-by: Eric Blake <eblake@redhat.com>
+Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+---
+ monitor/hmp.c | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/monitor/hmp.c b/monitor/hmp.c
+index d50c3124e1..a32dce7a35 100644
+--- a/monitor/hmp.c
++++ b/monitor/hmp.c
+@@ -980,6 +980,7 @@ static QDict *monitor_parse_arguments(Monitor *mon,
+ {
+ const char *tmp = p;
+ int skip_key = 0;
++ int ret;
+ /* option */
+
+ c = *typestr++;
+@@ -1002,8 +1003,22 @@ static QDict *monitor_parse_arguments(Monitor *mon,
+ }
+ if (skip_key) {
+ p = tmp;
++ } else if (*typestr == 'S') {
++ /* has option with string value */
++ typestr++;
++ tmp = p++;
++ while (qemu_isspace(*p)) {
++ p++;
++ }
++ ret = get_str(buf, sizeof(buf), &p);
++ if (ret < 0) {
++ monitor_printf(mon, "%s: value expected for -%c\n",
++ cmd->name, *tmp);
++ goto fail;
++ }
++ qdict_put_str(qdict, key, buf);
+ } else {
+- /* has option */
++ /* has boolean option */
+ p++;
+ qdict_put_bool(qdict, key, true);
+ }
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Stefan Reiter <s.reiter@proxmox.com>
-Date: Mon, 23 Aug 2021 11:28:32 +0200
-Subject: [PATCH] monitor/qmp: fix race with clients disconnecting early
-
-The following sequence can produce a race condition that results in
-responses meant for different clients being sent to the wrong one:
-
-(QMP, no OOB)
-1) client A connects
-2) client A sends 'qmp_capabilities'
-3) 'qmp_dispatch' runs in coroutine, schedules out to
- 'do_qmp_dispatch_bh' and yields
-4) client A disconnects (i.e. aborts, crashes, etc...)
-5) client B connects
-6) 'do_qmp_dispatch_bh' runs 'qmp_capabilities' and wakes calling coroutine
-7) capabilities are now set and 'mon->commands' is set to '&qmp_commands'
-8) 'qmp_dispatch' returns to 'monitor_qmp_dispatch'
-9) success message is sent to client B *without it ever having sent
- 'qmp_capabilities' itself*
-9a) even if client B ignores it, it will now presumably send it's own
- greeting, which will error because caps are already set
-
-The fix proposed here uses an atomic, sequential connection number
-stored in the MonitorQMP struct, which is incremented everytime a new
-client connects. Since it is not changed on CHR_EVENT_CLOSED, the
-behaviour of allowing a client to disconnect only one side of the
-connection is retained.
-
-The connection_nr needs to be exposed outside of the monitor subsystem,
-since qmp_dispatch lives in qapi code. It needs to be checked twice,
-once for actually running the command in the BH (fixes 7), and once for
-sending back a response (fixes 9).
-
-This satisfies my local reproducer - using multiple clients constantly
-looping to open a connection, send the greeting, then exiting no longer
-crashes other, normally behaving clients with unrelated responses.
-
-Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
----
- include/monitor/monitor.h | 1 +
- monitor/monitor-internal.h | 7 +++++++
- monitor/monitor.c | 15 +++++++++++++++
- monitor/qmp.c | 15 ++++++++++++++-
- qapi/qmp-dispatch.c | 21 +++++++++++++++++----
- stubs/monitor-core.c | 5 +++++
- 6 files changed, 59 insertions(+), 5 deletions(-)
-
-diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
-index 1a8a369b50..2c8a558c67 100644
---- a/include/monitor/monitor.h
-+++ b/include/monitor/monitor.h
-@@ -16,6 +16,7 @@ extern QemuOptsList qemu_mon_opts;
- Monitor *monitor_cur(void);
- Monitor *monitor_set_cur(Coroutine *co, Monitor *mon);
- bool monitor_cur_is_qmp(void);
-+int monitor_get_connection_nr(const Monitor *mon);
-
- void monitor_init_globals(void);
- void monitor_init_globals_core(void);
-diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
-index 9c3a09cb01..a92be8c3f7 100644
---- a/monitor/monitor-internal.h
-+++ b/monitor/monitor-internal.h
-@@ -144,6 +144,13 @@ typedef struct {
- QemuMutex qmp_queue_lock;
- /* Input queue that holds all the parsed QMP requests */
- GQueue *qmp_requests;
-+
-+ /*
-+ * A sequential number that gets incremented on every new CHR_EVENT_OPENED.
-+ * Used to avoid leftover responses in BHs from being sent to the wrong
-+ * client. Access with atomics.
-+ */
-+ int connection_nr;
- } MonitorQMP;
-
- /**
-diff --git a/monitor/monitor.c b/monitor/monitor.c
-index 46a171bca6..5ccdd2424b 100644
---- a/monitor/monitor.c
-+++ b/monitor/monitor.c
-@@ -135,6 +135,21 @@ bool monitor_cur_is_qmp(void)
- return cur_mon && monitor_is_qmp(cur_mon);
- }
-
-+/**
-+ * If @mon is a QMP monitor, return the connection_nr, otherwise -1.
-+ */
-+int monitor_get_connection_nr(const Monitor *mon)
-+{
-+ MonitorQMP *qmp_mon;
-+
-+ if (!monitor_is_qmp(mon)) {
-+ return -1;
-+ }
-+
-+ qmp_mon = container_of(mon, MonitorQMP, common);
-+ return qatomic_read(&qmp_mon->connection_nr);
-+}
-+
- /**
- * Is @mon is using readline?
- * Note: not all HMP monitors use readline, e.g., gdbserver has a
-diff --git a/monitor/qmp.c b/monitor/qmp.c
-index 092c527b6f..6b8cfcf6d8 100644
---- a/monitor/qmp.c
-+++ b/monitor/qmp.c
-@@ -141,6 +141,8 @@ static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req)
- QDict *rsp;
- QDict *error;
-
-+ int conn_nr_before = qatomic_read(&mon->connection_nr);
-+
- rsp = qmp_dispatch(mon->commands, req, qmp_oob_enabled(mon),
- &mon->common);
-
-@@ -156,7 +158,17 @@ static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req)
- }
- }
-
-- monitor_qmp_respond(mon, rsp);
-+ /*
-+ * qmp_dispatch might have yielded and waited for a BH, in which case there
-+ * is a chance a new client connected in the meantime - if this happened,
-+ * the command will not have been executed, but we also need to ensure that
-+ * we don't send back a corresponding response on a line that no longer
-+ * belongs to this request.
-+ */
-+ if (conn_nr_before == qatomic_read(&mon->connection_nr)) {
-+ monitor_qmp_respond(mon, rsp);
-+ }
-+
- qobject_unref(rsp);
- }
-
-@@ -444,6 +456,7 @@ static void monitor_qmp_event(void *opaque, QEMUChrEvent event)
-
- switch (event) {
- case CHR_EVENT_OPENED:
-+ qatomic_inc_fetch(&mon->connection_nr);
- mon->commands = &qmp_cap_negotiation_commands;
- monitor_qmp_caps_reset(mon);
- data = qmp_greeting(mon);
-diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
-index 59600210ce..95602446eb 100644
---- a/qapi/qmp-dispatch.c
-+++ b/qapi/qmp-dispatch.c
-@@ -120,16 +120,28 @@ typedef struct QmpDispatchBH {
- QObject **ret;
- Error **errp;
- Coroutine *co;
-+ int conn_nr;
- } QmpDispatchBH;
-
- static void do_qmp_dispatch_bh(void *opaque)
- {
- QmpDispatchBH *data = opaque;
-
-- assert(monitor_cur() == NULL);
-- monitor_set_cur(qemu_coroutine_self(), data->cur_mon);
-- data->cmd->fn(data->args, data->ret, data->errp);
-- monitor_set_cur(qemu_coroutine_self(), NULL);
-+ /*
-+ * A QMP monitor tracks it's client with a connection number, if this
-+ * changes during the scheduling delay of this BH, we must not execute the
-+ * command. Otherwise a badly placed 'qmp_capabilities' might affect the
-+ * connection state of a client it was never meant for.
-+ */
-+ if (data->conn_nr == monitor_get_connection_nr(data->cur_mon)) {
-+ assert(monitor_cur() == NULL);
-+ monitor_set_cur(qemu_coroutine_self(), data->cur_mon);
-+ data->cmd->fn(data->args, data->ret, data->errp);
-+ monitor_set_cur(qemu_coroutine_self(), NULL);
-+ } else {
-+ error_setg(data->errp, "active monitor connection changed");
-+ }
-+
- aio_co_wake(data->co);
- }
-
-@@ -243,6 +255,7 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
- .ret = &ret,
- .errp = &err,
- .co = qemu_coroutine_self(),
-+ .conn_nr = monitor_get_connection_nr(cur_mon),
- };
- aio_bh_schedule_oneshot(qemu_get_aio_context(), do_qmp_dispatch_bh,
- &data);
-diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c
-index d058a2a00d..3290b58120 100644
---- a/stubs/monitor-core.c
-+++ b/stubs/monitor-core.c
-@@ -13,6 +13,11 @@ Monitor *monitor_set_cur(Coroutine *co, Monitor *mon)
- return NULL;
- }
-
-+int monitor_get_connection_nr(const Monitor *mon)
-+{
-+ return -1;
-+}
-+
- void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
- {
- }
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Stefan Reiter <s.reiter@proxmox.com>
-Date: Wed, 1 Sep 2021 16:51:04 +0200
-Subject: [PATCH] monitor/hmp: add support for flag argument with value
-
-Adds support for the "-xS" parameter type, where "-x" denotes a flag
-name and the "S" suffix indicates that this flag is supposed to take an
-arbitrary string parameter.
-
-These parameters are always optional, the entry in the qdict will be
-omitted if the flag is not given.
-
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
----
- monitor/hmp.c | 17 ++++++++++++++++-
- 1 file changed, 16 insertions(+), 1 deletion(-)
-
-diff --git a/monitor/hmp.c b/monitor/hmp.c
-index d50c3124e1..a32dce7a35 100644
---- a/monitor/hmp.c
-+++ b/monitor/hmp.c
-@@ -980,6 +980,7 @@ static QDict *monitor_parse_arguments(Monitor *mon,
- {
- const char *tmp = p;
- int skip_key = 0;
-+ int ret;
- /* option */
-
- c = *typestr++;
-@@ -1002,8 +1003,22 @@ static QDict *monitor_parse_arguments(Monitor *mon,
- }
- if (skip_key) {
- p = tmp;
-+ } else if (*typestr == 'S') {
-+ /* has option with string value */
-+ typestr++;
-+ tmp = p++;
-+ while (qemu_isspace(*p)) {
-+ p++;
-+ }
-+ ret = get_str(buf, sizeof(buf), &p);
-+ if (ret < 0) {
-+ monitor_printf(mon, "%s: value expected for -%c\n",
-+ cmd->name, *tmp);
-+ goto fail;
-+ }
-+ qdict_put_str(qdict, key, buf);
- } else {
-- /* has option */
-+ /* has boolean option */
- p++;
- qdict_put_bool(qdict, key, true);
- }
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Stefan Reiter <s.reiter@proxmox.com>
+Date: Wed, 25 Aug 2021 11:14:13 +0200
+Subject: [PATCH] monitor: refactor set/expire_password and allow VNC display
+ id
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It is possible to specify more than one VNC server on the command line,
+either with an explicit ID or the auto-generated ones à la "default",
+"vnc2", "vnc3", ...
+
+It is not possible to change the password on one of these extra VNC
+displays though. Fix this by adding a "display" parameter to the
+"set_password" and "expire_password" QMP and HMP commands.
+
+For HMP, the display is specified using the "-d" value flag.
+
+For QMP, the schema is updated to explicitly express the supported
+variants of the commands with protocol-discriminated unions.
+
+Suggested-by: Eric Blake <eblake@redhat.com>
+Suggested-by: Markus Armbruster <armbru@redhat.com>
+Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+---
+ hmp-commands.hx | 29 ++++----
+ monitor/hmp-cmds.c | 57 +++++++++++++++-
+ monitor/qmp-cmds.c | 62 ++++++-----------
+ qapi/ui.json | 165 ++++++++++++++++++++++++++++++++++++++-------
+ 4 files changed, 233 insertions(+), 80 deletions(-)
+
+diff --git a/hmp-commands.hx b/hmp-commands.hx
+index 8e45bce2cd..d78e4cfc47 100644
+--- a/hmp-commands.hx
++++ b/hmp-commands.hx
+@@ -1514,34 +1514,35 @@ ERST
+
+ {
+ .name = "set_password",
+- .args_type = "protocol:s,password:s,connected:s?",
+- .params = "protocol password action-if-connected",
++ .args_type = "protocol:s,password:s,display:-dS,connected:s?",
++ .params = "protocol password [-d display] [action-if-connected]",
+ .help = "set spice/vnc password",
+ .cmd = hmp_set_password,
+ },
+
+ SRST
+-``set_password [ vnc | spice ] password [ action-if-connected ]``
+- Change spice/vnc password. Use zero to make the password stay valid
+- forever. *action-if-connected* specifies what should happen in
+- case a connection is established: *fail* makes the password change
+- fail. *disconnect* changes the password and disconnects the
+- client. *keep* changes the password and keeps the connection up.
+- *keep* is the default.
++``set_password [ vnc | spice ] password [ -d display ] [ action-if-connected ]``
++ Change spice/vnc password. *display* can be used with 'vnc' to specify
++ which display to set the password on. *action-if-connected* specifies
++ what should happen in case a connection is established: *fail* makes
++ the password change fail. *disconnect* changes the password and
++ disconnects the client. *keep* changes the password and keeps the
++ connection up. *keep* is the default.
+ ERST
+
+ {
+ .name = "expire_password",
+- .args_type = "protocol:s,time:s",
+- .params = "protocol time",
++ .args_type = "protocol:s,time:s,display:-dS",
++ .params = "protocol time [-d display]",
+ .help = "set spice/vnc password expire-time",
+ .cmd = hmp_expire_password,
+ },
+
+ SRST
+-``expire_password [ vnc | spice ]`` *expire-time*
+- Specify when a password for spice/vnc becomes
+- invalid. *expire-time* accepts:
++``expire_password [ vnc | spice ] expire-time [ -d display ]``
++ Specify when a password for spice/vnc becomes invalid.
++ *display* behaves the same as in ``set_password``.
++ *expire-time* accepts:
+
+ ``now``
+ Invalidate password instantly.
+diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
+index a7e197a90b..f4ef58d257 100644
+--- a/monitor/hmp-cmds.c
++++ b/monitor/hmp-cmds.c
+@@ -1451,10 +1451,41 @@ void hmp_set_password(Monitor *mon, const QDict *qdict)
+ {
+ const char *protocol = qdict_get_str(qdict, "protocol");
+ const char *password = qdict_get_str(qdict, "password");
++ const char *display = qdict_get_try_str(qdict, "display");
+ const char *connected = qdict_get_try_str(qdict, "connected");
+ Error *err = NULL;
++ DisplayProtocol proto;
+
+- qmp_set_password(protocol, password, !!connected, connected, &err);
++ SetPasswordOptions opts = {
++ .password = g_strdup(password),
++ .u.vnc.display = NULL,
++ };
++
++ proto = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
++ DISPLAY_PROTOCOL_VNC, &err);
++ if (err) {
++ hmp_handle_error(mon, err);
++ return;
++ }
++ opts.protocol = proto;
++
++ if (proto == DISPLAY_PROTOCOL_VNC) {
++ opts.u.vnc.has_display = !!display;
++ opts.u.vnc.display = g_strdup(display);
++ } else if (proto == DISPLAY_PROTOCOL_SPICE) {
++ opts.u.spice.has_connected = !!connected;
++ opts.u.spice.connected =
++ qapi_enum_parse(&SetPasswordAction_lookup, connected,
++ SET_PASSWORD_ACTION_KEEP, &err);
++ if (err) {
++ hmp_handle_error(mon, err);
++ return;
++ }
++ }
++
++ qmp_set_password(&opts, &err);
++ g_free(opts.password);
++ g_free(opts.u.vnc.display);
+ hmp_handle_error(mon, err);
+ }
+
+@@ -1462,9 +1493,31 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict)
+ {
+ const char *protocol = qdict_get_str(qdict, "protocol");
+ const char *whenstr = qdict_get_str(qdict, "time");
++ const char *display = qdict_get_try_str(qdict, "display");
+ Error *err = NULL;
++ DisplayProtocol proto;
+
+- qmp_expire_password(protocol, whenstr, &err);
++ ExpirePasswordOptions opts = {
++ .time = g_strdup(whenstr),
++ .u.vnc.display = NULL,
++ };
++
++ proto = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
++ DISPLAY_PROTOCOL_VNC, &err);
++ if (err) {
++ hmp_handle_error(mon, err);
++ return;
++ }
++ opts.protocol = proto;
++
++ if (proto == DISPLAY_PROTOCOL_VNC) {
++ opts.u.vnc.has_display = !!display;
++ opts.u.vnc.display = g_strdup(display);
++ }
++
++ qmp_expire_password(&opts, &err);
++ g_free(opts.time);
++ g_free(opts.u.vnc.display);
+ hmp_handle_error(mon, err);
+ }
+
+diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
+index f7d64a6457..65882b5997 100644
+--- a/monitor/qmp-cmds.c
++++ b/monitor/qmp-cmds.c
+@@ -164,45 +164,30 @@ void qmp_system_wakeup(Error **errp)
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp);
+ }
+
+-void qmp_set_password(const char *protocol, const char *password,
+- bool has_connected, const char *connected, Error **errp)
++void qmp_set_password(SetPasswordOptions *opts, Error **errp)
+ {
+- int disconnect_if_connected = 0;
+- int fail_if_connected = 0;
+- int rc;
++ bool disconnect_if_connected = false;
++ bool fail_if_connected = false;
++ int rc = 0;
+
+- if (has_connected) {
+- if (strcmp(connected, "fail") == 0) {
+- fail_if_connected = 1;
+- } else if (strcmp(connected, "disconnect") == 0) {
+- disconnect_if_connected = 1;
+- } else if (strcmp(connected, "keep") == 0) {
+- /* nothing */
+- } else {
+- error_setg(errp, QERR_INVALID_PARAMETER, "connected");
+- return;
+- }
+- }
+-
+- if (strcmp(protocol, "spice") == 0) {
++ if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
+ if (!qemu_using_spice(errp)) {
+ return;
+ }
+- rc = qemu_spice.set_passwd(password, fail_if_connected,
++ if (opts->u.spice.has_connected) {
++ fail_if_connected =
++ opts->u.spice.connected == SET_PASSWORD_ACTION_FAIL;
++ disconnect_if_connected =
++ opts->u.spice.connected == SET_PASSWORD_ACTION_DISCONNECT;
++ }
++ rc = qemu_spice.set_passwd(opts->password, fail_if_connected,
+ disconnect_if_connected);
+- } else if (strcmp(protocol, "vnc") == 0) {
+- if (fail_if_connected || disconnect_if_connected) {
+- /* vnc supports "connected=keep" only */
+- error_setg(errp, QERR_INVALID_PARAMETER, "connected");
+- return;
+- }
++ } else if (opts->protocol == DISPLAY_PROTOCOL_VNC) {
+ /* Note that setting an empty password will not disable login through
+ * this interface. */
+- rc = vnc_display_password(NULL, password);
+- } else {
+- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol",
+- "'vnc' or 'spice'");
+- return;
++ rc = vnc_display_password(
++ opts->u.vnc.has_display ? opts->u.vnc.display : NULL,
++ opts->password);
+ }
+
+ if (rc != 0) {
+@@ -210,11 +195,11 @@ void qmp_set_password(const char *protocol, const char *password,
+ }
+ }
+
+-void qmp_expire_password(const char *protocol, const char *whenstr,
+- Error **errp)
++void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp)
+ {
+ time_t when;
+ int rc;
++ const char* whenstr = opts->time;
+
+ if (strcmp(whenstr, "now") == 0) {
+ when = 0;
+@@ -226,17 +211,14 @@ void qmp_expire_password(const char *protocol, const char *whenstr,
+ when = strtoull(whenstr, NULL, 10);
+ }
+
+- if (strcmp(protocol, "spice") == 0) {
++ if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
+ if (!qemu_using_spice(errp)) {
+ return;
+ }
+ rc = qemu_spice.set_pw_expire(when);
+- } else if (strcmp(protocol, "vnc") == 0) {
+- rc = vnc_display_pw_expire(NULL, when);
+- } else {
+- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol",
+- "'vnc' or 'spice'");
+- return;
++ } else if (opts->protocol == DISPLAY_PROTOCOL_VNC) {
++ rc = vnc_display_pw_expire(
++ opts->u.vnc.has_display ? opts->u.vnc.display : NULL, when);
+ }
+
+ if (rc != 0) {
+diff --git a/qapi/ui.json b/qapi/ui.json
+index fd9677d48e..cba8665b73 100644
+--- a/qapi/ui.json
++++ b/qapi/ui.json
+@@ -9,22 +9,23 @@
+ { 'include': 'common.json' }
+ { 'include': 'sockets.json' }
+
++##
++# @DisplayProtocol:
++#
++# Display protocols which support changing password options.
++#
++# Since: 6.2
++#
++##
++{ 'enum': 'DisplayProtocol',
++ 'data': [ { 'name': 'vnc', 'if': 'defined(CONFIG_VNC)' },
++ { 'name': 'spice', 'if': 'defined(CONFIG_SPICE)' } ] }
++
+ ##
+ # @set_password:
+ #
+ # Sets the password of a remote display session.
+ #
+-# @protocol: - 'vnc' to modify the VNC server password
+-# - 'spice' to modify the Spice server password
+-#
+-# @password: the new password
+-#
+-# @connected: how to handle existing clients when changing the
+-# password. If nothing is specified, defaults to 'keep'
+-# 'fail' to fail the command if clients are connected
+-# 'disconnect' to disconnect existing clients
+-# 'keep' to maintain existing clients
+-#
+ # Returns: - Nothing on success
+ # - If Spice is not enabled, DeviceNotFound
+ #
+@@ -37,16 +38,123 @@
+ # <- { "return": {} }
+ #
+ ##
+-{ 'command': 'set_password',
+- 'data': {'protocol': 'str', 'password': 'str', '*connected': 'str'} }
++{ 'command': 'set_password', 'boxed': true, 'data': 'SetPasswordOptions' }
++
++##
++# @SetPasswordOptions:
++#
++# Data required to set a new password on a display server protocol.
++#
++# @protocol: - 'vnc' to modify the VNC server password
++# - 'spice' to modify the Spice server password
++#
++# @password: the new password
++#
++# Since: 6.2
++#
++##
++{ 'union': 'SetPasswordOptions',
++ 'base': { 'protocol': 'DisplayProtocol',
++ 'password': 'str' },
++ 'discriminator': 'protocol',
++ 'data': { 'vnc': 'SetPasswordOptionsVnc',
++ 'spice': 'SetPasswordOptionsSpice' } }
++
++##
++# @SetPasswordAction:
++#
++# An action to take on changing a password on a connection with active clients.
++#
++# @fail: fail the command if clients are connected
++#
++# @disconnect: disconnect existing clients
++#
++# @keep: maintain existing clients
++#
++# Since: 6.2
++#
++##
++{ 'enum': 'SetPasswordAction',
++ 'data': [ 'fail', 'disconnect', 'keep' ] }
++
++##
++# @SetPasswordActionVnc:
++#
++# See @SetPasswordAction. VNC only supports the keep action. 'connection'
++# should just be omitted for VNC, this is kept for backwards compatibility.
++#
++# @keep: maintain existing clients
++#
++# Since: 6.2
++#
++##
++{ 'enum': 'SetPasswordActionVnc',
++ 'data': [ 'keep' ] }
++
++##
++# @SetPasswordOptionsSpice:
++#
++# Options for set_password specific to the VNC procotol.
++#
++# @connected: How to handle existing clients when changing the
++# password. If nothing is specified, defaults to 'keep'.
++#
++# Since: 6.2
++#
++##
++{ 'struct': 'SetPasswordOptionsSpice',
++ 'data': { '*connected': 'SetPasswordAction' } }
++
++##
++# @SetPasswordOptionsVnc:
++#
++# Options for set_password specific to the VNC procotol.
++#
++# @display: The id of the display where the password should be changed.
++# Defaults to the first.
++#
++# @connected: How to handle existing clients when changing the
++# password.
++#
++# Features:
++# @deprecated: For VNC, @connected will always be 'keep', parameter should be
++# omitted.
++#
++# Since: 6.2
++#
++##
++{ 'struct': 'SetPasswordOptionsVnc',
++ 'data': { '*display': 'str',
++ '*connected': { 'type': 'SetPasswordActionVnc',
++ 'features': ['deprecated'] } } }
+
+ ##
+ # @expire_password:
+ #
+ # Expire the password of a remote display server.
+ #
+-# @protocol: the name of the remote display protocol 'vnc' or 'spice'
++# Returns: - Nothing on success
++# - If @protocol is 'spice' and Spice is not active, DeviceNotFound
+ #
++# Since: 0.14
++#
++# Example:
++#
++# -> { "execute": "expire_password", "arguments": { "protocol": "vnc",
++# "time": "+60" } }
++# <- { "return": {} }
++#
++##
++{ 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' }
++
++##
++# @ExpirePasswordOptions:
++#
++# Data required to set password expiration on a display server protocol.
++#
++# @protocol: - 'vnc' to modify the VNC server expiration
++# - 'spice' to modify the Spice server expiration
++
+ # @time: when to expire the password.
+ #
+ # - 'now' to expire the password immediately
+@@ -54,24 +162,33 @@
+ # - '+INT' where INT is the number of seconds from now (integer)
+ # - 'INT' where INT is the absolute time in seconds
+ #
+-# Returns: - Nothing on success
+-# - If @protocol is 'spice' and Spice is not active, DeviceNotFound
+-#
+-# Since: 0.14
+-#
+ # Notes: Time is relative to the server and currently there is no way to
+ # coordinate server time with client time. It is not recommended to
+ # use the absolute time version of the @time parameter unless you're
+ # sure you are on the same machine as the QEMU instance.
+ #
+-# Example:
++# Since: 6.2
+ #
+-# -> { "execute": "expire_password", "arguments": { "protocol": "vnc",
+-# "time": "+60" } }
+-# <- { "return": {} }
++##
++{ 'union': 'ExpirePasswordOptions',
++ 'base': { 'protocol': 'DisplayProtocol',
++ 'time': 'str' },
++ 'discriminator': 'protocol',
++ 'data': { 'vnc': 'ExpirePasswordOptionsVnc' } }
++
++##
++# @ExpirePasswordOptionsVnc:
++#
++# Options for expire_password specific to the VNC procotol.
++#
++# @display: The id of the display where the expiration should be changed.
++# Defaults to the first.
++#
++# Since: 6.2
+ #
+ ##
+-{ 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
++{ 'struct': 'ExpirePasswordOptionsVnc',
++ 'data': { '*display': 'str' } }
+
+ ##
+ # @screendump:
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Stefano Garzarella <sgarzare@redhat.com>
+Date: Fri, 10 Sep 2021 14:45:33 +0200
+Subject: [PATCH] block/mirror: fix NULL pointer dereference in
+ mirror_wait_on_conflicts()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In mirror_iteration() we call mirror_wait_on_conflicts() with
+`self` parameter set to NULL.
+
+Starting from commit d44dae1a7c we dereference `self` pointer in
+mirror_wait_on_conflicts() without checks if it is not NULL.
+
+Backtrace:
+ Program terminated with signal SIGSEGV, Segmentation fault.
+ #0 mirror_wait_on_conflicts (self=0x0, s=<optimized out>, offset=<optimized out>, bytes=<optimized out>)
+ at ../block/mirror.c:172
+ 172 self->waiting_for_op = op;
+ [Current thread is 1 (Thread 0x7f0908931ec0 (LWP 380249))]
+ (gdb) bt
+ #0 mirror_wait_on_conflicts (self=0x0, s=<optimized out>, offset=<optimized out>, bytes=<optimized out>)
+ at ../block/mirror.c:172
+ #1 0x00005610c5d9d631 in mirror_run (job=0x5610c76a2c00, errp=<optimized out>) at ../block/mirror.c:491
+ #2 0x00005610c5d58726 in job_co_entry (opaque=0x5610c76a2c00) at ../job.c:917
+ #3 0x00005610c5f046c6 in coroutine_trampoline (i0=<optimized out>, i1=<optimized out>)
+ at ../util/coroutine-ucontext.c:173
+ #4 0x00007f0909975820 in ?? () at ../sysdeps/unix/sysv/linux/x86_64/__start_context.S:91
+ from /usr/lib64/libc.so.6
+
+Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2001404
+Fixes: d44dae1a7c ("block/mirror: fix active mirror dead-lock in mirror_wait_on_conflicts")
+Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
+Message-Id: <20210910124533.288318-1-sgarzare@redhat.com>
+Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
+Signed-off-by: Hanna Reitz <hreitz@redhat.com>
+(cherry picked from commit 66fed30c9cd11854fc878a4eceb507e915d7c9cd)
+Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+---
+ block/mirror.c | 25 ++++++++++++++++---------
+ 1 file changed, 16 insertions(+), 9 deletions(-)
+
+diff --git a/block/mirror.c b/block/mirror.c
+index 98fc66eabf..85b781bc21 100644
+--- a/block/mirror.c
++++ b/block/mirror.c
+@@ -160,18 +160,25 @@ static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self,
+ if (ranges_overlap(self_start_chunk, self_nb_chunks,
+ op_start_chunk, op_nb_chunks))
+ {
+- /*
+- * If the operation is already (indirectly) waiting for us, or
+- * will wait for us as soon as it wakes up, then just go on
+- * (instead of producing a deadlock in the former case).
+- */
+- if (op->waiting_for_op) {
+- continue;
++ if (self) {
++ /*
++ * If the operation is already (indirectly) waiting for us,
++ * or will wait for us as soon as it wakes up, then just go
++ * on (instead of producing a deadlock in the former case).
++ */
++ if (op->waiting_for_op) {
++ continue;
++ }
++
++ self->waiting_for_op = op;
+ }
+
+- self->waiting_for_op = op;
+ qemu_co_queue_wait(&op->waiting_requests, NULL);
+- self->waiting_for_op = NULL;
++
++ if (self) {
++ self->waiting_for_op = NULL;
++ }
++
+ break;
+ }
+ }
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Stefan Reiter <s.reiter@proxmox.com>
-Date: Wed, 25 Aug 2021 11:14:13 +0200
-Subject: [PATCH] monitor: refactor set/expire_password and allow VNC display
- id
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-It is possible to specify more than one VNC server on the command line,
-either with an explicit ID or the auto-generated ones à la "default",
-"vnc2", "vnc3", ...
-
-It is not possible to change the password on one of these extra VNC
-displays though. Fix this by adding a "display" parameter to the
-"set_password" and "expire_password" QMP and HMP commands.
-
-For HMP, the display is specified using the "-d" value flag.
-
-For QMP, the schema is updated to explicitly express the supported
-variants of the commands with protocol-discriminated unions.
-
-Suggested-by: Eric Blake <eblake@redhat.com>
-Suggested-by: Markus Armbruster <armbru@redhat.com>
-Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
----
- hmp-commands.hx | 29 ++++----
- monitor/hmp-cmds.c | 57 +++++++++++++++-
- monitor/qmp-cmds.c | 62 ++++++-----------
- qapi/ui.json | 163 ++++++++++++++++++++++++++++++++++++++-------
- 4 files changed, 232 insertions(+), 79 deletions(-)
-
-diff --git a/hmp-commands.hx b/hmp-commands.hx
-index 8e45bce2cd..d78e4cfc47 100644
---- a/hmp-commands.hx
-+++ b/hmp-commands.hx
-@@ -1514,34 +1514,35 @@ ERST
-
- {
- .name = "set_password",
-- .args_type = "protocol:s,password:s,connected:s?",
-- .params = "protocol password action-if-connected",
-+ .args_type = "protocol:s,password:s,display:-dS,connected:s?",
-+ .params = "protocol password [-d display] [action-if-connected]",
- .help = "set spice/vnc password",
- .cmd = hmp_set_password,
- },
-
- SRST
--``set_password [ vnc | spice ] password [ action-if-connected ]``
-- Change spice/vnc password. Use zero to make the password stay valid
-- forever. *action-if-connected* specifies what should happen in
-- case a connection is established: *fail* makes the password change
-- fail. *disconnect* changes the password and disconnects the
-- client. *keep* changes the password and keeps the connection up.
-- *keep* is the default.
-+``set_password [ vnc | spice ] password [ -d display ] [ action-if-connected ]``
-+ Change spice/vnc password. *display* can be used with 'vnc' to specify
-+ which display to set the password on. *action-if-connected* specifies
-+ what should happen in case a connection is established: *fail* makes
-+ the password change fail. *disconnect* changes the password and
-+ disconnects the client. *keep* changes the password and keeps the
-+ connection up. *keep* is the default.
- ERST
-
- {
- .name = "expire_password",
-- .args_type = "protocol:s,time:s",
-- .params = "protocol time",
-+ .args_type = "protocol:s,time:s,display:-dS",
-+ .params = "protocol time [-d display]",
- .help = "set spice/vnc password expire-time",
- .cmd = hmp_expire_password,
- },
-
- SRST
--``expire_password [ vnc | spice ]`` *expire-time*
-- Specify when a password for spice/vnc becomes
-- invalid. *expire-time* accepts:
-+``expire_password [ vnc | spice ] expire-time [ -d display ]``
-+ Specify when a password for spice/vnc becomes invalid.
-+ *display* behaves the same as in ``set_password``.
-+ *expire-time* accepts:
-
- ``now``
- Invalidate password instantly.
-diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
-index e00255f7ee..047294e1ed 100644
---- a/monitor/hmp-cmds.c
-+++ b/monitor/hmp-cmds.c
-@@ -1451,10 +1451,41 @@ void hmp_set_password(Monitor *mon, const QDict *qdict)
- {
- const char *protocol = qdict_get_str(qdict, "protocol");
- const char *password = qdict_get_str(qdict, "password");
-+ const char *display = qdict_get_try_str(qdict, "display");
- const char *connected = qdict_get_try_str(qdict, "connected");
- Error *err = NULL;
-+ DisplayProtocol proto;
-
-- qmp_set_password(protocol, password, !!connected, connected, &err);
-+ SetPasswordOptions opts = {
-+ .password = g_strdup(password),
-+ .u.vnc.display = NULL,
-+ };
-+
-+ proto = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
-+ DISPLAY_PROTOCOL_VNC, &err);
-+ if (err) {
-+ hmp_handle_error(mon, err);
-+ return;
-+ }
-+ opts.protocol = proto;
-+
-+ if (proto == DISPLAY_PROTOCOL_VNC) {
-+ opts.u.vnc.has_display = !!display;
-+ opts.u.vnc.display = g_strdup(display);
-+ } else if (proto == DISPLAY_PROTOCOL_SPICE) {
-+ opts.u.spice.has_connected = !!connected;
-+ opts.u.spice.connected =
-+ qapi_enum_parse(&SetPasswordAction_lookup, connected,
-+ SET_PASSWORD_ACTION_KEEP, &err);
-+ if (err) {
-+ hmp_handle_error(mon, err);
-+ return;
-+ }
-+ }
-+
-+ qmp_set_password(&opts, &err);
-+ g_free(opts.password);
-+ g_free(opts.u.vnc.display);
- hmp_handle_error(mon, err);
- }
-
-@@ -1462,9 +1493,31 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict)
- {
- const char *protocol = qdict_get_str(qdict, "protocol");
- const char *whenstr = qdict_get_str(qdict, "time");
-+ const char *display = qdict_get_try_str(qdict, "display");
- Error *err = NULL;
-+ DisplayProtocol proto;
-+
-+ ExpirePasswordOptions opts = {
-+ .time = g_strdup(whenstr),
-+ .u.vnc.display = NULL,
-+ };
-+
-+ proto = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
-+ DISPLAY_PROTOCOL_VNC, &err);
-+ if (err) {
-+ hmp_handle_error(mon, err);
-+ return;
-+ }
-+ opts.protocol = proto;
-+
-+ if (proto == DISPLAY_PROTOCOL_VNC) {
-+ opts.u.vnc.has_display = !!display;
-+ opts.u.vnc.display = g_strdup(display);
-+ }
-
-- qmp_expire_password(protocol, whenstr, &err);
-+ qmp_expire_password(&opts, &err);
-+ g_free(opts.time);
-+ g_free(opts.u.vnc.display);
- hmp_handle_error(mon, err);
- }
-
-diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
-index f7d64a6457..65882b5997 100644
---- a/monitor/qmp-cmds.c
-+++ b/monitor/qmp-cmds.c
-@@ -164,45 +164,30 @@ void qmp_system_wakeup(Error **errp)
- qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp);
- }
-
--void qmp_set_password(const char *protocol, const char *password,
-- bool has_connected, const char *connected, Error **errp)
-+void qmp_set_password(SetPasswordOptions *opts, Error **errp)
- {
-- int disconnect_if_connected = 0;
-- int fail_if_connected = 0;
-- int rc;
--
-- if (has_connected) {
-- if (strcmp(connected, "fail") == 0) {
-- fail_if_connected = 1;
-- } else if (strcmp(connected, "disconnect") == 0) {
-- disconnect_if_connected = 1;
-- } else if (strcmp(connected, "keep") == 0) {
-- /* nothing */
-- } else {
-- error_setg(errp, QERR_INVALID_PARAMETER, "connected");
-- return;
-- }
-- }
-+ bool disconnect_if_connected = false;
-+ bool fail_if_connected = false;
-+ int rc = 0;
-
-- if (strcmp(protocol, "spice") == 0) {
-+ if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
- if (!qemu_using_spice(errp)) {
- return;
- }
-- rc = qemu_spice.set_passwd(password, fail_if_connected,
-- disconnect_if_connected);
-- } else if (strcmp(protocol, "vnc") == 0) {
-- if (fail_if_connected || disconnect_if_connected) {
-- /* vnc supports "connected=keep" only */
-- error_setg(errp, QERR_INVALID_PARAMETER, "connected");
-- return;
-+ if (opts->u.spice.has_connected) {
-+ fail_if_connected =
-+ opts->u.spice.connected == SET_PASSWORD_ACTION_FAIL;
-+ disconnect_if_connected =
-+ opts->u.spice.connected == SET_PASSWORD_ACTION_DISCONNECT;
- }
-+ rc = qemu_spice.set_passwd(opts->password, fail_if_connected,
-+ disconnect_if_connected);
-+ } else if (opts->protocol == DISPLAY_PROTOCOL_VNC) {
- /* Note that setting an empty password will not disable login through
- * this interface. */
-- rc = vnc_display_password(NULL, password);
-- } else {
-- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol",
-- "'vnc' or 'spice'");
-- return;
-+ rc = vnc_display_password(
-+ opts->u.vnc.has_display ? opts->u.vnc.display : NULL,
-+ opts->password);
- }
-
- if (rc != 0) {
-@@ -210,11 +195,11 @@ void qmp_set_password(const char *protocol, const char *password,
- }
- }
-
--void qmp_expire_password(const char *protocol, const char *whenstr,
-- Error **errp)
-+void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp)
- {
- time_t when;
- int rc;
-+ const char* whenstr = opts->time;
-
- if (strcmp(whenstr, "now") == 0) {
- when = 0;
-@@ -226,17 +211,14 @@ void qmp_expire_password(const char *protocol, const char *whenstr,
- when = strtoull(whenstr, NULL, 10);
- }
-
-- if (strcmp(protocol, "spice") == 0) {
-+ if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
- if (!qemu_using_spice(errp)) {
- return;
- }
- rc = qemu_spice.set_pw_expire(when);
-- } else if (strcmp(protocol, "vnc") == 0) {
-- rc = vnc_display_pw_expire(NULL, when);
-- } else {
-- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol",
-- "'vnc' or 'spice'");
-- return;
-+ } else if (opts->protocol == DISPLAY_PROTOCOL_VNC) {
-+ rc = vnc_display_pw_expire(
-+ opts->u.vnc.has_display ? opts->u.vnc.display : NULL, when);
- }
-
- if (rc != 0) {
-diff --git a/qapi/ui.json b/qapi/ui.json
-index fd9677d48e..0177cf4ee9 100644
---- a/qapi/ui.json
-+++ b/qapi/ui.json
-@@ -10,20 +10,21 @@
- { 'include': 'sockets.json' }
-
- ##
--# @set_password:
-+# @DisplayProtocol:
- #
--# Sets the password of a remote display session.
-+# Display protocols which support changing password options.
- #
--# @protocol: - 'vnc' to modify the VNC server password
--# - 'spice' to modify the Spice server password
-+# Since: 6.2
- #
--# @password: the new password
-+##
-+{ 'enum': 'DisplayProtocol',
-+ 'data': [ { 'name': 'vnc', 'if': 'defined(CONFIG_VNC)' },
-+ { 'name': 'spice', 'if': 'defined(CONFIG_SPICE)' } ] }
-+
-+##
-+# @set_password:
- #
--# @connected: how to handle existing clients when changing the
--# password. If nothing is specified, defaults to 'keep'
--# 'fail' to fail the command if clients are connected
--# 'disconnect' to disconnect existing clients
--# 'keep' to maintain existing clients
-+# Sets the password of a remote display session.
- #
- # Returns: - Nothing on success
- # - If Spice is not enabled, DeviceNotFound
-@@ -37,16 +38,123 @@
- # <- { "return": {} }
- #
- ##
--{ 'command': 'set_password',
-- 'data': {'protocol': 'str', 'password': 'str', '*connected': 'str'} }
-+{ 'command': 'set_password', 'boxed': true, 'data': 'SetPasswordOptions' }
-+
-+##
-+# @SetPasswordOptions:
-+#
-+# Data required to set a new password on a display server protocol.
-+#
-+# @protocol: - 'vnc' to modify the VNC server password
-+# - 'spice' to modify the Spice server password
-+#
-+# @password: the new password
-+#
-+# Since: 6.2
-+#
-+##
-+{ 'union': 'SetPasswordOptions',
-+ 'base': { 'protocol': 'DisplayProtocol',
-+ 'password': 'str' },
-+ 'discriminator': 'protocol',
-+ 'data': { 'vnc': 'SetPasswordOptionsVnc',
-+ 'spice': 'SetPasswordOptionsSpice' } }
-+
-+##
-+# @SetPasswordAction:
-+#
-+# An action to take on changing a password on a connection with active clients.
-+#
-+# @fail: fail the command if clients are connected
-+#
-+# @disconnect: disconnect existing clients
-+#
-+# @keep: maintain existing clients
-+#
-+# Since: 6.2
-+#
-+##
-+{ 'enum': 'SetPasswordAction',
-+ 'data': [ 'fail', 'disconnect', 'keep' ] }
-+
-+##
-+# @SetPasswordActionVnc:
-+#
-+# See @SetPasswordAction. VNC only supports the keep action. 'connection'
-+# should just be omitted for VNC, this is kept for backwards compatibility.
-+#
-+# @keep: maintain existing clients
-+#
-+# Since: 6.2
-+#
-+##
-+{ 'enum': 'SetPasswordActionVnc',
-+ 'data': [ 'keep' ] }
-+
-+##
-+# @SetPasswordOptionsSpice:
-+#
-+# Options for set_password specific to the VNC procotol.
-+#
-+# @connected: How to handle existing clients when changing the
-+# password. If nothing is specified, defaults to 'keep'.
-+#
-+# Since: 6.2
-+#
-+##
-+{ 'struct': 'SetPasswordOptionsSpice',
-+ 'data': { '*connected': 'SetPasswordAction' } }
-+
-+##
-+# @SetPasswordOptionsVnc:
-+#
-+# Options for set_password specific to the VNC procotol.
-+#
-+# @display: The id of the display where the password should be changed.
-+# Defaults to the first.
-+#
-+# @connected: How to handle existing clients when changing the
-+# password.
-+#
-+# Features:
-+# @deprecated: For VNC, @connected will always be 'keep', parameter should be
-+# omitted.
-+#
-+# Since: 6.2
-+#
-+##
-+{ 'struct': 'SetPasswordOptionsVnc',
-+ 'data': { '*display': 'str',
-+ '*connected': { 'type': 'SetPasswordActionVnc',
-+ 'features': ['deprecated'] } } }
-
- ##
- # @expire_password:
- #
- # Expire the password of a remote display server.
- #
--# @protocol: the name of the remote display protocol 'vnc' or 'spice'
-+# Returns: - Nothing on success
-+# - If @protocol is 'spice' and Spice is not active, DeviceNotFound
-+#
-+# Since: 0.14
-+#
-+# Example:
-+#
-+# -> { "execute": "expire_password", "arguments": { "protocol": "vnc",
-+# "time": "+60" } }
-+# <- { "return": {} }
-+#
-+##
-+{ 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' }
-+
-+##
-+# @ExpirePasswordOptions:
-+#
-+# Data required to set password expiration on a display server protocol.
- #
-+# @protocol: - 'vnc' to modify the VNC server expiration
-+# - 'spice' to modify the Spice server expiration
-+
- # @time: when to expire the password.
- #
- # - 'now' to expire the password immediately
-@@ -54,24 +162,33 @@
- # - '+INT' where INT is the number of seconds from now (integer)
- # - 'INT' where INT is the absolute time in seconds
- #
--# Returns: - Nothing on success
--# - If @protocol is 'spice' and Spice is not active, DeviceNotFound
--#
--# Since: 0.14
--#
- # Notes: Time is relative to the server and currently there is no way to
- # coordinate server time with client time. It is not recommended to
- # use the absolute time version of the @time parameter unless you're
- # sure you are on the same machine as the QEMU instance.
- #
--# Example:
-+# Since: 6.2
- #
--# -> { "execute": "expire_password", "arguments": { "protocol": "vnc",
--# "time": "+60" } }
--# <- { "return": {} }
-+##
-+{ 'union': 'ExpirePasswordOptions',
-+ 'base': { 'protocol': 'DisplayProtocol',
-+ 'time': 'str' },
-+ 'discriminator': 'protocol',
-+ 'data': { 'vnc': 'ExpirePasswordOptionsVnc' } }
-+
-+##
-+# @ExpirePasswordOptionsVnc:
-+#
-+# Options for expire_password specific to the VNC procotol.
-+#
-+# @display: The id of the display where the expiration should be changed.
-+# Defaults to the first.
-+#
-+# Since: 6.2
- #
- ##
--{ 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
-+{ 'struct': 'ExpirePasswordOptionsVnc',
-+ 'data': { '*display': 'str' } }
-
- ##
- # @screendump:
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Stefan Reiter <s.reiter@proxmox.com>
-Date: Wed, 25 Aug 2021 11:08:41 +0200
-Subject: [PATCH] monitor/hmp: correctly invert password argument detection
- again
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Commit cfb5387a1d 'hmp: remove "change vnc TARGET" command' claims to
-remove the HMP "change vnc" command, but doesn't actually do that.
-Instead it rewires it to use 'qmp_change_vnc_password', and in the
-process inverts the argument detection - ignoring the first issue, this
-inversion is wrong, as this will now ask the user for a password if one
-is already provided, and simply fail if none is given.
-
-Fixes: cfb5387a1d ("hmp: remove "change vnc TARGET" command")
-Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
-Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
----
- monitor/hmp-cmds.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
-index 047294e1ed..f4ef58d257 100644
---- a/monitor/hmp-cmds.c
-+++ b/monitor/hmp-cmds.c
-@@ -1549,7 +1549,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
- }
- if (strcmp(target, "passwd") == 0 ||
- strcmp(target, "password") == 0) {
-- if (arg) {
-+ if (!arg) {
- MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
- monitor_read_password(hmp_mon, hmp_change_read_arg, NULL);
- return;
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Fri, 10 Sep 2021 11:42:03 +0200
-Subject: [PATCH] qxl: fix pre-save logic
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Oops. Logic is backwards.
-
-Fixes: 39b8a183e2f3 ("qxl: remove assert in qxl_pre_save.")
-Resolves: https://gitlab.com/qemu-project/qemu/-/issues/610
-Resolves: https://bugzilla.redhat.com//show_bug.cgi?id=2002907
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
-Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
-Message-Id: <20210910094203.3582378-1-kraxel@redhat.com>
----
- hw/display/qxl.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/hw/display/qxl.c b/hw/display/qxl.c
-index 43482d4364..29c80b4289 100644
---- a/hw/display/qxl.c
-+++ b/hw/display/qxl.c
-@@ -2252,7 +2252,7 @@ static int qxl_pre_save(void *opaque)
- } else {
- d->last_release_offset = (uint8_t *)d->last_release - ram_start;
- }
-- if (d->last_release_offset < d->vga.vram_size) {
-+ if (d->last_release_offset >= d->vga.vram_size) {
- return 1;
- }
-
+++ /dev/null
-From 14889c02315b196f28b02832362dead64b015b6e Mon Sep 17 00:00:00 2001
-From: Stefano Garzarella <sgarzare@redhat.com>
-Date: Fri, 10 Sep 2021 14:45:33 +0200
-Subject: [PATCH qemu] block/mirror: fix NULL pointer dereference in
- mirror_wait_on_conflicts()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-In mirror_iteration() we call mirror_wait_on_conflicts() with
-`self` parameter set to NULL.
-
-Starting from commit d44dae1a7c we dereference `self` pointer in
-mirror_wait_on_conflicts() without checks if it is not NULL.
-
-Backtrace:
- Program terminated with signal SIGSEGV, Segmentation fault.
- #0 mirror_wait_on_conflicts (self=0x0, s=<optimized out>, offset=<optimized out>, bytes=<optimized out>)
- at ../block/mirror.c:172
- 172 self->waiting_for_op = op;
- [Current thread is 1 (Thread 0x7f0908931ec0 (LWP 380249))]
- (gdb) bt
- #0 mirror_wait_on_conflicts (self=0x0, s=<optimized out>, offset=<optimized out>, bytes=<optimized out>)
- at ../block/mirror.c:172
- #1 0x00005610c5d9d631 in mirror_run (job=0x5610c76a2c00, errp=<optimized out>) at ../block/mirror.c:491
- #2 0x00005610c5d58726 in job_co_entry (opaque=0x5610c76a2c00) at ../job.c:917
- #3 0x00005610c5f046c6 in coroutine_trampoline (i0=<optimized out>, i1=<optimized out>)
- at ../util/coroutine-ucontext.c:173
- #4 0x00007f0909975820 in ?? () at ../sysdeps/unix/sysv/linux/x86_64/__start_context.S:91
- from /usr/lib64/libc.so.6
-
-Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2001404
-Fixes: d44dae1a7c ("block/mirror: fix active mirror dead-lock in mirror_wait_on_conflicts")
-Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
-Message-Id: <20210910124533.288318-1-sgarzare@redhat.com>
-Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Signed-off-by: Hanna Reitz <hreitz@redhat.com>
-(cherry picked from commit 66fed30c9cd11854fc878a4eceb507e915d7c9cd)
-Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
----
- block/mirror.c | 25 ++++++++++++++++---------
- 1 file changed, 16 insertions(+), 9 deletions(-)
-
-diff --git a/block/mirror.c b/block/mirror.c
-index 33477ade1b..6a126d18c8 100644
---- a/block/mirror.c
-+++ b/block/mirror.c
-@@ -162,18 +162,25 @@ static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self,
- if (ranges_overlap(self_start_chunk, self_nb_chunks,
- op_start_chunk, op_nb_chunks))
- {
-- /*
-- * If the operation is already (indirectly) waiting for us, or
-- * will wait for us as soon as it wakes up, then just go on
-- * (instead of producing a deadlock in the former case).
-- */
-- if (op->waiting_for_op) {
-- continue;
-+ if (self) {
-+ /*
-+ * If the operation is already (indirectly) waiting for us,
-+ * or will wait for us as soon as it wakes up, then just go
-+ * on (instead of producing a deadlock in the former case).
-+ */
-+ if (op->waiting_for_op) {
-+ continue;
-+ }
-+
-+ self->waiting_for_op = op;
- }
-
-- self->waiting_for_op = op;
- qemu_co_queue_wait(&op->waiting_requests, NULL);
-- self->waiting_for_op = NULL;
-+
-+ if (self) {
-+ self->waiting_for_op = NULL;
-+ }
-+
- break;
- }
- }
---
-2.30.2
-
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Paolo Bonzini <pbonzini@redhat.com>
-Date: Thu, 23 Sep 2021 09:04:36 -0400
-Subject: [PATCH] block: introduce max_hw_iov for use in scsi-generic
-
-Linux limits the size of iovecs to 1024 (UIO_MAXIOV in the kernel
-sources, IOV_MAX in POSIX). Because of this, on some host adapters
-requests with many iovecs are rejected with -EINVAL by the
-io_submit() or readv()/writev() system calls.
-
-In fact, the same limit applies to SG_IO as well. To fix both the
-EINVAL and the possible performance issues from using fewer iovecs
-than allowed by Linux (some HBAs have max_segments as low as 128),
-introduce a separate entry in BlockLimits to hold the max_segments
-value from sysfs. This new limit is used only for SG_IO and clamped
-to bs->bl.max_iov anyway, just like max_hw_transfer is clamped to
-bs->bl.max_transfer.
-
-Reported-by: Halil Pasic <pasic@linux.ibm.com>
-Cc: Hanna Reitz <hreitz@redhat.com>
-Cc: Kevin Wolf <kwolf@redhat.com>
-Cc: qemu-block@nongnu.org
-Cc: qemu-stable@nongnu.org
-Fixes: 18473467d5 ("file-posix: try BLKSECTGET on block devices too, do not round to power of 2", 2021-06-25)
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-Message-Id: <20210923130436.1187591-1-pbonzini@redhat.com>
-Signed-off-by: Kevin Wolf <kwolf@redhat.com>
----
- block/block-backend.c | 6 ++++++
- block/file-posix.c | 2 +-
- block/io.c | 1 +
- hw/scsi/scsi-generic.c | 2 +-
- include/block/block_int.h | 7 +++++++
- include/sysemu/block-backend.h | 1 +
- 6 files changed, 17 insertions(+), 2 deletions(-)
-
-diff --git a/block/block-backend.c b/block/block-backend.c
-index 6140d133e2..ba2b5ebb10 100644
---- a/block/block-backend.c
-+++ b/block/block-backend.c
-@@ -1986,6 +1986,12 @@ uint32_t blk_get_max_transfer(BlockBackend *blk)
- return ROUND_DOWN(max, blk_get_request_alignment(blk));
- }
-
-+int blk_get_max_hw_iov(BlockBackend *blk)
-+{
-+ return MIN_NON_ZERO(blk->root->bs->bl.max_hw_iov,
-+ blk->root->bs->bl.max_iov);
-+}
-+
- int blk_get_max_iov(BlockBackend *blk)
- {
- return blk->root->bs->bl.max_iov;
-diff --git a/block/file-posix.c b/block/file-posix.c
-index c62e42743d..53be0bdc1b 100644
---- a/block/file-posix.c
-+++ b/block/file-posix.c
-@@ -1273,7 +1273,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
-
- ret = hdev_get_max_segments(s->fd, &st);
- if (ret > 0) {
-- bs->bl.max_iov = ret;
-+ bs->bl.max_hw_iov = ret;
- }
- }
- }
-diff --git a/block/io.c b/block/io.c
-index 18d345a87a..bb0a254def 100644
---- a/block/io.c
-+++ b/block/io.c
-@@ -136,6 +136,7 @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
- dst->min_mem_alignment = MAX(dst->min_mem_alignment,
- src->min_mem_alignment);
- dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov);
-+ dst->max_hw_iov = MIN_NON_ZERO(dst->max_hw_iov, src->max_hw_iov);
- }
-
- typedef struct BdrvRefreshLimitsState {
-diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
-index 665baf900e..0306ccc7b1 100644
---- a/hw/scsi/scsi-generic.c
-+++ b/hw/scsi/scsi-generic.c
-@@ -180,7 +180,7 @@ static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len)
- page = r->req.cmd.buf[2];
- if (page == 0xb0) {
- uint64_t max_transfer = blk_get_max_hw_transfer(s->conf.blk);
-- uint32_t max_iov = blk_get_max_iov(s->conf.blk);
-+ uint32_t max_iov = blk_get_max_hw_iov(s->conf.blk);
-
- assert(max_transfer);
- max_transfer = MIN_NON_ZERO(max_transfer, max_iov * qemu_real_host_page_size)
-diff --git a/include/block/block_int.h b/include/block/block_int.h
-index ffe86068d4..f4c75e8ba9 100644
---- a/include/block/block_int.h
-+++ b/include/block/block_int.h
-@@ -718,6 +718,13 @@ typedef struct BlockLimits {
- */
- uint64_t max_hw_transfer;
-
-+ /* Maximal number of scatter/gather elements allowed by the hardware.
-+ * Applies whenever transfers to the device bypass the kernel I/O
-+ * scheduler, for example with SG_IO. If larger than max_iov
-+ * or if zero, blk_get_max_hw_iov will fall back to max_iov.
-+ */
-+ int max_hw_iov;
-+
- /* memory alignment, in bytes so that no bounce buffer is needed */
- size_t min_mem_alignment;
-
-diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
-index 29d4fdbf63..82bae55161 100644
---- a/include/sysemu/block-backend.h
-+++ b/include/sysemu/block-backend.h
-@@ -211,6 +211,7 @@ uint32_t blk_get_request_alignment(BlockBackend *blk);
- uint32_t blk_get_max_transfer(BlockBackend *blk);
- uint64_t blk_get_max_hw_transfer(BlockBackend *blk);
- int blk_get_max_iov(BlockBackend *blk);
-+int blk_get_max_hw_iov(BlockBackend *blk);
- void blk_set_guest_block_size(BlockBackend *blk, int align);
- void *blk_try_blockalign(BlockBackend *blk, size_t size);
- void *blk_blockalign(BlockBackend *blk, size_t size);
---
-2.30.2
-
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/block/file-posix.c b/block/file-posix.c
-index cb9bffe047..1bbeb9e658 100644
+index dd295cfc6d..3ac5177cbb 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -533,7 +533,7 @@ static QemuOptsList raw_runtime_opts = {
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
-index 6c50d3ab4f..00289a798a 100644
+index 21b33fbe2e..32514193a9 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
-@@ -2006,9 +2006,9 @@ uint64_t cpu_get_tsc(CPUX86State *env);
+@@ -2007,9 +2007,9 @@ uint64_t cpu_get_tsc(CPUX86State *env);
#define CPU_RESOLVING_TYPE TYPE_X86_CPU
#ifdef TARGET_X86_64
- error_report_err(local_err);
- ret = -1;
- goto out;
-- }
++ if (!blk1) {
++ ret = -1;
++ goto out;
++ }
+ }
- if (!drv->create_opts) {
- error_report("Format driver '%s' does not support image creation",
- drv->format_name);
- proto_drv->format_name);
- ret = -1;
- goto out;
-+ if (!blk1) {
-+ ret = -1;
-+ goto out;
-+ }
- }
+- }
- create_opts = qemu_opts_append(create_opts, drv->create_opts);
- create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
-
-- opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
-
+- opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
+
- size = blk_getlength(blk1);
- if (size < 0) {
- error_report("Failed to get size for '%s'", in.filename);
3 files changed, 81 insertions(+), 4 deletions(-)
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
-index 4b5d9e5e50..fddb5a4b82 100644
+index ae7867a8db..956e3f4e46 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
-@@ -807,8 +807,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f,
+@@ -820,8 +820,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f,
static void virtio_balloon_stat(void *opaque, BalloonInfo *info)
{
VirtIOBalloon *dev = opaque;
static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
-index e00255f7ee..f528055cb6 100644
+index f4ef58d257..c8b97909e7 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -698,7 +698,35 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict)
2 files changed, 8 insertions(+)
diff --git a/qapi/ui.json b/qapi/ui.json
-index fd9677d48e..4497bb9c1f 100644
+index cba8665b73..081115ea8a 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
-@@ -216,11 +216,14 @@
+@@ -333,11 +333,14 @@
#
# @channels: a list of @SpiceChannel for each active spice channel
#
.name = "balloon",
.args_type = "",
diff --git a/hmp-commands.hx b/hmp-commands.hx
-index 8e45bce2cd..96bd7e00bd 100644
+index d78e4cfc47..42203dbe92 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
-@@ -1743,3 +1743,36 @@ ERST
+@@ -1744,3 +1744,36 @@ ERST
.help = "start a round of guest dirty rate measurement",
.cmd = hmp_calc_dirty_rate,
},
+ return ret;
+}
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
-index f528055cb6..42a1eaf370 100644
+index c8b97909e7..64a84cf4ee 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
-@@ -1908,6 +1908,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
+@@ -1961,6 +1961,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, err);
}
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
[increase max IOV count in QEMUFile to actually write more data]
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
migration/qemu-file.c | 38 +++++++++++++++++++++++++-------------
migration/qemu-file.h | 1 +
2 files changed, 42 insertions(+), 20 deletions(-)
diff --git a/block/file-posix.c b/block/file-posix.c
-index 1bbeb9e658..f28c5a3e52 100644
+index 3ac5177cbb..907aa3f22e 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -2443,6 +2443,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/monitor/qmp.c b/monitor/qmp.c
-index 092c527b6f..0ef7cebb78 100644
+index 6b8cfcf6d8..3ec67e32d3 100644
--- a/monitor/qmp.c
+++ b/monitor/qmp.c
-@@ -506,8 +506,7 @@ void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
+@@ -519,8 +519,7 @@ void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
qemu_chr_fe_set_echo(&mon->common.chr, true);
/* Note: we run QMP monitor in I/O thread when @chr supports that */
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/hw/core/machine.c b/hw/core/machine.c
-index 54e040587d..905fcdc03d 100644
+index 2cf2f321f9..e0f857820d 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
-@@ -106,7 +106,8 @@ GlobalProperty hw_compat_4_0[] = {
+@@ -107,7 +107,8 @@ GlobalProperty hw_compat_4_0[] = {
{ "virtio-vga", "edid", "false" },
{ "virtio-gpu-device", "edid", "false" },
{ "virtio-device", "use-started", "false" },
and only if 'is-current').
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
hw/core/machine-qmp-cmds.c | 6 ++++++
include/hw/boards.h | 2 ++
qapi/machine.json | 4 +++-
- softmmu/vl.c | 23 +++++++++++++++++++++++
- 4 files changed, 34 insertions(+), 1 deletion(-)
+ softmmu/vl.c | 25 +++++++++++++++++++++++++
+ 4 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c
index 8f8d5d5276..370e66d9cc 100644
##
# @query-machines:
diff --git a/softmmu/vl.c b/softmmu/vl.c
-index d87cf6e103..e5010236f3 100644
+index d87cf6e103..e9d40065bc 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -1621,6 +1621,7 @@ static const QEMUOption *lookup_opt(int argc, char **argv,
Date: Mon, 6 Apr 2020 12:16:57 +0200
Subject: [PATCH] PVE-Backup: add vma backup format code
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/meson.build | 2 +
meson.build | 5 +
block_ss.add(when: 'CONFIG_QCOW1', if_true: files('qcow.c'))
diff --git a/meson.build b/meson.build
-index b3e7ec0e92..b84f62f882 100644
+index b3e7ec0e92..cc46eabb42 100644
--- a/meson.build
+++ b/meson.build
@@ -1064,6 +1064,8 @@ keyutils = dependency('libkeyutils', required: false,
- move BackupBlockJob declaration from block/backup.c to include/block/block_int.h
- block/backup.c - backup-job-create: also consider source cluster size
- job.c: make job_should_pause non-static
+
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/backup-dump.c | 168 ++++++++++++++++++++++++++++++++++++++
block/backup.c | 32 +++-----
'blkdebug.c',
'blklogwrites.c',
diff --git a/include/block/block_int.h b/include/block/block_int.h
-index 3e625a4261..4bd5fdb191 100644
+index 11442893d0..8f6135e6a5 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -26,6 +26,7 @@
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
[PVE-Backup: avoid coroutines to fix AIO freeze, cleanups]
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/meson.build | 5 +
block/monitor/block-hmp-cmds.c | 33 ++
{
.name = "usernet",
diff --git a/hmp-commands.hx b/hmp-commands.hx
-index 96bd7e00bd..b18063ce19 100644
+index 42203dbe92..7faba36b39 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -99,6 +99,35 @@ ERST
{
diff --git a/include/block/block_int.h b/include/block/block_int.h
-index 4bd5fdb191..10452dd50b 100644
+index 8f6135e6a5..4a572a2e34 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -66,7 +66,7 @@
void hmp_device_add(Monitor *mon, const QDict *qdict);
void hmp_device_del(Monitor *mon, const QDict *qdict);
diff --git a/meson.build b/meson.build
-index b84f62f882..c05c926cc3 100644
+index cc46eabb42..7d7e474313 100644
--- a/meson.build
+++ b/meson.build
@@ -1065,6 +1065,7 @@ keyutils = dependency('libkeyutils', required: false,
# Malloc tests
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
-index 42a1eaf370..55033530fb 100644
+index 64a84cf4ee..7efcd2d641 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -195,6 +195,50 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict)
create mode 100644 pbs-restore.c
diff --git a/meson.build b/meson.build
-index c05c926cc3..69a0fe80ef 100644
+index 7d7e474313..dd1c5bdb4e 100644
--- a/meson.build
+++ b/meson.build
@@ -2749,6 +2749,10 @@ if have_tools
false, NULL, false, NULL, !!devlist,
devlist, qdict_haskey(qdict, "speed"), speed, &error);
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
-index 55033530fb..27d35ab91b 100644
+index 7efcd2d641..b2b5f1298b 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -221,19 +221,42 @@ void hmp_info_backup(Monitor *mon, const QDict *qdict)
PVE: add zero block handling to PBS dump callback
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/monitor/block-hmp-cmds.c | 4 ++-
- pve-backup.c | 57 +++++++++++++++++++++++++++-------
+ pve-backup.c | 59 ++++++++++++++++++++++++++--------
qapi/block-core.json | 6 ++++
- 3 files changed, 54 insertions(+), 13 deletions(-)
+ 3 files changed, 55 insertions(+), 14 deletions(-)
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
index 3fca3ce3e9..69254396d5 100644
qemu_co_mutex_lock(&backup_state.dump_callback_mutex);
// avoid deadlock if job is cancelled
-@@ -147,17 +152,29 @@ pvebackup_co_dump_pbs_cb(
+@@ -147,16 +152,28 @@ pvebackup_co_dump_pbs_cb(
return -1;
}
- pbs_res = proxmox_backup_co_write_data(backup_state.pbs, di->dev_id, buf, start, size, &local_err);
-- qemu_co_mutex_unlock(&backup_state.dump_callback_mutex);
+ uint64_t transferred = 0;
+ uint64_t reused = 0;
+ while (transferred < size) {
+ uint64_t left = size - transferred;
+ uint64_t to_transfer = left < di->block_size ? left : di->block_size;
-
-- if (pbs_res < 0) {
-- pvebackup_propagate_error(local_err);
-- return pbs_res;
-- } else {
-- size_t reused = (pbs_res == 0) ? size : 0;
-- pvebackup_add_transfered_bytes(size, !buf ? size : 0, reused);
++
+ pbs_res = proxmox_backup_co_write_data(backup_state.pbs, di->dev_id,
+ is_zero_block ? NULL : buf + transferred, start + transferred,
+ to_transfer, &local_err);
+ }
+
+ reused += pbs_res == 0 ? to_transfer : 0;
- }
-
-+ qemu_co_mutex_unlock(&backup_state.dump_callback_mutex);
-+ pvebackup_add_transfered_bytes(size, is_zero_block ? size : 0, reused);
++ }
+
+ qemu_co_mutex_unlock(&backup_state.dump_callback_mutex);
+-
+- if (pbs_res < 0) {
+- pvebackup_propagate_error(local_err);
+- return pbs_res;
+- } else {
+- size_t reused = (pbs_res == 0) ? size : 0;
+- pvebackup_add_transfered_bytes(size, !buf ? size : 0, reused);
+- }
++ pvebackup_add_transfered_bytes(size, is_zero_block ? size : 0, reused);
+
return size;
}
-
@@ -178,6 +195,7 @@ pvebackup_co_dump_vma_cb(
int ret = -1;
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
[error cleanups, file_open implementation]
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/meson.build | 3 +
block/pbs.c | 271 +++++++++++++++++++++++++++++++++++++++++++
+
+block_init(bdrv_pbs_init);
diff --git a/configure b/configure
-index 9a79a004d7..7cb181b61a 100755
+index 6e308ed77f..869e97c72f 100755
--- a/configure
+++ b/configure
@@ -428,6 +428,7 @@ vdi=${default_feature:-yes}
crypto-afalg Linux AF_ALG crypto backend driver
capstone capstone disassembler support
debug-mutex mutex debugging support
-@@ -4622,6 +4628,9 @@ fi
+@@ -4624,6 +4630,9 @@ fi
if test "$linux_aio" = "yes" ; then
echo "CONFIG_LINUX_AIO=y" >> $config_host_mak
fi
echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak
fi
diff --git a/meson.build b/meson.build
-index 69a0fe80ef..3faec72a80 100644
+index dd1c5bdb4e..45c1f2de73 100644
--- a/meson.build
+++ b/meson.build
@@ -3111,6 +3111,7 @@ summary_info += {'lzfse support': liblzfse.found()}
the latest PBS backup.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
monitor/hmp-cmds.c | 28 ++++++-----
pve-backup.c | 117 ++++++++++++++++++++++++++++++++-----------
3 files changed, 159 insertions(+), 42 deletions(-)
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
-index 27d35ab91b..4fe2bbb26d 100644
+index b2b5f1298b..7a449edafa 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -198,6 +198,7 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict)
fitting.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
meson.build | 2 ++
os-posix.c | 7 +++++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/meson.build b/meson.build
-index 3faec72a80..bae9ebe0c0 100644
+index 45c1f2de73..44071acbb7 100644
--- a/meson.build
+++ b/meson.build
@@ -1065,6 +1065,7 @@ keyutils = dependency('libkeyutils', required: false,
Subject: [PATCH] PVE: Add sequential job transaction support
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
include/qemu/job.h | 12 ++++++++++++
job.c | 31 +++++++++++++++++++++++++++++++
transaction, so drives will still be backed up one after the other.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
- pve-backup.c | 167 +++++++++++++++------------------------------------
- 1 file changed, 49 insertions(+), 118 deletions(-)
+ pve-backup.c | 169 +++++++++++++++------------------------------------
+ 1 file changed, 50 insertions(+), 119 deletions(-)
diff --git a/pve-backup.c b/pve-backup.c
index 22420db26a..2e628d68e4 100644
proxmox_backup_abort(backup_state.pbs, "backup canceled");
}
-- qemu_mutex_unlock(&backup_state.backup_mutex);
--
++ /* it's enough to cancel one job in the transaction, the rest will follow
++ * automatically */
++ GList *bdi = g_list_first(backup_state.di_list);
++ BlockJob *cancel_job = bdi && bdi->data ?
++ ((PVEBackupDevInfo *)bdi->data)->job :
++ NULL;
++
++ /* ref the job before releasing the mutex, just to be safe */
++ if (cancel_job) {
++ job_ref(&cancel_job->job);
++ }
++
++ /* job_cancel_sync may enter the job, so we need to release the
++ * backup_mutex to avoid deadlock */
+ qemu_mutex_unlock(&backup_state.backup_mutex);
+
- for(;;) {
-
- BlockJob *next_job = NULL;
- while (l) {
- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
- l = g_list_next(l);
-+ /* it's enough to cancel one job in the transaction, the rest will follow
-+ * automatically */
-+ GList *bdi = g_list_first(backup_state.di_list);
-+ BlockJob *cancel_job = bdi && bdi->data ?
-+ ((PVEBackupDevInfo *)bdi->data)->job :
-+ NULL;
-
+-
- BlockJob *job = lookup_active_block_job(di);
- if (job != NULL) {
- next_job = job;
- break;
- }
- }
-+ /* ref the job before releasing the mutex, just to be safe */
-+ if (cancel_job) {
-+ job_ref(&cancel_job->job);
-+ }
-
+-
- qemu_mutex_unlock(&backup_state.backup_mutex);
-+ /* job_cancel_sync may enter the job, so we need to release the
-+ * backup_mutex to avoid deadlock */
-+ qemu_mutex_unlock(&backup_state.backup_mutex);
-
+-
- if (next_job) {
- AioContext *aio_context = next_job->job.aio_context;
- aio_context_acquire(aio_context);
[0] https://lists.gnu.org/archive/html/qemu-devel/2020-09/msg03515.html
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
pve-backup.c | 217 ++++++++++++++++++++++++++++---------------
qapi/block-core.json | 5 +-
- assert(!qemu_in_coroutine());
+ PVEBackupDevInfo *di = opaque;
+ di->completed_ret = ret;
-+
+
+ /*
+ * Schedule stream cleanup in async coroutine. close_image and finish might
+ * take a while, so we can't block on them here. This way it also doesn't
+ aio_context_release(job_ctx);
+ aio_co_enter(data->ctx, data->co);
+}
-
++
+static void coroutine_fn pvebackup_co_cancel(void *opaque)
+{
Error *cancel_err = NULL;
- if (!job || local_err != NULL) {
- Error *create_job_err = NULL;
- error_setg(&create_job_err, "backup_job_create failed: %s",
-- local_err ? error_get_pretty(local_err) : "null");
+ di->job = job;
-
-- pvebackup_propagate_error(create_job_err);
++
+ if (!job || local_err) {
+ error_setg(errp, "backup_job_create failed: %s",
-+ local_err ? error_get_pretty(local_err) : "null");
+ local_err ? error_get_pretty(local_err) : "null");
+-
+- pvebackup_propagate_error(create_job_err);
break;
}
safe migration is possible and makes sense.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
include/migration/misc.h | 3 ++
migration/meson.build | 2 +
transferred.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
migration/block-dirty-bitmap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
4 KiB buffer is big enough, even if many whitespaces are used.
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/iscsi.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
way instead)
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/monitor/block-hmp-cmds.c | 4 +-
hmp-commands.hx | 2 +
Error *error = NULL;
diff --git a/hmp-commands.hx b/hmp-commands.hx
-index b18063ce19..02c8f83ca3 100644
+index 7faba36b39..dca4e58858 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -109,6 +109,7 @@ ERST
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/monitor/block-hmp-cmds.c | 1 +
pve-backup.c | 3 +++
100% so the slow one is still necessary.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/pbs.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
Subject: [PATCH] PVE: block/stream: increase chunk size
Ceph favors bigger chunks, so increase to 4M.
+
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/stream.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Simply check for NULL and do nothing, there's no reason to pad the
target if it will be discarded anyway.
+
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/io.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/block/io.c b/block/io.c
-index a19942718b..9fa8252258 100644
+index f38e7f81d8..28c3a712b6 100644
--- a/block/io.c
+++ b/block/io.c
-@@ -1763,6 +1763,10 @@ static int bdrv_pad_request(BlockDriverState *bs,
+@@ -1764,6 +1764,10 @@ static int bdrv_pad_request(BlockDriverState *bs,
{
int ret;
once the backing image is removed. It will be replaced by 'file'.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
block/alloc-track.c | 345 ++++++++++++++++++++++++++++++++++++++++++++
block/meson.build | 1 +
Subject: [PATCH] PVE: whitelist 'invalid' QAPI names for backwards compat
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
qapi/pragma.json | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
migration_incoming_state_destroy
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
migration/savevm-async.c | 5 +++++
1 file changed, 5 insertions(+)
-extra/0001-qemu-sockets-fix-unix-socket-path-copy-again.patch
-extra/0002-monitor-qmp-fix-race-with-clients-disconnecting-earl.patch
-extra/0003-monitor-hmp-add-support-for-flag-argument-with-value.patch
-extra/0004-monitor-refactor-set-expire_password-and-allow-VNC-d.patch
-extra/0005-monitor-hmp-correctly-invert-password-argument-detec.patch
-extra/0006-qxl-fix-pre-save-logic.patch
-extra/0007-block-mirror-fix-NULL-pointer-dereference-in-mirror_.patch
-extra/0008-block-introduce-max_hw_iov-for-use-in-scsi-generic.patch
+extra/0001-monitor-qmp-fix-race-with-clients-disconnecting-earl.patch
+extra/0002-monitor-hmp-add-support-for-flag-argument-with-value.patch
+extra/0003-monitor-refactor-set-expire_password-and-allow-VNC-d.patch
+extra/0004-block-mirror-fix-NULL-pointer-dereference-in-mirror_.patch
bitmap-mirror/0001-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
bitmap-mirror/0002-drive-mirror-add-support-for-conditional-and-always-.patch
bitmap-mirror/0003-mirror-add-check-for-bitmap-mode-without-bitmap.patch
-Subproject commit f9baca549e44791be0dd98de15add3d8452a8af0
+Subproject commit 54e1f5be86dd11744e45da8be6afad01d01d59e7