diff options
author | Fabiano Rosas <farosas@suse.de> | 2024-06-17 15:57:22 -0300 |
---|---|---|
committer | Fabiano Rosas <farosas@suse.de> | 2024-06-21 09:44:53 -0300 |
commit | 87d67fadb9db5e87072c244df794c0755150fd2a (patch) | |
tree | eee42450888e7a5cb9e7833c770f9bc16e2776e5 /tests | |
parent | a93ad56053e54a94875faabb042d7c60fdd2fe20 (diff) | |
download | qemu-87d67fadb9db5e87072c244df794c0755150fd2a.zip qemu-87d67fadb9db5e87072c244df794c0755150fd2a.tar.gz qemu-87d67fadb9db5e87072c244df794c0755150fd2a.tar.bz2 |
monitor: Stop removing non-duplicated fds
monitor_fdsets_cleanup() currently has three responsibilities:
1- Remove the fds that have been marked for removal(->removed=true) by
qmp_remove_fd(). This is overly complicated, but ok.
2- Remove any file descriptors that have been passed into QEMU and
never duplicated[1,2]. A file descriptor without duplicates
indicates that no part of QEMU has made use of it. This is
problematic because the current implementation does it only if the
guest is not running and the monitor is closed.
3- Remove/free fdsets that have become empty due to the above
removals. This is ok.
The scenario described in (2) is starting to show some cracks now that
we're trying to consume fds from the migration code:
- Doing cleanup every time the last monitor connection closes works to
reap unused fds, but also has the side effect of forcing the
management layer to pass the file descriptors again in case of a
disconnect/re-connect, if that happened to be the only monitor
connection.
Another side effect is that removing an fd with qmp_remove_fd() is
effectively delayed until the last monitor connection closes.
The usage of mon_refcount is also problematic because it's racy.
- Checking runstate_is_running() skips the cleanup unless the VM is
running and avoids premature cleanup of the fds, but also has the
side effect of blocking the legitimate removal of an fd via
qmp_remove_fd() if the VM happens to be in another state.
This affects qmp_remove_fd() and qmp_query_fdsets() in particular
because requesting a removal at a bad time (guest stopped) might
cause an fd to never be removed, or to be removed at a much later
point in time, causing the query command to continue showing the
supposedly removed fd/fdset.
Note that file descriptors that *have* been duplicated are owned by
the code that uses them and will be removed after qemu_close() is
called. Therefore we've decided that the best course of action to
avoid the undesired side-effects is to stop managing non-duplicated
file descriptors.
1- efb87c1697 ("monitor: Clean up fd sets on monitor disconnect")
2- ebe52b592d ("monitor: Prevent removing fd from set during init")
Reviewed-by: Peter Xu <peterx@redhat.com>
[fix logic mistake: s/fdset_free/fdset_free_if_empty]
Signed-off-by: Fabiano Rosas <farosas@suse.de>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/qtest/libqtest.c | 15 | ||||
-rw-r--r-- | tests/qtest/libqtest.h | 2 | ||||
-rw-r--r-- | tests/qtest/migration-test.c | 82 |
3 files changed, 94 insertions, 5 deletions
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 18e2f7f..c7f6897 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -514,11 +514,6 @@ static QTestState *qtest_init_internal(const char *qemu_bin, kill(s->qemu_pid, SIGSTOP); } #endif - - /* ask endianness of the target */ - - s->big_endian = qtest_query_target_endianness(s); - return s; } @@ -527,11 +522,21 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) return qtest_init_internal(qtest_qemu_binary(NULL), extra_args); } +QTestState *qtest_init_with_env_no_handshake(const char *var, + const char *extra_args) +{ + return qtest_init_internal(qtest_qemu_binary(var), extra_args); +} + QTestState *qtest_init_with_env(const char *var, const char *extra_args) { QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args); QDict *greeting; + /* ask endianness of the target */ + + s->big_endian = qtest_query_target_endianness(s); + /* Read the QMP greeting and then do the handshake */ greeting = qtest_qmp_receive(s); qobject_unref(greeting); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index beb96b1..c261b7e 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -68,6 +68,8 @@ QTestState *qtest_init(const char *extra_args); */ QTestState *qtest_init_with_env(const char *var, const char *extra_args); +QTestState *qtest_init_with_env_no_handshake(const char *var, + const char *extra_args); /** * qtest_init_without_qmp_handshake: * @extra_args: other arguments to pass to QEMU. CAUTION: these diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 22b07bc..eb4d594 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -64,6 +64,7 @@ static QTestMigrationState dst_state; #define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ #define ANALYZE_SCRIPT "scripts/analyze-migration.py" +#define VMSTATE_CHECKER_SCRIPT "scripts/vmstate-static-checker.py" #define QEMU_VM_FILE_MAGIC 0x5145564d #define FILE_TEST_FILENAME "migfile" @@ -1589,6 +1590,85 @@ static void test_analyze_script(void) test_migrate_end(from, to, false); cleanup("migfile"); } + +static void test_vmstate_checker_script(void) +{ + g_autofree gchar *cmd_src = NULL; + g_autofree gchar *cmd_dst = NULL; + g_autofree gchar *vmstate_src = NULL; + g_autofree gchar *vmstate_dst = NULL; + const char *machine_alias, *machine_opts = ""; + g_autofree char *machine = NULL; + const char *arch = qtest_get_arch(); + int pid, wstatus; + const char *python = g_getenv("PYTHON"); + + if (!getenv(QEMU_ENV_SRC) && !getenv(QEMU_ENV_DST)) { + g_test_skip("Test needs two different QEMU versions"); + return; + } + + if (!python) { + g_test_skip("PYTHON variable not set"); + return; + } + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (g_str_equal(arch, "i386")) { + machine_alias = "pc"; + } else { + machine_alias = "q35"; + } + } else if (g_str_equal(arch, "s390x")) { + machine_alias = "s390-ccw-virtio"; + } else if (strcmp(arch, "ppc64") == 0) { + machine_alias = "pseries"; + } else if (strcmp(arch, "aarch64") == 0) { + machine_alias = "virt"; + } else { + g_assert_not_reached(); + } + + if (!qtest_has_machine(machine_alias)) { + g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); + g_test_skip(msg); + return; + } + + machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, + QEMU_ENV_DST); + + vmstate_src = g_strdup_printf("%s/vmstate-src", tmpfs); + vmstate_dst = g_strdup_printf("%s/vmstate-dst", tmpfs); + + cmd_dst = g_strdup_printf("-machine %s,%s -dump-vmstate %s", + machine, machine_opts, vmstate_dst); + cmd_src = g_strdup_printf("-machine %s,%s -dump-vmstate %s", + machine, machine_opts, vmstate_src); + + qtest_init_with_env_no_handshake(QEMU_ENV_SRC, cmd_src); + qtest_init_with_env_no_handshake(QEMU_ENV_DST, cmd_dst); + + pid = fork(); + if (!pid) { + close(1); + open("/dev/null", O_WRONLY); + execl(python, python, VMSTATE_CHECKER_SCRIPT, + "-s", vmstate_src, + "-d", vmstate_dst, + NULL); + g_assert_not_reached(); + } + + g_assert(waitpid(pid, &wstatus, 0) == pid); + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { + g_test_message("Failed to run vmstate-static-checker.py"); + g_test_fail(); + } + + cleanup("vmstate-src"); + cleanup("vmstate-dst"); +} #endif static void test_precopy_common(MigrateCommon *args) @@ -3522,6 +3602,8 @@ int main(int argc, char **argv) migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); + migration_test_add("/migration/vmstate-checker-script", + test_vmstate_checker_script); #endif /* |