For 6.0 misc patches under my radar.

V2:
  - "tests: Add tests for yank with the chardev-change case" updated
  - drop the readthedoc theme patch
 -----BEGIN PGP SIGNATURE-----
 
 iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmBltIwcHG1hcmNhbmRy
 ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5QzCEACOWNUlT5mTylY52sB4
 RXxt+vFFby6aj/M5/tXv7T4EHShWkycV5kEnGjUKiWQXfHfHRfOur6PXkbTMG5zY
 UBEuVAMWW50O2VQZR51W+kohZxsxNnimK2gnCTNGjDWOiofTFAcDf7Ycfxbg1TYU
 fsO3m/dl9cy1fBgCsm64+61T60DC5W0JRsxoRCR1qr4vbJtXjoYe9i21GMWOr548
 EVZo3XQDe5WYeTRyTpf1lHU0dLPrJqZuKmF6M3IQWXG7+ns7iMA0v/STmwsBwqSr
 W6vygj2vPKAi1b1X1z/t/IGXP7mOtTZMUZWxhdOcxqEgYyP4rZji02U33CCd0fCi
 wbD8VOmwvtqPeEHXu/b/dhpacgHis1w8jyJspAcW0MIpFJ+1mn+xtWnmMUlA2cOS
 Vmgirinycsim9TKA+jS3vTwT+/wwzqtWUY267m09tVhJwxvGOXQH1i+mlRRLoNcs
 2vf5iWanRbZgFJme8UYtqYB96pWIJjMa1FkMexJgK3VXgMA+Rjkr4MqIyuPoquyp
 /3PgoUU1LUmGh8F+mi8m88tpdgad6iM+UWXeRALsP7UFvP1Psjz8f6Fhh8uBeE7E
 wsdBsdTwwZ3zgLD4DxjpcZdLM+G7PT0nbeodnPWRuwebsYt3FymoCdmkS1CEn9ZT
 kbQxdeJhTa7QoacZUmQSAoXO6g==
 =UwXe
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/marcandre/tags/for-6.0-pull-request' into staging

For 6.0 misc patches under my radar.

V2:
 - "tests: Add tests for yank with the chardev-change case" updated
 - drop the readthedoc theme patch

# gpg: Signature made Thu 01 Apr 2021 12:54:52 BST
# gpg:                using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5
# gpg:                issuer "marcandre.lureau@redhat.com"
# gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full]
# gpg:                 aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full]
# Primary key fingerprint: 87A9 BD93 3F87 C606 D276  F62D DAE8 E109 7596 9CE5

* remotes/marcandre/tags/for-6.0-pull-request:
  tests: Add tests for yank with the chardev-change case
  chardev: Fix yank with the chardev-change case
  chardev/char.c: Always pass id to chardev_new
  chardev/char.c: Move object_property_try_add_child out of chardev_new
  yank: Always link full yank code
  yank: Remove dependency on qiochannel
  docs: simplify each section title
  dbus-vmstate: Increase the size of input stream buffer used during load
  util: fix use-after-free in module_load_one

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-04-01 17:08:48 +01:00
commit 415fa2fe91
25 changed files with 417 additions and 106 deletions

View file

@ -2822,7 +2822,8 @@ Yank feature
M: Lukas Straub <lukasstraub2@web.de> M: Lukas Straub <lukasstraub2@web.de>
S: Odd fixes S: Odd fixes
F: util/yank.c F: util/yank.c
F: stubs/yank.c F: migration/yank_functions*
F: tests/unit/test-yank.c
F: include/qemu/yank.h F: include/qemu/yank.h
F: qapi/yank.json F: qapi/yank.json

View file

@ -204,6 +204,8 @@ static int dbus_vmstate_post_load(void *opaque, int version_id)
m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL); m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
s = g_data_input_stream_new(m); s = g_data_input_stream_new(m);
g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
g_buffered_input_stream_set_buffer_size(G_BUFFERED_INPUT_STREAM(s),
DBUS_VMSTATE_SIZE_LIMIT);
nelem = g_data_input_stream_read_uint32(s, NULL, &err); nelem = g_data_input_stream_read_uint32(s, NULL, &err);
if (err) { if (err) {
@ -244,11 +246,23 @@ static int dbus_vmstate_post_load(void *opaque, int version_id)
} }
len = g_data_input_stream_read_uint32(s, NULL, &err); len = g_data_input_stream_read_uint32(s, NULL, &err);
if (len > DBUS_VMSTATE_SIZE_LIMIT) {
error_report("%s: Invalid vmstate size: %u", __func__, len);
return -1;
}
g_buffered_input_stream_fill(G_BUFFERED_INPUT_STREAM(s), len, NULL,
&err);
if (err) {
goto error;
}
avail = g_buffered_input_stream_get_available( avail = g_buffered_input_stream_get_available(
G_BUFFERED_INPUT_STREAM(s)); G_BUFFERED_INPUT_STREAM(s));
if (len > avail) {
if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) { error_report("%s: Not enough data available to load for Id: '%s'. "
error_report("%s: Invalid vmstate size: %u", __func__, len); "Available data size: %zu, Actual vmstate size: %u",
__func__, id, avail, len);
return -1; return -1;
} }

View file

@ -402,6 +402,13 @@ static void remove_hup_source(SocketChardev *s)
} }
} }
static void char_socket_yank_iochannel(void *opaque)
{
QIOChannel *ioc = QIO_CHANNEL(opaque);
qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
}
static void tcp_chr_free_connection(Chardev *chr) static void tcp_chr_free_connection(Chardev *chr)
{ {
SocketChardev *s = SOCKET_CHARDEV(chr); SocketChardev *s = SOCKET_CHARDEV(chr);
@ -424,7 +431,7 @@ static void tcp_chr_free_connection(Chardev *chr)
(s->state == TCP_CHARDEV_STATE_CONNECTING (s->state == TCP_CHARDEV_STATE_CONNECTING
|| s->state == TCP_CHARDEV_STATE_CONNECTED)) { || s->state == TCP_CHARDEV_STATE_CONNECTED)) {
yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label),
yank_generic_iochannel, char_socket_yank_iochannel,
QIO_CHANNEL(s->sioc)); QIO_CHANNEL(s->sioc));
} }
object_unref(OBJECT(s->sioc)); object_unref(OBJECT(s->sioc));
@ -946,7 +953,7 @@ static int tcp_chr_add_client(Chardev *chr, int fd)
tcp_chr_set_client_ioc_name(chr, sioc); tcp_chr_set_client_ioc_name(chr, sioc);
if (s->registered_yank) { if (s->registered_yank) {
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
yank_generic_iochannel, char_socket_yank_iochannel,
QIO_CHANNEL(sioc)); QIO_CHANNEL(sioc));
} }
ret = tcp_chr_new_client(chr, sioc); ret = tcp_chr_new_client(chr, sioc);
@ -965,7 +972,7 @@ static void tcp_chr_accept(QIONetListener *listener,
tcp_chr_set_client_ioc_name(chr, cioc); tcp_chr_set_client_ioc_name(chr, cioc);
if (s->registered_yank) { if (s->registered_yank) {
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
yank_generic_iochannel, char_socket_yank_iochannel,
QIO_CHANNEL(cioc)); QIO_CHANNEL(cioc));
} }
tcp_chr_new_client(chr, cioc); tcp_chr_new_client(chr, cioc);
@ -985,7 +992,7 @@ static int tcp_chr_connect_client_sync(Chardev *chr, Error **errp)
} }
if (s->registered_yank) { if (s->registered_yank) {
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
yank_generic_iochannel, char_socket_yank_iochannel,
QIO_CHANNEL(sioc)); QIO_CHANNEL(sioc));
} }
tcp_chr_new_client(chr, sioc); tcp_chr_new_client(chr, sioc);
@ -1005,7 +1012,7 @@ static void tcp_chr_accept_server_sync(Chardev *chr)
tcp_chr_set_client_ioc_name(chr, sioc); tcp_chr_set_client_ioc_name(chr, sioc);
if (s->registered_yank) { if (s->registered_yank) {
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
yank_generic_iochannel, char_socket_yank_iochannel,
QIO_CHANNEL(sioc)); QIO_CHANNEL(sioc));
} }
tcp_chr_new_client(chr, sioc); tcp_chr_new_client(chr, sioc);
@ -1119,7 +1126,13 @@ static void char_socket_finalize(Object *obj)
} }
g_free(s->tls_authz); g_free(s->tls_authz);
if (s->registered_yank) { if (s->registered_yank) {
yank_unregister_instance(CHARDEV_YANK_INSTANCE(chr->label)); /*
* In the chardev-change special-case, we shouldn't unregister the yank
* instance, as it still may be needed.
*/
if (!chr->handover_yank_instance) {
yank_unregister_instance(CHARDEV_YANK_INSTANCE(chr->label));
}
} }
qemu_chr_be_event(chr, CHR_EVENT_CLOSED); qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
@ -1138,7 +1151,7 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED);
if (s->registered_yank) { if (s->registered_yank) {
yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label),
yank_generic_iochannel, char_socket_yank_iochannel,
QIO_CHANNEL(sioc)); QIO_CHANNEL(sioc));
} }
check_report_connect_error(chr, err); check_report_connect_error(chr, err);
@ -1176,7 +1189,7 @@ static void tcp_chr_connect_client_async(Chardev *chr)
tcp_chr_set_client_ioc_name(chr, sioc); tcp_chr_set_client_ioc_name(chr, sioc);
if (s->registered_yank) { if (s->registered_yank) {
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
yank_generic_iochannel, char_socket_yank_iochannel,
QIO_CHANNEL(sioc)); QIO_CHANNEL(sioc));
} }
/* /*
@ -1417,8 +1430,14 @@ static void qmp_chardev_open_socket(Chardev *chr,
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS); qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
} }
if (!yank_register_instance(CHARDEV_YANK_INSTANCE(chr->label), errp)) { /*
return; * In the chardev-change special-case, we shouldn't register a new yank
* instance, as there already may be one.
*/
if (!chr->handover_yank_instance) {
if (!yank_register_instance(CHARDEV_YANK_INSTANCE(chr->label), errp)) {
return;
}
} }
s->registered_yank = true; s->registered_yank = true;
@ -1560,6 +1579,8 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
{ {
ChardevClass *cc = CHARDEV_CLASS(oc); ChardevClass *cc = CHARDEV_CLASS(oc);
cc->supports_yank = true;
cc->parse = qemu_chr_parse_socket; cc->parse = qemu_chr_parse_socket;
cc->open = qmp_chardev_open_socket; cc->open = qmp_chardev_open_socket;
cc->chr_wait_connected = tcp_chr_wait_connected; cc->chr_wait_connected = tcp_chr_wait_connected;

View file

@ -39,6 +39,7 @@
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/id.h" #include "qemu/id.h"
#include "qemu/coroutine.h" #include "qemu/coroutine.h"
#include "qemu/yank.h"
#include "chardev-internal.h" #include "chardev-internal.h"
@ -266,6 +267,7 @@ static void char_init(Object *obj)
{ {
Chardev *chr = CHARDEV(obj); Chardev *chr = CHARDEV(obj);
chr->handover_yank_instance = false;
chr->logfd = -1; chr->logfd = -1;
qemu_mutex_init(&chr->chr_write_lock); qemu_mutex_init(&chr->chr_write_lock);
@ -959,6 +961,7 @@ void qemu_chr_set_feature(Chardev *chr,
static Chardev *chardev_new(const char *id, const char *typename, static Chardev *chardev_new(const char *id, const char *typename,
ChardevBackend *backend, ChardevBackend *backend,
GMainContext *gcontext, GMainContext *gcontext,
bool handover_yank_instance,
Error **errp) Error **errp)
{ {
Object *obj; Object *obj;
@ -967,15 +970,19 @@ static Chardev *chardev_new(const char *id, const char *typename,
bool be_opened = true; bool be_opened = true;
assert(g_str_has_prefix(typename, "chardev-")); assert(g_str_has_prefix(typename, "chardev-"));
assert(id);
obj = object_new(typename); obj = object_new(typename);
chr = CHARDEV(obj); chr = CHARDEV(obj);
chr->handover_yank_instance = handover_yank_instance;
chr->label = g_strdup(id); chr->label = g_strdup(id);
chr->gcontext = gcontext; chr->gcontext = gcontext;
qemu_char_open(chr, backend, &be_opened, &local_err); qemu_char_open(chr, backend, &be_opened, &local_err);
if (local_err) { if (local_err) {
goto end; error_propagate(errp, local_err);
object_unref(obj);
return NULL;
} }
if (!chr->filename) { if (!chr->filename) {
@ -985,22 +992,6 @@ static Chardev *chardev_new(const char *id, const char *typename,
qemu_chr_be_event(chr, CHR_EVENT_OPENED); qemu_chr_be_event(chr, CHR_EVENT_OPENED);
} }
if (id) {
object_property_try_add_child(get_chardevs_root(), id, obj,
&local_err);
if (local_err) {
goto end;
}
object_unref(obj);
}
end:
if (local_err) {
error_propagate(errp, local_err);
object_unref(obj);
return NULL;
}
return chr; return chr;
} }
@ -1009,6 +1000,7 @@ Chardev *qemu_chardev_new(const char *id, const char *typename,
GMainContext *gcontext, GMainContext *gcontext,
Error **errp) Error **errp)
{ {
Chardev *chr;
g_autofree char *genid = NULL; g_autofree char *genid = NULL;
if (!id) { if (!id) {
@ -1016,7 +1008,19 @@ Chardev *qemu_chardev_new(const char *id, const char *typename,
id = genid; id = genid;
} }
return chardev_new(id, typename, backend, gcontext, errp); chr = chardev_new(id, typename, backend, gcontext, false, errp);
if (!chr) {
return NULL;
}
if (!object_property_try_add_child(get_chardevs_root(), id, OBJECT(chr),
errp)) {
object_unref(OBJECT(chr));
return NULL;
}
object_unref(OBJECT(chr));
return chr;
} }
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
@ -1032,11 +1036,18 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
} }
chr = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)), chr = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
backend, NULL, errp); backend, NULL, false, errp);
if (!chr) { if (!chr) {
return NULL; return NULL;
} }
if (!object_property_try_add_child(get_chardevs_root(), id, OBJECT(chr),
errp)) {
object_unref(OBJECT(chr));
return NULL;
}
object_unref(OBJECT(chr));
ret = g_new0(ChardevReturn, 1); ret = g_new0(ChardevReturn, 1);
if (CHARDEV_IS_PTY(chr)) { if (CHARDEV_IS_PTY(chr)) {
ret->pty = g_strdup(chr->filename + 4); ret->pty = g_strdup(chr->filename + 4);
@ -1050,9 +1061,10 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
Error **errp) Error **errp)
{ {
CharBackend *be; CharBackend *be;
const ChardevClass *cc; const ChardevClass *cc, *cc_new;
Chardev *chr, *chr_new; Chardev *chr, *chr_new;
bool closed_sent = false; bool closed_sent = false;
bool handover_yank_instance;
ChardevReturn *ret; ChardevReturn *ret;
chr = qemu_chr_find(id); chr = qemu_chr_find(id);
@ -1084,17 +1096,23 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
return NULL; return NULL;
} }
cc = char_get_class(ChardevBackendKind_str(backend->type), errp); cc = CHARDEV_GET_CLASS(chr);
if (!cc) { cc_new = char_get_class(ChardevBackendKind_str(backend->type), errp);
if (!cc_new) {
return NULL; return NULL;
} }
chr_new = chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)), /*
backend, chr->gcontext, errp); * The new chardev should not register a yank instance if the current
* chardev has registered one already.
*/
handover_yank_instance = cc->supports_yank && cc_new->supports_yank;
chr_new = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc_new)),
backend, chr->gcontext, handover_yank_instance, errp);
if (!chr_new) { if (!chr_new) {
return NULL; return NULL;
} }
chr_new->label = g_strdup(id);
if (chr->be_open && !chr_new->be_open) { if (chr->be_open && !chr_new->be_open) {
qemu_chr_be_event(chr, CHR_EVENT_CLOSED); qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
@ -1115,6 +1133,15 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
return NULL; return NULL;
} }
/* change successfull, clean up */
chr_new->handover_yank_instance = false;
/*
* When the old chardev is freed, it should not unregister the yank
* instance if the new chardev needs it.
*/
chr->handover_yank_instance = handover_yank_instance;
object_unparent(OBJECT(chr)); object_unparent(OBJECT(chr));
object_property_add_child(get_chardevs_root(), chr_new->label, object_property_add_child(get_chardevs_root(), chr_new->label,
OBJECT(chr_new)); OBJECT(chr_new));

View file

@ -1,8 +1,8 @@
.. This is the top level page for the 'devel' manual. .. This is the top level page for the 'devel' manual.
QEMU Developer's Guide Developer Information
====================== =====================
This manual documents various parts of the internals of QEMU. This manual documents various parts of the internals of QEMU.
You only need to read it if you are interested in reading or You only need to read it if you are interested in reading or

View file

@ -1,8 +1,8 @@
.. This is the top level page for the 'interop' manual. .. This is the top level page for the 'interop' manual.
QEMU System Emulation Management and Interoperability Guide System Emulation Management and Interoperability
=========================================================== ================================================
This manual contains documents and specifications that are useful This manual contains documents and specifications that are useful
for making QEMU interoperate with other software. for making QEMU interoperate with other software.

View file

@ -1,8 +1,8 @@
.. This is the top level page for the 'specs' manual .. This is the top level page for the 'specs' manual
QEMU System Emulation Guest Hardware Specifications System Emulation Guest Hardware Specifications
=================================================== ==============================================
Contents: Contents:

View file

@ -1,8 +1,8 @@
.. This is the top level page for the 'system' manual. .. This is the top level page for the 'system' manual.
QEMU System Emulation User's Guide System Emulation
================================== ================
This manual is the overall guide for users using QEMU This manual is the overall guide for users using QEMU
for full system emulation (as opposed to user-mode emulation). for full system emulation (as opposed to user-mode emulation).

View file

@ -1,8 +1,8 @@
.. This is the top level page for the 'tools' manual .. This is the top level page for the 'tools' manual
QEMU Tools Guide Tools
================ =====
Contents: Contents:

View file

@ -1,8 +1,8 @@
.. This is the top level page for the 'user' manual. .. This is the top level page for the 'user' manual.
QEMU User Mode Emulation User's Guide User Mode Emulation
===================================== ===================
This manual is the overall guide for users using QEMU This manual is the overall guide for users using QEMU
for user-mode emulation. In this mode, QEMU can launch for user-mode emulation. In this mode, QEMU can launch

View file

@ -65,6 +65,8 @@ struct Chardev {
char *filename; char *filename;
int logfd; int logfd;
int be_open; int be_open;
/* used to coordinate the chardev-change special-case: */
bool handover_yank_instance;
GSource *gsource; GSource *gsource;
GMainContext *gcontext; GMainContext *gcontext;
DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST); DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
@ -251,6 +253,7 @@ struct ChardevClass {
ObjectClass parent_class; ObjectClass parent_class;
bool internal; /* TODO: eventually use TYPE_USER_CREATABLE */ bool internal; /* TODO: eventually use TYPE_USER_CREATABLE */
bool supports_yank;
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
void (*open)(Chardev *chr, ChardevBackend *backend, void (*open)(Chardev *chr, ChardevBackend *backend,

View file

@ -73,16 +73,6 @@ void yank_unregister_function(const YankInstance *instance,
YankFn *func, YankFn *func,
void *opaque); void *opaque);
/**
* yank_generic_iochannel: Generic yank function for iochannel
*
* This is a generic yank function which will call qio_channel_shutdown on the
* provided QIOChannel.
*
* @opaque: QIOChannel to shutdown
*/
void yank_generic_iochannel(void *opaque);
#define BLOCKDEV_YANK_INSTANCE(the_node_name) (&(YankInstance) { \ #define BLOCKDEV_YANK_INSTANCE(the_node_name) (&(YankInstance) { \
.type = YANK_INSTANCE_TYPE_BLOCK_NODE, \ .type = YANK_INSTANCE_TYPE_BLOCK_NODE, \
.u.block_node.node_name = (the_node_name) }) .u.block_node.node_name = (the_node_name) })

View file

@ -20,6 +20,7 @@
#include "io/channel-tls.h" #include "io/channel-tls.h"
#include "io/channel-socket.h" #include "io/channel-socket.h"
#include "qemu/yank.h" #include "qemu/yank.h"
#include "yank_functions.h"
/** /**
* @migration_channel_process_incoming - Create new incoming migration channel * @migration_channel_process_incoming - Create new incoming migration channel
@ -38,7 +39,8 @@ void migration_channel_process_incoming(QIOChannel *ioc)
ioc, object_get_typename(OBJECT(ioc))); ioc, object_get_typename(OBJECT(ioc)));
if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET)) { if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET)) {
yank_register_function(MIGRATION_YANK_INSTANCE, yank_generic_iochannel, yank_register_function(MIGRATION_YANK_INSTANCE,
migration_yank_iochannel,
QIO_CHANNEL(ioc)); QIO_CHANNEL(ioc));
} }
@ -76,7 +78,7 @@ void migration_channel_connect(MigrationState *s,
if (!error) { if (!error) {
if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET)) { if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET)) {
yank_register_function(MIGRATION_YANK_INSTANCE, yank_register_function(MIGRATION_YANK_INSTANCE,
yank_generic_iochannel, migration_yank_iochannel,
QIO_CHANNEL(ioc)); QIO_CHANNEL(ioc));
} }

View file

@ -6,6 +6,7 @@ migration_files = files(
'vmstate.c', 'vmstate.c',
'qemu-file-channel.c', 'qemu-file-channel.c',
'qemu-file.c', 'qemu-file.c',
'yank_functions.c',
) )
softmmu_ss.add(migration_files) softmmu_ss.add(migration_files)

View file

@ -27,6 +27,7 @@
#include "qemu/yank.h" #include "qemu/yank.h"
#include "io/channel-socket.h" #include "io/channel-socket.h"
#include "yank_functions.h"
/* Multiple fd's */ /* Multiple fd's */
@ -989,7 +990,7 @@ int multifd_load_cleanup(Error **errp)
if (object_dynamic_cast(OBJECT(p->c), TYPE_QIO_CHANNEL_SOCKET) if (object_dynamic_cast(OBJECT(p->c), TYPE_QIO_CHANNEL_SOCKET)
&& OBJECT(p->c)->ref == 1) { && OBJECT(p->c)->ref == 1) {
yank_unregister_function(MIGRATION_YANK_INSTANCE, yank_unregister_function(MIGRATION_YANK_INSTANCE,
yank_generic_iochannel, migration_yank_iochannel,
QIO_CHANNEL(p->c)); QIO_CHANNEL(p->c));
} }

View file

@ -28,6 +28,7 @@
#include "io/channel-socket.h" #include "io/channel-socket.h"
#include "qemu/iov.h" #include "qemu/iov.h"
#include "qemu/yank.h" #include "qemu/yank.h"
#include "yank_functions.h"
static ssize_t channel_writev_buffer(void *opaque, static ssize_t channel_writev_buffer(void *opaque,
@ -108,7 +109,7 @@ static int channel_close(void *opaque, Error **errp)
if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET) if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET)
&& OBJECT(ioc)->ref == 1) { && OBJECT(ioc)->ref == 1) {
yank_unregister_function(MIGRATION_YANK_INSTANCE, yank_unregister_function(MIGRATION_YANK_INSTANCE,
yank_generic_iochannel, migration_yank_iochannel,
QIO_CHANNEL(ioc)); QIO_CHANNEL(ioc));
} }
object_unref(OBJECT(ioc)); object_unref(OBJECT(ioc));

View file

@ -0,0 +1,20 @@
/*
* migration yank functions
*
* Copyright (c) Lukas Straub <lukasstraub2@web.de>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "io/channel.h"
#include "yank_functions.h"
void migration_yank_iochannel(void *opaque)
{
QIOChannel *ioc = QIO_CHANNEL(opaque);
qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
}

View file

@ -0,0 +1,17 @@
/*
* migration yank functions
*
* Copyright (c) Lukas Straub <lukasstraub2@web.de>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
/**
* migration_yank_iochannel: yank function for iochannel
*
* This yank function will call qio_channel_shutdown on the provided QIOChannel.
*
* @opaque: QIOChannel to shutdown
*/
void migration_yank_iochannel(void *opaque);

View file

@ -46,7 +46,6 @@ stub_ss.add(files('vm-stop.c'))
stub_ss.add(files('win32-kbd-hook.c')) stub_ss.add(files('win32-kbd-hook.c'))
stub_ss.add(files('cpu-synchronize-state.c')) stub_ss.add(files('cpu-synchronize-state.c'))
if have_block if have_block
stub_ss.add(files('yank.c'))
stub_ss.add(files('replay-tools.c')) stub_ss.add(files('replay-tools.c'))
endif endif
if have_system if have_system

View file

@ -1,29 +0,0 @@
#include "qemu/osdep.h"
#include "qemu/yank.h"
bool yank_register_instance(const YankInstance *instance, Error **errp)
{
return true;
}
void yank_unregister_instance(const YankInstance *instance)
{
}
void yank_register_function(const YankInstance *instance,
YankFn *func,
void *opaque)
{
}
void yank_unregister_function(const YankInstance *instance,
YankFn *func,
void *opaque)
{
}
void yank_generic_iochannel(void *opaque)
{
}

View file

@ -123,7 +123,8 @@ if have_system
'test-util-sockets': ['socket-helpers.c'], 'test-util-sockets': ['socket-helpers.c'],
'test-base64': [], 'test-base64': [],
'test-bufferiszero': [], 'test-bufferiszero': [],
'test-vmstate': [migration, io] 'test-vmstate': [migration, io],
'test-yank': ['socket-helpers.c', qom, io, chardev]
} }
if 'CONFIG_INOTIFY1' in config_host if 'CONFIG_INOTIFY1' in config_host
tests += {'test-util-filemonitor': []} tests += {'test-util-filemonitor': []}

249
tests/unit/test-yank.c Normal file
View file

@ -0,0 +1,249 @@
/*
* Tests for QEMU yank feature
*
* Copyright (c) Lukas Straub <lukasstraub2@web.de>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include <glib/gstdio.h>
#include "qemu/config-file.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "chardev/char-fe.h"
#include "sysemu/sysemu.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-char.h"
#include "qapi/qapi-types-char.h"
#include "qapi/qapi-commands-yank.h"
#include "qapi/qapi-types-yank.h"
#include "io/channel-socket.h"
#include "socket-helpers.h"
typedef struct {
SocketAddress *addr;
bool old_yank;
bool new_yank;
bool fail;
} CharChangeTestConfig;
static int chardev_change(void *opaque)
{
return 0;
}
static bool is_yank_instance_registered(void)
{
YankInstanceList *list;
bool ret;
list = qmp_query_yank(&error_abort);
ret = !!list;
qapi_free_YankInstanceList(list);
return ret;
}
static gpointer accept_thread(gpointer data)
{
QIOChannelSocket *ioc = data;
QIOChannelSocket *cioc;
cioc = qio_channel_socket_accept(ioc, &error_abort);
object_unref(OBJECT(cioc));
return NULL;
}
static void char_change_test(gconstpointer opaque)
{
CharChangeTestConfig *conf = (gpointer) opaque;
SocketAddress *addr;
Chardev *chr;
CharBackend be;
ChardevReturn *ret;
QIOChannelSocket *ioc;
QemuThread thread;
/*
* Setup a listener socket and determine its address
* so we know the TCP port for the client later
*/
ioc = qio_channel_socket_new();
g_assert_nonnull(ioc);
qio_channel_socket_listen_sync(ioc, conf->addr, 1, &error_abort);
addr = qio_channel_socket_get_local_address(ioc, &error_abort);
g_assert_nonnull(addr);
ChardevBackend backend[2] = {
/* doesn't support yank */
{ .type = CHARDEV_BACKEND_KIND_NULL },
/* supports yank */
{
.type = CHARDEV_BACKEND_KIND_SOCKET,
.u.socket.data = &(ChardevSocket) {
.addr = &(SocketAddressLegacy) {
.type = SOCKET_ADDRESS_LEGACY_KIND_INET,
.u.inet.data = &addr->u.inet
},
.has_server = true,
.server = false
}
} };
ChardevBackend fail_backend[2] = {
/* doesn't support yank */
{
.type = CHARDEV_BACKEND_KIND_UDP,
.u.udp.data = &(ChardevUdp) {
.remote = &(SocketAddressLegacy) {
.type = SOCKET_ADDRESS_LEGACY_KIND_UNIX,
.u.q_unix.data = &(UnixSocketAddress) {
.path = (char *)""
}
}
}
},
/* supports yank */
{
.type = CHARDEV_BACKEND_KIND_SOCKET,
.u.socket.data = &(ChardevSocket) {
.addr = &(SocketAddressLegacy) {
.type = SOCKET_ADDRESS_LEGACY_KIND_INET,
.u.inet.data = &(InetSocketAddress) {
.host = (char *)"127.0.0.1",
.port = (char *)"0"
}
},
.has_server = true,
.server = false
}
} };
g_assert(!is_yank_instance_registered());
if (conf->old_yank) {
qemu_thread_create(&thread, "accept", accept_thread,
ioc, QEMU_THREAD_JOINABLE);
}
ret = qmp_chardev_add("chardev", &backend[conf->old_yank], &error_abort);
qapi_free_ChardevReturn(ret);
chr = qemu_chr_find("chardev");
g_assert_nonnull(chr);
g_assert(is_yank_instance_registered() == conf->old_yank);
qemu_chr_wait_connected(chr, &error_abort);
if (conf->old_yank) {
qemu_thread_join(&thread);
}
qemu_chr_fe_init(&be, chr, &error_abort);
/* allow chardev-change */
qemu_chr_fe_set_handlers(&be, NULL, NULL,
NULL, chardev_change, NULL, NULL, true);
if (conf->fail) {
g_setenv("QTEST_SILENT_ERRORS", "1", 1);
ret = qmp_chardev_change("chardev", &fail_backend[conf->new_yank],
NULL);
g_assert_null(ret);
g_assert(be.chr == chr);
g_assert(is_yank_instance_registered() == conf->old_yank);
g_unsetenv("QTEST_SILENT_ERRORS");
} else {
if (conf->new_yank) {
qemu_thread_create(&thread, "accept", accept_thread,
ioc, QEMU_THREAD_JOINABLE);
}
ret = qmp_chardev_change("chardev", &backend[conf->new_yank],
&error_abort);
if (conf->new_yank) {
qemu_thread_join(&thread);
}
g_assert_nonnull(ret);
g_assert(be.chr != chr);
g_assert(is_yank_instance_registered() == conf->new_yank);
}
object_unparent(OBJECT(be.chr));
object_unref(OBJECT(ioc));
qapi_free_ChardevReturn(ret);
qapi_free_SocketAddress(addr);
}
static SocketAddress tcpaddr = {
.type = SOCKET_ADDRESS_TYPE_INET,
.u.inet.host = (char *)"127.0.0.1",
.u.inet.port = (char *)"0",
};
int main(int argc, char **argv)
{
bool has_ipv4, has_ipv6;
qemu_init_main_loop(&error_abort);
socket_init();
g_test_init(&argc, &argv, NULL);
if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
g_printerr("socket_check_protocol_support() failed\n");
goto end;
}
if (!has_ipv4) {
goto end;
}
module_call_init(MODULE_INIT_QOM);
qemu_add_opts(&qemu_chardev_opts);
g_test_add_data_func("/yank/char_change/success/to_yank",
&(CharChangeTestConfig) { .addr = &tcpaddr,
.old_yank = false,
.new_yank = true,
.fail = false },
char_change_test);
g_test_add_data_func("/yank/char_change/fail/to_yank",
&(CharChangeTestConfig) { .addr = &tcpaddr,
.old_yank = false,
.new_yank = true,
.fail = true },
char_change_test);
g_test_add_data_func("/yank/char_change/success/yank_to_yank",
&(CharChangeTestConfig) { .addr = &tcpaddr,
.old_yank = true,
.new_yank = true,
.fail = false },
char_change_test);
g_test_add_data_func("/yank/char_change/fail/yank_to_yank",
&(CharChangeTestConfig) { .addr = &tcpaddr,
.old_yank = true,
.new_yank = true,
.fail = true },
char_change_test);
g_test_add_data_func("/yank/char_change/success/from_yank",
&(CharChangeTestConfig) { .addr = &tcpaddr,
.old_yank = true,
.new_yank = false,
.fail = false },
char_change_test);
g_test_add_data_func("/yank/char_change/fail/from_yank",
&(CharChangeTestConfig) { .addr = &tcpaddr,
.old_yank = true,
.new_yank = false,
.fail = true },
char_change_test);
end:
return g_test_run();
}

View file

@ -43,6 +43,7 @@ util_ss.add(files('stats64.c'))
util_ss.add(files('systemd.c')) util_ss.add(files('systemd.c'))
util_ss.add(when: 'CONFIG_POSIX', if_true: files('drm.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('drm.c'))
util_ss.add(files('guest-random.c')) util_ss.add(files('guest-random.c'))
util_ss.add(files('yank.c'))
if have_user if have_user
util_ss.add(files('selfmap.c')) util_ss.add(files('selfmap.c'))
@ -51,7 +52,6 @@ endif
if have_system if have_system
util_ss.add(files('crc-ccitt.c')) util_ss.add(files('crc-ccitt.c'))
util_ss.add(when: 'CONFIG_GIO', if_true: [files('dbus.c'), gio]) util_ss.add(when: 'CONFIG_GIO', if_true: [files('dbus.c'), gio])
util_ss.add(files('yank.c'))
util_ss.add(when: 'CONFIG_LINUX', if_true: files('userfaultfd.c')) util_ss.add(when: 'CONFIG_LINUX', if_true: files('userfaultfd.c'))
endif endif

View file

@ -230,10 +230,11 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail)
} }
} }
if (!g_hash_table_add(loaded_modules, module_name)) { if (g_hash_table_contains(loaded_modules, module_name)) {
g_free(module_name); g_free(module_name);
return true; return true;
} }
g_hash_table_add(loaded_modules, module_name);
search_dir = getenv("QEMU_MODULE_DIR"); search_dir = getenv("QEMU_MODULE_DIR");
if (search_dir != NULL) { if (search_dir != NULL) {

View file

@ -15,7 +15,6 @@
#include "qapi/qapi-commands-yank.h" #include "qapi/qapi-commands-yank.h"
#include "qapi/qapi-visit-yank.h" #include "qapi/qapi-visit-yank.h"
#include "qapi/clone-visitor.h" #include "qapi/clone-visitor.h"
#include "io/channel.h"
#include "qemu/yank.h" #include "qemu/yank.h"
struct YankFuncAndParam { struct YankFuncAndParam {
@ -151,13 +150,6 @@ void yank_unregister_function(const YankInstance *instance,
abort(); abort();
} }
void yank_generic_iochannel(void *opaque)
{
QIOChannel *ioc = QIO_CHANNEL(opaque);
qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
}
void qmp_yank(YankInstanceList *instances, void qmp_yank(YankInstanceList *instances,
Error **errp) Error **errp)
{ {