parent
969122885b
commit
82b4c45a95
@ -0,0 +1,128 @@
|
||||
From ef212eb3026a2460f6502a76afa3baedeb74d975 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
|
||||
Date: Tue, 6 Aug 2024 14:14:27 +0400
|
||||
Subject: [PATCH 1/5] Fix scanout version with pc-q35-rhel9.4.0
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
RH-MergeRequest: 383: Fix scanout version with pc-q35-rhel9.4.0
|
||||
RH-Jira: RHEL-53565
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/1] ed2860cfb933654b0046c1b59f78d2e50146bd6c
|
||||
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-52940
|
||||
|
||||
Introduce hw_compat_rhel_9_4_extra so that pc-q35-rhel9.4.0 has
|
||||
x-scanout-vmstate-version=1
|
||||
|
||||
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
---
|
||||
hw/arm/virt.c | 1 +
|
||||
hw/core/machine.c | 13 +++++++++++--
|
||||
hw/i386/pc_piix.c | 2 ++
|
||||
hw/i386/pc_q35.c | 3 +++
|
||||
hw/s390x/s390-virtio-ccw.c | 1 +
|
||||
include/hw/boards.h | 3 +++
|
||||
6 files changed, 21 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
|
||||
index e4a66affcb..851eff6b28 100644
|
||||
--- a/hw/arm/virt.c
|
||||
+++ b/hw/arm/virt.c
|
||||
@@ -3670,6 +3670,7 @@ type_init(rhel_machine_init);
|
||||
|
||||
static void rhel940_virt_options(MachineClass *mc)
|
||||
{
|
||||
+ compat_props_add(mc->compat_props, hw_compat_rhel_9_4_extra, hw_compat_rhel_9_4_extra_len);
|
||||
}
|
||||
DEFINE_RHEL_MACHINE_AS_LATEST(9, 4, 0)
|
||||
|
||||
diff --git a/hw/core/machine.c b/hw/core/machine.c
|
||||
index c8c460c916..fe0a28ef3b 100644
|
||||
--- a/hw/core/machine.c
|
||||
+++ b/hw/core/machine.c
|
||||
@@ -64,6 +64,17 @@ const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2);
|
||||
const char *rhel_old_machine_deprecation =
|
||||
"machine types for previous major releases are deprecated";
|
||||
|
||||
+/*
|
||||
+ * rhel_9_4_extra is used for <= 9.4 machines.
|
||||
+ *
|
||||
+ * (for compatibility and historical reasons, rhel_9_4 is used for <= 9.2 too)
|
||||
+ */
|
||||
+GlobalProperty hw_compat_rhel_9_4_extra[] = {
|
||||
+ /* hw_compat_rhel_9_4_extra from hw_compat_8_2 */
|
||||
+ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" },
|
||||
+};
|
||||
+const size_t hw_compat_rhel_9_4_extra_len = G_N_ELEMENTS(hw_compat_rhel_9_4_extra);
|
||||
+
|
||||
GlobalProperty hw_compat_rhel_9_4[] = {
|
||||
/* hw_compat_rhel_9_4 from hw_compat_8_0 */
|
||||
{ TYPE_VIRTIO_NET, "host_uso", "off"},
|
||||
@@ -81,8 +92,6 @@ GlobalProperty hw_compat_rhel_9_4[] = {
|
||||
{ "igb", "x-pcie-flr-init", "off" },
|
||||
/* hw_compat_rhel_9_4 jira RHEL-24045 */
|
||||
{ "virtio-mem", "dynamic-memslots", "off" },
|
||||
- /* hw_compat_rhel_9_4 from hw_compat_8_1 */
|
||||
- { "virtio-gpu-device", "x-scanout-vmstate-version", "1" },
|
||||
};
|
||||
const size_t hw_compat_rhel_9_4_len = G_N_ELEMENTS(hw_compat_rhel_9_4);
|
||||
|
||||
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
|
||||
index 54d1c58bce..c846673d87 100644
|
||||
--- a/hw/i386/pc_piix.c
|
||||
+++ b/hw/i386/pc_piix.c
|
||||
@@ -1031,6 +1031,8 @@ static void pc_machine_rhel760_options(MachineClass *m)
|
||||
"Use a different south bridge than PIIX3");
|
||||
|
||||
|
||||
+ compat_props_add(m->compat_props, hw_compat_rhel_9_4_extra,
|
||||
+ hw_compat_rhel_9_4_extra_len);
|
||||
compat_props_add(m->compat_props, hw_compat_rhel_9_4,
|
||||
hw_compat_rhel_9_4_len);
|
||||
compat_props_add(m->compat_props, hw_compat_rhel_9_3,
|
||||
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
|
||||
index cd5fb7380e..02bc3d515f 100644
|
||||
--- a/hw/i386/pc_q35.c
|
||||
+++ b/hw/i386/pc_q35.c
|
||||
@@ -731,6 +731,9 @@ static void pc_q35_machine_rhel940_options(MachineClass *m)
|
||||
m->desc = "RHEL-9.4.0 PC (Q35 + ICH9, 2009)";
|
||||
pcmc->smbios_stream_product = "RHEL";
|
||||
pcmc->smbios_stream_version = "9.4.0";
|
||||
+
|
||||
+ compat_props_add(m->compat_props, hw_compat_rhel_9_4_extra,
|
||||
+ hw_compat_rhel_9_4_extra_len);
|
||||
}
|
||||
|
||||
DEFINE_PC_MACHINE(q35_rhel940, "pc-q35-rhel9.4.0", pc_q35_init_rhel940,
|
||||
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
|
||||
index 24f4773179..cc6087c37a 100644
|
||||
--- a/hw/s390x/s390-virtio-ccw.c
|
||||
+++ b/hw/s390x/s390-virtio-ccw.c
|
||||
@@ -1277,6 +1277,7 @@ static void ccw_machine_rhel940_instance_options(MachineState *machine)
|
||||
|
||||
static void ccw_machine_rhel940_class_options(MachineClass *mc)
|
||||
{
|
||||
+ compat_props_add(mc->compat_props, hw_compat_rhel_9_4_extra, hw_compat_rhel_9_4_extra_len);
|
||||
}
|
||||
DEFINE_CCW_MACHINE(rhel940, "rhel9.4.0", true);
|
||||
|
||||
diff --git a/include/hw/boards.h b/include/hw/boards.h
|
||||
index 4edfdb0ddb..e6ae0e61cf 100644
|
||||
--- a/include/hw/boards.h
|
||||
+++ b/include/hw/boards.h
|
||||
@@ -505,6 +505,9 @@ extern const size_t hw_compat_2_2_len;
|
||||
extern GlobalProperty hw_compat_2_1[];
|
||||
extern const size_t hw_compat_2_1_len;
|
||||
|
||||
+extern GlobalProperty hw_compat_rhel_9_4_extra[];
|
||||
+extern const size_t hw_compat_rhel_9_4_extra_len;
|
||||
+
|
||||
extern GlobalProperty hw_compat_rhel_9_4[];
|
||||
extern const size_t hw_compat_rhel_9_4_len;
|
||||
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,126 @@
|
||||
From 9d00965910d5a7818ffe55b18d3e9dd4c7210e1b Mon Sep 17 00:00:00 2001
|
||||
From: Prasad Pandit <pjp@fedoraproject.org>
|
||||
Date: Thu, 25 Apr 2024 12:34:12 +0530
|
||||
Subject: [PATCH 4/4] linux-aio: add IO_CMD_FDSYNC command support
|
||||
|
||||
RH-Author: Prasad Pandit <None>
|
||||
RH-MergeRequest: 378: linux-aio: add IO_CMD_FDSYNC command support
|
||||
RH-Jira: RHEL-43261
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/1] 3b80d4a162aad1a87322078a7d7b060a9496035b
|
||||
|
||||
Libaio defines IO_CMD_FDSYNC command to sync all outstanding
|
||||
asynchronous I/O operations, by flushing out file data to the
|
||||
disk storage. Enable linux-aio to submit such aio request.
|
||||
|
||||
When using aio=native without fdsync() support, QEMU creates
|
||||
pthreads, and destroying these pthreads results in TLB flushes.
|
||||
In a real-time guest environment, TLB flushes cause a latency
|
||||
spike. This patch helps to avoid such spikes.
|
||||
|
||||
Jira: https://issues.redhat.com/browse/RHEL-43261
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Signed-off-by: Prasad Pandit <pjp@fedoraproject.org>
|
||||
Message-ID: <20240425070412.37248-1-ppandit@redhat.com>
|
||||
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
(cherry picked from commit 24687abf237e3c15816d689a8e4b08d7c3190dcb)
|
||||
Signed-off-by: Prasad Pandit <pjp@fedoraproject.org>
|
||||
---
|
||||
block/file-posix.c | 9 +++++++++
|
||||
block/linux-aio.c | 21 ++++++++++++++++++++-
|
||||
include/block/raw-aio.h | 1 +
|
||||
3 files changed, 30 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/block/file-posix.c b/block/file-posix.c
|
||||
index 35684f7e21..9831b08fb6 100644
|
||||
--- a/block/file-posix.c
|
||||
+++ b/block/file-posix.c
|
||||
@@ -159,6 +159,7 @@ typedef struct BDRVRawState {
|
||||
bool has_discard:1;
|
||||
bool has_write_zeroes:1;
|
||||
bool use_linux_aio:1;
|
||||
+ bool has_laio_fdsync:1;
|
||||
bool use_linux_io_uring:1;
|
||||
int page_cache_inconsistent; /* errno from fdatasync failure */
|
||||
bool has_fallocate;
|
||||
@@ -718,6 +719,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
+ if (s->use_linux_aio) {
|
||||
+ s->has_laio_fdsync = laio_has_fdsync(s->fd);
|
||||
+ }
|
||||
#else
|
||||
if (s->use_linux_aio) {
|
||||
error_setg(errp, "aio=native was specified, but is not supported "
|
||||
@@ -2599,6 +2603,11 @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs)
|
||||
if (raw_check_linux_io_uring(s)) {
|
||||
return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH);
|
||||
}
|
||||
+#endif
|
||||
+#ifdef CONFIG_LINUX_AIO
|
||||
+ if (s->has_laio_fdsync && raw_check_linux_aio(s)) {
|
||||
+ return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0);
|
||||
+ }
|
||||
#endif
|
||||
return raw_thread_pool_submit(handle_aiocb_flush, &acb);
|
||||
}
|
||||
diff --git a/block/linux-aio.c b/block/linux-aio.c
|
||||
index ec05d946f3..e3b5ec9aba 100644
|
||||
--- a/block/linux-aio.c
|
||||
+++ b/block/linux-aio.c
|
||||
@@ -384,6 +384,9 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||
case QEMU_AIO_READ:
|
||||
io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset);
|
||||
break;
|
||||
+ case QEMU_AIO_FLUSH:
|
||||
+ io_prep_fdsync(iocbs, fd);
|
||||
+ break;
|
||||
/* Currently Linux kernel does not support other operations */
|
||||
default:
|
||||
fprintf(stderr, "%s: invalid AIO request type 0x%x.\n",
|
||||
@@ -412,7 +415,7 @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
|
||||
AioContext *ctx = qemu_get_current_aio_context();
|
||||
struct qemu_laiocb laiocb = {
|
||||
.co = qemu_coroutine_self(),
|
||||
- .nbytes = qiov->size,
|
||||
+ .nbytes = qiov ? qiov->size : 0,
|
||||
.ctx = aio_get_linux_aio(ctx),
|
||||
.ret = -EINPROGRESS,
|
||||
.is_read = (type == QEMU_AIO_READ),
|
||||
@@ -486,3 +489,19 @@ void laio_cleanup(LinuxAioState *s)
|
||||
}
|
||||
g_free(s);
|
||||
}
|
||||
+
|
||||
+bool laio_has_fdsync(int fd)
|
||||
+{
|
||||
+ struct iocb cb;
|
||||
+ struct iocb *cbs[] = {&cb, NULL};
|
||||
+
|
||||
+ io_context_t ctx = 0;
|
||||
+ io_setup(1, &ctx);
|
||||
+
|
||||
+ /* check if host kernel supports IO_CMD_FDSYNC */
|
||||
+ io_prep_fdsync(&cb, fd);
|
||||
+ int ret = io_submit(ctx, 1, cbs);
|
||||
+
|
||||
+ io_destroy(ctx);
|
||||
+ return (ret == -EINVAL) ? false : true;
|
||||
+}
|
||||
diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h
|
||||
index 0f63c2800c..3166903a56 100644
|
||||
--- a/include/block/raw-aio.h
|
||||
+++ b/include/block/raw-aio.h
|
||||
@@ -60,6 +60,7 @@ void laio_cleanup(LinuxAioState *s);
|
||||
int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
|
||||
int type, uint64_t dev_max_batch);
|
||||
|
||||
+bool laio_has_fdsync(int);
|
||||
void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context);
|
||||
void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context);
|
||||
#endif
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,184 @@
|
||||
From 3311ddaffa78ad8d2c40c86cd69461ebeabe1052 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Tue, 6 Aug 2024 13:53:00 -0500
|
||||
Subject: [PATCH 3/5] nbd/server: CVE-2024-7409: Cap default max-connections to
|
||||
100
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z]
|
||||
RH-Jira: RHEL-52616
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [2/4] 17c9c81282e180485e2f2d584a3e0c73a6fbd66a (ebblake/qemu-kvm)
|
||||
|
||||
Allowing an unlimited number of clients to any web service is a recipe
|
||||
for a rudimentary denial of service attack: the client merely needs to
|
||||
open lots of sockets without closing them, until qemu no longer has
|
||||
any more fds available to allocate.
|
||||
|
||||
For qemu-nbd, we default to allowing only 1 connection unless more are
|
||||
explicitly asked for (-e or --shared); this was historically picked as
|
||||
a nice default (without an explicit -t, a non-persistent qemu-nbd goes
|
||||
away after a client disconnects, without needing any additional
|
||||
follow-up commands), and we are not going to change that interface now
|
||||
(besides, someday we want to point people towards qemu-storage-daemon
|
||||
instead of qemu-nbd).
|
||||
|
||||
But for qemu proper, and the newer qemu-storage-daemon, the QMP
|
||||
nbd-server-start command has historically had a default of unlimited
|
||||
number of connections, in part because unlike qemu-nbd it is
|
||||
inherently persistent until nbd-server-stop. Allowing multiple client
|
||||
sockets is particularly useful for clients that can take advantage of
|
||||
MULTI_CONN (creating parallel sockets to increase throughput),
|
||||
although known clients that do so (such as libnbd's nbdcopy) typically
|
||||
use only 8 or 16 connections (the benefits of scaling diminish once
|
||||
more sockets are competing for kernel attention). Picking a number
|
||||
large enough for typical use cases, but not unlimited, makes it
|
||||
slightly harder for a malicious client to perform a denial of service
|
||||
merely by opening lots of connections withot progressing through the
|
||||
handshake.
|
||||
|
||||
This change does not eliminate CVE-2024-7409 on its own, but reduces
|
||||
the chance for fd exhaustion or unlimited memory usage as an attack
|
||||
surface. On the other hand, by itself, it makes it more obvious that
|
||||
with a finite limit, we have the problem of an unauthenticated client
|
||||
holding 100 fds opened as a way to block out a legitimate client from
|
||||
being able to connect; thus, later patches will further add timeouts
|
||||
to reject clients that are not making progress.
|
||||
|
||||
This is an INTENTIONAL change in behavior, and will break any client
|
||||
of nbd-server-start that was not passing an explicit max-connections
|
||||
parameter, yet expects more than 100 simultaneous connections. We are
|
||||
not aware of any such client (as stated above, most clients aware of
|
||||
MULTI_CONN get by just fine on 8 or 16 connections, and probably cope
|
||||
with later connections failing by relying on the earlier connections;
|
||||
libvirt has not yet been passing max-connections, but generally
|
||||
creates NBD servers with the intent for a single client for the sake
|
||||
of live storage migration; meanwhile, the KubeSAN project anticipates
|
||||
a large cluster sharing multiple clients [up to 8 per node, and up to
|
||||
100 nodes in a cluster], but it currently uses qemu-nbd with an
|
||||
explicit --shared=0 rather than qemu-storage-daemon with
|
||||
nbd-server-start).
|
||||
|
||||
We considered using a deprecation period (declare that omitting
|
||||
max-parameters is deprecated, and make it mandatory in 3 releases -
|
||||
then we don't need to pick an arbitrary default); that has zero risk
|
||||
of breaking any apps that accidentally depended on more than 100
|
||||
connections, and where such breakage might not be noticed under unit
|
||||
testing but only under the larger loads of production usage. But it
|
||||
does not close the denial-of-service hole until far into the future,
|
||||
and requires all apps to change to add the parameter even if 100 was
|
||||
good enough. It also has a drawback that any app (like libvirt) that
|
||||
is accidentally relying on an unlimited default should seriously
|
||||
consider their own CVE now, at which point they are going to change to
|
||||
pass explicit max-connections sooner than waiting for 3 qemu releases.
|
||||
Finally, if our changed default breaks an app, that app can always
|
||||
pass in an explicit max-parameters with a larger value.
|
||||
|
||||
It is also intentional that the HMP interface to nbd-server-start is
|
||||
not changed to expose max-connections (any client needing to fine-tune
|
||||
things should be using QMP).
|
||||
|
||||
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240807174943.771624-12-eblake@redhat.com>
|
||||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
[ericb: Expand commit message to summarize Dan's argument for why we
|
||||
break corner-case back-compat behavior without a deprecation period]
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
|
||||
(cherry picked from commit c8a76dbd90c2f48df89b75bef74917f90a59b623)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-52616
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/monitor/block-hmp-cmds.c | 3 ++-
|
||||
blockdev-nbd.c | 8 ++++++++
|
||||
include/block/nbd.h | 7 +++++++
|
||||
qapi/block-export.json | 4 ++--
|
||||
4 files changed, 19 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
|
||||
index bdbb5cb141..2d7b4d80c8 100644
|
||||
--- a/block/monitor/block-hmp-cmds.c
|
||||
+++ b/block/monitor/block-hmp-cmds.c
|
||||
@@ -402,7 +402,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
- nbd_server_start(addr, NULL, NULL, 0, &local_err);
|
||||
+ nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS,
|
||||
+ &local_err);
|
||||
qapi_free_SocketAddress(addr);
|
||||
if (local_err != NULL) {
|
||||
goto exit;
|
||||
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
|
||||
index 267a1de903..24ba5382db 100644
|
||||
--- a/blockdev-nbd.c
|
||||
+++ b/blockdev-nbd.c
|
||||
@@ -170,6 +170,10 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||
|
||||
void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
|
||||
{
|
||||
+ if (!arg->has_max_connections) {
|
||||
+ arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
|
||||
+ }
|
||||
+
|
||||
nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz,
|
||||
arg->max_connections, errp);
|
||||
}
|
||||
@@ -182,6 +186,10 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr,
|
||||
{
|
||||
SocketAddress *addr_flat = socket_address_flatten(addr);
|
||||
|
||||
+ if (!has_max_connections) {
|
||||
+ max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
|
||||
+ }
|
||||
+
|
||||
nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp);
|
||||
qapi_free_SocketAddress(addr_flat);
|
||||
}
|
||||
diff --git a/include/block/nbd.h b/include/block/nbd.h
|
||||
index 1d4d65922d..d4f8b21aec 100644
|
||||
--- a/include/block/nbd.h
|
||||
+++ b/include/block/nbd.h
|
||||
@@ -39,6 +39,13 @@ extern const BlockExportDriver blk_exp_nbd;
|
||||
*/
|
||||
#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10
|
||||
|
||||
+/*
|
||||
+ * NBD_DEFAULT_MAX_CONNECTIONS: Number of client sockets to allow at
|
||||
+ * once; must be large enough to allow a MULTI_CONN-aware client like
|
||||
+ * nbdcopy to create its typical number of 8-16 sockets.
|
||||
+ */
|
||||
+#define NBD_DEFAULT_MAX_CONNECTIONS 100
|
||||
+
|
||||
/* Handshake phase structs - this struct is passed on the wire */
|
||||
|
||||
typedef struct NBDOption {
|
||||
diff --git a/qapi/block-export.json b/qapi/block-export.json
|
||||
index 7874a49ba7..1d255d77e3 100644
|
||||
--- a/qapi/block-export.json
|
||||
+++ b/qapi/block-export.json
|
||||
@@ -28,7 +28,7 @@
|
||||
# @max-connections: The maximum number of connections to allow at the
|
||||
# same time, 0 for unlimited. Setting this to 1 also stops the
|
||||
# server from advertising multiple client support (since 5.2;
|
||||
-# default: 0)
|
||||
+# default: 100)
|
||||
#
|
||||
# Since: 4.2
|
||||
##
|
||||
@@ -63,7 +63,7 @@
|
||||
# @max-connections: The maximum number of connections to allow at the
|
||||
# same time, 0 for unlimited. Setting this to 1 also stops the
|
||||
# server from advertising multiple client support (since 5.2;
|
||||
-# default: 0).
|
||||
+# default: 100).
|
||||
#
|
||||
# Returns: error if the server is already running.
|
||||
#
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,173 @@
|
||||
From 1c2fe81349a99cf0c28549426c7ad0c06d942ae3 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Wed, 7 Aug 2024 12:23:13 -0500
|
||||
Subject: [PATCH 5/5] nbd/server: CVE-2024-7409: Close stray clients at
|
||||
server-stop
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z]
|
||||
RH-Jira: RHEL-52616
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [4/4] 54d56c0d92d92b2b6a0f81a01853881286beae0e (ebblake/qemu-kvm)
|
||||
|
||||
A malicious client can attempt to connect to an NBD server, and then
|
||||
intentionally delay progress in the handshake, including if it does
|
||||
not know the TLS secrets. Although the previous two patches reduce
|
||||
this behavior by capping the default max-connections parameter and
|
||||
killing slow clients, they did not eliminate the possibility of a
|
||||
client waiting to close the socket until after the QMP nbd-server-stop
|
||||
command is executed, at which point qemu would SEGV when trying to
|
||||
dereference the NULL nbd_server global which is no longer present.
|
||||
This amounts to a denial of service attack. Worse, if another NBD
|
||||
server is started before the malicious client disconnects, I cannot
|
||||
rule out additional adverse effects when the old client interferes
|
||||
with the connection count of the new server (although the most likely
|
||||
is a crash due to an assertion failure when checking
|
||||
nbd_server->connections > 0).
|
||||
|
||||
For environments without this patch, the CVE can be mitigated by
|
||||
ensuring (such as via a firewall) that only trusted clients can
|
||||
connect to an NBD server. Note that using frameworks like libvirt
|
||||
that ensure that TLS is used and that nbd-server-stop is not executed
|
||||
while any trusted clients are still connected will only help if there
|
||||
is also no possibility for an untrusted client to open a connection
|
||||
but then stall on the NBD handshake.
|
||||
|
||||
Given the previous patches, it would be possible to guarantee that no
|
||||
clients remain connected by having nbd-server-stop sleep for longer
|
||||
than the default handshake deadline before finally freeing the global
|
||||
nbd_server object, but that could make QMP non-responsive for a long
|
||||
time. So intead, this patch fixes the problem by tracking all client
|
||||
sockets opened while the server is running, and forcefully closing any
|
||||
such sockets remaining without a completed handshake at the time of
|
||||
nbd-server-stop, then waiting until the coroutines servicing those
|
||||
sockets notice the state change. nbd-server-stop now has a second
|
||||
AIO_WAIT_WHILE_UNLOCKED (the first is indirectly through the
|
||||
blk_exp_close_all_type() that disconnects all clients that completed
|
||||
handshakes), but forced socket shutdown is enough to progress the
|
||||
coroutines and quickly tear down all clients before the server is
|
||||
freed, thus finally fixing the CVE.
|
||||
|
||||
This patch relies heavily on the fact that nbd/server.c guarantees
|
||||
that it only calls nbd_blockdev_client_closed() from the main loop
|
||||
(see the assertion in nbd_client_put() and the hoops used in
|
||||
nbd_client_put_nonzero() to achieve that); if we did not have that
|
||||
guarantee, we would also need a mutex protecting our accesses of the
|
||||
list of connections to survive re-entrancy from independent iothreads.
|
||||
|
||||
Although I did not actually try to test old builds, it looks like this
|
||||
problem has existed since at least commit 862172f45c (v2.12.0, 2017) -
|
||||
even back when that patch started using a QIONetListener to handle
|
||||
listening on multiple sockets, nbd_server_free() was already unaware
|
||||
that the nbd_blockdev_client_closed callback can be reached later by a
|
||||
client thread that has not completed handshakes (and therefore the
|
||||
client's socket never got added to the list closed in
|
||||
nbd_export_close_all), despite that patch intentionally tearing down
|
||||
the QIONetListener to prevent new clients.
|
||||
|
||||
Reported-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com>
|
||||
Fixes: CVE-2024-7409
|
||||
CC: qemu-stable@nongnu.org
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240807174943.771624-14-eblake@redhat.com>
|
||||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
|
||||
(cherry picked from commit 3e7ef738c8462c45043a1d39f702a0990406a3b3)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-52616
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
blockdev-nbd.c | 35 ++++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 34 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
|
||||
index 24ba5382db..f73409ae49 100644
|
||||
--- a/blockdev-nbd.c
|
||||
+++ b/blockdev-nbd.c
|
||||
@@ -21,12 +21,18 @@
|
||||
#include "io/channel-socket.h"
|
||||
#include "io/net-listener.h"
|
||||
|
||||
+typedef struct NBDConn {
|
||||
+ QIOChannelSocket *cioc;
|
||||
+ QLIST_ENTRY(NBDConn) next;
|
||||
+} NBDConn;
|
||||
+
|
||||
typedef struct NBDServerData {
|
||||
QIONetListener *listener;
|
||||
QCryptoTLSCreds *tlscreds;
|
||||
char *tlsauthz;
|
||||
uint32_t max_connections;
|
||||
uint32_t connections;
|
||||
+ QLIST_HEAD(, NBDConn) conns;
|
||||
} NBDServerData;
|
||||
|
||||
static NBDServerData *nbd_server;
|
||||
@@ -51,6 +57,14 @@ int nbd_server_max_connections(void)
|
||||
|
||||
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||
{
|
||||
+ NBDConn *conn = nbd_client_owner(client);
|
||||
+
|
||||
+ assert(qemu_in_main_thread() && nbd_server);
|
||||
+
|
||||
+ object_unref(OBJECT(conn->cioc));
|
||||
+ QLIST_REMOVE(conn, next);
|
||||
+ g_free(conn);
|
||||
+
|
||||
nbd_client_put(client);
|
||||
assert(nbd_server->connections > 0);
|
||||
nbd_server->connections--;
|
||||
@@ -60,14 +74,20 @@ static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||
static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
gpointer opaque)
|
||||
{
|
||||
+ NBDConn *conn = g_new0(NBDConn, 1);
|
||||
+
|
||||
+ assert(qemu_in_main_thread() && nbd_server);
|
||||
nbd_server->connections++;
|
||||
+ object_ref(OBJECT(cioc));
|
||||
+ conn->cioc = cioc;
|
||||
+ QLIST_INSERT_HEAD(&nbd_server->conns, conn, next);
|
||||
nbd_update_server_watch(nbd_server);
|
||||
|
||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||
/* TODO - expose handshake timeout as QMP option */
|
||||
nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
|
||||
nbd_server->tlscreds, nbd_server->tlsauthz,
|
||||
- nbd_blockdev_client_closed, NULL);
|
||||
+ nbd_blockdev_client_closed, conn);
|
||||
}
|
||||
|
||||
static void nbd_update_server_watch(NBDServerData *s)
|
||||
@@ -81,12 +101,25 @@ static void nbd_update_server_watch(NBDServerData *s)
|
||||
|
||||
static void nbd_server_free(NBDServerData *server)
|
||||
{
|
||||
+ NBDConn *conn, *tmp;
|
||||
+
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Forcefully close the listener socket, and any clients that have
|
||||
+ * not yet disconnected on their own.
|
||||
+ */
|
||||
qio_net_listener_disconnect(server->listener);
|
||||
object_unref(OBJECT(server->listener));
|
||||
+ QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) {
|
||||
+ qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH,
|
||||
+ NULL);
|
||||
+ }
|
||||
+
|
||||
+ AIO_WAIT_WHILE_UNLOCKED(NULL, server->connections > 0);
|
||||
+
|
||||
if (server->tlscreds) {
|
||||
object_unref(OBJECT(server->tlscreds));
|
||||
}
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,134 @@
|
||||
From 71c525e45f3f2a421ad651bc50eff94be925b36c Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Thu, 8 Aug 2024 16:05:08 -0500
|
||||
Subject: [PATCH 4/5] nbd/server: CVE-2024-7409: Drop non-negotiating clients
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z]
|
||||
RH-Jira: RHEL-52616
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [3/4] 4394e60a8733cd57ee6c8cb140171c7f61cc13a3 (ebblake/qemu-kvm)
|
||||
|
||||
A client that opens a socket but does not negotiate is merely hogging
|
||||
qemu's resources (an open fd and a small amount of memory); and a
|
||||
malicious client that can access the port where NBD is listening can
|
||||
attempt a denial of service attack by intentionally opening and
|
||||
abandoning lots of unfinished connections. The previous patch put a
|
||||
default bound on the number of such ongoing connections, but once that
|
||||
limit is hit, no more clients can connect (including legitimate ones).
|
||||
The solution is to insist that clients complete handshake within a
|
||||
reasonable time limit, defaulting to 10 seconds. A client that has
|
||||
not successfully completed NBD_OPT_GO by then (including the case of
|
||||
where the client didn't know TLS credentials to even reach the point
|
||||
of NBD_OPT_GO) is wasting our time and does not deserve to stay
|
||||
connected. Later patches will allow fine-tuning the limit away from
|
||||
the default value (including disabling it for doing integration
|
||||
testing of the handshake process itself).
|
||||
|
||||
Note that this patch in isolation actually makes it more likely to see
|
||||
qemu SEGV after nbd-server-stop, as any client socket still connected
|
||||
when the server shuts down will now be closed after 10 seconds rather
|
||||
than at the client's whims. That will be addressed in the next patch.
|
||||
|
||||
For a demo of this patch in action:
|
||||
$ qemu-nbd -f raw -r -t -e 10 file &
|
||||
$ nbdsh --opt-mode -c '
|
||||
H = list()
|
||||
for i in range(20):
|
||||
print(i)
|
||||
H.insert(i, nbd.NBD())
|
||||
H[i].set_opt_mode(True)
|
||||
H[i].connect_uri("nbd://localhost")
|
||||
'
|
||||
$ kill $!
|
||||
|
||||
where later connections get to start progressing once earlier ones are
|
||||
forcefully dropped for taking too long, rather than hanging.
|
||||
|
||||
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240807174943.771624-13-eblake@redhat.com>
|
||||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
[eblake: rebase to changes earlier in series, reduce scope of timer]
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
|
||||
(cherry picked from commit b9b72cb3ce15b693148bd09cef7e50110566d8a0)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-52616
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
nbd/server.c | 28 +++++++++++++++++++++++++++-
|
||||
nbd/trace-events | 1 +
|
||||
2 files changed, 28 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/nbd/server.c b/nbd/server.c
|
||||
index e50012499f..39285cc971 100644
|
||||
--- a/nbd/server.c
|
||||
+++ b/nbd/server.c
|
||||
@@ -3186,22 +3186,48 @@ static void nbd_client_receive_next_request(NBDClient *client)
|
||||
}
|
||||
}
|
||||
|
||||
+static void nbd_handshake_timer_cb(void *opaque)
|
||||
+{
|
||||
+ QIOChannel *ioc = opaque;
|
||||
+
|
||||
+ trace_nbd_handshake_timer_cb();
|
||||
+ qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
+}
|
||||
+
|
||||
static coroutine_fn void nbd_co_client_start(void *opaque)
|
||||
{
|
||||
NBDClient *client = opaque;
|
||||
Error *local_err = NULL;
|
||||
+ QEMUTimer *handshake_timer = NULL;
|
||||
|
||||
qemu_co_mutex_init(&client->send_lock);
|
||||
|
||||
- /* TODO - utilize client->handshake_max_secs */
|
||||
+ /*
|
||||
+ * Create a timer to bound the time spent in negotiation. If the
|
||||
+ * timer expires, it is likely nbd_negotiate will fail because the
|
||||
+ * socket was shutdown.
|
||||
+ */
|
||||
+ if (client->handshake_max_secs > 0) {
|
||||
+ handshake_timer = aio_timer_new(qemu_get_aio_context(),
|
||||
+ QEMU_CLOCK_REALTIME,
|
||||
+ SCALE_NS,
|
||||
+ nbd_handshake_timer_cb,
|
||||
+ client->sioc);
|
||||
+ timer_mod(handshake_timer,
|
||||
+ qemu_clock_get_ns(QEMU_CLOCK_REALTIME) +
|
||||
+ client->handshake_max_secs * NANOSECONDS_PER_SECOND);
|
||||
+ }
|
||||
+
|
||||
if (nbd_negotiate(client, &local_err)) {
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
+ timer_free(handshake_timer);
|
||||
client_close(client, false);
|
||||
return;
|
||||
}
|
||||
|
||||
+ timer_free(handshake_timer);
|
||||
WITH_QEMU_LOCK_GUARD(&client->lock) {
|
||||
nbd_client_receive_next_request(client);
|
||||
}
|
||||
diff --git a/nbd/trace-events b/nbd/trace-events
|
||||
index 00ae3216a1..cbd0a4ab7e 100644
|
||||
--- a/nbd/trace-events
|
||||
+++ b/nbd/trace-events
|
||||
@@ -76,6 +76,7 @@ nbd_co_receive_request_payload_received(uint64_t cookie, uint64_t len) "Payload
|
||||
nbd_co_receive_ext_payload_compliance(uint64_t from, uint64_t len) "client sent non-compliant write without payload flag: from=0x%" PRIx64 ", len=0x%" PRIx64
|
||||
nbd_co_receive_align_compliance(const char *op, uint64_t from, uint64_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx64 ", align=0x%" PRIx32
|
||||
nbd_trip(void) "Reading request"
|
||||
+nbd_handshake_timer_cb(void) "client took too long to negotiate"
|
||||
|
||||
# client-connection.c
|
||||
nbd_connect_thread_sleep(uint64_t timeout) "timeout %" PRIu64
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,175 @@
|
||||
From c8c7dc7a445892a820223be2e7617a790e0400f5 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Wed, 7 Aug 2024 08:50:01 -0500
|
||||
Subject: [PATCH 2/5] nbd/server: Plumb in new args to nbd_client_add()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z]
|
||||
RH-Jira: RHEL-52616
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/4] 658d80c80a1163fcad456a34ec20d7828273a36c (ebblake/qemu-kvm)
|
||||
|
||||
Upcoming patches to fix a CVE need to track an opaque pointer passed
|
||||
in by the owner of a client object, as well as request for a time
|
||||
limit on how fast negotiation must complete. Prepare for that by
|
||||
changing the signature of nbd_client_new() and adding an accessor to
|
||||
get at the opaque pointer, although for now the two servers
|
||||
(qemu-nbd.c and blockdev-nbd.c) do not change behavior even though
|
||||
they pass in a new default timeout value.
|
||||
|
||||
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240807174943.771624-11-eblake@redhat.com>
|
||||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
[eblake: s/LIMIT/MAX_SECS/ as suggested by Dan]
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
|
||||
(cherry picked from commit fb1c2aaa981e0a2fa6362c9985f1296b74f055ac)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-52616
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
blockdev-nbd.c | 6 ++++--
|
||||
include/block/nbd.h | 11 ++++++++++-
|
||||
nbd/server.c | 20 +++++++++++++++++---
|
||||
qemu-nbd.c | 4 +++-
|
||||
4 files changed, 34 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
|
||||
index 213012435f..267a1de903 100644
|
||||
--- a/blockdev-nbd.c
|
||||
+++ b/blockdev-nbd.c
|
||||
@@ -64,8 +64,10 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
nbd_update_server_watch(nbd_server);
|
||||
|
||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||
- nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz,
|
||||
- nbd_blockdev_client_closed);
|
||||
+ /* TODO - expose handshake timeout as QMP option */
|
||||
+ nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
|
||||
+ nbd_server->tlscreds, nbd_server->tlsauthz,
|
||||
+ nbd_blockdev_client_closed, NULL);
|
||||
}
|
||||
|
||||
static void nbd_update_server_watch(NBDServerData *s)
|
||||
diff --git a/include/block/nbd.h b/include/block/nbd.h
|
||||
index 4e7bd6342f..1d4d65922d 100644
|
||||
--- a/include/block/nbd.h
|
||||
+++ b/include/block/nbd.h
|
||||
@@ -33,6 +33,12 @@ typedef struct NBDMetaContexts NBDMetaContexts;
|
||||
|
||||
extern const BlockExportDriver blk_exp_nbd;
|
||||
|
||||
+/*
|
||||
+ * NBD_DEFAULT_HANDSHAKE_MAX_SECS: Number of seconds in which client must
|
||||
+ * succeed at NBD_OPT_GO before being forcefully dropped as too slow.
|
||||
+ */
|
||||
+#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10
|
||||
+
|
||||
/* Handshake phase structs - this struct is passed on the wire */
|
||||
|
||||
typedef struct NBDOption {
|
||||
@@ -403,9 +409,12 @@ AioContext *nbd_export_aio_context(NBDExport *exp);
|
||||
NBDExport *nbd_export_find(const char *name);
|
||||
|
||||
void nbd_client_new(QIOChannelSocket *sioc,
|
||||
+ uint32_t handshake_max_secs,
|
||||
QCryptoTLSCreds *tlscreds,
|
||||
const char *tlsauthz,
|
||||
- void (*close_fn)(NBDClient *, bool));
|
||||
+ void (*close_fn)(NBDClient *, bool),
|
||||
+ void *owner);
|
||||
+void *nbd_client_owner(NBDClient *client);
|
||||
void nbd_client_get(NBDClient *client);
|
||||
void nbd_client_put(NBDClient *client);
|
||||
|
||||
diff --git a/nbd/server.c b/nbd/server.c
|
||||
index 892797bb11..e50012499f 100644
|
||||
--- a/nbd/server.c
|
||||
+++ b/nbd/server.c
|
||||
@@ -124,12 +124,14 @@ struct NBDMetaContexts {
|
||||
struct NBDClient {
|
||||
int refcount; /* atomic */
|
||||
void (*close_fn)(NBDClient *client, bool negotiated);
|
||||
+ void *owner;
|
||||
|
||||
QemuMutex lock;
|
||||
|
||||
NBDExport *exp;
|
||||
QCryptoTLSCreds *tlscreds;
|
||||
char *tlsauthz;
|
||||
+ uint32_t handshake_max_secs;
|
||||
QIOChannelSocket *sioc; /* The underlying data channel */
|
||||
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
|
||||
|
||||
@@ -3191,6 +3193,7 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
|
||||
|
||||
qemu_co_mutex_init(&client->send_lock);
|
||||
|
||||
+ /* TODO - utilize client->handshake_max_secs */
|
||||
if (nbd_negotiate(client, &local_err)) {
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
@@ -3205,14 +3208,17 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
|
||||
}
|
||||
|
||||
/*
|
||||
- * Create a new client listener using the given channel @sioc.
|
||||
+ * Create a new client listener using the given channel @sioc and @owner.
|
||||
* Begin servicing it in a coroutine. When the connection closes, call
|
||||
- * @close_fn with an indication of whether the client completed negotiation.
|
||||
+ * @close_fn with an indication of whether the client completed negotiation
|
||||
+ * within @handshake_max_secs seconds (0 for unbounded).
|
||||
*/
|
||||
void nbd_client_new(QIOChannelSocket *sioc,
|
||||
+ uint32_t handshake_max_secs,
|
||||
QCryptoTLSCreds *tlscreds,
|
||||
const char *tlsauthz,
|
||||
- void (*close_fn)(NBDClient *, bool))
|
||||
+ void (*close_fn)(NBDClient *, bool),
|
||||
+ void *owner)
|
||||
{
|
||||
NBDClient *client;
|
||||
Coroutine *co;
|
||||
@@ -3225,13 +3231,21 @@ void nbd_client_new(QIOChannelSocket *sioc,
|
||||
object_ref(OBJECT(client->tlscreds));
|
||||
}
|
||||
client->tlsauthz = g_strdup(tlsauthz);
|
||||
+ client->handshake_max_secs = handshake_max_secs;
|
||||
client->sioc = sioc;
|
||||
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
|
||||
object_ref(OBJECT(client->sioc));
|
||||
client->ioc = QIO_CHANNEL(sioc);
|
||||
object_ref(OBJECT(client->ioc));
|
||||
client->close_fn = close_fn;
|
||||
+ client->owner = owner;
|
||||
|
||||
co = qemu_coroutine_create(nbd_co_client_start, client);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
+
|
||||
+void *
|
||||
+nbd_client_owner(NBDClient *client)
|
||||
+{
|
||||
+ return client->owner;
|
||||
+}
|
||||
diff --git a/qemu-nbd.c b/qemu-nbd.c
|
||||
index bac0b5e3ec..01b4a63c31 100644
|
||||
--- a/qemu-nbd.c
|
||||
+++ b/qemu-nbd.c
|
||||
@@ -389,7 +389,9 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
|
||||
nb_fds++;
|
||||
nbd_update_server_watch();
|
||||
- nbd_client_new(cioc, tlscreds, tlsauthz, nbd_client_closed);
|
||||
+ /* TODO - expose handshake timeout as command line option */
|
||||
+ nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
|
||||
+ tlscreds, tlsauthz, nbd_client_closed, NULL);
|
||||
}
|
||||
|
||||
static void nbd_update_server_watch(void)
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,38 @@
|
||||
From aa0ad36a56cbc0528306e47112081b0db124e512 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
|
||||
Date: Wed, 5 Jun 2024 10:28:20 +0400
|
||||
Subject: [PATCH 3/4] rhel 9.4.0 machine type compat for virtio-gpu migration
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
RH-MergeRequest: 377: virtio-gpu: fix scanout migration post-load
|
||||
RH-Jira: RHEL-36181
|
||||
RH-Acked-by: Peter Xu <peterx@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [3/3] 270e932e04eb2d730550f3a56b53cbb17f5e66f8
|
||||
|
||||
Jira: https://issues.redhat.com/browse/RHEL-36181
|
||||
|
||||
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
---
|
||||
hw/core/machine.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/hw/core/machine.c b/hw/core/machine.c
|
||||
index a0843ab93e..c8c460c916 100644
|
||||
--- a/hw/core/machine.c
|
||||
+++ b/hw/core/machine.c
|
||||
@@ -81,6 +81,8 @@ GlobalProperty hw_compat_rhel_9_4[] = {
|
||||
{ "igb", "x-pcie-flr-init", "off" },
|
||||
/* hw_compat_rhel_9_4 jira RHEL-24045 */
|
||||
{ "virtio-mem", "dynamic-memslots", "off" },
|
||||
+ /* hw_compat_rhel_9_4 from hw_compat_8_1 */
|
||||
+ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" },
|
||||
};
|
||||
const size_t hw_compat_rhel_9_4_len = G_N_ELEMENTS(hw_compat_rhel_9_4);
|
||||
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,196 @@
|
||||
From 523423436e83b01a83c60bc44bd08471992aaff4 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
|
||||
Date: Mon, 15 Jan 2024 16:34:33 +0400
|
||||
Subject: [PATCH 1/4] virtio-gpu: fix scanout migration post-load
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
RH-MergeRequest: 377: virtio-gpu: fix scanout migration post-load
|
||||
RH-Jira: RHEL-36181
|
||||
RH-Acked-by: Peter Xu <peterx@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/3] 05e9dbf7f602d62ec2f336a855daf57e6ee8f3f3
|
||||
|
||||
The current post-loading code for scanout has a FIXME: it doesn't take
|
||||
the resource region/rect into account. But there is more, when adding
|
||||
blob migration support in commit f66767f75c9, I didn't realize that blob
|
||||
resources could be used for scanouts. This situationn leads to a crash
|
||||
during post-load, as they don't have an associated res->image.
|
||||
|
||||
virtio_gpu_do_set_scanout() handle all cases, but requires the
|
||||
associated virtio_gpu_framebuffer, which is currently not saved during
|
||||
migration.
|
||||
|
||||
Add a v2 of "virtio-gpu-one-scanout" with the framebuffer fields, so we
|
||||
can restore blob scanouts, as well as fixing the existing FIXME.
|
||||
|
||||
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
Reviewed-by: Sebastian Ott <sebott@redhat.com>
|
||||
(cherry picked from commit dfcf74fa68c88233209aafc5f82728d0b9a1af5c)
|
||||
---
|
||||
hw/display/virtio-gpu.c | 55 +++++++++++++++++++++++++++-------
|
||||
include/hw/virtio/virtio-gpu.h | 1 +
|
||||
2 files changed, 46 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
|
||||
index 1702190ead..bc485d60ea 100644
|
||||
--- a/hw/display/virtio-gpu.c
|
||||
+++ b/hw/display/virtio-gpu.c
|
||||
@@ -598,6 +598,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data)
|
||||
static void virtio_gpu_update_scanout(VirtIOGPU *g,
|
||||
uint32_t scanout_id,
|
||||
struct virtio_gpu_simple_resource *res,
|
||||
+ struct virtio_gpu_framebuffer *fb,
|
||||
struct virtio_gpu_rect *r)
|
||||
{
|
||||
struct virtio_gpu_simple_resource *ores;
|
||||
@@ -615,9 +616,10 @@ static void virtio_gpu_update_scanout(VirtIOGPU *g,
|
||||
scanout->y = r->y;
|
||||
scanout->width = r->width;
|
||||
scanout->height = r->height;
|
||||
+ scanout->fb = *fb;
|
||||
}
|
||||
|
||||
-static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
|
||||
+static bool virtio_gpu_do_set_scanout(VirtIOGPU *g,
|
||||
uint32_t scanout_id,
|
||||
struct virtio_gpu_framebuffer *fb,
|
||||
struct virtio_gpu_simple_resource *res,
|
||||
@@ -643,7 +645,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
|
||||
r->x, r->y, r->width, r->height,
|
||||
fb->width, fb->height);
|
||||
*error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
|
||||
- return;
|
||||
+ return false;
|
||||
}
|
||||
|
||||
g->parent_obj.enable = 1;
|
||||
@@ -651,11 +653,12 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
|
||||
if (res->blob) {
|
||||
if (console_has_gl(scanout->con)) {
|
||||
if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) {
|
||||
- virtio_gpu_update_scanout(g, scanout_id, res, r);
|
||||
+ virtio_gpu_update_scanout(g, scanout_id, res, fb, r);
|
||||
} else {
|
||||
*error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
|
||||
+ return false;
|
||||
}
|
||||
- return;
|
||||
+ return true;
|
||||
}
|
||||
|
||||
data = res->blob;
|
||||
@@ -684,7 +687,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
|
||||
scanout->ds = qemu_create_displaysurface_pixman(rect);
|
||||
if (!scanout->ds) {
|
||||
*error = VIRTIO_GPU_RESP_ERR_UNSPEC;
|
||||
- return;
|
||||
+ return false;
|
||||
}
|
||||
#ifdef WIN32
|
||||
qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset);
|
||||
@@ -695,7 +698,8 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
|
||||
scanout->ds);
|
||||
}
|
||||
|
||||
- virtio_gpu_update_scanout(g, scanout_id, res, r);
|
||||
+ virtio_gpu_update_scanout(g, scanout_id, res, fb, r);
|
||||
+ return true;
|
||||
}
|
||||
|
||||
static void virtio_gpu_set_scanout(VirtIOGPU *g,
|
||||
@@ -1166,8 +1170,9 @@ static void virtio_gpu_cursor_bh(void *opaque)
|
||||
|
||||
static const VMStateDescription vmstate_virtio_gpu_scanout = {
|
||||
.name = "virtio-gpu-one-scanout",
|
||||
- .version_id = 1,
|
||||
- .fields = (VMStateField[]) {
|
||||
+ .version_id = 2,
|
||||
+ .minimum_version_id = 1,
|
||||
+ .fields = (const VMStateField[]) {
|
||||
VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout),
|
||||
VMSTATE_UINT32(width, struct virtio_gpu_scanout),
|
||||
VMSTATE_UINT32(height, struct virtio_gpu_scanout),
|
||||
@@ -1178,6 +1183,12 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = {
|
||||
VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout),
|
||||
VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout),
|
||||
VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout),
|
||||
+ VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2),
|
||||
+ VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2),
|
||||
+ VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2),
|
||||
+ VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2),
|
||||
+ VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2),
|
||||
+ VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
@@ -1349,6 +1360,7 @@ static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size,
|
||||
if (!res->blob_size) {
|
||||
continue;
|
||||
}
|
||||
+ assert(!res->image);
|
||||
qemu_put_be32(f, res->resource_id);
|
||||
qemu_put_be32(f, res->blob_size);
|
||||
qemu_put_be32(f, res->iov_cnt);
|
||||
@@ -1411,11 +1423,11 @@ static int virtio_gpu_post_load(void *opaque, int version_id)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
|
||||
- /* FIXME: should take scanout.r.{x,y} into account */
|
||||
scanout = &g->parent_obj.scanout[i];
|
||||
if (!scanout->resource_id) {
|
||||
continue;
|
||||
}
|
||||
+
|
||||
res = virtio_gpu_find_resource(g, scanout->resource_id);
|
||||
if (!res) {
|
||||
return -EINVAL;
|
||||
@@ -1428,7 +1440,30 @@ static int virtio_gpu_post_load(void *opaque, int version_id)
|
||||
qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0);
|
||||
#endif
|
||||
|
||||
- dpy_gfx_replace_surface(scanout->con, scanout->ds);
|
||||
+ if (scanout->fb.format != 0) {
|
||||
+ uint32_t error = 0;
|
||||
+ struct virtio_gpu_rect r = {
|
||||
+ .x = scanout->x,
|
||||
+ .y = scanout->y,
|
||||
+ .width = scanout->width,
|
||||
+ .height = scanout->height
|
||||
+ };
|
||||
+
|
||||
+ if (!virtio_gpu_do_set_scanout(g, i, &scanout->fb, res, &r, &error)) {
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ } else {
|
||||
+ /* legacy v1 migration support */
|
||||
+ if (!res->image) {
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ scanout->ds = qemu_create_displaysurface_pixman(res->image);
|
||||
+#ifdef WIN32
|
||||
+ qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0);
|
||||
+#endif
|
||||
+ dpy_gfx_replace_surface(scanout->con, scanout->ds);
|
||||
+ }
|
||||
+
|
||||
dpy_gfx_update_full(scanout->con);
|
||||
if (scanout->cursor.resource_id) {
|
||||
update_cursor(g, &scanout->cursor);
|
||||
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
|
||||
index 584ba2ed73..9a13afb34e 100644
|
||||
--- a/include/hw/virtio/virtio-gpu.h
|
||||
+++ b/include/hw/virtio/virtio-gpu.h
|
||||
@@ -81,6 +81,7 @@ struct virtio_gpu_scanout {
|
||||
uint32_t resource_id;
|
||||
struct virtio_gpu_update_cursor cursor;
|
||||
QEMUCursor *current_cursor;
|
||||
+ struct virtio_gpu_framebuffer fb;
|
||||
};
|
||||
|
||||
struct virtio_gpu_requested_state {
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,122 @@
|
||||
From 7741a7d1c1d719ff681f73a023f27d5951b98882 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
|
||||
Date: Thu, 16 May 2024 12:40:22 +0400
|
||||
Subject: [PATCH 2/4] virtio-gpu: fix v2 migration
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
RH-MergeRequest: 377: virtio-gpu: fix scanout migration post-load
|
||||
RH-Jira: RHEL-36181
|
||||
RH-Acked-by: Peter Xu <peterx@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [2/3] 482c37c10ac7e5e8a6c36ac2eb806ac02beeefdc
|
||||
|
||||
Commit dfcf74fa ("virtio-gpu: fix scanout migration post-load") broke
|
||||
forward/backward version migration. Versioning of nested VMSD structures
|
||||
is not straightforward, as the wire format doesn't have nested
|
||||
structures versions. Introduce x-scanout-vmstate-version and a field
|
||||
test to save/load appropriately according to the machine version.
|
||||
|
||||
Fixes: dfcf74fa ("virtio-gpu: fix scanout migration post-load")
|
||||
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
Signed-off-by: Peter Xu <peterx@redhat.com>
|
||||
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
|
||||
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
|
||||
[fixed long lines]
|
||||
Signed-off-by: Fabiano Rosas <farosas@suse.de>
|
||||
|
||||
[ Move the hw-compat from 8.2 to 8.1 ]
|
||||
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
(cherry picked from commit 40a23ef643664b5c1021a9789f9d680b6294fb50)
|
||||
---
|
||||
hw/core/machine.c | 1 +
|
||||
hw/display/virtio-gpu.c | 30 ++++++++++++++++++++++--------
|
||||
include/hw/virtio/virtio-gpu.h | 1 +
|
||||
3 files changed, 24 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/hw/core/machine.c b/hw/core/machine.c
|
||||
index 309f6ba685..a0843ab93e 100644
|
||||
--- a/hw/core/machine.c
|
||||
+++ b/hw/core/machine.c
|
||||
@@ -37,6 +37,7 @@ GlobalProperty hw_compat_8_1[] = {
|
||||
{ "ramfb", "x-migrate", "off" },
|
||||
{ "vfio-pci-nohotplug", "x-ramfb-migrate", "off" },
|
||||
{ "igb", "x-pcie-flr-init", "off" },
|
||||
+ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" },
|
||||
};
|
||||
const size_t hw_compat_8_1_len = G_N_ELEMENTS(hw_compat_8_1);
|
||||
|
||||
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
|
||||
index bc485d60ea..9d35a016ae 100644
|
||||
--- a/hw/display/virtio-gpu.c
|
||||
+++ b/hw/display/virtio-gpu.c
|
||||
@@ -1168,10 +1168,17 @@ static void virtio_gpu_cursor_bh(void *opaque)
|
||||
virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq);
|
||||
}
|
||||
|
||||
+static bool scanout_vmstate_after_v2(void *opaque, int version)
|
||||
+{
|
||||
+ struct VirtIOGPUBase *base = container_of(opaque, VirtIOGPUBase, scanout);
|
||||
+ struct VirtIOGPU *gpu = container_of(base, VirtIOGPU, parent_obj);
|
||||
+
|
||||
+ return gpu->scanout_vmstate_version >= 2;
|
||||
+}
|
||||
+
|
||||
static const VMStateDescription vmstate_virtio_gpu_scanout = {
|
||||
.name = "virtio-gpu-one-scanout",
|
||||
- .version_id = 2,
|
||||
- .minimum_version_id = 1,
|
||||
+ .version_id = 1,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout),
|
||||
VMSTATE_UINT32(width, struct virtio_gpu_scanout),
|
||||
@@ -1183,12 +1190,18 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = {
|
||||
VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout),
|
||||
VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout),
|
||||
VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout),
|
||||
- VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2),
|
||||
- VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2),
|
||||
- VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2),
|
||||
- VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2),
|
||||
- VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2),
|
||||
- VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2),
|
||||
+ VMSTATE_UINT32_TEST(fb.format, struct virtio_gpu_scanout,
|
||||
+ scanout_vmstate_after_v2),
|
||||
+ VMSTATE_UINT32_TEST(fb.bytes_pp, struct virtio_gpu_scanout,
|
||||
+ scanout_vmstate_after_v2),
|
||||
+ VMSTATE_UINT32_TEST(fb.width, struct virtio_gpu_scanout,
|
||||
+ scanout_vmstate_after_v2),
|
||||
+ VMSTATE_UINT32_TEST(fb.height, struct virtio_gpu_scanout,
|
||||
+ scanout_vmstate_after_v2),
|
||||
+ VMSTATE_UINT32_TEST(fb.stride, struct virtio_gpu_scanout,
|
||||
+ scanout_vmstate_after_v2),
|
||||
+ VMSTATE_UINT32_TEST(fb.offset, struct virtio_gpu_scanout,
|
||||
+ scanout_vmstate_after_v2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
@@ -1668,6 +1681,7 @@ static Property virtio_gpu_properties[] = {
|
||||
DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags,
|
||||
VIRTIO_GPU_FLAG_BLOB_ENABLED, false),
|
||||
DEFINE_PROP_SIZE("hostmem", VirtIOGPU, parent_obj.conf.hostmem, 0),
|
||||
+ DEFINE_PROP_UINT8("x-scanout-vmstate-version", VirtIOGPU, scanout_vmstate_version, 2),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
|
||||
index 9a13afb34e..18500432c6 100644
|
||||
--- a/include/hw/virtio/virtio-gpu.h
|
||||
+++ b/include/hw/virtio/virtio-gpu.h
|
||||
@@ -177,6 +177,7 @@ typedef struct VGPUDMABuf {
|
||||
struct VirtIOGPU {
|
||||
VirtIOGPUBase parent_obj;
|
||||
|
||||
+ uint8_t scanout_vmstate_version;
|
||||
uint64_t conf_max_hostmem;
|
||||
|
||||
VirtQueue *ctrl_vq;
|
||||
--
|
||||
2.39.3
|
||||
|
Loading…
Reference in new issue