qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Fabiano Rosas <farosas@suse.de>
To: qemu-devel@nongnu.org
Cc: "Juan Quintela" <quintela@redhat.com>,
	"Peter Xu" <peterx@redhat.com>,
	"Philippe Mathieu-Daudé" <philmd@linaro.org>,
	"Thomas Huth" <thuth@redhat.com>,
	"Laurent Vivier" <lvivier@redhat.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"Leonardo Bras" <leobras@redhat.com>
Subject: [RFC PATCH 1/1] qtest/migration: Support more than one QEMU binary
Date: Tue,  3 Oct 2023 11:19:32 -0300	[thread overview]
Message-ID: <20231003141932.2367-2-farosas@suse.de> (raw)
In-Reply-To: <20231003141932.2367-1-farosas@suse.de>

We have strict rules around migration compatibility between different
QEMU versions but not a single test that validates the migration state
between different binaries.

Add some infrastructure to allow running the migration tests with two
different QEMU binaries as migration source and destination.

The code now recognizes two new environment variables QTEST_QEMU_SRC
and QTEST_QEMU_DST. Only one of the two is expected to be used along
with the existing QTEST_QEMU_BINARY, which will automatically be used
for the other side of migration.

Usage:
QTEST_QEMU_DST=./build-8.2.0/qemu-system-x86_64 \
QTEST_QEMU_BINARY=../build-8.1.0/qemu-system-x86_64 \
./tests/qtest/migration-test

This code also works for when debugging with GDB to pass the same
binary, but different GDB options for src and dst.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/migration-helpers.c | 168 ++++++++++++++++++++++++++++++++
 tests/qtest/migration-helpers.h |   3 +
 tests/qtest/migration-test.c    |  52 ++++++++--
 3 files changed, 216 insertions(+), 7 deletions(-)

diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c
index be00c52d00..e84360c3b3 100644
--- a/tests/qtest/migration-helpers.c
+++ b/tests/qtest/migration-helpers.c
@@ -12,6 +12,8 @@
 
 #include "qemu/osdep.h"
 #include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qlist.h"
+#include "qapi/qmp/qstring.h"
 
 #include "migration-helpers.h"
 
@@ -180,3 +182,169 @@ void wait_for_migration_fail(QTestState *from, bool allow_active)
     g_assert(qdict_get_bool(rsp_return, "running"));
     qobject_unref(rsp_return);
 }
+
+static char *query_pkg_version(QTestState *who)
+{
+    QDict *rsp;
+    char *pkg;
+
+    rsp = qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-version' }");
+    g_assert(rsp);
+
+    pkg = g_strdup(qdict_get_str(rsp, "package"));
+    qobject_unref(rsp);
+
+    return pkg;
+}
+
+static QList *query_machines(void)
+{
+    QDict *response;
+    QList *list;
+    QTestState *qts;
+
+    qts = qtest_init("-machine none");
+    response = qtest_qmp(qts, "{ 'execute': 'query-machines' }");
+    g_assert(response);
+    list = qdict_get_qlist(response, "return");
+    g_assert(list);
+
+    qtest_quit(qts);
+    return list;
+}
+
+static char *get_default_machine(QList *list)
+{
+    QDict *info;
+    QListEntry *entry;
+    QString *qstr;
+    char *name = NULL;
+
+    QLIST_FOREACH_ENTRY(list, entry) {
+        info = qobject_to(QDict, qlist_entry_obj(entry));
+        g_assert(info);
+
+        if (qdict_get(info, "is-default")) {
+            qstr = qobject_to(QString, qdict_get(info, "name"));
+            g_assert(qstr);
+            name = g_strdup(qstring_get_str(qstr));
+            break;
+        }
+    }
+
+    g_assert(name);
+    return name;
+}
+
+static bool search_default_machine(QList *list, const char *theirs)
+{
+    QDict *info;
+    QListEntry *entry;
+    QString *qstr;
+
+    if (!theirs) {
+        return false;
+    }
+
+    QLIST_FOREACH_ENTRY(list, entry) {
+        info = qobject_to(QDict, qlist_entry_obj(entry));
+        g_assert(info);
+
+        qstr = qobject_to(QString, qdict_get(info, "name"));
+        g_assert(qstr);
+
+        if (g_str_equal(qstring_get_str(qstr), theirs)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * We need to ensure that both QEMU instances set via the QTEST_QEMU_*
+ * vars will use the same machine type. Use a custom query_machines
+ * function because the generic one in libqtest has a cache that would
+ * return the same machines for both binaries.
+ */
+char *find_common_machine_type(const char *bin)
+{
+    QList *m1, *m2;
+    g_autofree char *def1 = NULL;
+    g_autofree char *def2 = NULL;
+    const char *qemu_bin = getenv("QTEST_QEMU_BINARY");
+
+    m1 = query_machines();
+
+    g_setenv("QTEST_QEMU_BINARY", bin, true);
+    m2 = query_machines();
+    g_setenv("QTEST_QEMU_BINARY", qemu_bin, true);
+
+    def1 = get_default_machine(m1);
+    def2 = get_default_machine(m2);
+
+    if (g_str_equal(def1, def2)) {
+        /* either can be used */
+        return g_strdup(def1);
+    }
+
+    if (search_default_machine(m1, def2)) {
+        return g_strdup(def2);
+    }
+
+    if (search_default_machine(m2, def1)) {
+        return g_strdup(def1);
+    }
+
+    g_assert_not_reached();
+}
+
+/*
+ * Init a guest for migration tests using an alternate QEMU binary for
+ * either the source or destination, depending on @var. The other
+ * binary should be set as usual via QTEST_QEMU_BINARY.
+ *
+ * Expected values:
+ *   QTEST_QEMU_SRC
+ *   QTEST_QEMU_DST
+ *
+ * Warning: The generic parts of qtest could be using
+ * QTEST_QEMU_BINARY to query for properties before we reach the
+ * migration code. If the alternate binary is too dissimilar that
+ * could cause issues.
+ */
+static QTestState *init_vm(const char *extra_args, const char *var)
+{
+    const char *alt_bin = getenv(var);
+    const char *qemu_bin = getenv("QTEST_QEMU_BINARY");
+    g_autofree char *pkg = NULL;
+    bool src = !!strstr(var, "SRC");
+    QTestState *qts;
+
+    if (alt_bin) {
+        g_setenv("QTEST_QEMU_BINARY", alt_bin, true);
+    }
+
+    qts = qtest_init(extra_args);
+    pkg = query_pkg_version(qts);
+
+    g_test_message("Using %s (%s) as migration %s",
+                   alt_bin ? alt_bin : qemu_bin,
+                   pkg,
+                   src ? "source" : "destination");
+
+    if (alt_bin) {
+        /* restore the original */
+        g_setenv("QTEST_QEMU_BINARY", qemu_bin, true);
+    }
+    return qts;
+}
+
+QTestState *mig_init_src(const char *extra_args)
+{
+    return init_vm(extra_args, "QTEST_QEMU_SRC");
+}
+
+QTestState *mig_init_dst(const char *extra_args)
+{
+    return init_vm(extra_args, "QTEST_QEMU_DST");
+}
diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h
index 009e250e90..aabdbc7507 100644
--- a/tests/qtest/migration-helpers.h
+++ b/tests/qtest/migration-helpers.h
@@ -33,4 +33,7 @@ void wait_for_migration_complete(QTestState *who);
 
 void wait_for_migration_fail(QTestState *from, bool allow_active);
 
+QTestState *mig_init_src(const char *extra_args);
+QTestState *mig_init_dst(const char *extra_args);
+char *find_common_machine_type(const char *bin);
 #endif /* MIGRATION_HELPERS_H */
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 1b43df5ca7..60f0b15417 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -45,6 +45,7 @@ unsigned end_address;
 static bool uffd_feature_thread_id;
 static bool got_src_stop;
 static bool got_dst_resume;
+static char *common_machine_type;
 
 /*
  * An initial 3 MB offset is used as that corresponds
@@ -712,6 +713,7 @@ static int test_migrate_start(QTestState **from, QTestState **to,
     g_autofree char *shmem_path = NULL;
     const char *arch = qtest_get_arch();
     const char *memory_size;
+    g_autofree char *machine = NULL;
 
     if (args->use_shmem) {
         if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) {
@@ -723,18 +725,30 @@ static int test_migrate_start(QTestState **from, QTestState **to,
     got_src_stop = false;
     got_dst_resume = false;
     bootpath = g_strdup_printf("%s/bootsect", tmpfs);
+
     if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
         /* the assembled x86 boot sector should be exactly one sector large */
         assert(sizeof(x86_bootsect) == 512);
         init_bootfile(bootpath, x86_bootsect, sizeof(x86_bootsect));
         memory_size = "150M";
-        arch_opts = g_strdup_printf("-drive file=%s,format=raw", bootpath);
+
+        if (common_machine_type) {
+            machine = g_strdup_printf("-machine %s", common_machine_type);
+        }
+
+        arch_opts = g_strdup_printf("%s -drive file=%s,format=raw",
+                                    machine, bootpath);
         start_address = X86_TEST_MEM_START;
         end_address = X86_TEST_MEM_END;
     } else if (g_str_equal(arch, "s390x")) {
         init_bootfile(bootpath, s390x_elf, sizeof(s390x_elf));
         memory_size = "128M";
-        arch_opts = g_strdup_printf("-bios %s", bootpath);
+
+        if (common_machine_type) {
+            machine = g_strdup_printf("-machine %s", common_machine_type);
+        }
+
+        arch_opts = g_strdup_printf("%s -bios %s", machine, bootpath);
         start_address = S390_TEST_MEM_START;
         end_address = S390_TEST_MEM_END;
     } else if (strcmp(arch, "ppc64") == 0) {
@@ -745,12 +759,24 @@ static int test_migrate_start(QTestState **from, QTestState **to,
                                       "'nvramrc=hex .\" _\" begin %x %x "
                                       "do i c@ 1 + i c! 1000 +loop .\" B\" 0 "
                                       "until'", end_address, start_address);
-        arch_opts = g_strdup("-nodefaults -machine vsmt=8");
+
+        if (common_machine_type) {
+            machine = g_strdup_printf("%s,", common_machine_type);
+        }
+
+        arch_opts = g_strdup_printf("-nodefaults -machine %svsmt=8", machine);
     } else if (strcmp(arch, "aarch64") == 0) {
         init_bootfile(bootpath, aarch64_kernel, sizeof(aarch64_kernel));
         memory_size = "150M";
-        arch_opts = g_strdup_printf("-machine virt,gic-version=max -cpu max "
-                                    "-kernel %s", bootpath);
+
+        if (common_machine_type) {
+            machine = g_strdup_printf("%s", common_machine_type);
+        } else {
+            machine = g_strdup("virt");
+        }
+
+        arch_opts = g_strdup_printf("-machine %s,gic-version=max -cpu max "
+                                    "-kernel %s", machine, bootpath);
         start_address = ARM_TEST_MEM_START;
         end_address = ARM_TEST_MEM_END;
 
@@ -799,7 +825,7 @@ static int test_migrate_start(QTestState **from, QTestState **to,
                                  args->opts_source ? args->opts_source : "",
                                  ignore_stderr);
     if (!args->only_target) {
-        *from = qtest_init(cmd_source);
+        *from = mig_init_src(cmd_source);
         qtest_qmp_set_event_callback(*from,
                                      migrate_watch_for_stop,
                                      &got_src_stop);
@@ -819,7 +845,7 @@ static int test_migrate_start(QTestState **from, QTestState **to,
                                  shmem_opts,
                                  args->opts_target ? args->opts_target : "",
                                  ignore_stderr);
-    *to = qtest_init(cmd_target);
+    *to = mig_init_dst(cmd_target);
     qtest_qmp_set_event_callback(*to,
                                  migrate_watch_for_resume,
                                  &got_dst_resume);
@@ -2769,6 +2795,8 @@ int main(int argc, char **argv)
     const char *arch;
     g_autoptr(GError) err = NULL;
     int ret;
+    const char *qemu_src = getenv("QTEST_QEMU_SRC");
+    const char *qemu_dst = getenv("QTEST_QEMU_DST");
 
     g_test_init(&argc, &argv, NULL);
 
@@ -2780,6 +2808,16 @@ int main(int argc, char **argv)
         return 0;
     }
 
+    if (qemu_src || qemu_dst) {
+        if (qemu_src && qemu_dst) {
+            g_test_message("Only one of QTEST_QEMU_SRC, QTEST_QEMU_DST is allowed.");
+            exit(1);
+        }
+        common_machine_type = find_common_machine_type(qemu_src ? qemu_src : qemu_dst);
+        g_test_message("Using two different QEMU binaries. Common machine type: %s",
+                       common_machine_type);
+    }
+
     has_uffd = ufd_version_check();
     arch = qtest_get_arch();
 
-- 
2.35.3



  reply	other threads:[~2023-10-03 14:21 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-03 14:19 [RFC PATCH 0/1] tests/migration-test: Allow testing older machine types Fabiano Rosas
2023-10-03 14:19 ` Fabiano Rosas [this message]
2023-10-03 15:24   ` [RFC PATCH 1/1] qtest/migration: Support more than one QEMU binary Philippe Mathieu-Daudé
2023-10-03 15:54     ` Daniel P. Berrangé
2023-10-03 16:24       ` Fabiano Rosas
2023-10-04  8:45         ` Juan Quintela
2023-10-04 15:59           ` Fabiano Rosas
2023-10-04 17:56             ` Daniel P. Berrangé
2023-10-04 18:09               ` Juan Quintela
2023-10-04 18:21                 ` Thomas Huth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231003141932.2367-2-farosas@suse.de \
    --to=farosas@suse.de \
    --cc=leobras@redhat.com \
    --cc=lvivier@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=peterx@redhat.com \
    --cc=philmd@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=quintela@redhat.com \
    --cc=thuth@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).