From 6b76264ed0fed402df09aab8edacf1ee03d87c7f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Nov 2024 16:50:39 +0000 Subject: tests/qtest: Add qtest_system_reset() utility function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have several qtest tests which want to reset the QEMU under test during the course of testing something. They currently generally have their own functions to do this, which work by sending a "system_reset" QMP command. However, "system_reset" only requests a reset, and many of the tests which send the QMP command forget the "and then wait for the QMP RESET event" part which is needed to ensure that the reset has completed. Provide a qtest_system_reset() function in libqtest so that we don't need to reimplement this in multiple different tests. A few tests (for example device hotplug related tests) want to perform the reset command and then wait for some other event that is produced during the reset sequence. For them we provide qtest_system_reset_nowait() so they can clearly indicate that they are deliberately not waiting for the RESET event. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 16 ++++++++++++++++ tests/qtest/libqtest.h | 25 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'tests') diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 817fd7a..8de5f1f 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -215,6 +215,22 @@ static void qtest_check_status(QTestState *s) #endif } +void qtest_system_reset_nowait(QTestState *s) +{ + /* Request the system reset, but do not wait for it to complete */ + qtest_qmp_assert_success(s, "{'execute': 'system_reset' }"); +} + +void qtest_system_reset(QTestState *s) +{ + qtest_system_reset_nowait(s); + /* + * Wait for the RESET event, which is sent once the system reset + * has actually completed. + */ + qtest_qmp_eventwait(s, "RESET"); +} + void qtest_wait_qemu(QTestState *s) { if (s->qemu_pid != -1) { diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index beb96b1..f23d80e 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -89,6 +89,31 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args); QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd); /** + * qtest_system_reset: + * @s: #QTestState instance to operate on. + * + * Send a "system_reset" command to the QEMU under test, and wait for + * the reset to complete before returning. + */ +void qtest_system_reset(QTestState *s); + +/** + * qtest_system_reset_nowait: + * @s: #QTestState instance to operate on. + * + * Send a "system_reset" command to the QEMU under test, but do not + * wait for the reset to complete before returning. The caller is + * responsible for waiting for either the RESET event or some other + * event of interest to them before proceeding. + * + * This function should only be used if you're specifically testing + * for some other event; in that case you can't use qtest_system_reset() + * because it will read and discard any other QMP events that arrive + * before the RESET event. + */ +void qtest_system_reset_nowait(QTestState *s); + +/** * qtest_wait_qemu: * @s: #QTestState instance to operate on. * -- cgit v1.1 From 9881d3d1687d1f18dc605a589d13ae5b8a9db456 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Nov 2024 16:50:40 +0000 Subject: tests/qtest: Use qtest_system_reset() instead of open-coded versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the qtest_system_reset() function in various tests that were previously open-coding the system-reset. Note that in several cases this fixes a bug where the test did not wait for the RESET QMP event before continuing. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Fabiano Rosas --- tests/qtest/bios-tables-test.c | 4 ++-- tests/qtest/boot-order-test.c | 7 +------ tests/qtest/hd-geo-test.c | 9 +-------- tests/qtest/q35-test.c | 12 ++---------- tests/qtest/qos-test.c | 3 +-- tests/qtest/stm32l4x5_gpio-test.c | 10 +--------- tests/qtest/stm32l4x5_syscfg-test.c | 12 ++---------- 7 files changed, 10 insertions(+), 47 deletions(-) (limited to 'tests') diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 16d0ffb..6035ec2 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -959,7 +959,7 @@ static void test_acpi_piix4_tcg_bridge(void) free_test_data(&data); /* check that reboot/reset doesn't change any ACPI tables */ - qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + qtest_system_reset(data.qts); process_acpi_tables(&data); free_test_data(&data); } @@ -1216,7 +1216,7 @@ static void test_acpi_q35_multif_bridge(void) free_test_data(&data); /* check that reboot/reset doesn't change any ACPI tables */ - qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + qtest_system_reset(data.qts); process_acpi_tables(&data); free_test_data(&data); } diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c index c67b8cf..4c851c2 100644 --- a/tests/qtest/boot-order-test.c +++ b/tests/qtest/boot-order-test.c @@ -40,12 +40,7 @@ static void test_a_boot_order(const char *machine, machine ?: "", test_args); actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_boot); - qtest_qmp_assert_success(qts, "{ 'execute': 'system_reset' }"); - /* - * system_reset only requests reset. We get a RESET event after - * the actual reset completes. Need to wait for that. - */ - qtest_qmp_eventwait(qts, "RESET"); + qtest_system_reset(qts); actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_reboot); qtest_quit(qts); diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c index 85eb8d7..1c73dea 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -900,7 +900,6 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid, QTestState *qts; char *joined_args; QFWCFG *fw_cfg; - QDict *response; int i; joined_args = g_strjoinv(" ", args->argv); @@ -913,13 +912,7 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid, /* unplug device an restart */ qtest_qmp_device_del_send(qts, devid); - response = qtest_qmp(qts, - "{ 'execute': 'system_reset', 'arguments': { }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - qtest_qmp_eventwait(qts, "RESET"); + qtest_system_reset(qts); read_bootdevices(fw_cfg, expected2); diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c index c922d81..7f58fc3 100644 --- a/tests/qtest/q35-test.c +++ b/tests/qtest/q35-test.c @@ -83,7 +83,6 @@ static void test_smram_lock(void) { QPCIBus *pcibus; QPCIDevice *pcidev; - QDict *response; QTestState *qts; qts = qtest_init("-M q35"); @@ -107,10 +106,7 @@ static void test_smram_lock(void) g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); /* reset */ - response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + qtest_system_reset(qts); /* check open is settable again */ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); @@ -194,7 +190,6 @@ static void test_smram_smbase_lock(void) { QPCIBus *pcibus; QPCIDevice *pcidev; - QDict *response; QTestState *qts; int i; @@ -237,10 +232,7 @@ static void test_smram_smbase_lock(void) } /* reset */ - response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + qtest_system_reset(qts); /* check RAM at SMBASE is available after reset */ g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index 114f6be..2f7e75a 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -103,8 +103,7 @@ static void restart_qemu_or_continue(char *path) old_path = g_strdup(path); qtest_start(path); } else { /* if cmd line is the same, reset the guest */ - qobject_unref(qmp("{ 'execute': 'system_reset' }")); - qmp_eventwait("RESET"); + qtest_system_reset(global_qtest); } } diff --git a/tests/qtest/stm32l4x5_gpio-test.c b/tests/qtest/stm32l4x5_gpio-test.c index c0686c7..3c6ea71 100644 --- a/tests/qtest/stm32l4x5_gpio-test.c +++ b/tests/qtest/stm32l4x5_gpio-test.c @@ -169,14 +169,6 @@ static uint32_t reset(uint32_t gpio, unsigned int offset) return 0x0; } -static void system_reset(void) -{ - QDict *r; - r = qtest_qmp(global_qtest, "{'execute': 'system_reset'}"); - g_assert_false(qdict_haskey(r, "error")); - qobject_unref(r); -} - static void test_idr_reset_value(void) { /* @@ -214,7 +206,7 @@ static void test_idr_reset_value(void) gpio_writel(GPIO_H, OTYPER, 0xDEADBEEF); gpio_writel(GPIO_H, PUPDR, 0xDEADBEEF); - system_reset(); + qtest_system_reset(global_qtest); uint32_t moder = gpio_readl(GPIO_A, MODER); uint32_t odr = gpio_readl(GPIO_A, ODR); diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c index d5c71e2..376c80e 100644 --- a/tests/qtest/stm32l4x5_syscfg-test.c +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -47,14 +47,6 @@ static void syscfg_set_irq(int num, int level) qtest_set_irq_in(global_qtest, SOC, NULL, num, level); } -static void system_reset(void) -{ - QDict *response; - response = qtest_qmp(global_qtest, "{'execute': 'system_reset'}"); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); -} - static void test_reset(void) { /* @@ -182,7 +174,7 @@ static void test_set_only_bits(void) syscfg_writel(SYSCFG_SWPR2, 0x00000000); g_assert_cmphex(syscfg_readl(SYSCFG_SWPR2), ==, 0xFFFFFFFF); - system_reset(); + qtest_system_reset(global_qtest); } static void test_clear_only_bits(void) @@ -194,7 +186,7 @@ static void test_clear_only_bits(void) syscfg_writel(SYSCFG_CFGR1, 0x00000001); g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000000); - system_reset(); + qtest_system_reset(global_qtest); } static void test_interrupt(void) -- cgit v1.1 From de62427cce9060e4d631b11ba7840bb03368a448 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Nov 2024 16:50:41 +0000 Subject: tests/qtest: Use qtest_system_reset_nowait() where appropriate In the device and drive plug/unplug tests we want to trigger a system reset and then see if we get the appropriate DEVICE_DELETED event. Use qtest_system_reset_nowait() here. Signed-off-by: Peter Maydell Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- tests/qtest/device-plug-test.c | 11 +---------- tests/qtest/drive_del-test.c | 7 +------ 2 files changed, 2 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c index c6f3315..127a7f9 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -15,15 +15,6 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" -static void system_reset(QTestState *qtest) -{ - QDict *resp; - - resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); -} - static void wait_device_deleted_event(QTestState *qtest, const char *id) { QDict *resp, *data; @@ -58,7 +49,7 @@ static void process_device_remove(QTestState *qtest, const char *id) * handled, removing the device. */ qtest_qmp_device_del_send(qtest, id); - system_reset(qtest); + qtest_system_reset_nowait(qtest); wait_device_deleted_event(qtest, id); } diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 7b67a4b..99f6fc2 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -154,15 +154,10 @@ static void device_add(QTestState *qts) static void device_del(QTestState *qts, bool and_reset) { - QDict *response; - qtest_qmp_device_del_send(qts, "dev0"); if (and_reset) { - response = qtest_qmp(qts, "{'execute': 'system_reset' }"); - g_assert(response); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); + qtest_system_reset_nowait(qts); } qtest_qmp_eventwait(qts, "DEVICE_DELETED"); -- cgit v1.1 From 56afcf2d481bdff5e320c94bb70af923b509551c Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:45 -0300 Subject: tests/qtest/migration: Standardize hook names Standardize the hook names: - change the names to .start|end_hook to match test_migrate_start|end() - use the migrate_hook_start_ and migrate_hook_end_ prefixes Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 342 ++++++++++++++++++++++--------------------- 1 file changed, 174 insertions(+), 168 deletions(-) (limited to 'tests') diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 74d3000..f0f3145 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -631,9 +631,9 @@ typedef void * (*TestMigrateStartHook)(QTestState *from, * @opaque is a pointer to state previously returned * by the TestMigrateStartHook if any, or NULL. */ -typedef void (*TestMigrateFinishHook)(QTestState *from, - QTestState *to, - void *opaque); +typedef void (*TestMigrateEndHook)(QTestState *from, + QTestState *to, + void *opaque); typedef struct { /* Optional: fine tune start parameters */ @@ -660,7 +660,7 @@ typedef struct { /* Optional: callback to run at start to set migration parameters */ TestMigrateStartHook start_hook; /* Optional: callback to run at finish to cleanup */ - TestMigrateFinishHook finish_hook; + TestMigrateEndHook end_hook; /* * Optional: normally we expect the migration process to complete. @@ -919,7 +919,7 @@ struct TestMigrateTLSPSKData { }; static void * -test_migrate_tls_psk_start_common(QTestState *from, +migrate_hook_start_tls_psk_common(QTestState *from, QTestState *to, bool mismatch) { @@ -964,23 +964,23 @@ test_migrate_tls_psk_start_common(QTestState *from, } static void * -test_migrate_tls_psk_start_match(QTestState *from, +migrate_hook_start_tls_psk_match(QTestState *from, QTestState *to) { - return test_migrate_tls_psk_start_common(from, to, false); + return migrate_hook_start_tls_psk_common(from, to, false); } static void * -test_migrate_tls_psk_start_mismatch(QTestState *from, +migrate_hook_start_tls_psk_mismatch(QTestState *from, QTestState *to) { - return test_migrate_tls_psk_start_common(from, to, true); + return migrate_hook_start_tls_psk_common(from, to, true); } static void -test_migrate_tls_psk_finish(QTestState *from, - QTestState *to, - void *opaque) +migrate_hook_end_tls_psk(QTestState *from, + QTestState *to, + void *opaque) { struct TestMigrateTLSPSKData *data = opaque; @@ -1021,7 +1021,7 @@ typedef struct { } TestMigrateTLSX509; static void * -test_migrate_tls_x509_start_common(QTestState *from, +migrate_hook_start_tls_x509_common(QTestState *from, QTestState *to, TestMigrateTLSX509 *args) { @@ -1114,7 +1114,7 @@ test_migrate_tls_x509_start_common(QTestState *from, * whatever host we were telling QEMU to connect to (if any) */ static void * -test_migrate_tls_x509_start_default_host(QTestState *from, +migrate_hook_start_tls_x509_default_host(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1122,7 +1122,7 @@ test_migrate_tls_x509_start_default_host(QTestState *from, .clientcert = true, .certipaddr = "127.0.0.1" }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } /* @@ -1131,7 +1131,7 @@ test_migrate_tls_x509_start_default_host(QTestState *from, * so we must give QEMU an explicit hostname to validate */ static void * -test_migrate_tls_x509_start_override_host(QTestState *from, +migrate_hook_start_tls_x509_override_host(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1139,7 +1139,7 @@ test_migrate_tls_x509_start_override_host(QTestState *from, .clientcert = true, .certhostname = "qemu.org", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } /* @@ -1148,7 +1148,7 @@ test_migrate_tls_x509_start_override_host(QTestState *from, * expect the client to reject the server */ static void * -test_migrate_tls_x509_start_mismatch_host(QTestState *from, +migrate_hook_start_tls_x509_mismatch_host(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1156,11 +1156,11 @@ test_migrate_tls_x509_start_mismatch_host(QTestState *from, .clientcert = true, .certipaddr = "10.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } static void * -test_migrate_tls_x509_start_friendly_client(QTestState *from, +migrate_hook_start_tls_x509_friendly_client(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1169,11 +1169,11 @@ test_migrate_tls_x509_start_friendly_client(QTestState *from, .authzclient = true, .certipaddr = "127.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } static void * -test_migrate_tls_x509_start_hostile_client(QTestState *from, +migrate_hook_start_tls_x509_hostile_client(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1183,7 +1183,7 @@ test_migrate_tls_x509_start_hostile_client(QTestState *from, .authzclient = true, .certipaddr = "127.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } /* @@ -1191,13 +1191,13 @@ test_migrate_tls_x509_start_hostile_client(QTestState *from, * and no server verification */ static void * -test_migrate_tls_x509_start_allow_anon_client(QTestState *from, +migrate_hook_start_tls_x509_allow_anon_client(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { .certipaddr = "127.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } /* @@ -1205,20 +1205,20 @@ test_migrate_tls_x509_start_allow_anon_client(QTestState *from, * and server verification rejecting */ static void * -test_migrate_tls_x509_start_reject_anon_client(QTestState *from, +migrate_hook_start_tls_x509_reject_anon_client(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { .verifyclient = true, .certipaddr = "127.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } static void -test_migrate_tls_x509_finish(QTestState *from, - QTestState *to, - void *opaque) +migrate_hook_end_tls_x509(QTestState *from, + QTestState *to, + void *opaque) { TestMigrateTLSX509Data *data = opaque; @@ -1314,8 +1314,8 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to, read_blocktime(to); } - if (args->finish_hook) { - args->finish_hook(from, to, args->postcopy_data); + if (args->end_hook) { + args->end_hook(from, to, args->postcopy_data); args->postcopy_data = NULL; } @@ -1362,8 +1362,8 @@ static void test_postcopy_preempt(void) static void test_postcopy_tls_psk(void) { MigrateCommon args = { - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_postcopy_common(&args); @@ -1373,8 +1373,8 @@ static void test_postcopy_preempt_tls_psk(void) { MigrateCommon args = { .postcopy_preempt = true, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_postcopy_common(&args); @@ -1596,8 +1596,8 @@ static void test_postcopy_recovery_fail_reconnect(void) static void test_postcopy_recovery_tls_psk(void) { MigrateCommon args = { - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_postcopy_recovery_common(&args); @@ -1619,8 +1619,8 @@ static void test_postcopy_preempt_all(void) { MigrateCommon args = { .postcopy_preempt = true, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_postcopy_recovery_common(&args); @@ -1794,8 +1794,8 @@ static void test_precopy_common(MigrateCommon *args) } finish: - if (args->finish_hook) { - args->finish_hook(from, to, data_hook); + if (args->end_hook) { + args->end_hook(from, to, data_hook); } test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); @@ -1899,8 +1899,8 @@ static void test_file_common(MigrateCommon *args, bool stop_src) } finish: - if (args->finish_hook) { - args->finish_hook(from, to, data_hook); + if (args->end_hook) { + args->end_hook(from, to, data_hook); } test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); @@ -1977,8 +1977,8 @@ static void test_precopy_unix_tls_psk(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_precopy_common(&args); @@ -1994,8 +1994,8 @@ static void test_precopy_unix_tls_x509_default_host(void) }, .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL_DEST_QUIT_ERR, }; @@ -2008,8 +2008,8 @@ static void test_precopy_unix_tls_x509_override_host(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2056,7 +2056,7 @@ static void test_ignore_shared(void) #endif static void * -test_migrate_xbzrle_start(QTestState *from, +migrate_hook_start_xbzrle(QTestState *from, QTestState *to) { migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); @@ -2073,7 +2073,7 @@ static void test_precopy_unix_xbzrle(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_xbzrle_start, + .start_hook = migrate_hook_start_xbzrle, .iterations = 2, /* * XBZRLE needs pages to be modified when doing the 2nd+ round @@ -2120,7 +2120,8 @@ static void fdset_add_fds(QTestState *qts, const char *file, int flags, } } -static void *file_offset_fdset_start_hook(QTestState *from, QTestState *to) +static void *migrate_hook_start_file_offset_fdset(QTestState *from, + QTestState *to) { g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); @@ -2137,7 +2138,7 @@ static void test_precopy_file_offset_fdset(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = file_offset_fdset_start_hook, + .start_hook = migrate_hook_start_file_offset_fdset, }; test_file_common(&args, false); @@ -2171,7 +2172,7 @@ static void test_precopy_file_offset_bad(void) test_file_common(&args, false); } -static void *test_mode_reboot_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) { migrate_set_parameter_str(from, "mode", "cpr-reboot"); migrate_set_parameter_str(to, "mode", "cpr-reboot"); @@ -2182,7 +2183,7 @@ static void *test_mode_reboot_start(QTestState *from, QTestState *to) return NULL; } -static void *migrate_mapped_ram_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_mapped_ram(QTestState *from, QTestState *to) { migrate_set_capability(from, "mapped-ram", true); migrate_set_capability(to, "mapped-ram", true); @@ -2198,7 +2199,7 @@ static void test_mode_reboot(void) .start.use_shmem = true, .connect_uri = uri, .listen_uri = "defer", - .start_hook = test_mode_reboot_start + .start_hook = migrate_hook_start_mode_reboot, }; test_file_common(&args, true); @@ -2211,7 +2212,7 @@ static void test_precopy_file_mapped_ram_live(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_mapped_ram_start, + .start_hook = migrate_hook_start_mapped_ram, }; test_file_common(&args, false); @@ -2224,15 +2225,16 @@ static void test_precopy_file_mapped_ram(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_mapped_ram_start, + .start_hook = migrate_hook_start_mapped_ram, }; test_file_common(&args, true); } -static void *migrate_multifd_mapped_ram_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_multifd_mapped_ram(QTestState *from, + QTestState *to) { - migrate_mapped_ram_start(from, to); + migrate_hook_start_mapped_ram(from, to); migrate_set_parameter_int(from, "multifd-channels", 4); migrate_set_parameter_int(to, "multifd-channels", 4); @@ -2250,7 +2252,7 @@ static void test_multifd_file_mapped_ram_live(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_multifd_mapped_ram_start, + .start_hook = migrate_hook_start_multifd_mapped_ram, }; test_file_common(&args, false); @@ -2263,15 +2265,16 @@ static void test_multifd_file_mapped_ram(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_multifd_mapped_ram_start, + .start_hook = migrate_hook_start_multifd_mapped_ram, }; test_file_common(&args, true); } -static void *multifd_mapped_ram_dio_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, + QTestState *to) { - migrate_multifd_mapped_ram_start(from, to); + migrate_hook_start_multifd_mapped_ram(from, to); migrate_set_parameter_bool(from, "direct-io", true); migrate_set_parameter_bool(to, "direct-io", true); @@ -2286,7 +2289,7 @@ static void test_multifd_file_mapped_ram_dio(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = multifd_mapped_ram_dio_start, + .start_hook = migrate_hook_start_multifd_mapped_ram_dio, }; if (!probe_o_direct_support(tmpfs)) { @@ -2298,8 +2301,9 @@ static void test_multifd_file_mapped_ram_dio(void) } #ifndef _WIN32 -static void multifd_mapped_ram_fdset_end(QTestState *from, QTestState *to, - void *opaque) +static void migrate_hook_end_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to, + void *opaque) { QDict *resp; QList *fdsets; @@ -2323,28 +2327,30 @@ static void multifd_mapped_ram_fdset_end(QTestState *from, QTestState *to, qobject_unref(resp); } -static void *multifd_mapped_ram_fdset_dio(QTestState *from, QTestState *to) +static void *migrate_hook_start_multifd_mapped_ram_fdset_dio(QTestState *from, + QTestState *to) { g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); fdset_add_fds(from, file, O_WRONLY, 2, true); fdset_add_fds(to, file, O_RDONLY, 2, true); - migrate_multifd_mapped_ram_start(from, to); + migrate_hook_start_multifd_mapped_ram(from, to); migrate_set_parameter_bool(from, "direct-io", true); migrate_set_parameter_bool(to, "direct-io", true); return NULL; } -static void *multifd_mapped_ram_fdset(QTestState *from, QTestState *to) +static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to) { g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); fdset_add_fds(from, file, O_WRONLY, 2, false); fdset_add_fds(to, file, O_RDONLY, 2, false); - migrate_multifd_mapped_ram_start(from, to); + migrate_hook_start_multifd_mapped_ram(from, to); return NULL; } @@ -2356,8 +2362,8 @@ static void test_multifd_file_mapped_ram_fdset(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = multifd_mapped_ram_fdset, - .finish_hook = multifd_mapped_ram_fdset_end, + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, }; test_file_common(&args, true); @@ -2370,8 +2376,8 @@ static void test_multifd_file_mapped_ram_fdset_dio(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = multifd_mapped_ram_fdset_dio, - .finish_hook = multifd_mapped_ram_fdset_end, + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, }; if (!probe_o_direct_support(tmpfs)) { @@ -2392,7 +2398,7 @@ static void test_precopy_tcp_plain(void) test_precopy_common(&args); } -static void *test_migrate_switchover_ack_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_switchover_ack(QTestState *from, QTestState *to) { migrate_set_capability(from, "return-path", true); @@ -2408,7 +2414,7 @@ static void test_precopy_tcp_switchover_ack(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_switchover_ack_start, + .start_hook = migrate_hook_start_switchover_ack, /* * Source VM must be running in order to consider the switchover ACK * when deciding to do switchover or not. @@ -2424,8 +2430,8 @@ static void test_precopy_tcp_tls_psk_match(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_precopy_common(&args); @@ -2438,8 +2444,8 @@ static void test_precopy_tcp_tls_psk_mismatch(void) .hide_stderr = true, }, .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_psk_start_mismatch, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, .result = MIG_TEST_FAIL, }; @@ -2451,8 +2457,8 @@ static void test_precopy_tcp_tls_x509_default_host(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2462,8 +2468,8 @@ static void test_precopy_tcp_tls_x509_override_host(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2476,8 +2482,8 @@ static void test_precopy_tcp_tls_x509_mismatch_host(void) .hide_stderr = true, }, .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_mismatch_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL_DEST_QUIT_ERR, }; @@ -2488,8 +2494,8 @@ static void test_precopy_tcp_tls_x509_friendly_client(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_friendly_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_friendly_client, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2502,8 +2508,8 @@ static void test_precopy_tcp_tls_x509_hostile_client(void) .hide_stderr = true, }, .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_hostile_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_hostile_client, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL, }; @@ -2514,8 +2520,8 @@ static void test_precopy_tcp_tls_x509_allow_anon_client(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_allow_anon_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2528,8 +2534,8 @@ static void test_precopy_tcp_tls_x509_reject_anon_client(void) .hide_stderr = true, }, .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_reject_anon_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL, }; @@ -2539,8 +2545,8 @@ static void test_precopy_tcp_tls_x509_reject_anon_client(void) #endif /* CONFIG_GNUTLS */ #ifndef _WIN32 -static void *test_migrate_fd_start_hook(QTestState *from, - QTestState *to) +static void *migrate_hook_start_fd(QTestState *from, + QTestState *to) { int ret; int pair[2]; @@ -2567,9 +2573,9 @@ static void *test_migrate_fd_start_hook(QTestState *from, return NULL; } -static void test_migrate_fd_finish_hook(QTestState *from, - QTestState *to, - void *opaque) +static void migrate_hook_end_fd(QTestState *from, + QTestState *to, + void *opaque) { QDict *rsp; const char *error_desc; @@ -2599,13 +2605,13 @@ static void test_migrate_precopy_fd_socket(void) MigrateCommon args = { .listen_uri = "defer", .connect_uri = "fd:fd-mig", - .start_hook = test_migrate_fd_start_hook, - .finish_hook = test_migrate_fd_finish_hook + .start_hook = migrate_hook_start_fd, + .end_hook = migrate_hook_end_fd, }; test_precopy_common(&args); } -static void *migrate_precopy_fd_file_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_precopy_fd_file(QTestState *from, QTestState *to) { g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); int src_flags = O_CREAT | O_RDWR; @@ -2638,8 +2644,8 @@ static void test_migrate_precopy_fd_file(void) MigrateCommon args = { .listen_uri = "defer", .connect_uri = "fd:fd-mig", - .start_hook = migrate_precopy_fd_file_start, - .finish_hook = test_migrate_fd_finish_hook + .start_hook = migrate_hook_start_precopy_fd_file, + .end_hook = migrate_hook_end_fd, }; test_file_common(&args, true); } @@ -2886,7 +2892,7 @@ static void test_migrate_auto_converge(void) } static void * -test_migrate_precopy_tcp_multifd_start_common(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, QTestState *to, const char *method) { @@ -2906,32 +2912,32 @@ test_migrate_precopy_tcp_multifd_start_common(QTestState *from, } static void * -test_migrate_precopy_tcp_multifd_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd(QTestState *from, QTestState *to) { - return test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); } static void * -test_migrate_precopy_tcp_multifd_start_zero_page_legacy(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_zero_page_legacy(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); migrate_set_parameter_str(from, "zero-page-detection", "legacy"); return NULL; } static void * -test_migration_precopy_tcp_multifd_start_no_zero_page(QTestState *from, - QTestState *to) +migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, + QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); migrate_set_parameter_str(from, "zero-page-detection", "none"); return NULL; } static void * -test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, QTestState *to) { /* @@ -2941,47 +2947,47 @@ test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from, migrate_set_parameter_int(from, "multifd-zlib-level", 2); migrate_set_parameter_int(to, "multifd-zlib-level", 2); - return test_migrate_precopy_tcp_multifd_start_common(from, to, "zlib"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); } #ifdef CONFIG_ZSTD static void * -test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from, QTestState *to) { migrate_set_parameter_int(from, "multifd-zstd-level", 2); migrate_set_parameter_int(to, "multifd-zstd-level", 2); - return test_migrate_precopy_tcp_multifd_start_common(from, to, "zstd"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd"); } #endif /* CONFIG_ZSTD */ #ifdef CONFIG_QATZIP static void * -test_migrate_precopy_tcp_multifd_qatzip_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from, QTestState *to) { migrate_set_parameter_int(from, "multifd-qatzip-level", 2); migrate_set_parameter_int(to, "multifd-qatzip-level", 2); - return test_migrate_precopy_tcp_multifd_start_common(from, to, "qatzip"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip"); } #endif #ifdef CONFIG_QPL static void * -test_migrate_precopy_tcp_multifd_qpl_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from, QTestState *to) { - return test_migrate_precopy_tcp_multifd_start_common(from, to, "qpl"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl"); } #endif /* CONFIG_QPL */ #ifdef CONFIG_UADK static void * -test_migrate_precopy_tcp_multifd_uadk_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, QTestState *to) { - return test_migrate_precopy_tcp_multifd_start_common(from, to, "uadk"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); } #endif /* CONFIG_UADK */ @@ -2989,7 +2995,7 @@ static void test_multifd_tcp_uri_none(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd, /* * Multifd is more complicated than most of the features, it * directly takes guest page buffers when sending, make sure @@ -3004,7 +3010,7 @@ static void test_multifd_tcp_zero_page_legacy(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start_zero_page_legacy, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, /* * Multifd is more complicated than most of the features, it * directly takes guest page buffers when sending, make sure @@ -3019,7 +3025,7 @@ static void test_multifd_tcp_no_zero_page(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migration_precopy_tcp_multifd_start_no_zero_page, + .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page, /* * Multifd is more complicated than most of the features, it * directly takes guest page buffers when sending, make sure @@ -3034,7 +3040,7 @@ static void test_multifd_tcp_channels_none(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd, .live = true, .connect_channels = ("[ { 'channel-type': 'main'," " 'addr': { 'transport': 'socket'," @@ -3049,7 +3055,7 @@ static void test_multifd_tcp_zlib(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_zlib_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, }; test_precopy_common(&args); } @@ -3059,7 +3065,7 @@ static void test_multifd_tcp_zstd(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_zstd_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, }; test_precopy_common(&args); } @@ -3070,7 +3076,7 @@ static void test_multifd_tcp_qatzip(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_qatzip_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, }; test_precopy_common(&args); } @@ -3081,7 +3087,7 @@ static void test_multifd_tcp_qpl(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_qpl_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, }; test_precopy_common(&args); } @@ -3092,7 +3098,7 @@ static void test_multifd_tcp_uadk(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_uadk_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, }; test_precopy_common(&args); } @@ -3100,60 +3106,60 @@ static void test_multifd_tcp_uadk(void) #ifdef CONFIG_GNUTLS static void * -test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from, +migrate_hook_start_multifd_tcp_tls_psk_match(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_psk_start_match(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_match(from, to); } static void * -test_migrate_multifd_tcp_tls_psk_start_mismatch(QTestState *from, +migrate_hook_start_multifd_tcp_tls_psk_mismatch(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_psk_start_mismatch(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_mismatch(from, to); } #ifdef CONFIG_TASN1 static void * -test_migrate_multifd_tls_x509_start_default_host(QTestState *from, +migrate_hook_start_multifd_tls_x509_default_host(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_default_host(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_default_host(from, to); } static void * -test_migrate_multifd_tls_x509_start_override_host(QTestState *from, +migrate_hook_start_multifd_tls_x509_override_host(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_override_host(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_override_host(from, to); } static void * -test_migrate_multifd_tls_x509_start_mismatch_host(QTestState *from, +migrate_hook_start_multifd_tls_x509_mismatch_host(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_mismatch_host(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_mismatch_host(from, to); } static void * -test_migrate_multifd_tls_x509_start_allow_anon_client(QTestState *from, +migrate_hook_start_multifd_tls_x509_allow_anon_client(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_allow_anon_client(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_allow_anon_client(from, to); } static void * -test_migrate_multifd_tls_x509_start_reject_anon_client(QTestState *from, +migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_reject_anon_client(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_reject_anon_client(from, to); } #endif /* CONFIG_TASN1 */ @@ -3161,8 +3167,8 @@ static void test_multifd_tcp_tls_psk_match(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_multifd_tcp_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_precopy_common(&args); } @@ -3174,8 +3180,8 @@ static void test_multifd_tcp_tls_psk_mismatch(void) .hide_stderr = true, }, .listen_uri = "defer", - .start_hook = test_migrate_multifd_tcp_tls_psk_start_mismatch, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, .result = MIG_TEST_FAIL, }; test_precopy_common(&args); @@ -3186,8 +3192,8 @@ static void test_multifd_tcp_tls_x509_default_host(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); } @@ -3196,8 +3202,8 @@ static void test_multifd_tcp_tls_x509_override_host(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); } @@ -3222,8 +3228,8 @@ static void test_multifd_tcp_tls_x509_mismatch_host(void) .hide_stderr = true, }, .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_mismatch_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL, }; test_precopy_common(&args); @@ -3233,8 +3239,8 @@ static void test_multifd_tcp_tls_x509_allow_anon_client(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_allow_anon_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); } @@ -3246,8 +3252,8 @@ static void test_multifd_tcp_tls_x509_reject_anon_client(void) .hide_stderr = true, }, .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_reject_anon_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL, }; test_precopy_common(&args); -- cgit v1.1 From 413aa2e986eb7941c1042cc8bb8299f16e7ca962 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:46 -0300 Subject: tests/qtest/migration: Stop calling everything "test" Test frameworks usually prefix "test_" to the entry point of the test code. Having every function prefixed with test_ makes it hard to understand the code and to grep for the actual tests. Remove the "test" prefix from everything that is not a test. In order to still keep some namespacing, stick to the "migrate_" prefix, which is the most used currently. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 72 ++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 36 deletions(-) (limited to 'tests') diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index f0f3145..30bc965 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -710,8 +710,8 @@ typedef struct { PostcopyRecoveryFailStage postcopy_recovery_fail_stage; } MigrateCommon; -static int test_migrate_start(QTestState **from, QTestState **to, - const char *uri, MigrateStart *args) +static int migrate_start(QTestState **from, QTestState **to, + const char *uri, MigrateStart *args) { g_autofree gchar *arch_source = NULL; g_autofree gchar *arch_target = NULL; @@ -876,7 +876,7 @@ static int test_migrate_start(QTestState **from, QTestState **to, return 0; } -static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) +static void migrate_end(QTestState *from, QTestState *to, bool test_dest) { unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; @@ -1255,7 +1255,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, { QTestState *from, *to; - if (test_migrate_start(&from, &to, "defer", &args->start)) { + if (migrate_start(&from, &to, "defer", &args->start)) { return -1; } @@ -1319,7 +1319,7 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to, args->postcopy_data = NULL; } - test_migrate_end(from, to, true); + migrate_end(from, to, true); } static void test_postcopy_common(MigrateCommon *args) @@ -1635,12 +1635,12 @@ static void test_baddest(void) }; QTestState *from, *to; - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { return; } migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); wait_for_migration_fail(from, false); - test_migrate_end(from, to, false); + migrate_end(from, to, false); } #ifndef _WIN32 @@ -1661,7 +1661,7 @@ static void test_analyze_script(void) } /* dummy url */ - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { return; } @@ -1693,7 +1693,7 @@ static void test_analyze_script(void) g_test_message("Failed to analyze the migration stream"); g_test_fail(); } - test_migrate_end(from, to, false); + migrate_end(from, to, false); cleanup("migfile"); } #endif @@ -1703,7 +1703,7 @@ static void test_precopy_common(MigrateCommon *args) QTestState *from, *to; void *data_hook = NULL; - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } @@ -1798,7 +1798,7 @@ finish: args->end_hook(from, to, data_hook); } - test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); } static void file_dirty_offset_region(void) @@ -1839,7 +1839,7 @@ static void test_file_common(MigrateCommon *args, bool stop_src) void *data_hook = NULL; bool check_offset = false; - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } @@ -1903,7 +1903,7 @@ finish: args->end_hook(from, to, data_hook); } - test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); } static void test_precopy_unix_plain(void) @@ -2024,7 +2024,7 @@ static void test_ignore_shared(void) g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) { + if (migrate_start(&from, &to, uri, false, true, NULL, NULL)) { return; } @@ -2051,7 +2051,7 @@ static void test_ignore_shared(void) /* Check whether shared RAM has been really skipped */ g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); - test_migrate_end(from, to, true); + migrate_end(from, to, true); } #endif @@ -2600,7 +2600,7 @@ static void migrate_hook_end_fd(QTestState *from, qobject_unref(rsp); } -static void test_migrate_precopy_fd_socket(void) +static void test_precopy_fd_socket(void) { MigrateCommon args = { .listen_uri = "defer", @@ -2639,7 +2639,7 @@ static void *migrate_hook_start_precopy_fd_file(QTestState *from, QTestState *to return NULL; } -static void test_migrate_precopy_fd_file(void) +static void test_precopy_fd_file(void) { MigrateCommon args = { .listen_uri = "defer", @@ -2656,7 +2656,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, args)) { + if (migrate_start(&from, &to, uri, args)) { return; } @@ -2680,7 +2680,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) wait_for_migration_complete(from); } - test_migrate_end(from, to, false); + migrate_end(from, to, false); } static void test_validate_uuid(void) @@ -2728,7 +2728,7 @@ static void do_test_validate_uri_channel(MigrateCommon *args) { QTestState *from, *to; - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } @@ -2740,7 +2740,7 @@ static void do_test_validate_uri_channel(MigrateCommon *args) * starts. */ migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); - test_migrate_end(from, to, false); + migrate_end(from, to, false); } static void test_validate_uri_channels_both_set(void) @@ -2788,7 +2788,7 @@ static void test_validate_uri_channels_none_set(void) * To make things even worse, we need to run the initial stage at * 3MB/s so we enter autoconverge even when host is (over)loaded. */ -static void test_migrate_auto_converge(void) +static void test_auto_converge(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); MigrateStart args = {}; @@ -2804,7 +2804,7 @@ static void test_migrate_auto_converge(void) uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; int max_try_count, hit = 0; - if (test_migrate_start(&from, &to, uri, &args)) { + if (migrate_start(&from, &to, uri, &args)) { return; } @@ -2888,7 +2888,7 @@ static void test_migrate_auto_converge(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); - test_migrate_end(from, to, true); + migrate_end(from, to, true); } static void * @@ -3279,7 +3279,7 @@ static void test_multifd_tcp_cancel(void) }; QTestState *from, *to, *to2; - if (test_migrate_start(&from, &to, "defer", &args)) { + if (migrate_start(&from, &to, "defer", &args)) { return; } @@ -3311,7 +3311,7 @@ static void test_multifd_tcp_cancel(void) /* * Ensure the source QEMU finishes its cancellation process before we - * proceed with the setup of the next migration. The test_migrate_start() + * proceed with the setup of the next migration. The migrate_start() * function and others might want to interact with the source in a way that * is not possible while the migration is not canceled properly. For * example, setting migration capabilities when the migration is still @@ -3323,7 +3323,7 @@ static void test_multifd_tcp_cancel(void) .only_target = true, }; - if (test_migrate_start(&from, &to2, "defer", &args)) { + if (migrate_start(&from, &to2, "defer", &args)) { return; } @@ -3347,7 +3347,7 @@ static void test_multifd_tcp_cancel(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); - test_migrate_end(from, to2, true); + migrate_end(from, to2, true); } static void calc_dirty_rate(QTestState *who, uint64_t calc_time) @@ -3633,7 +3633,7 @@ static void migrate_dirty_limit_wait_showup(QTestState *from, * And see if dirty limit migration works correctly. * This test case involves many passes, so it runs in slow mode only. */ -static void test_migrate_dirty_limit(void) +static void test_dirty_limit(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; @@ -3664,7 +3664,7 @@ static void test_migrate_dirty_limit(void) }; /* Start src, dst vm */ - if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { return; } @@ -3710,7 +3710,7 @@ static void test_migrate_dirty_limit(void) }; /* Restart dst vm, src vm already show up so we needn't wait anymore */ - if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { return; } @@ -3754,7 +3754,7 @@ static void test_migrate_dirty_limit(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); - test_migrate_end(from, to, true); + migrate_end(from, to, true); } static bool kvm_dirty_ring_supported(void) @@ -3957,9 +3957,9 @@ int main(int argc, char **argv) /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ #ifndef _WIN32 migration_test_add("/migration/precopy/fd/tcp", - test_migrate_precopy_fd_socket); + test_precopy_fd_socket); migration_test_add("/migration/precopy/fd/file", - test_migrate_precopy_fd_file); + test_precopy_fd_file); #endif migration_test_add("/migration/validate_uuid", test_validate_uuid); migration_test_add("/migration/validate_uuid_error", @@ -3977,11 +3977,11 @@ int main(int argc, char **argv) */ if (g_test_slow()) { migration_test_add("/migration/auto_converge", - test_migrate_auto_converge); + test_auto_converge); if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) { migration_test_add("/migration/dirty_limit", - test_migrate_dirty_limit); + test_dirty_limit); } } migration_test_add("/migration/multifd/tcp/uri/plain/none", -- cgit v1.1 From 212c19331b0c53ab299ae3d646409fad2da90602 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:47 -0300 Subject: tests/migration: Disambiguate guestperf vs. a-b The current build structure for migration tests is confusing. There is the tests/migration directory, which contains two different guest code implementations, one for the qtests (a-b-{bootblock|kernel}.S) and another for the guestperf script (stress.c). One uses a Makefile, while the other uses meson. The next patches will add a new qtests/migration/ directory to hold qtest code which will make the situation even more confusing. Move the guest code used by qtests into a new qtests/migration/ directory and rename the old one to tests/migration-stress. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/meson.build | 2 +- tests/migration-stress/guestperf-batch.py | 26 ++ tests/migration-stress/guestperf-plot.py | 26 ++ tests/migration-stress/guestperf.py | 27 ++ tests/migration-stress/guestperf/__init__.py | 0 tests/migration-stress/guestperf/comparison.py | 161 +++++++ tests/migration-stress/guestperf/engine.py | 505 ++++++++++++++++++++ tests/migration-stress/guestperf/hardware.py | 66 +++ tests/migration-stress/guestperf/plot.py | 623 +++++++++++++++++++++++++ tests/migration-stress/guestperf/progress.py | 129 +++++ tests/migration-stress/guestperf/report.py | 98 ++++ tests/migration-stress/guestperf/scenario.py | 112 +++++ tests/migration-stress/guestperf/shell.py | 297 ++++++++++++ tests/migration-stress/guestperf/timings.py | 55 +++ tests/migration-stress/initrd-stress.sh | 10 + tests/migration-stress/meson.build | 18 + tests/migration-stress/stress.c | 328 +++++++++++++ tests/migration/Makefile | 40 -- tests/migration/aarch64/Makefile | 18 - tests/migration/aarch64/a-b-kernel.S | 74 --- tests/migration/aarch64/a-b-kernel.h | 18 - tests/migration/guestperf-batch.py | 26 -- tests/migration/guestperf-plot.py | 26 -- tests/migration/guestperf.py | 27 -- tests/migration/guestperf/__init__.py | 0 tests/migration/guestperf/comparison.py | 161 ------- tests/migration/guestperf/engine.py | 505 -------------------- tests/migration/guestperf/hardware.py | 66 --- tests/migration/guestperf/plot.py | 623 ------------------------- tests/migration/guestperf/progress.py | 129 ----- tests/migration/guestperf/report.py | 98 ---- tests/migration/guestperf/scenario.py | 112 ----- tests/migration/guestperf/shell.py | 296 ------------ tests/migration/guestperf/timings.py | 55 --- tests/migration/i386/Makefile | 23 - tests/migration/i386/a-b-bootblock.S | 145 ------ tests/migration/i386/a-b-bootblock.h | 61 --- tests/migration/initrd-stress.sh | 10 - tests/migration/meson.build | 18 - tests/migration/migration-test.h | 36 -- tests/migration/ppc64/Makefile | 15 - tests/migration/ppc64/a-b-kernel.S | 66 --- tests/migration/ppc64/a-b-kernel.h | 42 -- tests/migration/s390x/Makefile | 24 - tests/migration/s390x/a-b-bios.c | 44 -- tests/migration/s390x/a-b-bios.h | 279 ----------- tests/migration/stress.c | 328 ------------- tests/qtest/migration-test.c | 10 +- tests/qtest/migration/Makefile | 40 ++ tests/qtest/migration/aarch64/Makefile | 18 + tests/qtest/migration/aarch64/a-b-kernel.S | 74 +++ tests/qtest/migration/aarch64/a-b-kernel.h | 18 + tests/qtest/migration/i386/Makefile | 23 + tests/qtest/migration/i386/a-b-bootblock.S | 145 ++++++ tests/qtest/migration/i386/a-b-bootblock.h | 61 +++ tests/qtest/migration/migration-test.h | 36 ++ tests/qtest/migration/ppc64/Makefile | 15 + tests/qtest/migration/ppc64/a-b-kernel.S | 66 +++ tests/qtest/migration/ppc64/a-b-kernel.h | 42 ++ tests/qtest/migration/s390x/Makefile | 24 + tests/qtest/migration/s390x/a-b-bios.c | 44 ++ tests/qtest/migration/s390x/a-b-bios.h | 279 +++++++++++ 62 files changed, 3372 insertions(+), 3371 deletions(-) create mode 100755 tests/migration-stress/guestperf-batch.py create mode 100755 tests/migration-stress/guestperf-plot.py create mode 100755 tests/migration-stress/guestperf.py create mode 100644 tests/migration-stress/guestperf/__init__.py create mode 100644 tests/migration-stress/guestperf/comparison.py create mode 100644 tests/migration-stress/guestperf/engine.py create mode 100644 tests/migration-stress/guestperf/hardware.py create mode 100644 tests/migration-stress/guestperf/plot.py create mode 100644 tests/migration-stress/guestperf/progress.py create mode 100644 tests/migration-stress/guestperf/report.py create mode 100644 tests/migration-stress/guestperf/scenario.py create mode 100644 tests/migration-stress/guestperf/shell.py create mode 100644 tests/migration-stress/guestperf/timings.py create mode 100755 tests/migration-stress/initrd-stress.sh create mode 100644 tests/migration-stress/meson.build create mode 100644 tests/migration-stress/stress.c delete mode 100644 tests/migration/Makefile delete mode 100644 tests/migration/aarch64/Makefile delete mode 100644 tests/migration/aarch64/a-b-kernel.S delete mode 100644 tests/migration/aarch64/a-b-kernel.h delete mode 100755 tests/migration/guestperf-batch.py delete mode 100755 tests/migration/guestperf-plot.py delete mode 100755 tests/migration/guestperf.py delete mode 100644 tests/migration/guestperf/__init__.py delete mode 100644 tests/migration/guestperf/comparison.py delete mode 100644 tests/migration/guestperf/engine.py delete mode 100644 tests/migration/guestperf/hardware.py delete mode 100644 tests/migration/guestperf/plot.py delete mode 100644 tests/migration/guestperf/progress.py delete mode 100644 tests/migration/guestperf/report.py delete mode 100644 tests/migration/guestperf/scenario.py delete mode 100644 tests/migration/guestperf/shell.py delete mode 100644 tests/migration/guestperf/timings.py delete mode 100644 tests/migration/i386/Makefile delete mode 100644 tests/migration/i386/a-b-bootblock.S delete mode 100644 tests/migration/i386/a-b-bootblock.h delete mode 100755 tests/migration/initrd-stress.sh delete mode 100644 tests/migration/meson.build delete mode 100644 tests/migration/migration-test.h delete mode 100644 tests/migration/ppc64/Makefile delete mode 100644 tests/migration/ppc64/a-b-kernel.S delete mode 100644 tests/migration/ppc64/a-b-kernel.h delete mode 100644 tests/migration/s390x/Makefile delete mode 100644 tests/migration/s390x/a-b-bios.c delete mode 100644 tests/migration/s390x/a-b-bios.h delete mode 100644 tests/migration/stress.c create mode 100644 tests/qtest/migration/Makefile create mode 100644 tests/qtest/migration/aarch64/Makefile create mode 100644 tests/qtest/migration/aarch64/a-b-kernel.S create mode 100644 tests/qtest/migration/aarch64/a-b-kernel.h create mode 100644 tests/qtest/migration/i386/Makefile create mode 100644 tests/qtest/migration/i386/a-b-bootblock.S create mode 100644 tests/qtest/migration/i386/a-b-bootblock.h create mode 100644 tests/qtest/migration/migration-test.h create mode 100644 tests/qtest/migration/ppc64/Makefile create mode 100644 tests/qtest/migration/ppc64/a-b-kernel.S create mode 100644 tests/qtest/migration/ppc64/a-b-kernel.h create mode 100644 tests/qtest/migration/s390x/Makefile create mode 100644 tests/qtest/migration/s390x/a-b-bios.c create mode 100644 tests/qtest/migration/s390x/a-b-bios.h (limited to 'tests') diff --git a/tests/meson.build b/tests/meson.build index 907a4c1..f96c1be 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -84,5 +84,5 @@ endif subdir('unit') subdir('qapi-schema') subdir('qtest') -subdir('migration') +subdir('migration-stress') subdir('functional') diff --git a/tests/migration-stress/guestperf-batch.py b/tests/migration-stress/guestperf-batch.py new file mode 100755 index 0000000..9485eef --- /dev/null +++ b/tests/migration-stress/guestperf-batch.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Migration test batch comparison invocation +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + +import sys + +from guestperf.shell import BatchShell + +shell = BatchShell() +sys.exit(shell.run(sys.argv[1:])) diff --git a/tests/migration-stress/guestperf-plot.py b/tests/migration-stress/guestperf-plot.py new file mode 100755 index 0000000..32977b4 --- /dev/null +++ b/tests/migration-stress/guestperf-plot.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Migration test graph plotting command +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + +import sys + +from guestperf.shell import PlotShell + +shell = PlotShell() +sys.exit(shell.run(sys.argv[1:])) diff --git a/tests/migration-stress/guestperf.py b/tests/migration-stress/guestperf.py new file mode 100755 index 0000000..07182f2 --- /dev/null +++ b/tests/migration-stress/guestperf.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Migration test direct invocation command +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + + +import sys + +from guestperf.shell import Shell + +shell = Shell() +sys.exit(shell.run(sys.argv[1:])) diff --git a/tests/migration-stress/guestperf/__init__.py b/tests/migration-stress/guestperf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/migration-stress/guestperf/comparison.py b/tests/migration-stress/guestperf/comparison.py new file mode 100644 index 0000000..42cc037 --- /dev/null +++ b/tests/migration-stress/guestperf/comparison.py @@ -0,0 +1,161 @@ +# +# Migration test scenario comparison mapping +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + +from guestperf.scenario import Scenario + +class Comparison(object): + def __init__(self, name, scenarios): + self._name = name + self._scenarios = scenarios + +COMPARISONS = [ + # Looking at effect of pausing guest during migration + # at various stages of iteration over RAM + Comparison("pause-iters", scenarios = [ + Scenario("pause-iters-0", + pause=True, pause_iters=0), + Scenario("pause-iters-1", + pause=True, pause_iters=1), + Scenario("pause-iters-5", + pause=True, pause_iters=5), + Scenario("pause-iters-20", + pause=True, pause_iters=20), + ]), + + + # Looking at use of post-copy in relation to bandwidth + # available for migration + Comparison("post-copy-bandwidth", scenarios = [ + Scenario("post-copy-bw-100mbs", + post_copy=True, bandwidth=12), + Scenario("post-copy-bw-300mbs", + post_copy=True, bandwidth=37), + Scenario("post-copy-bw-1gbs", + post_copy=True, bandwidth=125), + Scenario("post-copy-bw-10gbs", + post_copy=True, bandwidth=1250), + Scenario("post-copy-bw-100gbs", + post_copy=True, bandwidth=12500), + ]), + + + # Looking at effect of starting post-copy at different + # stages of the migration + Comparison("post-copy-iters", scenarios = [ + Scenario("post-copy-iters-0", + post_copy=True, post_copy_iters=0), + Scenario("post-copy-iters-1", + post_copy=True, post_copy_iters=1), + Scenario("post-copy-iters-5", + post_copy=True, post_copy_iters=5), + Scenario("post-copy-iters-20", + post_copy=True, post_copy_iters=20), + ]), + + + # Looking at effect of auto-converge with different + # throttling percentage step rates + Comparison("auto-converge-iters", scenarios = [ + Scenario("auto-converge-step-5", + auto_converge=True, auto_converge_step=5), + Scenario("auto-converge-step-10", + auto_converge=True, auto_converge_step=10), + Scenario("auto-converge-step-20", + auto_converge=True, auto_converge_step=20), + ]), + + + # Looking at use of auto-converge in relation to bandwidth + # available for migration + Comparison("auto-converge-bandwidth", scenarios = [ + Scenario("auto-converge-bw-100mbs", + auto_converge=True, bandwidth=12), + Scenario("auto-converge-bw-300mbs", + auto_converge=True, bandwidth=37), + Scenario("auto-converge-bw-1gbs", + auto_converge=True, bandwidth=125), + Scenario("auto-converge-bw-10gbs", + auto_converge=True, bandwidth=1250), + Scenario("auto-converge-bw-100gbs", + auto_converge=True, bandwidth=12500), + ]), + + + # Looking at effect of multi-thread compression with + # varying numbers of threads + Comparison("compr-mt", scenarios = [ + Scenario("compr-mt-threads-1", + compression_mt=True, compression_mt_threads=1), + Scenario("compr-mt-threads-2", + compression_mt=True, compression_mt_threads=2), + Scenario("compr-mt-threads-4", + compression_mt=True, compression_mt_threads=4), + ]), + + + # Looking at effect of xbzrle compression with varying + # cache sizes + Comparison("compr-xbzrle", scenarios = [ + Scenario("compr-xbzrle-cache-5", + compression_xbzrle=True, compression_xbzrle_cache=5), + Scenario("compr-xbzrle-cache-10", + compression_xbzrle=True, compression_xbzrle_cache=10), + Scenario("compr-xbzrle-cache-20", + compression_xbzrle=True, compression_xbzrle_cache=10), + Scenario("compr-xbzrle-cache-50", + compression_xbzrle=True, compression_xbzrle_cache=50), + ]), + + + # Looking at effect of multifd with + # varying numbers of channels + Comparison("compr-multifd", scenarios = [ + Scenario("compr-multifd-channels-4", + multifd=True, multifd_channels=2), + Scenario("compr-multifd-channels-8", + multifd=True, multifd_channels=8), + Scenario("compr-multifd-channels-32", + multifd=True, multifd_channels=32), + Scenario("compr-multifd-channels-64", + multifd=True, multifd_channels=64), + ]), + + # Looking at effect of dirty-limit with + # varying x_vcpu_dirty_limit_period + Comparison("compr-dirty-limit-period", scenarios = [ + Scenario("compr-dirty-limit-period-500", + dirty_limit=True, x_vcpu_dirty_limit_period=500), + Scenario("compr-dirty-limit-period-800", + dirty_limit=True, x_vcpu_dirty_limit_period=800), + Scenario("compr-dirty-limit-period-1000", + dirty_limit=True, x_vcpu_dirty_limit_period=1000), + ]), + + + # Looking at effect of dirty-limit with + # varying vcpu_dirty_limit + Comparison("compr-dirty-limit", scenarios = [ + Scenario("compr-dirty-limit-10MB", + dirty_limit=True, vcpu_dirty_limit=10), + Scenario("compr-dirty-limit-20MB", + dirty_limit=True, vcpu_dirty_limit=20), + Scenario("compr-dirty-limit-50MB", + dirty_limit=True, vcpu_dirty_limit=50), + ]), +] diff --git a/tests/migration-stress/guestperf/engine.py b/tests/migration-stress/guestperf/engine.py new file mode 100644 index 0000000..608d727 --- /dev/null +++ b/tests/migration-stress/guestperf/engine.py @@ -0,0 +1,505 @@ +# +# Migration test main engine +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + + +import os +import re +import sys +import time + +from guestperf.progress import Progress, ProgressStats +from guestperf.report import Report +from guestperf.timings import TimingRecord, Timings + +sys.path.append(os.path.join(os.path.dirname(__file__), + '..', '..', '..', 'python')) +from qemu.machine import QEMUMachine + + +class Engine(object): + + def __init__(self, binary, dst_host, kernel, initrd, transport="tcp", + sleep=15, verbose=False, debug=False): + + self._binary = binary # Path to QEMU binary + self._dst_host = dst_host # Hostname of target host + self._kernel = kernel # Path to kernel image + self._initrd = initrd # Path to stress initrd + self._transport = transport # 'unix' or 'tcp' or 'rdma' + self._sleep = sleep + self._verbose = verbose + self._debug = debug + + if debug: + self._verbose = debug + + def _vcpu_timing(self, pid, tid_list): + records = [] + now = time.time() + + jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) + for tid in tid_list: + statfile = "/proc/%d/task/%d/stat" % (pid, tid) + with open(statfile, "r") as fh: + stat = fh.readline() + fields = stat.split(" ") + stime = int(fields[13]) + utime = int(fields[14]) + records.append(TimingRecord(tid, now, 1000 * (stime + utime) / jiffies_per_sec)) + return records + + def _cpu_timing(self, pid): + now = time.time() + + jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) + statfile = "/proc/%d/stat" % pid + with open(statfile, "r") as fh: + stat = fh.readline() + fields = stat.split(" ") + stime = int(fields[13]) + utime = int(fields[14]) + return TimingRecord(pid, now, 1000 * (stime + utime) / jiffies_per_sec) + + def _migrate_progress(self, vm): + info = vm.cmd("query-migrate") + + if "ram" not in info: + info["ram"] = {} + + return Progress( + info.get("status", "active"), + ProgressStats( + info["ram"].get("transferred", 0), + info["ram"].get("remaining", 0), + info["ram"].get("total", 0), + info["ram"].get("duplicate", 0), + info["ram"].get("skipped", 0), + info["ram"].get("normal", 0), + info["ram"].get("normal-bytes", 0), + info["ram"].get("dirty-pages-rate", 0), + info["ram"].get("mbps", 0), + info["ram"].get("dirty-sync-count", 0) + ), + time.time(), + info.get("total-time", 0), + info.get("downtime", 0), + info.get("expected-downtime", 0), + info.get("setup-time", 0), + info.get("cpu-throttle-percentage", 0), + info.get("dirty-limit-throttle-time-per-round", 0), + info.get("dirty-limit-ring-full-time", 0), + ) + + def _migrate(self, hardware, scenario, src, dst, connect_uri): + src_qemu_time = [] + src_vcpu_time = [] + src_pid = src.get_pid() + + vcpus = src.cmd("query-cpus-fast") + src_threads = [] + for vcpu in vcpus: + src_threads.append(vcpu["thread-id"]) + + # XXX how to get dst timings on remote host ? + + if self._verbose: + print("Sleeping %d seconds for initial guest workload run" % self._sleep) + sleep_secs = self._sleep + while sleep_secs > 1: + src_qemu_time.append(self._cpu_timing(src_pid)) + src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) + time.sleep(1) + sleep_secs -= 1 + + if self._verbose: + print("Starting migration") + if scenario._auto_converge: + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "auto-converge", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + cpu_throttle_increment=scenario._auto_converge_step) + + if scenario._post_copy: + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "postcopy-ram", + "state": True } + ]) + resp = dst.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "postcopy-ram", + "state": True } + ]) + + resp = src.cmd("migrate-set-parameters", + max_bandwidth=scenario._bandwidth * 1024 * 1024) + + resp = src.cmd("migrate-set-parameters", + downtime_limit=scenario._downtime) + + if scenario._compression_mt: + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "compress", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + compress_threads=scenario._compression_mt_threads) + resp = dst.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "compress", + "state": True } + ]) + resp = dst.cmd("migrate-set-parameters", + decompress_threads=scenario._compression_mt_threads) + + if scenario._compression_xbzrle: + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "xbzrle", + "state": True } + ]) + resp = dst.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "xbzrle", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + xbzrle_cache_size=( + hardware._mem * + 1024 * 1024 * 1024 / 100 * + scenario._compression_xbzrle_cache)) + + if scenario._multifd: + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "multifd", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + multifd_channels=scenario._multifd_channels) + resp = dst.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "multifd", + "state": True } + ]) + resp = dst.cmd("migrate-set-parameters", + multifd_channels=scenario._multifd_channels) + + if scenario._dirty_limit: + if not hardware._dirty_ring_size: + raise Exception("dirty ring size must be configured when " + "testing dirty limit migration") + + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "dirty-limit", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + x_vcpu_dirty_limit_period=scenario._x_vcpu_dirty_limit_period) + resp = src.cmd("migrate-set-parameters", + vcpu_dirty_limit=scenario._vcpu_dirty_limit) + + resp = src.cmd("migrate", uri=connect_uri) + + post_copy = False + paused = False + + progress_history = [] + + start = time.time() + loop = 0 + while True: + loop = loop + 1 + time.sleep(0.05) + + progress = self._migrate_progress(src) + if (loop % 20) == 0: + src_qemu_time.append(self._cpu_timing(src_pid)) + src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) + + if (len(progress_history) == 0 or + (progress_history[-1]._ram._iterations < + progress._ram._iterations)): + progress_history.append(progress) + + if progress._status in ("completed", "failed", "cancelled"): + if progress._status == "completed" and paused: + dst.cmd("cont") + if progress_history[-1] != progress: + progress_history.append(progress) + + if progress._status == "completed": + if self._verbose: + print("Sleeping %d seconds for final guest workload run" % self._sleep) + sleep_secs = self._sleep + while sleep_secs > 1: + time.sleep(1) + src_qemu_time.append(self._cpu_timing(src_pid)) + src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) + sleep_secs -= 1 + + return [progress_history, src_qemu_time, src_vcpu_time] + + if self._verbose and (loop % 20) == 0: + print("Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( + progress._ram._iterations, + progress._ram._remaining_bytes / (1024 * 1024), + progress._ram._total_bytes / (1024 * 1024), + progress._ram._transferred_bytes / (1024 * 1024), + progress._ram._transfer_rate_mbs, + )) + + if progress._ram._iterations > scenario._max_iters: + if self._verbose: + print("No completion after %d iterations over RAM" % scenario._max_iters) + src.cmd("migrate_cancel") + continue + + if time.time() > (start + scenario._max_time): + if self._verbose: + print("No completion after %d seconds" % scenario._max_time) + src.cmd("migrate_cancel") + continue + + if (scenario._post_copy and + progress._ram._iterations >= scenario._post_copy_iters and + not post_copy): + if self._verbose: + print("Switching to post-copy after %d iterations" % scenario._post_copy_iters) + resp = src.cmd("migrate-start-postcopy") + post_copy = True + + if (scenario._pause and + progress._ram._iterations >= scenario._pause_iters and + not paused): + if self._verbose: + print("Pausing VM after %d iterations" % scenario._pause_iters) + resp = src.cmd("stop") + paused = True + + def _is_ppc64le(self): + _, _, _, _, machine = os.uname() + if machine == "ppc64le": + return True + return False + + def _get_guest_console_args(self): + if self._is_ppc64le(): + return "console=hvc0" + else: + return "console=ttyS0" + + def _get_qemu_serial_args(self): + if self._is_ppc64le(): + return ["-chardev", "stdio,id=cdev0", + "-device", "spapr-vty,chardev=cdev0"] + else: + return ["-chardev", "stdio,id=cdev0", + "-device", "isa-serial,chardev=cdev0"] + + def _get_common_args(self, hardware, tunnelled=False): + args = [ + "noapic", + "edd=off", + "printk.time=1", + "noreplace-smp", + "cgroup_disable=memory", + "pci=noearly", + ] + + args.append(self._get_guest_console_args()) + + if self._debug: + args.append("debug") + else: + args.append("quiet") + + args.append("ramsize=%s" % hardware._mem) + + cmdline = " ".join(args) + if tunnelled: + cmdline = "'" + cmdline + "'" + + argv = [ + "-cpu", "host", + "-kernel", self._kernel, + "-initrd", self._initrd, + "-append", cmdline, + "-m", str((hardware._mem * 1024) + 512), + "-smp", str(hardware._cpus), + ] + if hardware._dirty_ring_size: + argv.extend(["-accel", "kvm,dirty-ring-size=%s" % + hardware._dirty_ring_size]) + else: + argv.extend(["-accel", "kvm"]) + + argv.extend(self._get_qemu_serial_args()) + + if self._debug: + argv.extend(["-machine", "graphics=off"]) + + if hardware._prealloc_pages: + argv_source += ["-mem-path", "/dev/shm", + "-mem-prealloc"] + if hardware._locked_pages: + argv_source += ["-overcommit", "mem-lock=on"] + if hardware._huge_pages: + pass + + return argv + + def _get_src_args(self, hardware): + return self._get_common_args(hardware) + + def _get_dst_args(self, hardware, uri): + tunnelled = False + if self._dst_host != "localhost": + tunnelled = True + argv = self._get_common_args(hardware, tunnelled) + return argv + ["-incoming", uri] + + @staticmethod + def _get_common_wrapper(cpu_bind, mem_bind): + wrapper = [] + if len(cpu_bind) > 0 or len(mem_bind) > 0: + wrapper.append("numactl") + if cpu_bind: + wrapper.append("--physcpubind=%s" % ",".join(cpu_bind)) + if mem_bind: + wrapper.append("--membind=%s" % ",".join(mem_bind)) + + return wrapper + + def _get_src_wrapper(self, hardware): + return self._get_common_wrapper(hardware._src_cpu_bind, hardware._src_mem_bind) + + def _get_dst_wrapper(self, hardware): + wrapper = self._get_common_wrapper(hardware._dst_cpu_bind, hardware._dst_mem_bind) + if self._dst_host != "localhost": + return ["ssh", + "-R", "9001:localhost:9001", + self._dst_host] + wrapper + else: + return wrapper + + def _get_timings(self, vm): + log = vm.get_log() + if not log: + return [] + if self._debug: + print(log) + + regex = r"[^\s]+\s\((\d+)\):\sINFO:\s(\d+)ms\scopied\s\d+\sGB\sin\s(\d+)ms" + matcher = re.compile(regex) + records = [] + for line in log.split("\n"): + match = matcher.match(line) + if match: + records.append(TimingRecord(int(match.group(1)), + int(match.group(2)) / 1000.0, + int(match.group(3)))) + return records + + def run(self, hardware, scenario, result_dir=os.getcwd()): + abs_result_dir = os.path.join(result_dir, scenario._name) + + if self._transport == "tcp": + uri = "tcp:%s:9000" % self._dst_host + elif self._transport == "rdma": + uri = "rdma:%s:9000" % self._dst_host + elif self._transport == "unix": + if self._dst_host != "localhost": + raise Exception("Running use unix migration transport for non-local host") + uri = "unix:/var/tmp/qemu-migrate-%d.migrate" % os.getpid() + try: + os.remove(uri[5:]) + os.remove(monaddr) + except: + pass + + if self._dst_host != "localhost": + dstmonaddr = ("localhost", 9001) + else: + dstmonaddr = "/var/tmp/qemu-dst-%d-monitor.sock" % os.getpid() + srcmonaddr = "/var/tmp/qemu-src-%d-monitor.sock" % os.getpid() + + src = QEMUMachine(self._binary, + args=self._get_src_args(hardware), + wrapper=self._get_src_wrapper(hardware), + name="qemu-src-%d" % os.getpid(), + monitor_address=srcmonaddr) + + dst = QEMUMachine(self._binary, + args=self._get_dst_args(hardware, uri), + wrapper=self._get_dst_wrapper(hardware), + name="qemu-dst-%d" % os.getpid(), + monitor_address=dstmonaddr) + + try: + src.launch() + dst.launch() + + ret = self._migrate(hardware, scenario, src, dst, uri) + progress_history = ret[0] + qemu_timings = ret[1] + vcpu_timings = ret[2] + if uri[0:5] == "unix:" and os.path.exists(uri[5:]): + os.remove(uri[5:]) + + if os.path.exists(srcmonaddr): + os.remove(srcmonaddr) + + if self._dst_host == "localhost" and os.path.exists(dstmonaddr): + os.remove(dstmonaddr) + + if self._verbose: + print("Finished migration") + + src.shutdown() + dst.shutdown() + + return Report(hardware, scenario, progress_history, + Timings(self._get_timings(src) + self._get_timings(dst)), + Timings(qemu_timings), + Timings(vcpu_timings), + self._binary, self._dst_host, self._kernel, + self._initrd, self._transport, self._sleep) + except Exception as e: + if self._debug: + print("Failed: %s" % str(e)) + try: + src.shutdown() + except: + pass + try: + dst.shutdown() + except: + pass + + if self._debug: + print(src.get_log()) + print(dst.get_log()) + raise + diff --git a/tests/migration-stress/guestperf/hardware.py b/tests/migration-stress/guestperf/hardware.py new file mode 100644 index 0000000..f779cc0 --- /dev/null +++ b/tests/migration-stress/guestperf/hardware.py @@ -0,0 +1,66 @@ +# +# Migration test hardware configuration description +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + + +class Hardware(object): + def __init__(self, cpus=1, mem=1, + src_cpu_bind=None, src_mem_bind=None, + dst_cpu_bind=None, dst_mem_bind=None, + prealloc_pages = False, + huge_pages=False, locked_pages=False, + dirty_ring_size=0): + self._cpus = cpus + self._mem = mem # GiB + self._src_mem_bind = src_mem_bind # List of NUMA nodes + self._src_cpu_bind = src_cpu_bind # List of pCPUs + self._dst_mem_bind = dst_mem_bind # List of NUMA nodes + self._dst_cpu_bind = dst_cpu_bind # List of pCPUs + self._prealloc_pages = prealloc_pages + self._huge_pages = huge_pages + self._locked_pages = locked_pages + self._dirty_ring_size = dirty_ring_size + + + def serialize(self): + return { + "cpus": self._cpus, + "mem": self._mem, + "src_mem_bind": self._src_mem_bind, + "dst_mem_bind": self._dst_mem_bind, + "src_cpu_bind": self._src_cpu_bind, + "dst_cpu_bind": self._dst_cpu_bind, + "prealloc_pages": self._prealloc_pages, + "huge_pages": self._huge_pages, + "locked_pages": self._locked_pages, + "dirty_ring_size": self._dirty_ring_size, + } + + @classmethod + def deserialize(cls, data): + return cls( + data["cpus"], + data["mem"], + data["src_cpu_bind"], + data["src_mem_bind"], + data["dst_cpu_bind"], + data["dst_mem_bind"], + data["prealloc_pages"], + data["huge_pages"], + data["locked_pages"], + data["dirty_ring_size"]) diff --git a/tests/migration-stress/guestperf/plot.py b/tests/migration-stress/guestperf/plot.py new file mode 100644 index 0000000..30b3f66 --- /dev/null +++ b/tests/migration-stress/guestperf/plot.py @@ -0,0 +1,623 @@ +# +# Migration test graph plotting +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + +import sys + + +class Plot(object): + + # Generated using + # http://tools.medialab.sciences-po.fr/iwanthue/ + COLORS = ["#CD54D0", + "#79D94C", + "#7470CD", + "#D2D251", + "#863D79", + "#76DDA6", + "#D4467B", + "#61923D", + "#CB9CCA", + "#D98F36", + "#8CC8DA", + "#CE4831", + "#5E7693", + "#9B803F", + "#412F4C", + "#CECBA6", + "#6D3229", + "#598B73", + "#C8827C", + "#394427"] + + def __init__(self, + reports, + migration_iters, + total_guest_cpu, + split_guest_cpu, + qemu_cpu, + vcpu_cpu): + + self._reports = reports + self._migration_iters = migration_iters + self._total_guest_cpu = total_guest_cpu + self._split_guest_cpu = split_guest_cpu + self._qemu_cpu = qemu_cpu + self._vcpu_cpu = vcpu_cpu + self._color_idx = 0 + + def _next_color(self): + color = self.COLORS[self._color_idx] + self._color_idx += 1 + if self._color_idx >= len(self.COLORS): + self._color_idx = 0 + return color + + def _get_progress_label(self, progress): + if progress: + return "\n\n" + "\n".join( + ["Status: %s" % progress._status, + "Iteration: %d" % progress._ram._iterations, + "Throttle: %02d%%" % progress._throttle_pcent, + "Dirty rate: %dMB/s" % (progress._ram._dirty_rate_pps * 4 / 1024.0)]) + else: + return "\n\n" + "\n".join( + ["Status: %s" % "none", + "Iteration: %d" % 0]) + + def _find_start_time(self, report): + startqemu = report._qemu_timings._records[0]._timestamp + startguest = report._guest_timings._records[0]._timestamp + if startqemu < startguest: + return startqemu + else: + return stasrtguest + + def _get_guest_max_value(self, report): + maxvalue = 0 + for record in report._guest_timings._records: + if record._value > maxvalue: + maxvalue = record._value + return maxvalue + + def _get_qemu_max_value(self, report): + maxvalue = 0 + oldvalue = None + oldtime = None + for record in report._qemu_timings._records: + if oldvalue is not None: + cpudelta = (record._value - oldvalue) / 1000.0 + timedelta = record._timestamp - oldtime + if timedelta == 0: + continue + util = cpudelta / timedelta * 100.0 + else: + util = 0 + oldvalue = record._value + oldtime = record._timestamp + + if util > maxvalue: + maxvalue = util + return maxvalue + + def _get_total_guest_cpu_graph(self, report, starttime): + xaxis = [] + yaxis = [] + labels = [] + progress_idx = -1 + for record in report._guest_timings._records: + while ((progress_idx + 1) < len(report._progress_history) and + report._progress_history[progress_idx + 1]._now < record._timestamp): + progress_idx = progress_idx + 1 + + if progress_idx >= 0: + progress = report._progress_history[progress_idx] + else: + progress = None + + xaxis.append(record._timestamp - starttime) + yaxis.append(record._value) + labels.append(self._get_progress_label(progress)) + + from plotly import graph_objs as go + return go.Scatter(x=xaxis, + y=yaxis, + name="Guest PIDs: %s" % report._scenario._name, + mode='lines', + line={ + "dash": "solid", + "color": self._next_color(), + "shape": "linear", + "width": 1 + }, + text=labels) + + def _get_split_guest_cpu_graphs(self, report, starttime): + threads = {} + for record in report._guest_timings._records: + if record._tid in threads: + continue + threads[record._tid] = { + "xaxis": [], + "yaxis": [], + "labels": [], + } + + progress_idx = -1 + for record in report._guest_timings._records: + while ((progress_idx + 1) < len(report._progress_history) and + report._progress_history[progress_idx + 1]._now < record._timestamp): + progress_idx = progress_idx + 1 + + if progress_idx >= 0: + progress = report._progress_history[progress_idx] + else: + progress = None + + threads[record._tid]["xaxis"].append(record._timestamp - starttime) + threads[record._tid]["yaxis"].append(record._value) + threads[record._tid]["labels"].append(self._get_progress_label(progress)) + + + graphs = [] + from plotly import graph_objs as go + for tid in threads.keys(): + graphs.append( + go.Scatter(x=threads[tid]["xaxis"], + y=threads[tid]["yaxis"], + name="PID %s: %s" % (tid, report._scenario._name), + mode="lines", + line={ + "dash": "solid", + "color": self._next_color(), + "shape": "linear", + "width": 1 + }, + text=threads[tid]["labels"])) + return graphs + + def _get_migration_iters_graph(self, report, starttime): + xaxis = [] + yaxis = [] + labels = [] + for progress in report._progress_history: + xaxis.append(progress._now - starttime) + yaxis.append(0) + labels.append(self._get_progress_label(progress)) + + from plotly import graph_objs as go + return go.Scatter(x=xaxis, + y=yaxis, + text=labels, + name="Migration iterations", + mode="markers", + marker={ + "color": self._next_color(), + "symbol": "star", + "size": 5 + }) + + def _get_qemu_cpu_graph(self, report, starttime): + xaxis = [] + yaxis = [] + labels = [] + progress_idx = -1 + + first = report._qemu_timings._records[0] + abstimestamps = [first._timestamp] + absvalues = [first._value] + + for record in report._qemu_timings._records[1:]: + while ((progress_idx + 1) < len(report._progress_history) and + report._progress_history[progress_idx + 1]._now < record._timestamp): + progress_idx = progress_idx + 1 + + if progress_idx >= 0: + progress = report._progress_history[progress_idx] + else: + progress = None + + oldvalue = absvalues[-1] + oldtime = abstimestamps[-1] + + cpudelta = (record._value - oldvalue) / 1000.0 + timedelta = record._timestamp - oldtime + if timedelta == 0: + continue + util = cpudelta / timedelta * 100.0 + + abstimestamps.append(record._timestamp) + absvalues.append(record._value) + + xaxis.append(record._timestamp - starttime) + yaxis.append(util) + labels.append(self._get_progress_label(progress)) + + from plotly import graph_objs as go + return go.Scatter(x=xaxis, + y=yaxis, + yaxis="y2", + name="QEMU: %s" % report._scenario._name, + mode='lines', + line={ + "dash": "solid", + "color": self._next_color(), + "shape": "linear", + "width": 1 + }, + text=labels) + + def _get_vcpu_cpu_graphs(self, report, starttime): + threads = {} + for record in report._vcpu_timings._records: + if record._tid in threads: + continue + threads[record._tid] = { + "xaxis": [], + "yaxis": [], + "labels": [], + "absvalue": [record._value], + "abstime": [record._timestamp], + } + + progress_idx = -1 + for record in report._vcpu_timings._records: + while ((progress_idx + 1) < len(report._progress_history) and + report._progress_history[progress_idx + 1]._now < record._timestamp): + progress_idx = progress_idx + 1 + + if progress_idx >= 0: + progress = report._progress_history[progress_idx] + else: + progress = None + + oldvalue = threads[record._tid]["absvalue"][-1] + oldtime = threads[record._tid]["abstime"][-1] + + cpudelta = (record._value - oldvalue) / 1000.0 + timedelta = record._timestamp - oldtime + if timedelta == 0: + continue + util = cpudelta / timedelta * 100.0 + if util > 100: + util = 100 + + threads[record._tid]["absvalue"].append(record._value) + threads[record._tid]["abstime"].append(record._timestamp) + + threads[record._tid]["xaxis"].append(record._timestamp - starttime) + threads[record._tid]["yaxis"].append(util) + threads[record._tid]["labels"].append(self._get_progress_label(progress)) + + + graphs = [] + from plotly import graph_objs as go + for tid in threads.keys(): + graphs.append( + go.Scatter(x=threads[tid]["xaxis"], + y=threads[tid]["yaxis"], + yaxis="y2", + name="VCPU %s: %s" % (tid, report._scenario._name), + mode="lines", + line={ + "dash": "solid", + "color": self._next_color(), + "shape": "linear", + "width": 1 + }, + text=threads[tid]["labels"])) + return graphs + + def _generate_chart_report(self, report): + graphs = [] + starttime = self._find_start_time(report) + if self._total_guest_cpu: + graphs.append(self._get_total_guest_cpu_graph(report, starttime)) + if self._split_guest_cpu: + graphs.extend(self._get_split_guest_cpu_graphs(report, starttime)) + if self._qemu_cpu: + graphs.append(self._get_qemu_cpu_graph(report, starttime)) + if self._vcpu_cpu: + graphs.extend(self._get_vcpu_cpu_graphs(report, starttime)) + if self._migration_iters: + graphs.append(self._get_migration_iters_graph(report, starttime)) + return graphs + + def _generate_annotation(self, starttime, progress): + return { + "text": progress._status, + "x": progress._now - starttime, + "y": 10, + } + + def _generate_annotations(self, report): + starttime = self._find_start_time(report) + annotations = {} + started = False + for progress in report._progress_history: + if progress._status == "setup": + continue + if progress._status not in annotations: + annotations[progress._status] = self._generate_annotation(starttime, progress) + + return annotations.values() + + def _generate_chart(self): + from plotly.offline import plot + from plotly import graph_objs as go + + graphs = [] + yaxismax = 0 + yaxismax2 = 0 + for report in self._reports: + graphs.extend(self._generate_chart_report(report)) + + maxvalue = self._get_guest_max_value(report) + if maxvalue > yaxismax: + yaxismax = maxvalue + + maxvalue = self._get_qemu_max_value(report) + if maxvalue > yaxismax2: + yaxismax2 = maxvalue + + yaxismax += 100 + if not self._qemu_cpu: + yaxismax2 = 110 + yaxismax2 += 10 + + annotations = [] + if self._migration_iters: + for report in self._reports: + annotations.extend(self._generate_annotations(report)) + + layout = go.Layout(title="Migration comparison", + xaxis={ + "title": "Wallclock time (secs)", + "showgrid": False, + }, + yaxis={ + "title": "Memory update speed (ms/GB)", + "showgrid": False, + "range": [0, yaxismax], + }, + yaxis2={ + "title": "Hostutilization (%)", + "overlaying": "y", + "side": "right", + "range": [0, yaxismax2], + "showgrid": False, + }, + annotations=annotations) + + figure = go.Figure(data=graphs, layout=layout) + + return plot(figure, + show_link=False, + include_plotlyjs=False, + output_type="div") + + + def _generate_report(self): + pieces = [] + for report in self._reports: + pieces.append(""" +

Report %s

+ +""" % report._scenario._name) + + pieces.append(""" + + + + + + + + + + + + + + + + + + + + + + + +""" % (report._binary, report._kernel, + report._initrd, report._transport, report._dst_host)) + + hardware = report._hardware + pieces.append(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" % (hardware._cpus, hardware._mem, + ",".join(hardware._src_cpu_bind), + ",".join(hardware._src_mem_bind), + ",".join(hardware._dst_cpu_bind), + ",".join(hardware._dst_mem_bind), + "yes" if hardware._prealloc_pages else "no", + "yes" if hardware._locked_pages else "no", + "yes" if hardware._huge_pages else "no")) + + scenario = report._scenario + pieces.append(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" % (scenario._downtime, scenario._bandwidth, + scenario._max_iters, scenario._max_time, + "yes" if scenario._pause else "no", scenario._pause_iters, + "yes" if scenario._post_copy else "no", scenario._post_copy_iters, + "yes" if scenario._auto_converge else "no", scenario._auto_converge_step, + "yes" if scenario._compression_mt else "no", scenario._compression_mt_threads, + "yes" if scenario._compression_xbzrle else "no", scenario._compression_xbzrle_cache)) + + pieces.append(""" +
Test config
Emulator:%s
Kernel:%s
Ramdisk:%s
Transport:%s
Host:%s
Hardware config
CPUs:%d
RAM:%d GB
Source CPU bind:%s
Source RAM bind:%s
Dest CPU bind:%s
Dest RAM bind:%s
Preallocate RAM:%s
Locked RAM:%s
Huge pages:%s
Scenario config
Max downtime:%d milli-sec
Max bandwidth:%d MB/sec
Max iters:%d
Max time:%d secs
Pause:%s
Pause iters:%d
Post-copy:%s
Post-copy iters:%d
Auto-converge:%s
Auto-converge iters:%d
MT compression:%s
MT compression threads:%d
XBZRLE compression:%s
XBZRLE compression cache:%d%% of RAM
+""") + + return "\n".join(pieces) + + def _generate_style(self): + return """ +#report table tr th { + text-align: right; +} +#report table tr td { + text-align: left; +} +#report table tr.subhead th { + background: rgb(192, 192, 192); + text-align: center; +} + +""" + + def generate_html(self, fh): + print(""" + + + + Migration report + + +

Migration report

+

Chart summary

+
+""" % self._generate_style(), file=fh) + print(self._generate_chart(), file=fh) + print(""" +
+

Report details

+
+""", file=fh) + print(self._generate_report(), file=fh) + print(""" +
+ + +""", file=fh) + + def generate(self, filename): + if filename is None: + self.generate_html(sys.stdout) + else: + with open(filename, "w") as fh: + self.generate_html(fh) diff --git a/tests/migration-stress/guestperf/progress.py b/tests/migration-stress/guestperf/progress.py new file mode 100644 index 0000000..d490584 --- /dev/null +++ b/tests/migration-stress/guestperf/progress.py @@ -0,0 +1,129 @@ +# +# Migration test migration operation progress +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + + +class ProgressStats(object): + + def __init__(self, + transferred_bytes, + remaining_bytes, + total_bytes, + duplicate_pages, + skipped_pages, + normal_pages, + normal_bytes, + dirty_rate_pps, + transfer_rate_mbs, + iterations): + self._transferred_bytes = transferred_bytes + self._remaining_bytes = remaining_bytes + self._total_bytes = total_bytes + self._duplicate_pages = duplicate_pages + self._skipped_pages = skipped_pages + self._normal_pages = normal_pages + self._normal_bytes = normal_bytes + self._dirty_rate_pps = dirty_rate_pps + self._transfer_rate_mbs = transfer_rate_mbs + self._iterations = iterations + + def serialize(self): + return { + "transferred_bytes": self._transferred_bytes, + "remaining_bytes": self._remaining_bytes, + "total_bytes": self._total_bytes, + "duplicate_pages": self._duplicate_pages, + "skipped_pages": self._skipped_pages, + "normal_pages": self._normal_pages, + "normal_bytes": self._normal_bytes, + "dirty_rate_pps": self._dirty_rate_pps, + "transfer_rate_mbs": self._transfer_rate_mbs, + "iterations": self._iterations, + } + + @classmethod + def deserialize(cls, data): + return cls( + data["transferred_bytes"], + data["remaining_bytes"], + data["total_bytes"], + data["duplicate_pages"], + data["skipped_pages"], + data["normal_pages"], + data["normal_bytes"], + data["dirty_rate_pps"], + data["transfer_rate_mbs"], + data["iterations"]) + + +class Progress(object): + + def __init__(self, + status, + ram, + now, + duration, + downtime, + downtime_expected, + setup_time, + throttle_pcent, + dirty_limit_throttle_time_per_round, + dirty_limit_ring_full_time): + + self._status = status + self._ram = ram + self._now = now + self._duration = duration + self._downtime = downtime + self._downtime_expected = downtime_expected + self._setup_time = setup_time + self._throttle_pcent = throttle_pcent + self._dirty_limit_throttle_time_per_round = \ + dirty_limit_throttle_time_per_round + self._dirty_limit_ring_full_time = \ + dirty_limit_ring_full_time + + def serialize(self): + return { + "status": self._status, + "ram": self._ram.serialize(), + "now": self._now, + "duration": self._duration, + "downtime": self._downtime, + "downtime_expected": self._downtime_expected, + "setup_time": self._setup_time, + "throttle_pcent": self._throttle_pcent, + "dirty_limit_throttle_time_per_round": + self._dirty_limit_throttle_time_per_round, + "dirty_limit_ring_full_time": + self._dirty_limit_ring_full_time, + } + + @classmethod + def deserialize(cls, data): + return cls( + data["status"], + ProgressStats.deserialize(data["ram"]), + data["now"], + data["duration"], + data["downtime"], + data["downtime_expected"], + data["setup_time"], + data["throttle_pcent"], + data["dirty_limit_throttle_time_per_round"], + data["dirty_limit_ring_full_time"]) diff --git a/tests/migration-stress/guestperf/report.py b/tests/migration-stress/guestperf/report.py new file mode 100644 index 0000000..1efd40c --- /dev/null +++ b/tests/migration-stress/guestperf/report.py @@ -0,0 +1,98 @@ +# +# Migration test output result reporting +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + +import json + +from guestperf.hardware import Hardware +from guestperf.scenario import Scenario +from guestperf.progress import Progress +from guestperf.timings import Timings + +class Report(object): + + def __init__(self, + hardware, + scenario, + progress_history, + guest_timings, + qemu_timings, + vcpu_timings, + binary, + dst_host, + kernel, + initrd, + transport, + sleep): + + self._hardware = hardware + self._scenario = scenario + self._progress_history = progress_history + self._guest_timings = guest_timings + self._qemu_timings = qemu_timings + self._vcpu_timings = vcpu_timings + self._binary = binary + self._dst_host = dst_host + self._kernel = kernel + self._initrd = initrd + self._transport = transport + self._sleep = sleep + + def serialize(self): + return { + "hardware": self._hardware.serialize(), + "scenario": self._scenario.serialize(), + "progress_history": [progress.serialize() for progress in self._progress_history], + "guest_timings": self._guest_timings.serialize(), + "qemu_timings": self._qemu_timings.serialize(), + "vcpu_timings": self._vcpu_timings.serialize(), + "binary": self._binary, + "dst_host": self._dst_host, + "kernel": self._kernel, + "initrd": self._initrd, + "transport": self._transport, + "sleep": self._sleep, + } + + @classmethod + def deserialize(cls, data): + return cls( + Hardware.deserialize(data["hardware"]), + Scenario.deserialize(data["scenario"]), + [Progress.deserialize(record) for record in data["progress_history"]], + Timings.deserialize(data["guest_timings"]), + Timings.deserialize(data["qemu_timings"]), + Timings.deserialize(data["vcpu_timings"]), + data["binary"], + data["dst_host"], + data["kernel"], + data["initrd"], + data["transport"], + data["sleep"]) + + def to_json(self): + return json.dumps(self.serialize(), indent=4) + + @classmethod + def from_json(cls, data): + return cls.deserialize(json.loads(data)) + + @classmethod + def from_json_file(cls, filename): + with open(filename, "r") as fh: + return cls.deserialize(json.load(fh)) diff --git a/tests/migration-stress/guestperf/scenario.py b/tests/migration-stress/guestperf/scenario.py new file mode 100644 index 0000000..154c4f5 --- /dev/null +++ b/tests/migration-stress/guestperf/scenario.py @@ -0,0 +1,112 @@ +# +# Migration test scenario parameter description +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + + +class Scenario(object): + + def __init__(self, name, + downtime=500, + bandwidth=125000, # 1000 gig-e, effectively unlimited + max_iters=30, + max_time=300, + pause=False, pause_iters=5, + post_copy=False, post_copy_iters=5, + auto_converge=False, auto_converge_step=10, + compression_mt=False, compression_mt_threads=1, + compression_xbzrle=False, compression_xbzrle_cache=10, + multifd=False, multifd_channels=2, + dirty_limit=False, x_vcpu_dirty_limit_period=500, + vcpu_dirty_limit=1): + + self._name = name + + # General migration tunables + self._downtime = downtime # milliseconds + self._bandwidth = bandwidth # MiB per second + self._max_iters = max_iters + self._max_time = max_time # seconds + + + # Strategies for ensuring completion + self._pause = pause + self._pause_iters = pause_iters + + self._post_copy = post_copy + self._post_copy_iters = post_copy_iters + + self._auto_converge = auto_converge + self._auto_converge_step = auto_converge_step # percentage CPU time + + self._compression_mt = compression_mt + self._compression_mt_threads = compression_mt_threads + + self._compression_xbzrle = compression_xbzrle + self._compression_xbzrle_cache = compression_xbzrle_cache # percentage of guest RAM + + self._multifd = multifd + self._multifd_channels = multifd_channels + + self._dirty_limit = dirty_limit + self._x_vcpu_dirty_limit_period = x_vcpu_dirty_limit_period + self._vcpu_dirty_limit = vcpu_dirty_limit + + def serialize(self): + return { + "name": self._name, + "downtime": self._downtime, + "bandwidth": self._bandwidth, + "max_iters": self._max_iters, + "max_time": self._max_time, + "pause": self._pause, + "pause_iters": self._pause_iters, + "post_copy": self._post_copy, + "post_copy_iters": self._post_copy_iters, + "auto_converge": self._auto_converge, + "auto_converge_step": self._auto_converge_step, + "compression_mt": self._compression_mt, + "compression_mt_threads": self._compression_mt_threads, + "compression_xbzrle": self._compression_xbzrle, + "compression_xbzrle_cache": self._compression_xbzrle_cache, + "multifd": self._multifd, + "multifd_channels": self._multifd_channels, + "dirty_limit": self._dirty_limit, + "x_vcpu_dirty_limit_period": self._x_vcpu_dirty_limit_period, + "vcpu_dirty_limit": self._vcpu_dirty_limit, + } + + @classmethod + def deserialize(cls, data): + return cls( + data["name"], + data["downtime"], + data["bandwidth"], + data["max_iters"], + data["max_time"], + data["pause"], + data["pause_iters"], + data["post_copy"], + data["post_copy_iters"], + data["auto_converge"], + data["auto_converge_step"], + data["compression_mt"], + data["compression_mt_threads"], + data["compression_xbzrle"], + data["compression_xbzrle_cache"], + data["multifd"], + data["multifd_channels"]) diff --git a/tests/migration-stress/guestperf/shell.py b/tests/migration-stress/guestperf/shell.py new file mode 100644 index 0000000..046afeb --- /dev/null +++ b/tests/migration-stress/guestperf/shell.py @@ -0,0 +1,297 @@ +# +# Migration test command line shell integration +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + + +import argparse +import fnmatch +import os +import os.path +import platform +import sys +import logging + +from guestperf.hardware import Hardware +from guestperf.engine import Engine +from guestperf.scenario import Scenario +from guestperf.comparison import COMPARISONS +from guestperf.plot import Plot +from guestperf.report import Report + + +class BaseShell(object): + + def __init__(self): + parser = argparse.ArgumentParser(description="Migration Test Tool") + + # Test args + parser.add_argument("--debug", dest="debug", default=False, action="store_true") + parser.add_argument("--verbose", dest="verbose", default=False, action="store_true") + parser.add_argument("--sleep", dest="sleep", default=15, type=int) + parser.add_argument("--binary", dest="binary", default="/usr/bin/qemu-system-x86_64") + parser.add_argument("--dst-host", dest="dst_host", default="localhost") + parser.add_argument("--kernel", dest="kernel", default="/boot/vmlinuz-%s" % platform.release()) + parser.add_argument("--initrd", dest="initrd", + default="tests/migration-stress/initrd-stress.img") + parser.add_argument("--transport", dest="transport", default="unix") + + + # Hardware args + parser.add_argument("--cpus", dest="cpus", default=1, type=int) + parser.add_argument("--mem", dest="mem", default=1, type=int) + parser.add_argument("--src-cpu-bind", dest="src_cpu_bind", default="") + parser.add_argument("--src-mem-bind", dest="src_mem_bind", default="") + parser.add_argument("--dst-cpu-bind", dest="dst_cpu_bind", default="") + parser.add_argument("--dst-mem-bind", dest="dst_mem_bind", default="") + parser.add_argument("--prealloc-pages", dest="prealloc_pages", default=False) + parser.add_argument("--huge-pages", dest="huge_pages", default=False) + parser.add_argument("--locked-pages", dest="locked_pages", default=False) + parser.add_argument("--dirty-ring-size", dest="dirty_ring_size", + default=0, type=int) + + self._parser = parser + + def get_engine(self, args): + return Engine(binary=args.binary, + dst_host=args.dst_host, + kernel=args.kernel, + initrd=args.initrd, + transport=args.transport, + sleep=args.sleep, + debug=args.debug, + verbose=args.verbose) + + def get_hardware(self, args): + def split_map(value): + if value == "": + return [] + return value.split(",") + + return Hardware(cpus=args.cpus, + mem=args.mem, + + src_cpu_bind=split_map(args.src_cpu_bind), + src_mem_bind=split_map(args.src_mem_bind), + dst_cpu_bind=split_map(args.dst_cpu_bind), + dst_mem_bind=split_map(args.dst_mem_bind), + + locked_pages=args.locked_pages, + huge_pages=args.huge_pages, + prealloc_pages=args.prealloc_pages, + + dirty_ring_size=args.dirty_ring_size) + + +class Shell(BaseShell): + + def __init__(self): + super(Shell, self).__init__() + + parser = self._parser + + parser.add_argument("--output", dest="output", default=None) + + # Scenario args + parser.add_argument("--max-iters", dest="max_iters", default=30, type=int) + parser.add_argument("--max-time", dest="max_time", default=300, type=int) + parser.add_argument("--bandwidth", dest="bandwidth", default=125000, type=int) + parser.add_argument("--downtime", dest="downtime", default=500, type=int) + + parser.add_argument("--pause", dest="pause", default=False, action="store_true") + parser.add_argument("--pause-iters", dest="pause_iters", default=5, type=int) + + parser.add_argument("--post-copy", dest="post_copy", default=False, action="store_true") + parser.add_argument("--post-copy-iters", dest="post_copy_iters", default=5, type=int) + + parser.add_argument("--auto-converge", dest="auto_converge", default=False, action="store_true") + parser.add_argument("--auto-converge-step", dest="auto_converge_step", default=10, type=int) + + parser.add_argument("--compression-mt", dest="compression_mt", default=False, action="store_true") + parser.add_argument("--compression-mt-threads", dest="compression_mt_threads", default=1, type=int) + + parser.add_argument("--compression-xbzrle", dest="compression_xbzrle", default=False, action="store_true") + parser.add_argument("--compression-xbzrle-cache", dest="compression_xbzrle_cache", default=10, type=int) + + parser.add_argument("--multifd", dest="multifd", default=False, + action="store_true") + parser.add_argument("--multifd-channels", dest="multifd_channels", + default=2, type=int) + + parser.add_argument("--dirty-limit", dest="dirty_limit", default=False, + action="store_true") + + parser.add_argument("--x-vcpu-dirty-limit-period", + dest="x_vcpu_dirty_limit_period", + default=500, type=int) + + parser.add_argument("--vcpu-dirty-limit", + dest="vcpu_dirty_limit", + default=1, type=int) + + def get_scenario(self, args): + return Scenario(name="perfreport", + downtime=args.downtime, + bandwidth=args.bandwidth, + max_iters=args.max_iters, + max_time=args.max_time, + + pause=args.pause, + pause_iters=args.pause_iters, + + post_copy=args.post_copy, + post_copy_iters=args.post_copy_iters, + + auto_converge=args.auto_converge, + auto_converge_step=args.auto_converge_step, + + compression_mt=args.compression_mt, + compression_mt_threads=args.compression_mt_threads, + + compression_xbzrle=args.compression_xbzrle, + compression_xbzrle_cache=args.compression_xbzrle_cache, + + multifd=args.multifd, + multifd_channels=args.multifd_channels, + + dirty_limit=args.dirty_limit, + x_vcpu_dirty_limit_period=\ + args.x_vcpu_dirty_limit_period, + vcpu_dirty_limit=args.vcpu_dirty_limit) + + def run(self, argv): + args = self._parser.parse_args(argv) + logging.basicConfig(level=(logging.DEBUG if args.debug else + logging.INFO if args.verbose else + logging.WARN)) + + + engine = self.get_engine(args) + hardware = self.get_hardware(args) + scenario = self.get_scenario(args) + + try: + report = engine.run(hardware, scenario) + if args.output is None: + print(report.to_json()) + else: + with open(args.output, "w") as fh: + print(report.to_json(), file=fh) + return 0 + except Exception as e: + print("Error: %s" % str(e), file=sys.stderr) + if args.debug: + raise + return 1 + + +class BatchShell(BaseShell): + + def __init__(self): + super(BatchShell, self).__init__() + + parser = self._parser + + parser.add_argument("--filter", dest="filter", default="*") + parser.add_argument("--output", dest="output", default=os.getcwd()) + + def run(self, argv): + args = self._parser.parse_args(argv) + logging.basicConfig(level=(logging.DEBUG if args.debug else + logging.INFO if args.verbose else + logging.WARN)) + + + engine = self.get_engine(args) + hardware = self.get_hardware(args) + + try: + for comparison in COMPARISONS: + compdir = os.path.join(args.output, comparison._name) + for scenario in comparison._scenarios: + name = os.path.join(comparison._name, scenario._name) + if not fnmatch.fnmatch(name, args.filter): + if args.verbose: + print("Skipping %s" % name) + continue + + if args.verbose: + print("Running %s" % name) + + dirname = os.path.join(args.output, comparison._name) + filename = os.path.join(dirname, scenario._name + ".json") + if not os.path.exists(dirname): + os.makedirs(dirname) + report = engine.run(hardware, scenario) + with open(filename, "w") as fh: + print(report.to_json(), file=fh) + except Exception as e: + print("Error: %s" % str(e), file=sys.stderr) + if args.debug: + raise + + +class PlotShell(object): + + def __init__(self): + super(PlotShell, self).__init__() + + self._parser = argparse.ArgumentParser(description="Migration Test Tool") + + self._parser.add_argument("--output", dest="output", default=None) + + self._parser.add_argument("--debug", dest="debug", default=False, action="store_true") + self._parser.add_argument("--verbose", dest="verbose", default=False, action="store_true") + + self._parser.add_argument("--migration-iters", dest="migration_iters", default=False, action="store_true") + self._parser.add_argument("--total-guest-cpu", dest="total_guest_cpu", default=False, action="store_true") + self._parser.add_argument("--split-guest-cpu", dest="split_guest_cpu", default=False, action="store_true") + self._parser.add_argument("--qemu-cpu", dest="qemu_cpu", default=False, action="store_true") + self._parser.add_argument("--vcpu-cpu", dest="vcpu_cpu", default=False, action="store_true") + + self._parser.add_argument("reports", nargs='*') + + def run(self, argv): + args = self._parser.parse_args(argv) + logging.basicConfig(level=(logging.DEBUG if args.debug else + logging.INFO if args.verbose else + logging.WARN)) + + + if len(args.reports) == 0: + print("At least one report required", file=sys.stderr) + return 1 + + if not (args.qemu_cpu or + args.vcpu_cpu or + args.total_guest_cpu or + args.split_guest_cpu): + print("At least one chart type is required", file=sys.stderr) + return 1 + + reports = [] + for report in args.reports: + reports.append(Report.from_json_file(report)) + + plot = Plot(reports, + args.migration_iters, + args.total_guest_cpu, + args.split_guest_cpu, + args.qemu_cpu, + args.vcpu_cpu) + + plot.generate(args.output) diff --git a/tests/migration-stress/guestperf/timings.py b/tests/migration-stress/guestperf/timings.py new file mode 100644 index 0000000..2374010 --- /dev/null +++ b/tests/migration-stress/guestperf/timings.py @@ -0,0 +1,55 @@ +# +# Migration test timing records +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + + +class TimingRecord(object): + + def __init__(self, tid, timestamp, value): + + self._tid = tid + self._timestamp = timestamp + self._value = value + + def serialize(self): + return { + "tid": self._tid, + "timestamp": self._timestamp, + "value": self._value + } + + @classmethod + def deserialize(cls, data): + return cls( + data["tid"], + data["timestamp"], + data["value"]) + + +class Timings(object): + + def __init__(self, records): + + self._records = records + + def serialize(self): + return [record.serialize() for record in self._records] + + @classmethod + def deserialize(cls, data): + return Timings([TimingRecord.deserialize(record) for record in data]) diff --git a/tests/migration-stress/initrd-stress.sh b/tests/migration-stress/initrd-stress.sh new file mode 100755 index 0000000..0f20ac2 --- /dev/null +++ b/tests/migration-stress/initrd-stress.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +INITRD="$1" +STRESS="$2" + +INITRD_DIR=$(mktemp -d -p '' "initrd-stress.XXXXXX") +trap 'rm -rf $INITRD_DIR' EXIT + +cp "$STRESS" "$INITRD_DIR/init" +(cd "$INITRD_DIR" && (find | cpio --quiet -o -H newc | gzip -9)) > "$INITRD" diff --git a/tests/migration-stress/meson.build b/tests/migration-stress/meson.build new file mode 100644 index 0000000..a91aa61 --- /dev/null +++ b/tests/migration-stress/meson.build @@ -0,0 +1,18 @@ +sysprof = dependency('sysprof-capture-4', method: 'pkg-config', required: false) +glib_static = dependency('glib-2.0', version: glib_req_ver, required: false, + method: 'pkg-config', static: true) + +stress = executable( + 'stress', + files('stress.c'), + dependencies: [glib_static, sysprof], + link_args: ['-static'], + build_by_default: false, +) + +custom_target( + 'initrd-stress.img', + output: 'initrd-stress.img', + input: stress, + command: [find_program('initrd-stress.sh'), '@OUTPUT@', '@INPUT@'] +) diff --git a/tests/migration-stress/stress.c b/tests/migration-stress/stress.c new file mode 100644 index 0000000..88acf8d --- /dev/null +++ b/tests/migration-stress/stress.c @@ -0,0 +1,328 @@ +/* + * Migration stress workload + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include +#include +#include +#include +#include +#include + +const char *argv0; + +#define RAM_PAGE_SIZE 4096 + +#ifndef CONFIG_GETTID +static int gettid(void) +{ + return syscall(SYS_gettid); +} +#endif + +static __attribute__((noreturn)) void exit_failure(void) +{ + if (getpid() == 1) { + sync(); + reboot(RB_POWER_OFF); + fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n", + argv0, gettid(), strerror(errno)); + abort(); + } else { + exit(1); + } +} + +static int get_command_arg_str(const char *name, + char **val) +{ + static char line[1024]; + FILE *fp = fopen("/proc/cmdline", "r"); + char *start, *end; + + if (fp == NULL) { + fprintf(stderr, "%s (%05d): ERROR: cannot open /proc/cmdline: %s\n", + argv0, gettid(), strerror(errno)); + return -1; + } + + if (!fgets(line, sizeof line, fp)) { + fprintf(stderr, "%s (%05d): ERROR: cannot read /proc/cmdline: %s\n", + argv0, gettid(), strerror(errno)); + fclose(fp); + return -1; + } + fclose(fp); + + start = strstr(line, name); + if (!start) + return 0; + + start += strlen(name); + + if (*start != '=') { + fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n", + argv0, gettid(), name); + } + start++; + + end = strstr(start, " "); + if (!end) + end = strstr(start, "\n"); + + if (end == start) { + fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n", + argv0, gettid(), name); + return -1; + } + + if (end) + *val = g_strndup(start, end - start); + else + *val = g_strdup(start); + return 1; +} + + +static int get_command_arg_ull(const char *name, + unsigned long long *val) +{ + char *valstr; + char *end; + + int ret = get_command_arg_str(name, &valstr); + if (ret <= 0) + return ret; + + errno = 0; + *val = strtoll(valstr, &end, 10); + if (errno || *end) { + fprintf(stderr, "%s (%05d): ERROR: cannot parse %s value %s\n", + argv0, gettid(), name, valstr); + g_free(valstr); + return -1; + } + g_free(valstr); + return 0; +} + + +static int random_bytes(char *buf, size_t len) +{ + int fd; + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "%s (%05d): ERROR: cannot open /dev/urandom: %s\n", + argv0, gettid(), strerror(errno)); + return -1; + } + + if (read(fd, buf, len) != len) { + fprintf(stderr, "%s (%05d): ERROR: cannot read /dev/urandom: %s\n", + argv0, gettid(), strerror(errno)); + close(fd); + return -1; + } + + close(fd); + + return 0; +} + + +static unsigned long long now(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return (tv.tv_sec * 1000ull) + (tv.tv_usec / 1000ull); +} + +static void stressone(unsigned long long ramsizeMB) +{ + size_t pagesPerMB = 1024 * 1024 / RAM_PAGE_SIZE; + g_autofree char *ram = g_malloc(ramsizeMB * 1024 * 1024); + char *ramptr; + size_t i, j, k; + g_autofree char *data = g_malloc(RAM_PAGE_SIZE); + char *dataptr; + size_t nMB = 0; + unsigned long long before, after; + + /* We don't care about initial state, but we do want + * to fault it all into RAM, otherwise the first iter + * of the loop below will be quite slow. We can't use + * 0x0 as the byte as gcc optimizes that away into a + * calloc instead :-) */ + memset(ram, 0xfe, ramsizeMB * 1024 * 1024); + + if (random_bytes(data, RAM_PAGE_SIZE) < 0) { + return; + } + + before = now(); + + while (1) { + + ramptr = ram; + for (i = 0; i < ramsizeMB; i++, nMB++) { + for (j = 0; j < pagesPerMB; j++) { + dataptr = data; + for (k = 0; k < RAM_PAGE_SIZE; k += sizeof(long long)) { + ramptr += sizeof(long long); + dataptr += sizeof(long long); + *(unsigned long long *)ramptr ^= *(unsigned long long *)dataptr; + } + } + + if (nMB == 1024) { + after = now(); + fprintf(stderr, "%s (%05d): INFO: %06llums copied 1 GB in %05llums\n", + argv0, gettid(), after, after - before); + before = now(); + nMB = 0; + } + } + } +} + + +static void *stressthread(void *arg) +{ + unsigned long long ramsizeMB = *(unsigned long long *)arg; + + stressone(ramsizeMB); + + return NULL; +} + +static void stress(unsigned long long ramsizeGB, int ncpus) +{ + size_t i; + unsigned long long ramsizeMB = ramsizeGB * 1024 / ncpus; + ncpus--; + + for (i = 0; i < ncpus; i++) { + pthread_t thr; + pthread_create(&thr, NULL, + stressthread, &ramsizeMB); + } + + stressone(ramsizeMB); +} + + +static int mount_misc(const char *fstype, const char *dir) +{ + if (g_mkdir_with_parents(dir, 0755) < 0 && errno != EEXIST) { + fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n", + argv0, gettid(), dir, strerror(errno)); + return -1; + } + + if (mount("none", dir, fstype, 0, NULL) < 0) { + fprintf(stderr, "%s (%05d): ERROR: cannot mount %s: %s\n", + argv0, gettid(), dir, strerror(errno)); + return -1; + } + + return 0; +} + +static int mount_all(void) +{ + if (mount_misc("proc", "/proc") < 0 || + mount_misc("sysfs", "/sys") < 0 || + mount_misc("tmpfs", "/dev") < 0) + return -1; + + mknod("/dev/urandom", 0777 | S_IFCHR, makedev(1, 9)); + mknod("/dev/random", 0777 | S_IFCHR, makedev(1, 8)); + + return 0; +} + +int main(int argc, char **argv) +{ + unsigned long long ramsizeGB = 1; + char *end; + int ch; + int opt_ind = 0; + const char *sopt = "hr:c:"; + struct option lopt[] = { + { "help", no_argument, NULL, 'h' }, + { "ramsize", required_argument, NULL, 'r' }, + { "cpus", required_argument, NULL, 'c' }, + { NULL, 0, NULL, 0 } + }; + int ret; + int ncpus = 0; + + argv0 = argv[0]; + + while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { + switch (ch) { + case 'r': + errno = 0; + ramsizeGB = strtoll(optarg, &end, 10); + if (errno != 0 || *end) { + fprintf(stderr, "%s (%05d): ERROR: Cannot parse RAM size %s\n", + argv0, gettid(), optarg); + exit_failure(); + } + break; + + case 'c': + errno = 0; + ncpus = strtoll(optarg, &end, 10); + if (errno != 0 || *end) { + fprintf(stderr, "%s (%05d): ERROR: Cannot parse CPU count %s\n", + argv0, gettid(), optarg); + exit_failure(); + } + break; + + case '?': + case 'h': + fprintf(stderr, "%s: [--help][--ramsize GB][--cpus N]\n", argv0); + exit_failure(); + } + } + + if (getpid() == 1) { + if (mount_all() < 0) + exit_failure(); + + ret = get_command_arg_ull("ramsize", &ramsizeGB); + if (ret < 0) + exit_failure(); + } + + if (ncpus == 0) + ncpus = sysconf(_SC_NPROCESSORS_ONLN); + + fprintf(stdout, "%s (%05d): INFO: RAM %llu GiB across %d CPUs\n", + argv0, gettid(), ramsizeGB, ncpus); + + stress(ramsizeGB, ncpus); + + exit_failure(); +} diff --git a/tests/migration/Makefile b/tests/migration/Makefile deleted file mode 100644 index 2c5ee28..0000000 --- a/tests/migration/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright (c) 2018 Red Hat, Inc. and/or its affiliates -# -# This work is licensed under the terms of the GNU GPL, version 2 or later. -# See the COPYING file in the top-level directory. -# - -TARGET_LIST = i386 aarch64 s390x ppc64 - -SRC_PATH = ../.. - -.PHONY: help $(TARGET_LIST) -help: - @echo "Create migration guest includes. We generate a binary." - @echo "And then convert that binary to an include file that can be" - @echo "run in a guest." - @echo "Possible operations are:" - @echo - @echo " $(MAKE) clean Remove all intermediate files" - @echo " $(MAKE) target Generate for that target" - @echo " $(MAKE) CROSS_PREFIX=... target" - @echo " Cross-compile than target" - @echo " Possible targets are: $(TARGET_LIST)" - -override define __note -/* This file is automatically generated from the assembly file in - * tests/migration/$@. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both - * the header and the assembler differences in your patch submission. - */ -endef -export __note - -$(TARGET_LIST): - $(MAKE) CROSS_PREFIX=$(CROSS_PREFIX) -C $@ - -clean: - for target in $(TARGET_LIST); do \ - $(MAKE) -C $$target clean; \ - done diff --git a/tests/migration/aarch64/Makefile b/tests/migration/aarch64/Makefile deleted file mode 100644 index 9c4fa18..0000000 --- a/tests/migration/aarch64/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# To specify cross compiler prefix, use CROSS_PREFIX= -# $ make CROSS_PREFIX=aarch64-linux-gnu- - -.PHONY: all clean -all: a-b-kernel.h - -a-b-kernel.h: aarch64.kernel - echo "$$__note" > $@ - xxd -i $< | sed -e 's/.*int.*//' >> $@ - -aarch64.kernel: aarch64.elf - $(CROSS_PREFIX)objcopy -O binary $< $@ - -aarch64.elf: a-b-kernel.S - $(CROSS_PREFIX)gcc -o $@ -nostdlib -Wl,--build-id=none $< - -clean: - $(RM) *.kernel *.elf diff --git a/tests/migration/aarch64/a-b-kernel.S b/tests/migration/aarch64/a-b-kernel.S deleted file mode 100644 index a4103ec..0000000 --- a/tests/migration/aarch64/a-b-kernel.S +++ /dev/null @@ -1,74 +0,0 @@ -# -# Copyright (c) 2018 Red Hat, Inc. and/or its affiliates -# -# Author: -# Wei Huang -# -# This work is licensed under the terms of the GNU GPL, version 2 or later. -# See the COPYING file in the top-level directory. -# -# Note: Please make sure the compiler compiles the assembly code below with -# pc-relative address. Also the branch instructions should use relative -# addresses only. - -#include "../migration-test.h" - -.section .text - - .globl _start - -_start: - /* disable MMU to use phys mem address */ - mrs x0, sctlr_el1 - bic x0, x0, #(1<<0) - msr sctlr_el1, x0 - isb - - /* traverse test memory region */ - mov x0, #ARM_TEST_MEM_START - mov x1, #ARM_TEST_MEM_END - - /* output char 'A' to PL011 */ - mov w3, 'A' - mov x2, #ARM_MACH_VIRT_UART - strb w3, [x2] - - /* clean up memory */ - mov w3, #0 - mov x4, x0 -clean: - strb w3, [x4] - add x4, x4, #TEST_MEM_PAGE_SIZE - cmp x4, x1 - ble clean - - /* w5 keeps a counter so we can limit the output speed */ - mov w5, #0 - - /* main body */ -mainloop: - mov x4, x0 - -innerloop: - /* increment the first byte of each page by 1 */ - ldrb w3, [x4] - add w3, w3, #1 - strb w3, [x4] - - /* make sure QEMU user space can see consistent data as MMU is off */ - dc civac, x4 - - add x4, x4, #TEST_MEM_PAGE_SIZE - cmp x4, x1 - blt innerloop - - add w5, w5, #1 - and w5, w5, #0x1f - cmp w5, #0 - bne mainloop - - /* output char 'B' to PL011 */ - mov w3, 'B' - strb w3, [x2] - - b mainloop diff --git a/tests/migration/aarch64/a-b-kernel.h b/tests/migration/aarch64/a-b-kernel.h deleted file mode 100644 index 34e518d..0000000 --- a/tests/migration/aarch64/a-b-kernel.h +++ /dev/null @@ -1,18 +0,0 @@ -/* This file is automatically generated from the assembly file in - * tests/migration/aarch64. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both - * the header and the assembler differences in your patch submission. - */ -unsigned char aarch64_kernel[] = { - 0x00, 0x10, 0x38, 0xd5, 0x00, 0xf8, 0x7f, 0x92, 0x00, 0x10, 0x18, 0xd5, - 0xdf, 0x3f, 0x03, 0xd5, 0x00, 0x02, 0xa8, 0xd2, 0x01, 0xc8, 0xa8, 0xd2, - 0x23, 0x08, 0x80, 0x52, 0x02, 0x20, 0xa1, 0xd2, 0x43, 0x00, 0x00, 0x39, - 0x03, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x00, 0x39, - 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, 0xad, 0xff, 0xff, 0x54, - 0x05, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x40, 0x39, - 0x63, 0x04, 0x00, 0x11, 0x83, 0x00, 0x00, 0x39, 0x24, 0x7e, 0x0b, 0xd5, - 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, 0x4b, 0xff, 0xff, 0x54, - 0xa5, 0x04, 0x00, 0x11, 0xa5, 0x10, 0x00, 0x12, 0xbf, 0x00, 0x00, 0x71, - 0xa1, 0xfe, 0xff, 0x54, 0x43, 0x08, 0x80, 0x52, 0x43, 0x00, 0x00, 0x39, - 0xf2, 0xff, 0xff, 0x17 -}; diff --git a/tests/migration/guestperf-batch.py b/tests/migration/guestperf-batch.py deleted file mode 100755 index 9485eef..0000000 --- a/tests/migration/guestperf-batch.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 -# -# Migration test batch comparison invocation -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - -import sys - -from guestperf.shell import BatchShell - -shell = BatchShell() -sys.exit(shell.run(sys.argv[1:])) diff --git a/tests/migration/guestperf-plot.py b/tests/migration/guestperf-plot.py deleted file mode 100755 index 32977b4..0000000 --- a/tests/migration/guestperf-plot.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 -# -# Migration test graph plotting command -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - -import sys - -from guestperf.shell import PlotShell - -shell = PlotShell() -sys.exit(shell.run(sys.argv[1:])) diff --git a/tests/migration/guestperf.py b/tests/migration/guestperf.py deleted file mode 100755 index 07182f2..0000000 --- a/tests/migration/guestperf.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 -# -# Migration test direct invocation command -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - - -import sys - -from guestperf.shell import Shell - -shell = Shell() -sys.exit(shell.run(sys.argv[1:])) diff --git a/tests/migration/guestperf/__init__.py b/tests/migration/guestperf/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/migration/guestperf/comparison.py b/tests/migration/guestperf/comparison.py deleted file mode 100644 index 42cc037..0000000 --- a/tests/migration/guestperf/comparison.py +++ /dev/null @@ -1,161 +0,0 @@ -# -# Migration test scenario comparison mapping -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - -from guestperf.scenario import Scenario - -class Comparison(object): - def __init__(self, name, scenarios): - self._name = name - self._scenarios = scenarios - -COMPARISONS = [ - # Looking at effect of pausing guest during migration - # at various stages of iteration over RAM - Comparison("pause-iters", scenarios = [ - Scenario("pause-iters-0", - pause=True, pause_iters=0), - Scenario("pause-iters-1", - pause=True, pause_iters=1), - Scenario("pause-iters-5", - pause=True, pause_iters=5), - Scenario("pause-iters-20", - pause=True, pause_iters=20), - ]), - - - # Looking at use of post-copy in relation to bandwidth - # available for migration - Comparison("post-copy-bandwidth", scenarios = [ - Scenario("post-copy-bw-100mbs", - post_copy=True, bandwidth=12), - Scenario("post-copy-bw-300mbs", - post_copy=True, bandwidth=37), - Scenario("post-copy-bw-1gbs", - post_copy=True, bandwidth=125), - Scenario("post-copy-bw-10gbs", - post_copy=True, bandwidth=1250), - Scenario("post-copy-bw-100gbs", - post_copy=True, bandwidth=12500), - ]), - - - # Looking at effect of starting post-copy at different - # stages of the migration - Comparison("post-copy-iters", scenarios = [ - Scenario("post-copy-iters-0", - post_copy=True, post_copy_iters=0), - Scenario("post-copy-iters-1", - post_copy=True, post_copy_iters=1), - Scenario("post-copy-iters-5", - post_copy=True, post_copy_iters=5), - Scenario("post-copy-iters-20", - post_copy=True, post_copy_iters=20), - ]), - - - # Looking at effect of auto-converge with different - # throttling percentage step rates - Comparison("auto-converge-iters", scenarios = [ - Scenario("auto-converge-step-5", - auto_converge=True, auto_converge_step=5), - Scenario("auto-converge-step-10", - auto_converge=True, auto_converge_step=10), - Scenario("auto-converge-step-20", - auto_converge=True, auto_converge_step=20), - ]), - - - # Looking at use of auto-converge in relation to bandwidth - # available for migration - Comparison("auto-converge-bandwidth", scenarios = [ - Scenario("auto-converge-bw-100mbs", - auto_converge=True, bandwidth=12), - Scenario("auto-converge-bw-300mbs", - auto_converge=True, bandwidth=37), - Scenario("auto-converge-bw-1gbs", - auto_converge=True, bandwidth=125), - Scenario("auto-converge-bw-10gbs", - auto_converge=True, bandwidth=1250), - Scenario("auto-converge-bw-100gbs", - auto_converge=True, bandwidth=12500), - ]), - - - # Looking at effect of multi-thread compression with - # varying numbers of threads - Comparison("compr-mt", scenarios = [ - Scenario("compr-mt-threads-1", - compression_mt=True, compression_mt_threads=1), - Scenario("compr-mt-threads-2", - compression_mt=True, compression_mt_threads=2), - Scenario("compr-mt-threads-4", - compression_mt=True, compression_mt_threads=4), - ]), - - - # Looking at effect of xbzrle compression with varying - # cache sizes - Comparison("compr-xbzrle", scenarios = [ - Scenario("compr-xbzrle-cache-5", - compression_xbzrle=True, compression_xbzrle_cache=5), - Scenario("compr-xbzrle-cache-10", - compression_xbzrle=True, compression_xbzrle_cache=10), - Scenario("compr-xbzrle-cache-20", - compression_xbzrle=True, compression_xbzrle_cache=10), - Scenario("compr-xbzrle-cache-50", - compression_xbzrle=True, compression_xbzrle_cache=50), - ]), - - - # Looking at effect of multifd with - # varying numbers of channels - Comparison("compr-multifd", scenarios = [ - Scenario("compr-multifd-channels-4", - multifd=True, multifd_channels=2), - Scenario("compr-multifd-channels-8", - multifd=True, multifd_channels=8), - Scenario("compr-multifd-channels-32", - multifd=True, multifd_channels=32), - Scenario("compr-multifd-channels-64", - multifd=True, multifd_channels=64), - ]), - - # Looking at effect of dirty-limit with - # varying x_vcpu_dirty_limit_period - Comparison("compr-dirty-limit-period", scenarios = [ - Scenario("compr-dirty-limit-period-500", - dirty_limit=True, x_vcpu_dirty_limit_period=500), - Scenario("compr-dirty-limit-period-800", - dirty_limit=True, x_vcpu_dirty_limit_period=800), - Scenario("compr-dirty-limit-period-1000", - dirty_limit=True, x_vcpu_dirty_limit_period=1000), - ]), - - - # Looking at effect of dirty-limit with - # varying vcpu_dirty_limit - Comparison("compr-dirty-limit", scenarios = [ - Scenario("compr-dirty-limit-10MB", - dirty_limit=True, vcpu_dirty_limit=10), - Scenario("compr-dirty-limit-20MB", - dirty_limit=True, vcpu_dirty_limit=20), - Scenario("compr-dirty-limit-50MB", - dirty_limit=True, vcpu_dirty_limit=50), - ]), -] diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py deleted file mode 100644 index 608d727..0000000 --- a/tests/migration/guestperf/engine.py +++ /dev/null @@ -1,505 +0,0 @@ -# -# Migration test main engine -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - - -import os -import re -import sys -import time - -from guestperf.progress import Progress, ProgressStats -from guestperf.report import Report -from guestperf.timings import TimingRecord, Timings - -sys.path.append(os.path.join(os.path.dirname(__file__), - '..', '..', '..', 'python')) -from qemu.machine import QEMUMachine - - -class Engine(object): - - def __init__(self, binary, dst_host, kernel, initrd, transport="tcp", - sleep=15, verbose=False, debug=False): - - self._binary = binary # Path to QEMU binary - self._dst_host = dst_host # Hostname of target host - self._kernel = kernel # Path to kernel image - self._initrd = initrd # Path to stress initrd - self._transport = transport # 'unix' or 'tcp' or 'rdma' - self._sleep = sleep - self._verbose = verbose - self._debug = debug - - if debug: - self._verbose = debug - - def _vcpu_timing(self, pid, tid_list): - records = [] - now = time.time() - - jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) - for tid in tid_list: - statfile = "/proc/%d/task/%d/stat" % (pid, tid) - with open(statfile, "r") as fh: - stat = fh.readline() - fields = stat.split(" ") - stime = int(fields[13]) - utime = int(fields[14]) - records.append(TimingRecord(tid, now, 1000 * (stime + utime) / jiffies_per_sec)) - return records - - def _cpu_timing(self, pid): - now = time.time() - - jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) - statfile = "/proc/%d/stat" % pid - with open(statfile, "r") as fh: - stat = fh.readline() - fields = stat.split(" ") - stime = int(fields[13]) - utime = int(fields[14]) - return TimingRecord(pid, now, 1000 * (stime + utime) / jiffies_per_sec) - - def _migrate_progress(self, vm): - info = vm.cmd("query-migrate") - - if "ram" not in info: - info["ram"] = {} - - return Progress( - info.get("status", "active"), - ProgressStats( - info["ram"].get("transferred", 0), - info["ram"].get("remaining", 0), - info["ram"].get("total", 0), - info["ram"].get("duplicate", 0), - info["ram"].get("skipped", 0), - info["ram"].get("normal", 0), - info["ram"].get("normal-bytes", 0), - info["ram"].get("dirty-pages-rate", 0), - info["ram"].get("mbps", 0), - info["ram"].get("dirty-sync-count", 0) - ), - time.time(), - info.get("total-time", 0), - info.get("downtime", 0), - info.get("expected-downtime", 0), - info.get("setup-time", 0), - info.get("cpu-throttle-percentage", 0), - info.get("dirty-limit-throttle-time-per-round", 0), - info.get("dirty-limit-ring-full-time", 0), - ) - - def _migrate(self, hardware, scenario, src, dst, connect_uri): - src_qemu_time = [] - src_vcpu_time = [] - src_pid = src.get_pid() - - vcpus = src.cmd("query-cpus-fast") - src_threads = [] - for vcpu in vcpus: - src_threads.append(vcpu["thread-id"]) - - # XXX how to get dst timings on remote host ? - - if self._verbose: - print("Sleeping %d seconds for initial guest workload run" % self._sleep) - sleep_secs = self._sleep - while sleep_secs > 1: - src_qemu_time.append(self._cpu_timing(src_pid)) - src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) - time.sleep(1) - sleep_secs -= 1 - - if self._verbose: - print("Starting migration") - if scenario._auto_converge: - resp = src.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "auto-converge", - "state": True } - ]) - resp = src.cmd("migrate-set-parameters", - cpu_throttle_increment=scenario._auto_converge_step) - - if scenario._post_copy: - resp = src.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "postcopy-ram", - "state": True } - ]) - resp = dst.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "postcopy-ram", - "state": True } - ]) - - resp = src.cmd("migrate-set-parameters", - max_bandwidth=scenario._bandwidth * 1024 * 1024) - - resp = src.cmd("migrate-set-parameters", - downtime_limit=scenario._downtime) - - if scenario._compression_mt: - resp = src.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "compress", - "state": True } - ]) - resp = src.cmd("migrate-set-parameters", - compress_threads=scenario._compression_mt_threads) - resp = dst.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "compress", - "state": True } - ]) - resp = dst.cmd("migrate-set-parameters", - decompress_threads=scenario._compression_mt_threads) - - if scenario._compression_xbzrle: - resp = src.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "xbzrle", - "state": True } - ]) - resp = dst.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "xbzrle", - "state": True } - ]) - resp = src.cmd("migrate-set-parameters", - xbzrle_cache_size=( - hardware._mem * - 1024 * 1024 * 1024 / 100 * - scenario._compression_xbzrle_cache)) - - if scenario._multifd: - resp = src.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "multifd", - "state": True } - ]) - resp = src.cmd("migrate-set-parameters", - multifd_channels=scenario._multifd_channels) - resp = dst.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "multifd", - "state": True } - ]) - resp = dst.cmd("migrate-set-parameters", - multifd_channels=scenario._multifd_channels) - - if scenario._dirty_limit: - if not hardware._dirty_ring_size: - raise Exception("dirty ring size must be configured when " - "testing dirty limit migration") - - resp = src.cmd("migrate-set-capabilities", - capabilities = [ - { "capability": "dirty-limit", - "state": True } - ]) - resp = src.cmd("migrate-set-parameters", - x_vcpu_dirty_limit_period=scenario._x_vcpu_dirty_limit_period) - resp = src.cmd("migrate-set-parameters", - vcpu_dirty_limit=scenario._vcpu_dirty_limit) - - resp = src.cmd("migrate", uri=connect_uri) - - post_copy = False - paused = False - - progress_history = [] - - start = time.time() - loop = 0 - while True: - loop = loop + 1 - time.sleep(0.05) - - progress = self._migrate_progress(src) - if (loop % 20) == 0: - src_qemu_time.append(self._cpu_timing(src_pid)) - src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) - - if (len(progress_history) == 0 or - (progress_history[-1]._ram._iterations < - progress._ram._iterations)): - progress_history.append(progress) - - if progress._status in ("completed", "failed", "cancelled"): - if progress._status == "completed" and paused: - dst.cmd("cont") - if progress_history[-1] != progress: - progress_history.append(progress) - - if progress._status == "completed": - if self._verbose: - print("Sleeping %d seconds for final guest workload run" % self._sleep) - sleep_secs = self._sleep - while sleep_secs > 1: - time.sleep(1) - src_qemu_time.append(self._cpu_timing(src_pid)) - src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) - sleep_secs -= 1 - - return [progress_history, src_qemu_time, src_vcpu_time] - - if self._verbose and (loop % 20) == 0: - print("Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( - progress._ram._iterations, - progress._ram._remaining_bytes / (1024 * 1024), - progress._ram._total_bytes / (1024 * 1024), - progress._ram._transferred_bytes / (1024 * 1024), - progress._ram._transfer_rate_mbs, - )) - - if progress._ram._iterations > scenario._max_iters: - if self._verbose: - print("No completion after %d iterations over RAM" % scenario._max_iters) - src.cmd("migrate_cancel") - continue - - if time.time() > (start + scenario._max_time): - if self._verbose: - print("No completion after %d seconds" % scenario._max_time) - src.cmd("migrate_cancel") - continue - - if (scenario._post_copy and - progress._ram._iterations >= scenario._post_copy_iters and - not post_copy): - if self._verbose: - print("Switching to post-copy after %d iterations" % scenario._post_copy_iters) - resp = src.cmd("migrate-start-postcopy") - post_copy = True - - if (scenario._pause and - progress._ram._iterations >= scenario._pause_iters and - not paused): - if self._verbose: - print("Pausing VM after %d iterations" % scenario._pause_iters) - resp = src.cmd("stop") - paused = True - - def _is_ppc64le(self): - _, _, _, _, machine = os.uname() - if machine == "ppc64le": - return True - return False - - def _get_guest_console_args(self): - if self._is_ppc64le(): - return "console=hvc0" - else: - return "console=ttyS0" - - def _get_qemu_serial_args(self): - if self._is_ppc64le(): - return ["-chardev", "stdio,id=cdev0", - "-device", "spapr-vty,chardev=cdev0"] - else: - return ["-chardev", "stdio,id=cdev0", - "-device", "isa-serial,chardev=cdev0"] - - def _get_common_args(self, hardware, tunnelled=False): - args = [ - "noapic", - "edd=off", - "printk.time=1", - "noreplace-smp", - "cgroup_disable=memory", - "pci=noearly", - ] - - args.append(self._get_guest_console_args()) - - if self._debug: - args.append("debug") - else: - args.append("quiet") - - args.append("ramsize=%s" % hardware._mem) - - cmdline = " ".join(args) - if tunnelled: - cmdline = "'" + cmdline + "'" - - argv = [ - "-cpu", "host", - "-kernel", self._kernel, - "-initrd", self._initrd, - "-append", cmdline, - "-m", str((hardware._mem * 1024) + 512), - "-smp", str(hardware._cpus), - ] - if hardware._dirty_ring_size: - argv.extend(["-accel", "kvm,dirty-ring-size=%s" % - hardware._dirty_ring_size]) - else: - argv.extend(["-accel", "kvm"]) - - argv.extend(self._get_qemu_serial_args()) - - if self._debug: - argv.extend(["-machine", "graphics=off"]) - - if hardware._prealloc_pages: - argv_source += ["-mem-path", "/dev/shm", - "-mem-prealloc"] - if hardware._locked_pages: - argv_source += ["-overcommit", "mem-lock=on"] - if hardware._huge_pages: - pass - - return argv - - def _get_src_args(self, hardware): - return self._get_common_args(hardware) - - def _get_dst_args(self, hardware, uri): - tunnelled = False - if self._dst_host != "localhost": - tunnelled = True - argv = self._get_common_args(hardware, tunnelled) - return argv + ["-incoming", uri] - - @staticmethod - def _get_common_wrapper(cpu_bind, mem_bind): - wrapper = [] - if len(cpu_bind) > 0 or len(mem_bind) > 0: - wrapper.append("numactl") - if cpu_bind: - wrapper.append("--physcpubind=%s" % ",".join(cpu_bind)) - if mem_bind: - wrapper.append("--membind=%s" % ",".join(mem_bind)) - - return wrapper - - def _get_src_wrapper(self, hardware): - return self._get_common_wrapper(hardware._src_cpu_bind, hardware._src_mem_bind) - - def _get_dst_wrapper(self, hardware): - wrapper = self._get_common_wrapper(hardware._dst_cpu_bind, hardware._dst_mem_bind) - if self._dst_host != "localhost": - return ["ssh", - "-R", "9001:localhost:9001", - self._dst_host] + wrapper - else: - return wrapper - - def _get_timings(self, vm): - log = vm.get_log() - if not log: - return [] - if self._debug: - print(log) - - regex = r"[^\s]+\s\((\d+)\):\sINFO:\s(\d+)ms\scopied\s\d+\sGB\sin\s(\d+)ms" - matcher = re.compile(regex) - records = [] - for line in log.split("\n"): - match = matcher.match(line) - if match: - records.append(TimingRecord(int(match.group(1)), - int(match.group(2)) / 1000.0, - int(match.group(3)))) - return records - - def run(self, hardware, scenario, result_dir=os.getcwd()): - abs_result_dir = os.path.join(result_dir, scenario._name) - - if self._transport == "tcp": - uri = "tcp:%s:9000" % self._dst_host - elif self._transport == "rdma": - uri = "rdma:%s:9000" % self._dst_host - elif self._transport == "unix": - if self._dst_host != "localhost": - raise Exception("Running use unix migration transport for non-local host") - uri = "unix:/var/tmp/qemu-migrate-%d.migrate" % os.getpid() - try: - os.remove(uri[5:]) - os.remove(monaddr) - except: - pass - - if self._dst_host != "localhost": - dstmonaddr = ("localhost", 9001) - else: - dstmonaddr = "/var/tmp/qemu-dst-%d-monitor.sock" % os.getpid() - srcmonaddr = "/var/tmp/qemu-src-%d-monitor.sock" % os.getpid() - - src = QEMUMachine(self._binary, - args=self._get_src_args(hardware), - wrapper=self._get_src_wrapper(hardware), - name="qemu-src-%d" % os.getpid(), - monitor_address=srcmonaddr) - - dst = QEMUMachine(self._binary, - args=self._get_dst_args(hardware, uri), - wrapper=self._get_dst_wrapper(hardware), - name="qemu-dst-%d" % os.getpid(), - monitor_address=dstmonaddr) - - try: - src.launch() - dst.launch() - - ret = self._migrate(hardware, scenario, src, dst, uri) - progress_history = ret[0] - qemu_timings = ret[1] - vcpu_timings = ret[2] - if uri[0:5] == "unix:" and os.path.exists(uri[5:]): - os.remove(uri[5:]) - - if os.path.exists(srcmonaddr): - os.remove(srcmonaddr) - - if self._dst_host == "localhost" and os.path.exists(dstmonaddr): - os.remove(dstmonaddr) - - if self._verbose: - print("Finished migration") - - src.shutdown() - dst.shutdown() - - return Report(hardware, scenario, progress_history, - Timings(self._get_timings(src) + self._get_timings(dst)), - Timings(qemu_timings), - Timings(vcpu_timings), - self._binary, self._dst_host, self._kernel, - self._initrd, self._transport, self._sleep) - except Exception as e: - if self._debug: - print("Failed: %s" % str(e)) - try: - src.shutdown() - except: - pass - try: - dst.shutdown() - except: - pass - - if self._debug: - print(src.get_log()) - print(dst.get_log()) - raise - diff --git a/tests/migration/guestperf/hardware.py b/tests/migration/guestperf/hardware.py deleted file mode 100644 index f779cc0..0000000 --- a/tests/migration/guestperf/hardware.py +++ /dev/null @@ -1,66 +0,0 @@ -# -# Migration test hardware configuration description -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - - -class Hardware(object): - def __init__(self, cpus=1, mem=1, - src_cpu_bind=None, src_mem_bind=None, - dst_cpu_bind=None, dst_mem_bind=None, - prealloc_pages = False, - huge_pages=False, locked_pages=False, - dirty_ring_size=0): - self._cpus = cpus - self._mem = mem # GiB - self._src_mem_bind = src_mem_bind # List of NUMA nodes - self._src_cpu_bind = src_cpu_bind # List of pCPUs - self._dst_mem_bind = dst_mem_bind # List of NUMA nodes - self._dst_cpu_bind = dst_cpu_bind # List of pCPUs - self._prealloc_pages = prealloc_pages - self._huge_pages = huge_pages - self._locked_pages = locked_pages - self._dirty_ring_size = dirty_ring_size - - - def serialize(self): - return { - "cpus": self._cpus, - "mem": self._mem, - "src_mem_bind": self._src_mem_bind, - "dst_mem_bind": self._dst_mem_bind, - "src_cpu_bind": self._src_cpu_bind, - "dst_cpu_bind": self._dst_cpu_bind, - "prealloc_pages": self._prealloc_pages, - "huge_pages": self._huge_pages, - "locked_pages": self._locked_pages, - "dirty_ring_size": self._dirty_ring_size, - } - - @classmethod - def deserialize(cls, data): - return cls( - data["cpus"], - data["mem"], - data["src_cpu_bind"], - data["src_mem_bind"], - data["dst_cpu_bind"], - data["dst_mem_bind"], - data["prealloc_pages"], - data["huge_pages"], - data["locked_pages"], - data["dirty_ring_size"]) diff --git a/tests/migration/guestperf/plot.py b/tests/migration/guestperf/plot.py deleted file mode 100644 index 30b3f66..0000000 --- a/tests/migration/guestperf/plot.py +++ /dev/null @@ -1,623 +0,0 @@ -# -# Migration test graph plotting -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - -import sys - - -class Plot(object): - - # Generated using - # http://tools.medialab.sciences-po.fr/iwanthue/ - COLORS = ["#CD54D0", - "#79D94C", - "#7470CD", - "#D2D251", - "#863D79", - "#76DDA6", - "#D4467B", - "#61923D", - "#CB9CCA", - "#D98F36", - "#8CC8DA", - "#CE4831", - "#5E7693", - "#9B803F", - "#412F4C", - "#CECBA6", - "#6D3229", - "#598B73", - "#C8827C", - "#394427"] - - def __init__(self, - reports, - migration_iters, - total_guest_cpu, - split_guest_cpu, - qemu_cpu, - vcpu_cpu): - - self._reports = reports - self._migration_iters = migration_iters - self._total_guest_cpu = total_guest_cpu - self._split_guest_cpu = split_guest_cpu - self._qemu_cpu = qemu_cpu - self._vcpu_cpu = vcpu_cpu - self._color_idx = 0 - - def _next_color(self): - color = self.COLORS[self._color_idx] - self._color_idx += 1 - if self._color_idx >= len(self.COLORS): - self._color_idx = 0 - return color - - def _get_progress_label(self, progress): - if progress: - return "\n\n" + "\n".join( - ["Status: %s" % progress._status, - "Iteration: %d" % progress._ram._iterations, - "Throttle: %02d%%" % progress._throttle_pcent, - "Dirty rate: %dMB/s" % (progress._ram._dirty_rate_pps * 4 / 1024.0)]) - else: - return "\n\n" + "\n".join( - ["Status: %s" % "none", - "Iteration: %d" % 0]) - - def _find_start_time(self, report): - startqemu = report._qemu_timings._records[0]._timestamp - startguest = report._guest_timings._records[0]._timestamp - if startqemu < startguest: - return startqemu - else: - return stasrtguest - - def _get_guest_max_value(self, report): - maxvalue = 0 - for record in report._guest_timings._records: - if record._value > maxvalue: - maxvalue = record._value - return maxvalue - - def _get_qemu_max_value(self, report): - maxvalue = 0 - oldvalue = None - oldtime = None - for record in report._qemu_timings._records: - if oldvalue is not None: - cpudelta = (record._value - oldvalue) / 1000.0 - timedelta = record._timestamp - oldtime - if timedelta == 0: - continue - util = cpudelta / timedelta * 100.0 - else: - util = 0 - oldvalue = record._value - oldtime = record._timestamp - - if util > maxvalue: - maxvalue = util - return maxvalue - - def _get_total_guest_cpu_graph(self, report, starttime): - xaxis = [] - yaxis = [] - labels = [] - progress_idx = -1 - for record in report._guest_timings._records: - while ((progress_idx + 1) < len(report._progress_history) and - report._progress_history[progress_idx + 1]._now < record._timestamp): - progress_idx = progress_idx + 1 - - if progress_idx >= 0: - progress = report._progress_history[progress_idx] - else: - progress = None - - xaxis.append(record._timestamp - starttime) - yaxis.append(record._value) - labels.append(self._get_progress_label(progress)) - - from plotly import graph_objs as go - return go.Scatter(x=xaxis, - y=yaxis, - name="Guest PIDs: %s" % report._scenario._name, - mode='lines', - line={ - "dash": "solid", - "color": self._next_color(), - "shape": "linear", - "width": 1 - }, - text=labels) - - def _get_split_guest_cpu_graphs(self, report, starttime): - threads = {} - for record in report._guest_timings._records: - if record._tid in threads: - continue - threads[record._tid] = { - "xaxis": [], - "yaxis": [], - "labels": [], - } - - progress_idx = -1 - for record in report._guest_timings._records: - while ((progress_idx + 1) < len(report._progress_history) and - report._progress_history[progress_idx + 1]._now < record._timestamp): - progress_idx = progress_idx + 1 - - if progress_idx >= 0: - progress = report._progress_history[progress_idx] - else: - progress = None - - threads[record._tid]["xaxis"].append(record._timestamp - starttime) - threads[record._tid]["yaxis"].append(record._value) - threads[record._tid]["labels"].append(self._get_progress_label(progress)) - - - graphs = [] - from plotly import graph_objs as go - for tid in threads.keys(): - graphs.append( - go.Scatter(x=threads[tid]["xaxis"], - y=threads[tid]["yaxis"], - name="PID %s: %s" % (tid, report._scenario._name), - mode="lines", - line={ - "dash": "solid", - "color": self._next_color(), - "shape": "linear", - "width": 1 - }, - text=threads[tid]["labels"])) - return graphs - - def _get_migration_iters_graph(self, report, starttime): - xaxis = [] - yaxis = [] - labels = [] - for progress in report._progress_history: - xaxis.append(progress._now - starttime) - yaxis.append(0) - labels.append(self._get_progress_label(progress)) - - from plotly import graph_objs as go - return go.Scatter(x=xaxis, - y=yaxis, - text=labels, - name="Migration iterations", - mode="markers", - marker={ - "color": self._next_color(), - "symbol": "star", - "size": 5 - }) - - def _get_qemu_cpu_graph(self, report, starttime): - xaxis = [] - yaxis = [] - labels = [] - progress_idx = -1 - - first = report._qemu_timings._records[0] - abstimestamps = [first._timestamp] - absvalues = [first._value] - - for record in report._qemu_timings._records[1:]: - while ((progress_idx + 1) < len(report._progress_history) and - report._progress_history[progress_idx + 1]._now < record._timestamp): - progress_idx = progress_idx + 1 - - if progress_idx >= 0: - progress = report._progress_history[progress_idx] - else: - progress = None - - oldvalue = absvalues[-1] - oldtime = abstimestamps[-1] - - cpudelta = (record._value - oldvalue) / 1000.0 - timedelta = record._timestamp - oldtime - if timedelta == 0: - continue - util = cpudelta / timedelta * 100.0 - - abstimestamps.append(record._timestamp) - absvalues.append(record._value) - - xaxis.append(record._timestamp - starttime) - yaxis.append(util) - labels.append(self._get_progress_label(progress)) - - from plotly import graph_objs as go - return go.Scatter(x=xaxis, - y=yaxis, - yaxis="y2", - name="QEMU: %s" % report._scenario._name, - mode='lines', - line={ - "dash": "solid", - "color": self._next_color(), - "shape": "linear", - "width": 1 - }, - text=labels) - - def _get_vcpu_cpu_graphs(self, report, starttime): - threads = {} - for record in report._vcpu_timings._records: - if record._tid in threads: - continue - threads[record._tid] = { - "xaxis": [], - "yaxis": [], - "labels": [], - "absvalue": [record._value], - "abstime": [record._timestamp], - } - - progress_idx = -1 - for record in report._vcpu_timings._records: - while ((progress_idx + 1) < len(report._progress_history) and - report._progress_history[progress_idx + 1]._now < record._timestamp): - progress_idx = progress_idx + 1 - - if progress_idx >= 0: - progress = report._progress_history[progress_idx] - else: - progress = None - - oldvalue = threads[record._tid]["absvalue"][-1] - oldtime = threads[record._tid]["abstime"][-1] - - cpudelta = (record._value - oldvalue) / 1000.0 - timedelta = record._timestamp - oldtime - if timedelta == 0: - continue - util = cpudelta / timedelta * 100.0 - if util > 100: - util = 100 - - threads[record._tid]["absvalue"].append(record._value) - threads[record._tid]["abstime"].append(record._timestamp) - - threads[record._tid]["xaxis"].append(record._timestamp - starttime) - threads[record._tid]["yaxis"].append(util) - threads[record._tid]["labels"].append(self._get_progress_label(progress)) - - - graphs = [] - from plotly import graph_objs as go - for tid in threads.keys(): - graphs.append( - go.Scatter(x=threads[tid]["xaxis"], - y=threads[tid]["yaxis"], - yaxis="y2", - name="VCPU %s: %s" % (tid, report._scenario._name), - mode="lines", - line={ - "dash": "solid", - "color": self._next_color(), - "shape": "linear", - "width": 1 - }, - text=threads[tid]["labels"])) - return graphs - - def _generate_chart_report(self, report): - graphs = [] - starttime = self._find_start_time(report) - if self._total_guest_cpu: - graphs.append(self._get_total_guest_cpu_graph(report, starttime)) - if self._split_guest_cpu: - graphs.extend(self._get_split_guest_cpu_graphs(report, starttime)) - if self._qemu_cpu: - graphs.append(self._get_qemu_cpu_graph(report, starttime)) - if self._vcpu_cpu: - graphs.extend(self._get_vcpu_cpu_graphs(report, starttime)) - if self._migration_iters: - graphs.append(self._get_migration_iters_graph(report, starttime)) - return graphs - - def _generate_annotation(self, starttime, progress): - return { - "text": progress._status, - "x": progress._now - starttime, - "y": 10, - } - - def _generate_annotations(self, report): - starttime = self._find_start_time(report) - annotations = {} - started = False - for progress in report._progress_history: - if progress._status == "setup": - continue - if progress._status not in annotations: - annotations[progress._status] = self._generate_annotation(starttime, progress) - - return annotations.values() - - def _generate_chart(self): - from plotly.offline import plot - from plotly import graph_objs as go - - graphs = [] - yaxismax = 0 - yaxismax2 = 0 - for report in self._reports: - graphs.extend(self._generate_chart_report(report)) - - maxvalue = self._get_guest_max_value(report) - if maxvalue > yaxismax: - yaxismax = maxvalue - - maxvalue = self._get_qemu_max_value(report) - if maxvalue > yaxismax2: - yaxismax2 = maxvalue - - yaxismax += 100 - if not self._qemu_cpu: - yaxismax2 = 110 - yaxismax2 += 10 - - annotations = [] - if self._migration_iters: - for report in self._reports: - annotations.extend(self._generate_annotations(report)) - - layout = go.Layout(title="Migration comparison", - xaxis={ - "title": "Wallclock time (secs)", - "showgrid": False, - }, - yaxis={ - "title": "Memory update speed (ms/GB)", - "showgrid": False, - "range": [0, yaxismax], - }, - yaxis2={ - "title": "Hostutilization (%)", - "overlaying": "y", - "side": "right", - "range": [0, yaxismax2], - "showgrid": False, - }, - annotations=annotations) - - figure = go.Figure(data=graphs, layout=layout) - - return plot(figure, - show_link=False, - include_plotlyjs=False, - output_type="div") - - - def _generate_report(self): - pieces = [] - for report in self._reports: - pieces.append(""" -

Report %s

- -""" % report._scenario._name) - - pieces.append(""" - - - - - - - - - - - - - - - - - - - - - - - -""" % (report._binary, report._kernel, - report._initrd, report._transport, report._dst_host)) - - hardware = report._hardware - pieces.append(""" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -""" % (hardware._cpus, hardware._mem, - ",".join(hardware._src_cpu_bind), - ",".join(hardware._src_mem_bind), - ",".join(hardware._dst_cpu_bind), - ",".join(hardware._dst_mem_bind), - "yes" if hardware._prealloc_pages else "no", - "yes" if hardware._locked_pages else "no", - "yes" if hardware._huge_pages else "no")) - - scenario = report._scenario - pieces.append(""" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -""" % (scenario._downtime, scenario._bandwidth, - scenario._max_iters, scenario._max_time, - "yes" if scenario._pause else "no", scenario._pause_iters, - "yes" if scenario._post_copy else "no", scenario._post_copy_iters, - "yes" if scenario._auto_converge else "no", scenario._auto_converge_step, - "yes" if scenario._compression_mt else "no", scenario._compression_mt_threads, - "yes" if scenario._compression_xbzrle else "no", scenario._compression_xbzrle_cache)) - - pieces.append(""" -
Test config
Emulator:%s
Kernel:%s
Ramdisk:%s
Transport:%s
Host:%s
Hardware config
CPUs:%d
RAM:%d GB
Source CPU bind:%s
Source RAM bind:%s
Dest CPU bind:%s
Dest RAM bind:%s
Preallocate RAM:%s
Locked RAM:%s
Huge pages:%s
Scenario config
Max downtime:%d milli-sec
Max bandwidth:%d MB/sec
Max iters:%d
Max time:%d secs
Pause:%s
Pause iters:%d
Post-copy:%s
Post-copy iters:%d
Auto-converge:%s
Auto-converge iters:%d
MT compression:%s
MT compression threads:%d
XBZRLE compression:%s
XBZRLE compression cache:%d%% of RAM
-""") - - return "\n".join(pieces) - - def _generate_style(self): - return """ -#report table tr th { - text-align: right; -} -#report table tr td { - text-align: left; -} -#report table tr.subhead th { - background: rgb(192, 192, 192); - text-align: center; -} - -""" - - def generate_html(self, fh): - print(""" - - - - Migration report - - -

Migration report

-

Chart summary

-
-""" % self._generate_style(), file=fh) - print(self._generate_chart(), file=fh) - print(""" -
-

Report details

-
-""", file=fh) - print(self._generate_report(), file=fh) - print(""" -
- - -""", file=fh) - - def generate(self, filename): - if filename is None: - self.generate_html(sys.stdout) - else: - with open(filename, "w") as fh: - self.generate_html(fh) diff --git a/tests/migration/guestperf/progress.py b/tests/migration/guestperf/progress.py deleted file mode 100644 index d490584..0000000 --- a/tests/migration/guestperf/progress.py +++ /dev/null @@ -1,129 +0,0 @@ -# -# Migration test migration operation progress -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - - -class ProgressStats(object): - - def __init__(self, - transferred_bytes, - remaining_bytes, - total_bytes, - duplicate_pages, - skipped_pages, - normal_pages, - normal_bytes, - dirty_rate_pps, - transfer_rate_mbs, - iterations): - self._transferred_bytes = transferred_bytes - self._remaining_bytes = remaining_bytes - self._total_bytes = total_bytes - self._duplicate_pages = duplicate_pages - self._skipped_pages = skipped_pages - self._normal_pages = normal_pages - self._normal_bytes = normal_bytes - self._dirty_rate_pps = dirty_rate_pps - self._transfer_rate_mbs = transfer_rate_mbs - self._iterations = iterations - - def serialize(self): - return { - "transferred_bytes": self._transferred_bytes, - "remaining_bytes": self._remaining_bytes, - "total_bytes": self._total_bytes, - "duplicate_pages": self._duplicate_pages, - "skipped_pages": self._skipped_pages, - "normal_pages": self._normal_pages, - "normal_bytes": self._normal_bytes, - "dirty_rate_pps": self._dirty_rate_pps, - "transfer_rate_mbs": self._transfer_rate_mbs, - "iterations": self._iterations, - } - - @classmethod - def deserialize(cls, data): - return cls( - data["transferred_bytes"], - data["remaining_bytes"], - data["total_bytes"], - data["duplicate_pages"], - data["skipped_pages"], - data["normal_pages"], - data["normal_bytes"], - data["dirty_rate_pps"], - data["transfer_rate_mbs"], - data["iterations"]) - - -class Progress(object): - - def __init__(self, - status, - ram, - now, - duration, - downtime, - downtime_expected, - setup_time, - throttle_pcent, - dirty_limit_throttle_time_per_round, - dirty_limit_ring_full_time): - - self._status = status - self._ram = ram - self._now = now - self._duration = duration - self._downtime = downtime - self._downtime_expected = downtime_expected - self._setup_time = setup_time - self._throttle_pcent = throttle_pcent - self._dirty_limit_throttle_time_per_round = \ - dirty_limit_throttle_time_per_round - self._dirty_limit_ring_full_time = \ - dirty_limit_ring_full_time - - def serialize(self): - return { - "status": self._status, - "ram": self._ram.serialize(), - "now": self._now, - "duration": self._duration, - "downtime": self._downtime, - "downtime_expected": self._downtime_expected, - "setup_time": self._setup_time, - "throttle_pcent": self._throttle_pcent, - "dirty_limit_throttle_time_per_round": - self._dirty_limit_throttle_time_per_round, - "dirty_limit_ring_full_time": - self._dirty_limit_ring_full_time, - } - - @classmethod - def deserialize(cls, data): - return cls( - data["status"], - ProgressStats.deserialize(data["ram"]), - data["now"], - data["duration"], - data["downtime"], - data["downtime_expected"], - data["setup_time"], - data["throttle_pcent"], - data["dirty_limit_throttle_time_per_round"], - data["dirty_limit_ring_full_time"]) diff --git a/tests/migration/guestperf/report.py b/tests/migration/guestperf/report.py deleted file mode 100644 index 1efd40c..0000000 --- a/tests/migration/guestperf/report.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# Migration test output result reporting -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - -import json - -from guestperf.hardware import Hardware -from guestperf.scenario import Scenario -from guestperf.progress import Progress -from guestperf.timings import Timings - -class Report(object): - - def __init__(self, - hardware, - scenario, - progress_history, - guest_timings, - qemu_timings, - vcpu_timings, - binary, - dst_host, - kernel, - initrd, - transport, - sleep): - - self._hardware = hardware - self._scenario = scenario - self._progress_history = progress_history - self._guest_timings = guest_timings - self._qemu_timings = qemu_timings - self._vcpu_timings = vcpu_timings - self._binary = binary - self._dst_host = dst_host - self._kernel = kernel - self._initrd = initrd - self._transport = transport - self._sleep = sleep - - def serialize(self): - return { - "hardware": self._hardware.serialize(), - "scenario": self._scenario.serialize(), - "progress_history": [progress.serialize() for progress in self._progress_history], - "guest_timings": self._guest_timings.serialize(), - "qemu_timings": self._qemu_timings.serialize(), - "vcpu_timings": self._vcpu_timings.serialize(), - "binary": self._binary, - "dst_host": self._dst_host, - "kernel": self._kernel, - "initrd": self._initrd, - "transport": self._transport, - "sleep": self._sleep, - } - - @classmethod - def deserialize(cls, data): - return cls( - Hardware.deserialize(data["hardware"]), - Scenario.deserialize(data["scenario"]), - [Progress.deserialize(record) for record in data["progress_history"]], - Timings.deserialize(data["guest_timings"]), - Timings.deserialize(data["qemu_timings"]), - Timings.deserialize(data["vcpu_timings"]), - data["binary"], - data["dst_host"], - data["kernel"], - data["initrd"], - data["transport"], - data["sleep"]) - - def to_json(self): - return json.dumps(self.serialize(), indent=4) - - @classmethod - def from_json(cls, data): - return cls.deserialize(json.loads(data)) - - @classmethod - def from_json_file(cls, filename): - with open(filename, "r") as fh: - return cls.deserialize(json.load(fh)) diff --git a/tests/migration/guestperf/scenario.py b/tests/migration/guestperf/scenario.py deleted file mode 100644 index 154c4f5..0000000 --- a/tests/migration/guestperf/scenario.py +++ /dev/null @@ -1,112 +0,0 @@ -# -# Migration test scenario parameter description -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - - -class Scenario(object): - - def __init__(self, name, - downtime=500, - bandwidth=125000, # 1000 gig-e, effectively unlimited - max_iters=30, - max_time=300, - pause=False, pause_iters=5, - post_copy=False, post_copy_iters=5, - auto_converge=False, auto_converge_step=10, - compression_mt=False, compression_mt_threads=1, - compression_xbzrle=False, compression_xbzrle_cache=10, - multifd=False, multifd_channels=2, - dirty_limit=False, x_vcpu_dirty_limit_period=500, - vcpu_dirty_limit=1): - - self._name = name - - # General migration tunables - self._downtime = downtime # milliseconds - self._bandwidth = bandwidth # MiB per second - self._max_iters = max_iters - self._max_time = max_time # seconds - - - # Strategies for ensuring completion - self._pause = pause - self._pause_iters = pause_iters - - self._post_copy = post_copy - self._post_copy_iters = post_copy_iters - - self._auto_converge = auto_converge - self._auto_converge_step = auto_converge_step # percentage CPU time - - self._compression_mt = compression_mt - self._compression_mt_threads = compression_mt_threads - - self._compression_xbzrle = compression_xbzrle - self._compression_xbzrle_cache = compression_xbzrle_cache # percentage of guest RAM - - self._multifd = multifd - self._multifd_channels = multifd_channels - - self._dirty_limit = dirty_limit - self._x_vcpu_dirty_limit_period = x_vcpu_dirty_limit_period - self._vcpu_dirty_limit = vcpu_dirty_limit - - def serialize(self): - return { - "name": self._name, - "downtime": self._downtime, - "bandwidth": self._bandwidth, - "max_iters": self._max_iters, - "max_time": self._max_time, - "pause": self._pause, - "pause_iters": self._pause_iters, - "post_copy": self._post_copy, - "post_copy_iters": self._post_copy_iters, - "auto_converge": self._auto_converge, - "auto_converge_step": self._auto_converge_step, - "compression_mt": self._compression_mt, - "compression_mt_threads": self._compression_mt_threads, - "compression_xbzrle": self._compression_xbzrle, - "compression_xbzrle_cache": self._compression_xbzrle_cache, - "multifd": self._multifd, - "multifd_channels": self._multifd_channels, - "dirty_limit": self._dirty_limit, - "x_vcpu_dirty_limit_period": self._x_vcpu_dirty_limit_period, - "vcpu_dirty_limit": self._vcpu_dirty_limit, - } - - @classmethod - def deserialize(cls, data): - return cls( - data["name"], - data["downtime"], - data["bandwidth"], - data["max_iters"], - data["max_time"], - data["pause"], - data["pause_iters"], - data["post_copy"], - data["post_copy_iters"], - data["auto_converge"], - data["auto_converge_step"], - data["compression_mt"], - data["compression_mt_threads"], - data["compression_xbzrle"], - data["compression_xbzrle_cache"], - data["multifd"], - data["multifd_channels"]) diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py deleted file mode 100644 index c85d89e..0000000 --- a/tests/migration/guestperf/shell.py +++ /dev/null @@ -1,296 +0,0 @@ -# -# Migration test command line shell integration -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - - -import argparse -import fnmatch -import os -import os.path -import platform -import sys -import logging - -from guestperf.hardware import Hardware -from guestperf.engine import Engine -from guestperf.scenario import Scenario -from guestperf.comparison import COMPARISONS -from guestperf.plot import Plot -from guestperf.report import Report - - -class BaseShell(object): - - def __init__(self): - parser = argparse.ArgumentParser(description="Migration Test Tool") - - # Test args - parser.add_argument("--debug", dest="debug", default=False, action="store_true") - parser.add_argument("--verbose", dest="verbose", default=False, action="store_true") - parser.add_argument("--sleep", dest="sleep", default=15, type=int) - parser.add_argument("--binary", dest="binary", default="/usr/bin/qemu-system-x86_64") - parser.add_argument("--dst-host", dest="dst_host", default="localhost") - parser.add_argument("--kernel", dest="kernel", default="/boot/vmlinuz-%s" % platform.release()) - parser.add_argument("--initrd", dest="initrd", default="tests/migration/initrd-stress.img") - parser.add_argument("--transport", dest="transport", default="unix") - - - # Hardware args - parser.add_argument("--cpus", dest="cpus", default=1, type=int) - parser.add_argument("--mem", dest="mem", default=1, type=int) - parser.add_argument("--src-cpu-bind", dest="src_cpu_bind", default="") - parser.add_argument("--src-mem-bind", dest="src_mem_bind", default="") - parser.add_argument("--dst-cpu-bind", dest="dst_cpu_bind", default="") - parser.add_argument("--dst-mem-bind", dest="dst_mem_bind", default="") - parser.add_argument("--prealloc-pages", dest="prealloc_pages", default=False) - parser.add_argument("--huge-pages", dest="huge_pages", default=False) - parser.add_argument("--locked-pages", dest="locked_pages", default=False) - parser.add_argument("--dirty-ring-size", dest="dirty_ring_size", - default=0, type=int) - - self._parser = parser - - def get_engine(self, args): - return Engine(binary=args.binary, - dst_host=args.dst_host, - kernel=args.kernel, - initrd=args.initrd, - transport=args.transport, - sleep=args.sleep, - debug=args.debug, - verbose=args.verbose) - - def get_hardware(self, args): - def split_map(value): - if value == "": - return [] - return value.split(",") - - return Hardware(cpus=args.cpus, - mem=args.mem, - - src_cpu_bind=split_map(args.src_cpu_bind), - src_mem_bind=split_map(args.src_mem_bind), - dst_cpu_bind=split_map(args.dst_cpu_bind), - dst_mem_bind=split_map(args.dst_mem_bind), - - locked_pages=args.locked_pages, - huge_pages=args.huge_pages, - prealloc_pages=args.prealloc_pages, - - dirty_ring_size=args.dirty_ring_size) - - -class Shell(BaseShell): - - def __init__(self): - super(Shell, self).__init__() - - parser = self._parser - - parser.add_argument("--output", dest="output", default=None) - - # Scenario args - parser.add_argument("--max-iters", dest="max_iters", default=30, type=int) - parser.add_argument("--max-time", dest="max_time", default=300, type=int) - parser.add_argument("--bandwidth", dest="bandwidth", default=125000, type=int) - parser.add_argument("--downtime", dest="downtime", default=500, type=int) - - parser.add_argument("--pause", dest="pause", default=False, action="store_true") - parser.add_argument("--pause-iters", dest="pause_iters", default=5, type=int) - - parser.add_argument("--post-copy", dest="post_copy", default=False, action="store_true") - parser.add_argument("--post-copy-iters", dest="post_copy_iters", default=5, type=int) - - parser.add_argument("--auto-converge", dest="auto_converge", default=False, action="store_true") - parser.add_argument("--auto-converge-step", dest="auto_converge_step", default=10, type=int) - - parser.add_argument("--compression-mt", dest="compression_mt", default=False, action="store_true") - parser.add_argument("--compression-mt-threads", dest="compression_mt_threads", default=1, type=int) - - parser.add_argument("--compression-xbzrle", dest="compression_xbzrle", default=False, action="store_true") - parser.add_argument("--compression-xbzrle-cache", dest="compression_xbzrle_cache", default=10, type=int) - - parser.add_argument("--multifd", dest="multifd", default=False, - action="store_true") - parser.add_argument("--multifd-channels", dest="multifd_channels", - default=2, type=int) - - parser.add_argument("--dirty-limit", dest="dirty_limit", default=False, - action="store_true") - - parser.add_argument("--x-vcpu-dirty-limit-period", - dest="x_vcpu_dirty_limit_period", - default=500, type=int) - - parser.add_argument("--vcpu-dirty-limit", - dest="vcpu_dirty_limit", - default=1, type=int) - - def get_scenario(self, args): - return Scenario(name="perfreport", - downtime=args.downtime, - bandwidth=args.bandwidth, - max_iters=args.max_iters, - max_time=args.max_time, - - pause=args.pause, - pause_iters=args.pause_iters, - - post_copy=args.post_copy, - post_copy_iters=args.post_copy_iters, - - auto_converge=args.auto_converge, - auto_converge_step=args.auto_converge_step, - - compression_mt=args.compression_mt, - compression_mt_threads=args.compression_mt_threads, - - compression_xbzrle=args.compression_xbzrle, - compression_xbzrle_cache=args.compression_xbzrle_cache, - - multifd=args.multifd, - multifd_channels=args.multifd_channels, - - dirty_limit=args.dirty_limit, - x_vcpu_dirty_limit_period=\ - args.x_vcpu_dirty_limit_period, - vcpu_dirty_limit=args.vcpu_dirty_limit) - - def run(self, argv): - args = self._parser.parse_args(argv) - logging.basicConfig(level=(logging.DEBUG if args.debug else - logging.INFO if args.verbose else - logging.WARN)) - - - engine = self.get_engine(args) - hardware = self.get_hardware(args) - scenario = self.get_scenario(args) - - try: - report = engine.run(hardware, scenario) - if args.output is None: - print(report.to_json()) - else: - with open(args.output, "w") as fh: - print(report.to_json(), file=fh) - return 0 - except Exception as e: - print("Error: %s" % str(e), file=sys.stderr) - if args.debug: - raise - return 1 - - -class BatchShell(BaseShell): - - def __init__(self): - super(BatchShell, self).__init__() - - parser = self._parser - - parser.add_argument("--filter", dest="filter", default="*") - parser.add_argument("--output", dest="output", default=os.getcwd()) - - def run(self, argv): - args = self._parser.parse_args(argv) - logging.basicConfig(level=(logging.DEBUG if args.debug else - logging.INFO if args.verbose else - logging.WARN)) - - - engine = self.get_engine(args) - hardware = self.get_hardware(args) - - try: - for comparison in COMPARISONS: - compdir = os.path.join(args.output, comparison._name) - for scenario in comparison._scenarios: - name = os.path.join(comparison._name, scenario._name) - if not fnmatch.fnmatch(name, args.filter): - if args.verbose: - print("Skipping %s" % name) - continue - - if args.verbose: - print("Running %s" % name) - - dirname = os.path.join(args.output, comparison._name) - filename = os.path.join(dirname, scenario._name + ".json") - if not os.path.exists(dirname): - os.makedirs(dirname) - report = engine.run(hardware, scenario) - with open(filename, "w") as fh: - print(report.to_json(), file=fh) - except Exception as e: - print("Error: %s" % str(e), file=sys.stderr) - if args.debug: - raise - - -class PlotShell(object): - - def __init__(self): - super(PlotShell, self).__init__() - - self._parser = argparse.ArgumentParser(description="Migration Test Tool") - - self._parser.add_argument("--output", dest="output", default=None) - - self._parser.add_argument("--debug", dest="debug", default=False, action="store_true") - self._parser.add_argument("--verbose", dest="verbose", default=False, action="store_true") - - self._parser.add_argument("--migration-iters", dest="migration_iters", default=False, action="store_true") - self._parser.add_argument("--total-guest-cpu", dest="total_guest_cpu", default=False, action="store_true") - self._parser.add_argument("--split-guest-cpu", dest="split_guest_cpu", default=False, action="store_true") - self._parser.add_argument("--qemu-cpu", dest="qemu_cpu", default=False, action="store_true") - self._parser.add_argument("--vcpu-cpu", dest="vcpu_cpu", default=False, action="store_true") - - self._parser.add_argument("reports", nargs='*') - - def run(self, argv): - args = self._parser.parse_args(argv) - logging.basicConfig(level=(logging.DEBUG if args.debug else - logging.INFO if args.verbose else - logging.WARN)) - - - if len(args.reports) == 0: - print("At least one report required", file=sys.stderr) - return 1 - - if not (args.qemu_cpu or - args.vcpu_cpu or - args.total_guest_cpu or - args.split_guest_cpu): - print("At least one chart type is required", file=sys.stderr) - return 1 - - reports = [] - for report in args.reports: - reports.append(Report.from_json_file(report)) - - plot = Plot(reports, - args.migration_iters, - args.total_guest_cpu, - args.split_guest_cpu, - args.qemu_cpu, - args.vcpu_cpu) - - plot.generate(args.output) diff --git a/tests/migration/guestperf/timings.py b/tests/migration/guestperf/timings.py deleted file mode 100644 index 2374010..0000000 --- a/tests/migration/guestperf/timings.py +++ /dev/null @@ -1,55 +0,0 @@ -# -# Migration test timing records -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - - -class TimingRecord(object): - - def __init__(self, tid, timestamp, value): - - self._tid = tid - self._timestamp = timestamp - self._value = value - - def serialize(self): - return { - "tid": self._tid, - "timestamp": self._timestamp, - "value": self._value - } - - @classmethod - def deserialize(cls, data): - return cls( - data["tid"], - data["timestamp"], - data["value"]) - - -class Timings(object): - - def __init__(self, records): - - self._records = records - - def serialize(self): - return [record.serialize() for record in self._records] - - @classmethod - def deserialize(cls, data): - return Timings([TimingRecord.deserialize(record) for record in data]) diff --git a/tests/migration/i386/Makefile b/tests/migration/i386/Makefile deleted file mode 100644 index 37a72ae..0000000 --- a/tests/migration/i386/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# To specify cross compiler prefix, use CROSS_PREFIX= -# $ make CROSS_PREFIX=x86_64-linux-gnu- - -.PHONY: all clean -all: a-b-bootblock.h - -a-b-bootblock.h: x86.bootsect x86.o - echo "$$__note" > header.tmp - xxd -i $< | sed -e 's/.*int.*//' >> header.tmp - nm x86.o | awk '{print "#define SYM_"$$3" 0x"$$1}' >> header.tmp - mv header.tmp $@ - -x86.bootsect: x86.boot - dd if=$< of=$@ bs=256 count=2 skip=124 - -x86.boot: x86.o - $(CROSS_PREFIX)objcopy -O binary $< $@ - -x86.o: a-b-bootblock.S - $(CROSS_PREFIX)gcc -I.. -m32 -march=i486 -c $< -o $@ - -clean: - @rm -rf *.boot *.o *.bootsect diff --git a/tests/migration/i386/a-b-bootblock.S b/tests/migration/i386/a-b-bootblock.S deleted file mode 100644 index 6f39eb6..0000000 --- a/tests/migration/i386/a-b-bootblock.S +++ /dev/null @@ -1,145 +0,0 @@ -# x86 bootblock used in migration test -# repeatedly increments the first byte of each page in a 100MB -# range. -# Outputs an initial 'A' on serial followed by repeated 'B's -# -# Copyright (c) 2016 Red Hat, Inc. and/or its affiliates -# This work is licensed under the terms of the GNU GPL, version 2 or later. -# See the COPYING file in the top-level directory. -# -# Author: dgilbert@redhat.com - -#include "migration-test.h" - -#define ACPI_ENABLE 0xf1 -#define ACPI_PORT_SMI_CMD 0xb2 -#define ACPI_PM_BASE 0x600 -#define PM1A_CNT_OFFSET 4 - -#define ACPI_SCI_ENABLE 0x0001 -#define ACPI_SLEEP_TYPE 0x0400 -#define ACPI_SLEEP_ENABLE 0x2000 -#define SLEEP (ACPI_SCI_ENABLE + ACPI_SLEEP_TYPE + ACPI_SLEEP_ENABLE) - -#define LOW_ADDR X86_TEST_MEM_START -#define HIGH_ADDR X86_TEST_MEM_END - -/* Save the suspended status at an address that is not written in the loop. */ -#define suspended (X86_TEST_MEM_START + 4) - -.code16 -.org 0x7c00 - .file "fill.s" - .text - .globl start - .type start, @function -start: # at 0x7c00 ? - cli - lgdt gdtdesc - mov $1,%eax - mov %eax,%cr0 # Protected mode enable - data32 ljmp $8,$0x7c20 - -.org 0x7c20 -.code32 - # A20 enable - not sure I actually need this - inb $0x92,%al - or $2,%al - outb %al, $0x92 - - # set up DS for the whole of RAM (needed on KVM) - mov $16,%eax - mov %eax,%ds - -# Start from 1MB -.set TEST_MEM_START, X86_TEST_MEM_START -.set TEST_MEM_END, X86_TEST_MEM_END - - mov $65,%ax - mov $0x3f8,%dx - outb %al,%dx - - # bl keeps a counter so we limit the output speed - mov $0, %bl - -pre_zero: - mov $TEST_MEM_START,%eax -do_zero: - movb $0, (%eax) - add $4096,%eax - cmp $TEST_MEM_END,%eax - jl do_zero - -mainloop: - mov $TEST_MEM_START,%eax -innerloop: - incb (%eax) - add $4096,%eax - cmp $TEST_MEM_END,%eax - jl innerloop - - inc %bl - andb $0x3f,%bl - jnz mainloop - - mov $66,%ax - mov $0x3f8,%dx - outb %al,%dx - - # should this test suspend? - mov (suspend_me),%eax - cmp $0,%eax - je mainloop - - # are we waking after suspend? do not suspend again. - mov $suspended,%eax - mov (%eax),%eax - cmp $1,%eax - je mainloop - - # enable acpi - mov $ACPI_ENABLE,%al - outb %al,$ACPI_PORT_SMI_CMD - - # suspend to ram - mov $suspended,%eax - movl $1,(%eax) - mov $SLEEP,%ax - mov $(ACPI_PM_BASE + PM1A_CNT_OFFSET),%dx - outw %ax,%dx - # not reached. The wakeup causes reset and restart at 0x7c00, and we - # do not save and restore registers as a real kernel would do. - - - # GDT magic from old (GPLv2) Grub startup.S - .p2align 2 /* force 4-byte alignment */ -gdt: - .word 0, 0 - .byte 0, 0, 0, 0 - - /* -- code segment -- - * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present - * type = 32bit code execute/read, DPL = 0 - */ - .word 0xFFFF, 0 - .byte 0, 0x9A, 0xCF, 0 - - /* -- data segment -- - * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present - * type = 32 bit data read/write, DPL = 0 - */ - .word 0xFFFF, 0 - .byte 0, 0x92, 0xCF, 0 - -gdtdesc: - .word 0x27 /* limit */ - .long gdt /* addr */ - - /* test launcher can poke a 1 here to exercise suspend */ -suspend_me: - .int 0 - -/* I'm a bootable disk */ -.org 0x7dfe - .byte 0x55 - .byte 0xAA diff --git a/tests/migration/i386/a-b-bootblock.h b/tests/migration/i386/a-b-bootblock.h deleted file mode 100644 index c83f871..0000000 --- a/tests/migration/i386/a-b-bootblock.h +++ /dev/null @@ -1,61 +0,0 @@ -/* This file is automatically generated from the assembly file in - * tests/migration/i386. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both - * the header and the assembler differences in your patch submission. - */ -unsigned char x86_bootsect[] = { - 0xfa, 0x0f, 0x01, 0x16, 0xb8, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00, - 0x0f, 0x22, 0xc0, 0x66, 0xea, 0x20, 0x7c, 0x00, 0x00, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x92, 0x0c, 0x02, - 0xe6, 0x92, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, 0x66, 0xb8, 0x41, - 0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xb3, 0x00, 0xb8, 0x00, 0x00, 0x10, - 0x00, 0xc6, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, - 0x40, 0x06, 0x7c, 0xf1, 0xb8, 0x00, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x05, - 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40, 0x06, 0x7c, 0xf2, 0xfe, - 0xc3, 0x80, 0xe3, 0x3f, 0x75, 0xe6, 0x66, 0xb8, 0x42, 0x00, 0x66, 0xba, - 0xf8, 0x03, 0xee, 0xa1, 0xbe, 0x7c, 0x00, 0x00, 0x83, 0xf8, 0x00, 0x74, - 0xd3, 0xb8, 0x04, 0x00, 0x10, 0x00, 0x8b, 0x00, 0x83, 0xf8, 0x01, 0x74, - 0xc7, 0xb0, 0xf1, 0xe6, 0xb2, 0xb8, 0x04, 0x00, 0x10, 0x00, 0xc7, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x66, 0xb8, 0x01, 0x24, 0x66, 0xba, 0x04, 0x06, - 0x66, 0xef, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0xa0, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa -}; - -#define SYM_do_zero 0x00007c3d -#define SYM_gdt 0x00007ca0 -#define SYM_gdtdesc 0x00007cb8 -#define SYM_innerloop 0x00007c51 -#define SYM_mainloop 0x00007c4c -#define SYM_pre_zero 0x00007c38 -#define SYM_start 0x00007c00 -#define SYM_suspend_me 0x00007cbe -#define SYM_TEST_MEM_END 0x06400000 -#define SYM_TEST_MEM_START 0x00100000 diff --git a/tests/migration/initrd-stress.sh b/tests/migration/initrd-stress.sh deleted file mode 100755 index 0f20ac2..0000000 --- a/tests/migration/initrd-stress.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -INITRD="$1" -STRESS="$2" - -INITRD_DIR=$(mktemp -d -p '' "initrd-stress.XXXXXX") -trap 'rm -rf $INITRD_DIR' EXIT - -cp "$STRESS" "$INITRD_DIR/init" -(cd "$INITRD_DIR" && (find | cpio --quiet -o -H newc | gzip -9)) > "$INITRD" diff --git a/tests/migration/meson.build b/tests/migration/meson.build deleted file mode 100644 index a91aa61..0000000 --- a/tests/migration/meson.build +++ /dev/null @@ -1,18 +0,0 @@ -sysprof = dependency('sysprof-capture-4', method: 'pkg-config', required: false) -glib_static = dependency('glib-2.0', version: glib_req_ver, required: false, - method: 'pkg-config', static: true) - -stress = executable( - 'stress', - files('stress.c'), - dependencies: [glib_static, sysprof], - link_args: ['-static'], - build_by_default: false, -) - -custom_target( - 'initrd-stress.img', - output: 'initrd-stress.img', - input: stress, - command: [find_program('initrd-stress.sh'), '@OUTPUT@', '@INPUT@'] -) diff --git a/tests/migration/migration-test.h b/tests/migration/migration-test.h deleted file mode 100644 index 194df7df6..0000000 --- a/tests/migration/migration-test.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018 Red Hat, Inc. and/or its affiliates - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef MIGRATION_TEST_H -#define MIGRATION_TEST_H - -/* Common */ -#define TEST_MEM_PAGE_SIZE 4096 - -/* x86 */ -#define X86_TEST_MEM_START (1 * 1024 * 1024) -#define X86_TEST_MEM_END (100 * 1024 * 1024) - -/* S390 */ -#define S390_TEST_MEM_START (1 * 1024 * 1024) -#define S390_TEST_MEM_END (100 * 1024 * 1024) - -/* PPC */ -#define PPC_TEST_MEM_START (1 * 1024 * 1024) -#define PPC_TEST_MEM_END (100 * 1024 * 1024) -#define PPC_H_PUT_TERM_CHAR 0x58 - -/* ARM */ -#define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024) -#define ARM_TEST_MEM_END (0x40000000 + 100 * 1024 * 1024) -#define ARM_MACH_VIRT_UART 0x09000000 -/* AArch64 kernel load address is 0x40080000, and the test memory starts at - * 0x40100000. So the maximum allowable kernel size is 512KB. - */ -#define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) - -#endif /* MIGRATION_TEST_H */ diff --git a/tests/migration/ppc64/Makefile b/tests/migration/ppc64/Makefile deleted file mode 100644 index a3a2d98..0000000 --- a/tests/migration/ppc64/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -.PHONY: all clean -all: a-b-kernel.h - -a-b-kernel.h: ppc64.kernel - echo "$$__note" > $@ - xxd -i $< | sed -e 's/.*int.*//' >> $@ - -ppc64.kernel: ppc64.elf - $(CROSS_PREFIX)objcopy -O binary -S $< $@ - -ppc64.elf: a-b-kernel.S - $(CROSS_PREFIX)gcc -static -o $@ -nostdlib -Wl,--build-id=none $< - -clean: - $(RM) *.kernel *.elf diff --git a/tests/migration/ppc64/a-b-kernel.S b/tests/migration/ppc64/a-b-kernel.S deleted file mode 100644 index 0613a8d..0000000 --- a/tests/migration/ppc64/a-b-kernel.S +++ /dev/null @@ -1,66 +0,0 @@ -# -# Copyright (c) 2024 IBM, Inc -# -# 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 "../migration-test.h" - -.section .text - -.macro print ch - li %r3,PPC_H_PUT_TERM_CHAR - li %r4,0 - li %r5,1 - li %r6,\ch - sldi %r6,%r6,56 - sc 1 -.endm - - .globl _start -_start: -. = 0x100 - /* - * Enter 64-bit mode. Not necessary because the test uses 32-bit - * addresses, but those constants could easily be changed and break - * in 32-bit mode. - */ - mfmsr %r9 - li %r10,-1 - rldimi %r9,%r10,63,0 - mtmsrd %r9 - - /* - * Set up test memory region. Non-volatiles are used because the - * hcall can clobber regs. - * r20 - start address - * r21 - number of pages - */ - lis %r20,PPC_TEST_MEM_START@h - ori %r20,%r20,PPC_TEST_MEM_START@l - lis %r9,PPC_TEST_MEM_END@h - ori %r9,%r9,PPC_TEST_MEM_END@l - subf %r21,%r20,%r9 - li %r10,TEST_MEM_PAGE_SIZE - divd %r21,%r21,%r10 - - print 'A' - - li %r3,0 - mr %r9,%r20 - mtctr %r21 -1: stb %r3,0(%r9) - addi %r9,%r9,TEST_MEM_PAGE_SIZE - bdnz 1b - -loop: - mr %r9,%r20 - mtctr %r21 -1: lbz %r3,0(%r9) - addi %r3,%r3,1 - stb %r3,0(%r9) - addi %r9,%r9,TEST_MEM_PAGE_SIZE - bdnz 1b - - print 'B' - b loop diff --git a/tests/migration/ppc64/a-b-kernel.h b/tests/migration/ppc64/a-b-kernel.h deleted file mode 100644 index 673317e..0000000 --- a/tests/migration/ppc64/a-b-kernel.h +++ /dev/null @@ -1,42 +0,0 @@ -/* This file is automatically generated from the assembly file in - * tests/migration/ppc64. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both - * the header and the assembler differences in your patch submission. - */ -unsigned char ppc64_kernel[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7d, 0x20, 0x00, 0xa6, 0x39, 0x40, 0xff, 0xff, - 0x79, 0x49, 0xf8, 0x0e, 0x7d, 0x20, 0x01, 0x64, 0x3e, 0x80, 0x00, 0x10, - 0x62, 0x94, 0x00, 0x00, 0x3d, 0x20, 0x06, 0x40, 0x61, 0x29, 0x00, 0x00, - 0x7e, 0xb4, 0x48, 0x50, 0x39, 0x40, 0x10, 0x00, 0x7e, 0xb5, 0x53, 0xd2, - 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, 0x38, 0xa0, 0x00, 0x01, - 0x38, 0xc0, 0x00, 0x41, 0x78, 0xc6, 0xc1, 0xc6, 0x44, 0x00, 0x00, 0x22, - 0x38, 0x60, 0x00, 0x00, 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, - 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, 0x42, 0x00, 0xff, 0xf8, - 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, 0x88, 0x69, 0x00, 0x00, - 0x38, 0x63, 0x00, 0x01, 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, - 0x42, 0x00, 0xff, 0xf0, 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, - 0x38, 0xa0, 0x00, 0x01, 0x38, 0xc0, 0x00, 0x42, 0x78, 0xc6, 0xc1, 0xc6, - 0x44, 0x00, 0x00, 0x22, 0x4b, 0xff, 0xff, 0xcc -}; - diff --git a/tests/migration/s390x/Makefile b/tests/migration/s390x/Makefile deleted file mode 100644 index 6671de2..0000000 --- a/tests/migration/s390x/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# To specify cross compiler prefix, use CROSS_PREFIX= -# $ make CROSS_PREFIX=s390x-linux-gnu- - -.PHONY: all clean -all: a-b-bios.h -fwdir=../../../pc-bios/s390-ccw - -CFLAGS+=-ffreestanding -fno-delete-null-pointer-checks -fPIE -Os \ - -msoft-float -march=z900 -fno-asynchronous-unwind-tables \ - -fno-stack-protector -Wl,-pie -Wl,--build-id=none -nostdlib - -a-b-bios.h: s390x.elf - echo "$$__note" > header.tmp - xxd -i $< | sed -e 's/.*int.*//' >> header.tmp - mv header.tmp $@ - -# We use common-page-size=16 to avoid big padding in the ELF file -s390x.elf: a-b-bios.c - $(CROSS_PREFIX)gcc $(CFLAGS) -I$(fwdir) $(fwdir)/start.S \ - $(fwdir)/sclp.c -Wl,-zcommon-page-size=16 -o $@ $< - $(CROSS_PREFIX)strip $@ - -clean: - @rm -rf *.elf *.o diff --git a/tests/migration/s390x/a-b-bios.c b/tests/migration/s390x/a-b-bios.c deleted file mode 100644 index ff99a3e..0000000 --- a/tests/migration/s390x/a-b-bios.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * S390 guest code used in migration tests - * - * Copyright 2018 Thomas Huth, Red Hat Inc. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#define LOADPARM_LEN 8 /* Needed for sclp.h */ - -#include -#include -#include - -char stack[0x8000] __attribute__((aligned(4096))); - -#define START_ADDRESS (1024 * 1024) -#define END_ADDRESS (100 * 1024 * 1024) - -void main(void) -{ - unsigned long addr; - - sclp_setup(); - sclp_print("A"); - - /* - * Make sure all of the pages have consistent contents before incrementing - * the first byte below. - */ - for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) { - *(volatile char *)addr = 0; - } - - while (1) { - for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) { - *(volatile char *)addr += 1; /* Change pages */ - } - sclp_print("B"); - } -} diff --git a/tests/migration/s390x/a-b-bios.h b/tests/migration/s390x/a-b-bios.h deleted file mode 100644 index 96103da..0000000 --- a/tests/migration/s390x/a-b-bios.h +++ /dev/null @@ -1,279 +0,0 @@ -/* This file is automatically generated from the a-b-bios.c file in - * tests/migration/s390x. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both - * the header and the assembler differences in your patch submission. - */ -unsigned char s390x_elf[] = { - 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x07, 0x00, 0x40, - 0x00, 0x0d, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x18, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x64, 0x74, 0xe5, 0x51, - 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x64, 0x74, 0xe5, 0x52, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x36, 0x34, 0x2e, 0x73, 0x6f, - 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0xeb, 0xef, 0xf0, 0x70, - 0x00, 0x24, 0xa7, 0xfb, 0xff, 0x60, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x5f, - 0xc0, 0x20, 0x00, 0x00, 0x02, 0xa8, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x75, - 0xa5, 0x2e, 0x00, 0x10, 0xa7, 0x19, 0x63, 0x00, 0x92, 0x00, 0x20, 0x00, - 0xa7, 0x2b, 0x10, 0x00, 0xa7, 0x17, 0xff, 0xfc, 0xa5, 0x1e, 0x00, 0x10, - 0xa7, 0x29, 0x63, 0x00, 0xe3, 0x30, 0x10, 0x00, 0x00, 0x90, 0xa7, 0x3a, - 0x00, 0x01, 0x42, 0x30, 0x10, 0x00, 0xa7, 0x1b, 0x10, 0x00, 0xa7, 0x27, - 0xff, 0xf7, 0xc0, 0x20, 0x00, 0x00, 0x02, 0x8a, 0xc0, 0xe5, 0x00, 0x00, - 0x01, 0x56, 0xa7, 0xf4, 0xff, 0xeb, 0x07, 0x07, 0xc0, 0xf0, 0x00, 0x00, - 0x4e, 0x5c, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x7d, 0xe3, 0x20, 0x20, 0x00, - 0x00, 0x04, 0xc0, 0x30, 0x00, 0x00, 0x96, 0xa3, 0xb9, 0x0b, 0x00, 0x32, - 0xb9, 0x02, 0x00, 0x33, 0xa7, 0x84, 0x00, 0x19, 0xa7, 0x3b, 0xff, 0xff, - 0xeb, 0x43, 0x00, 0x08, 0x00, 0x0c, 0xb9, 0x02, 0x00, 0x44, 0xb9, 0x04, - 0x00, 0x12, 0xa7, 0x84, 0x00, 0x09, 0xd7, 0xff, 0x10, 0x00, 0x10, 0x00, - 0x41, 0x10, 0x11, 0x00, 0xa7, 0x47, 0xff, 0xfb, 0xc0, 0x20, 0x00, 0x00, - 0x00, 0x0d, 0x44, 0x30, 0x20, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x5b, - 0xd2, 0x0f, 0x01, 0xd0, 0x20, 0x00, 0xa7, 0xf4, 0xff, 0xa1, 0xd7, 0x00, - 0x10, 0x00, 0x10, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x50, 0xb2, 0xb2, - 0x10, 0x00, 0xa7, 0xf4, 0x00, 0x00, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x25, - 0x96, 0x02, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x2f, 0xc0, 0x10, - 0x00, 0x00, 0x00, 0x2a, 0xe3, 0x10, 0x01, 0xb8, 0x00, 0x24, 0xc0, 0x10, - 0x00, 0x00, 0x00, 0x4b, 0xd2, 0x07, 0x01, 0xb0, 0x10, 0x00, 0xc0, 0x10, - 0x00, 0x00, 0x00, 0x3d, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x66, 0xf0, 0x00, - 0x00, 0x25, 0x96, 0xff, 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, - 0xc0, 0x10, 0x00, 0x00, 0x00, 0x1a, 0xe3, 0x10, 0x01, 0xf8, 0x00, 0x24, - 0xc0, 0x10, 0x00, 0x00, 0x00, 0x36, 0xd2, 0x07, 0x01, 0xf0, 0x10, 0x00, - 0xc0, 0x10, 0x00, 0x00, 0x00, 0x24, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x00, - 0xf0, 0x00, 0x00, 0x25, 0x94, 0xfd, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, - 0x00, 0x2f, 0x07, 0xfe, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x25, 0x94, 0x00, - 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, 0x07, 0xfe, 0x07, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0x00, 0x02, 0x00, 0x01, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0xeb, 0xbf, 0xf0, 0x58, - 0x00, 0x24, 0xc0, 0x10, 0x00, 0x00, 0x4e, 0x0d, 0xa7, 0xfb, 0xff, 0x60, - 0xb2, 0x20, 0x00, 0x21, 0xb2, 0x22, 0x00, 0xb0, 0x88, 0xb0, 0x00, 0x1c, - 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x91, 0xa7, 0xbe, 0x00, 0x03, 0xa7, 0x84, - 0x00, 0x13, 0xa7, 0xbe, 0x00, 0x02, 0xa7, 0x28, 0x00, 0x00, 0xa7, 0x74, - 0x00, 0x04, 0xa7, 0x28, 0xff, 0xfe, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, - 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, - 0xa7, 0x28, 0xff, 0xff, 0xa7, 0xf4, 0xff, 0xf5, 0x07, 0x07, 0x07, 0x07, - 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x01, 0x25, - 0xa7, 0xfb, 0xff, 0x60, 0xa7, 0xb9, 0x00, 0x00, 0xa7, 0x19, 0x00, 0x00, - 0xc0, 0x40, 0x00, 0x00, 0x4d, 0xd8, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0x37, - 0x00, 0x23, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0xd1, 0x18, 0x31, 0xa7, 0x1a, - 0x00, 0x06, 0x40, 0x10, 0x20, 0x08, 0xa7, 0x3a, 0x00, 0x0e, 0xa7, 0x18, - 0x1a, 0x00, 0x40, 0x30, 0x20, 0x00, 0x92, 0x00, 0x20, 0x02, 0x40, 0x10, - 0x20, 0x0a, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, - 0xff, 0xac, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xb9, 0x04, 0x00, 0x2b, - 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0xb9, 0x04, 0x00, 0x51, - 0xa7, 0x5b, 0x00, 0x01, 0xa7, 0x09, 0x0f, 0xf7, 0xb9, 0x21, 0x00, 0x50, - 0xa7, 0x24, 0xff, 0xd7, 0x41, 0xeb, 0x20, 0x00, 0x95, 0x0a, 0xe0, 0x00, - 0xa7, 0x74, 0x00, 0x08, 0x41, 0x11, 0x40, 0x0e, 0x92, 0x0d, 0x10, 0x00, - 0xb9, 0x04, 0x00, 0x15, 0x43, 0x5b, 0x20, 0x00, 0x42, 0x51, 0x40, 0x0e, - 0xa7, 0xbb, 0x00, 0x01, 0x41, 0x10, 0x10, 0x01, 0xa7, 0xf4, 0xff, 0xbf, - 0xc0, 0x50, 0x00, 0x00, 0x00, 0xd8, 0xc0, 0x10, 0x00, 0x00, 0x4d, 0x8d, - 0xa7, 0x48, 0x00, 0x1c, 0x40, 0x40, 0x10, 0x00, 0x50, 0x20, 0x10, 0x0c, - 0xa7, 0x48, 0x00, 0x04, 0xe3, 0x20, 0x50, 0x00, 0x00, 0x04, 0x40, 0x40, - 0x10, 0x0a, 0x50, 0x30, 0x10, 0x10, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x6b, - 0xa7, 0x39, 0x00, 0x40, 0xa7, 0x29, 0x00, 0x00, 0xc0, 0xf4, 0xff, 0xff, - 0xff, 0xe4, 0x07, 0x07, 0xb9, 0x04, 0x00, 0x13, 0xa7, 0x2a, 0xff, 0xff, - 0xb9, 0x04, 0x00, 0x34, 0xa7, 0x48, 0x00, 0x01, 0x15, 0x24, 0xa7, 0x24, - 0x00, 0x07, 0xb9, 0x04, 0x00, 0x21, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x7f, - 0xa7, 0x29, 0xff, 0xff, 0x07, 0xfe, 0x07, 0x07, 0xa7, 0x39, 0x00, 0x00, - 0x41, 0x13, 0x20, 0x00, 0x95, 0x00, 0x10, 0x00, 0xa7, 0x74, 0x00, 0x05, - 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x70, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0xf4, - 0xff, 0xf5, 0x07, 0x07, 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, - 0x00, 0x00, 0x00, 0x95, 0xa7, 0xfb, 0xff, 0x60, 0xb9, 0x04, 0x00, 0xb2, - 0xa7, 0x19, 0x00, 0x20, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0x40, 0x92, 0x00, - 0x20, 0x00, 0xa7, 0x2b, 0x00, 0x01, 0xa7, 0x17, 0xff, 0xfc, 0xc0, 0x10, - 0x00, 0x00, 0x4d, 0x37, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00, - 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x1d, - 0x12, 0x22, 0xa7, 0x74, 0x00, 0x19, 0xa7, 0x19, 0x00, 0x00, 0xc0, 0x40, - 0x00, 0x00, 0x00, 0x79, 0xa7, 0x39, 0x00, 0x08, 0xc0, 0x20, 0x00, 0x00, - 0x4d, 0x2c, 0x41, 0x21, 0x20, 0x00, 0xe3, 0x20, 0x20, 0x00, 0x00, 0x90, - 0x43, 0x22, 0x40, 0x00, 0x42, 0x21, 0xb0, 0x00, 0xa7, 0x1b, 0x00, 0x01, - 0xa7, 0x37, 0xff, 0xf2, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xeb, 0xbf, - 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0x07, 0x07, 0xeb, 0xaf, 0xf0, 0x50, - 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x00, 0x55, 0xa7, 0xfb, 0xff, 0x60, - 0xa7, 0x19, 0x0f, 0xf8, 0xb9, 0x21, 0x00, 0x31, 0xb9, 0x04, 0x00, 0xa2, - 0xa7, 0xc4, 0x00, 0x2a, 0xa7, 0xb9, 0x0f, 0xf8, 0xc0, 0x10, 0x00, 0x00, - 0x4c, 0xf6, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00, 0x92, 0x00, - 0x10, 0x02, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, - 0xfe, 0xda, 0xa7, 0xbb, 0x00, 0x01, 0xa7, 0x19, 0x00, 0x00, 0xa7, 0xb7, - 0x00, 0x17, 0xc0, 0x10, 0x00, 0x00, 0x4c, 0xe1, 0xe3, 0x40, 0xf1, 0x10, - 0x00, 0x04, 0xe3, 0x20, 0x10, 0x08, 0x00, 0x91, 0xa7, 0x2a, 0xff, 0xf9, - 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xaf, 0xf0, 0xf0, 0x00, 0x04, 0x07, 0xf4, - 0xb9, 0x04, 0x00, 0xb3, 0xa7, 0xf4, 0xff, 0xd8, 0xc0, 0x20, 0x00, 0x00, - 0x4c, 0xcc, 0x41, 0x31, 0xa0, 0x00, 0x41, 0x21, 0x20, 0x00, 0xa7, 0x1b, - 0x00, 0x01, 0xd2, 0x00, 0x30, 0x00, 0x20, 0x0f, 0xa7, 0xf4, 0xff, 0xdd, - 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x20, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, 0x26, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x2e, - 0x2d, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2c, - 0x25, 0x5f, 0x3e, 0x3f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, 0x2e, 0x61, 0x62, 0x63, - 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, - 0x51, 0x52, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x41, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x6f, 0xff, 0xfe, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xfb, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x6f, 0xff, 0xff, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, - 0x20, 0x28, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x31, 0x31, 0x2e, - 0x34, 0x2e, 0x30, 0x2d, 0x31, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x31, - 0x7e, 0x32, 0x32, 0x2e, 0x30, 0x34, 0x29, 0x20, 0x31, 0x31, 0x2e, 0x34, - 0x2e, 0x30, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, - 0x62, 0x00, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x00, 0x2e, 0x67, - 0x6e, 0x75, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, - 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, - 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x74, - 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00, - 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, - 0x74, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6f, 0xff, 0xff, 0xf6, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x37, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4e, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xd8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x09, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; diff --git a/tests/migration/stress.c b/tests/migration/stress.c deleted file mode 100644 index 88acf8d..0000000 --- a/tests/migration/stress.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Migration stress workload - * - * Copyright (c) 2016 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include -#include -#include -#include -#include -#include - -const char *argv0; - -#define RAM_PAGE_SIZE 4096 - -#ifndef CONFIG_GETTID -static int gettid(void) -{ - return syscall(SYS_gettid); -} -#endif - -static __attribute__((noreturn)) void exit_failure(void) -{ - if (getpid() == 1) { - sync(); - reboot(RB_POWER_OFF); - fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n", - argv0, gettid(), strerror(errno)); - abort(); - } else { - exit(1); - } -} - -static int get_command_arg_str(const char *name, - char **val) -{ - static char line[1024]; - FILE *fp = fopen("/proc/cmdline", "r"); - char *start, *end; - - if (fp == NULL) { - fprintf(stderr, "%s (%05d): ERROR: cannot open /proc/cmdline: %s\n", - argv0, gettid(), strerror(errno)); - return -1; - } - - if (!fgets(line, sizeof line, fp)) { - fprintf(stderr, "%s (%05d): ERROR: cannot read /proc/cmdline: %s\n", - argv0, gettid(), strerror(errno)); - fclose(fp); - return -1; - } - fclose(fp); - - start = strstr(line, name); - if (!start) - return 0; - - start += strlen(name); - - if (*start != '=') { - fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n", - argv0, gettid(), name); - } - start++; - - end = strstr(start, " "); - if (!end) - end = strstr(start, "\n"); - - if (end == start) { - fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n", - argv0, gettid(), name); - return -1; - } - - if (end) - *val = g_strndup(start, end - start); - else - *val = g_strdup(start); - return 1; -} - - -static int get_command_arg_ull(const char *name, - unsigned long long *val) -{ - char *valstr; - char *end; - - int ret = get_command_arg_str(name, &valstr); - if (ret <= 0) - return ret; - - errno = 0; - *val = strtoll(valstr, &end, 10); - if (errno || *end) { - fprintf(stderr, "%s (%05d): ERROR: cannot parse %s value %s\n", - argv0, gettid(), name, valstr); - g_free(valstr); - return -1; - } - g_free(valstr); - return 0; -} - - -static int random_bytes(char *buf, size_t len) -{ - int fd; - - fd = open("/dev/urandom", O_RDONLY); - if (fd < 0) { - fprintf(stderr, "%s (%05d): ERROR: cannot open /dev/urandom: %s\n", - argv0, gettid(), strerror(errno)); - return -1; - } - - if (read(fd, buf, len) != len) { - fprintf(stderr, "%s (%05d): ERROR: cannot read /dev/urandom: %s\n", - argv0, gettid(), strerror(errno)); - close(fd); - return -1; - } - - close(fd); - - return 0; -} - - -static unsigned long long now(void) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - return (tv.tv_sec * 1000ull) + (tv.tv_usec / 1000ull); -} - -static void stressone(unsigned long long ramsizeMB) -{ - size_t pagesPerMB = 1024 * 1024 / RAM_PAGE_SIZE; - g_autofree char *ram = g_malloc(ramsizeMB * 1024 * 1024); - char *ramptr; - size_t i, j, k; - g_autofree char *data = g_malloc(RAM_PAGE_SIZE); - char *dataptr; - size_t nMB = 0; - unsigned long long before, after; - - /* We don't care about initial state, but we do want - * to fault it all into RAM, otherwise the first iter - * of the loop below will be quite slow. We can't use - * 0x0 as the byte as gcc optimizes that away into a - * calloc instead :-) */ - memset(ram, 0xfe, ramsizeMB * 1024 * 1024); - - if (random_bytes(data, RAM_PAGE_SIZE) < 0) { - return; - } - - before = now(); - - while (1) { - - ramptr = ram; - for (i = 0; i < ramsizeMB; i++, nMB++) { - for (j = 0; j < pagesPerMB; j++) { - dataptr = data; - for (k = 0; k < RAM_PAGE_SIZE; k += sizeof(long long)) { - ramptr += sizeof(long long); - dataptr += sizeof(long long); - *(unsigned long long *)ramptr ^= *(unsigned long long *)dataptr; - } - } - - if (nMB == 1024) { - after = now(); - fprintf(stderr, "%s (%05d): INFO: %06llums copied 1 GB in %05llums\n", - argv0, gettid(), after, after - before); - before = now(); - nMB = 0; - } - } - } -} - - -static void *stressthread(void *arg) -{ - unsigned long long ramsizeMB = *(unsigned long long *)arg; - - stressone(ramsizeMB); - - return NULL; -} - -static void stress(unsigned long long ramsizeGB, int ncpus) -{ - size_t i; - unsigned long long ramsizeMB = ramsizeGB * 1024 / ncpus; - ncpus--; - - for (i = 0; i < ncpus; i++) { - pthread_t thr; - pthread_create(&thr, NULL, - stressthread, &ramsizeMB); - } - - stressone(ramsizeMB); -} - - -static int mount_misc(const char *fstype, const char *dir) -{ - if (g_mkdir_with_parents(dir, 0755) < 0 && errno != EEXIST) { - fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n", - argv0, gettid(), dir, strerror(errno)); - return -1; - } - - if (mount("none", dir, fstype, 0, NULL) < 0) { - fprintf(stderr, "%s (%05d): ERROR: cannot mount %s: %s\n", - argv0, gettid(), dir, strerror(errno)); - return -1; - } - - return 0; -} - -static int mount_all(void) -{ - if (mount_misc("proc", "/proc") < 0 || - mount_misc("sysfs", "/sys") < 0 || - mount_misc("tmpfs", "/dev") < 0) - return -1; - - mknod("/dev/urandom", 0777 | S_IFCHR, makedev(1, 9)); - mknod("/dev/random", 0777 | S_IFCHR, makedev(1, 8)); - - return 0; -} - -int main(int argc, char **argv) -{ - unsigned long long ramsizeGB = 1; - char *end; - int ch; - int opt_ind = 0; - const char *sopt = "hr:c:"; - struct option lopt[] = { - { "help", no_argument, NULL, 'h' }, - { "ramsize", required_argument, NULL, 'r' }, - { "cpus", required_argument, NULL, 'c' }, - { NULL, 0, NULL, 0 } - }; - int ret; - int ncpus = 0; - - argv0 = argv[0]; - - while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { - switch (ch) { - case 'r': - errno = 0; - ramsizeGB = strtoll(optarg, &end, 10); - if (errno != 0 || *end) { - fprintf(stderr, "%s (%05d): ERROR: Cannot parse RAM size %s\n", - argv0, gettid(), optarg); - exit_failure(); - } - break; - - case 'c': - errno = 0; - ncpus = strtoll(optarg, &end, 10); - if (errno != 0 || *end) { - fprintf(stderr, "%s (%05d): ERROR: Cannot parse CPU count %s\n", - argv0, gettid(), optarg); - exit_failure(); - } - break; - - case '?': - case 'h': - fprintf(stderr, "%s: [--help][--ramsize GB][--cpus N]\n", argv0); - exit_failure(); - } - } - - if (getpid() == 1) { - if (mount_all() < 0) - exit_failure(); - - ret = get_command_arg_ull("ramsize", &ramsizeGB); - if (ret < 0) - exit_failure(); - } - - if (ncpus == 0) - ncpus = sysconf(_SC_NPROCESSORS_ONLN); - - fprintf(stdout, "%s (%05d): INFO: RAM %llu GiB across %d CPUs\n", - argv0, gettid(), ramsizeGB, ncpus); - - stress(ramsizeGB, ncpus); - - exit_failure(); -} diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 30bc965..82b9170 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -24,7 +24,7 @@ #include "ppc-util.h" #include "migration-helpers.h" -#include "tests/migration/migration-test.h" +#include "migration/migration-test.h" #ifdef CONFIG_GNUTLS # include "tests/unit/crypto-tls-psk-helpers.h" # ifdef CONFIG_TASN1 @@ -138,10 +138,10 @@ static char *bootpath; /* The boot file modifies memory area in [start_address, end_address) * repeatedly. It outputs a 'B' at a fixed rate while it's still running. */ -#include "tests/migration/i386/a-b-bootblock.h" -#include "tests/migration/aarch64/a-b-kernel.h" -#include "tests/migration/ppc64/a-b-kernel.h" -#include "tests/migration/s390x/a-b-bios.h" +#include "migration/i386/a-b-bootblock.h" +#include "migration/aarch64/a-b-kernel.h" +#include "migration/ppc64/a-b-kernel.h" +#include "migration/s390x/a-b-bios.h" static void bootfile_delete(void) { diff --git a/tests/qtest/migration/Makefile b/tests/qtest/migration/Makefile new file mode 100644 index 0000000..2c5ee28 --- /dev/null +++ b/tests/qtest/migration/Makefile @@ -0,0 +1,40 @@ +# +# Copyright (c) 2018 Red Hat, Inc. and/or its affiliates +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# + +TARGET_LIST = i386 aarch64 s390x ppc64 + +SRC_PATH = ../.. + +.PHONY: help $(TARGET_LIST) +help: + @echo "Create migration guest includes. We generate a binary." + @echo "And then convert that binary to an include file that can be" + @echo "run in a guest." + @echo "Possible operations are:" + @echo + @echo " $(MAKE) clean Remove all intermediate files" + @echo " $(MAKE) target Generate for that target" + @echo " $(MAKE) CROSS_PREFIX=... target" + @echo " Cross-compile than target" + @echo " Possible targets are: $(TARGET_LIST)" + +override define __note +/* This file is automatically generated from the assembly file in + * tests/migration/$@. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +endef +export __note + +$(TARGET_LIST): + $(MAKE) CROSS_PREFIX=$(CROSS_PREFIX) -C $@ + +clean: + for target in $(TARGET_LIST); do \ + $(MAKE) -C $$target clean; \ + done diff --git a/tests/qtest/migration/aarch64/Makefile b/tests/qtest/migration/aarch64/Makefile new file mode 100644 index 0000000..9c4fa18 --- /dev/null +++ b/tests/qtest/migration/aarch64/Makefile @@ -0,0 +1,18 @@ +# To specify cross compiler prefix, use CROSS_PREFIX= +# $ make CROSS_PREFIX=aarch64-linux-gnu- + +.PHONY: all clean +all: a-b-kernel.h + +a-b-kernel.h: aarch64.kernel + echo "$$__note" > $@ + xxd -i $< | sed -e 's/.*int.*//' >> $@ + +aarch64.kernel: aarch64.elf + $(CROSS_PREFIX)objcopy -O binary $< $@ + +aarch64.elf: a-b-kernel.S + $(CROSS_PREFIX)gcc -o $@ -nostdlib -Wl,--build-id=none $< + +clean: + $(RM) *.kernel *.elf diff --git a/tests/qtest/migration/aarch64/a-b-kernel.S b/tests/qtest/migration/aarch64/a-b-kernel.S new file mode 100644 index 0000000..a4103ec --- /dev/null +++ b/tests/qtest/migration/aarch64/a-b-kernel.S @@ -0,0 +1,74 @@ +# +# Copyright (c) 2018 Red Hat, Inc. and/or its affiliates +# +# Author: +# Wei Huang +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# Note: Please make sure the compiler compiles the assembly code below with +# pc-relative address. Also the branch instructions should use relative +# addresses only. + +#include "../migration-test.h" + +.section .text + + .globl _start + +_start: + /* disable MMU to use phys mem address */ + mrs x0, sctlr_el1 + bic x0, x0, #(1<<0) + msr sctlr_el1, x0 + isb + + /* traverse test memory region */ + mov x0, #ARM_TEST_MEM_START + mov x1, #ARM_TEST_MEM_END + + /* output char 'A' to PL011 */ + mov w3, 'A' + mov x2, #ARM_MACH_VIRT_UART + strb w3, [x2] + + /* clean up memory */ + mov w3, #0 + mov x4, x0 +clean: + strb w3, [x4] + add x4, x4, #TEST_MEM_PAGE_SIZE + cmp x4, x1 + ble clean + + /* w5 keeps a counter so we can limit the output speed */ + mov w5, #0 + + /* main body */ +mainloop: + mov x4, x0 + +innerloop: + /* increment the first byte of each page by 1 */ + ldrb w3, [x4] + add w3, w3, #1 + strb w3, [x4] + + /* make sure QEMU user space can see consistent data as MMU is off */ + dc civac, x4 + + add x4, x4, #TEST_MEM_PAGE_SIZE + cmp x4, x1 + blt innerloop + + add w5, w5, #1 + and w5, w5, #0x1f + cmp w5, #0 + bne mainloop + + /* output char 'B' to PL011 */ + mov w3, 'B' + strb w3, [x2] + + b mainloop diff --git a/tests/qtest/migration/aarch64/a-b-kernel.h b/tests/qtest/migration/aarch64/a-b-kernel.h new file mode 100644 index 0000000..34e518d --- /dev/null +++ b/tests/qtest/migration/aarch64/a-b-kernel.h @@ -0,0 +1,18 @@ +/* This file is automatically generated from the assembly file in + * tests/migration/aarch64. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +unsigned char aarch64_kernel[] = { + 0x00, 0x10, 0x38, 0xd5, 0x00, 0xf8, 0x7f, 0x92, 0x00, 0x10, 0x18, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x00, 0x02, 0xa8, 0xd2, 0x01, 0xc8, 0xa8, 0xd2, + 0x23, 0x08, 0x80, 0x52, 0x02, 0x20, 0xa1, 0xd2, 0x43, 0x00, 0x00, 0x39, + 0x03, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x00, 0x39, + 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, 0xad, 0xff, 0xff, 0x54, + 0x05, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x40, 0x39, + 0x63, 0x04, 0x00, 0x11, 0x83, 0x00, 0x00, 0x39, 0x24, 0x7e, 0x0b, 0xd5, + 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, 0x4b, 0xff, 0xff, 0x54, + 0xa5, 0x04, 0x00, 0x11, 0xa5, 0x10, 0x00, 0x12, 0xbf, 0x00, 0x00, 0x71, + 0xa1, 0xfe, 0xff, 0x54, 0x43, 0x08, 0x80, 0x52, 0x43, 0x00, 0x00, 0x39, + 0xf2, 0xff, 0xff, 0x17 +}; diff --git a/tests/qtest/migration/i386/Makefile b/tests/qtest/migration/i386/Makefile new file mode 100644 index 0000000..37a72ae --- /dev/null +++ b/tests/qtest/migration/i386/Makefile @@ -0,0 +1,23 @@ +# To specify cross compiler prefix, use CROSS_PREFIX= +# $ make CROSS_PREFIX=x86_64-linux-gnu- + +.PHONY: all clean +all: a-b-bootblock.h + +a-b-bootblock.h: x86.bootsect x86.o + echo "$$__note" > header.tmp + xxd -i $< | sed -e 's/.*int.*//' >> header.tmp + nm x86.o | awk '{print "#define SYM_"$$3" 0x"$$1}' >> header.tmp + mv header.tmp $@ + +x86.bootsect: x86.boot + dd if=$< of=$@ bs=256 count=2 skip=124 + +x86.boot: x86.o + $(CROSS_PREFIX)objcopy -O binary $< $@ + +x86.o: a-b-bootblock.S + $(CROSS_PREFIX)gcc -I.. -m32 -march=i486 -c $< -o $@ + +clean: + @rm -rf *.boot *.o *.bootsect diff --git a/tests/qtest/migration/i386/a-b-bootblock.S b/tests/qtest/migration/i386/a-b-bootblock.S new file mode 100644 index 0000000..6f39eb6 --- /dev/null +++ b/tests/qtest/migration/i386/a-b-bootblock.S @@ -0,0 +1,145 @@ +# x86 bootblock used in migration test +# repeatedly increments the first byte of each page in a 100MB +# range. +# Outputs an initial 'A' on serial followed by repeated 'B's +# +# Copyright (c) 2016 Red Hat, Inc. and/or its affiliates +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# Author: dgilbert@redhat.com + +#include "migration-test.h" + +#define ACPI_ENABLE 0xf1 +#define ACPI_PORT_SMI_CMD 0xb2 +#define ACPI_PM_BASE 0x600 +#define PM1A_CNT_OFFSET 4 + +#define ACPI_SCI_ENABLE 0x0001 +#define ACPI_SLEEP_TYPE 0x0400 +#define ACPI_SLEEP_ENABLE 0x2000 +#define SLEEP (ACPI_SCI_ENABLE + ACPI_SLEEP_TYPE + ACPI_SLEEP_ENABLE) + +#define LOW_ADDR X86_TEST_MEM_START +#define HIGH_ADDR X86_TEST_MEM_END + +/* Save the suspended status at an address that is not written in the loop. */ +#define suspended (X86_TEST_MEM_START + 4) + +.code16 +.org 0x7c00 + .file "fill.s" + .text + .globl start + .type start, @function +start: # at 0x7c00 ? + cli + lgdt gdtdesc + mov $1,%eax + mov %eax,%cr0 # Protected mode enable + data32 ljmp $8,$0x7c20 + +.org 0x7c20 +.code32 + # A20 enable - not sure I actually need this + inb $0x92,%al + or $2,%al + outb %al, $0x92 + + # set up DS for the whole of RAM (needed on KVM) + mov $16,%eax + mov %eax,%ds + +# Start from 1MB +.set TEST_MEM_START, X86_TEST_MEM_START +.set TEST_MEM_END, X86_TEST_MEM_END + + mov $65,%ax + mov $0x3f8,%dx + outb %al,%dx + + # bl keeps a counter so we limit the output speed + mov $0, %bl + +pre_zero: + mov $TEST_MEM_START,%eax +do_zero: + movb $0, (%eax) + add $4096,%eax + cmp $TEST_MEM_END,%eax + jl do_zero + +mainloop: + mov $TEST_MEM_START,%eax +innerloop: + incb (%eax) + add $4096,%eax + cmp $TEST_MEM_END,%eax + jl innerloop + + inc %bl + andb $0x3f,%bl + jnz mainloop + + mov $66,%ax + mov $0x3f8,%dx + outb %al,%dx + + # should this test suspend? + mov (suspend_me),%eax + cmp $0,%eax + je mainloop + + # are we waking after suspend? do not suspend again. + mov $suspended,%eax + mov (%eax),%eax + cmp $1,%eax + je mainloop + + # enable acpi + mov $ACPI_ENABLE,%al + outb %al,$ACPI_PORT_SMI_CMD + + # suspend to ram + mov $suspended,%eax + movl $1,(%eax) + mov $SLEEP,%ax + mov $(ACPI_PM_BASE + PM1A_CNT_OFFSET),%dx + outw %ax,%dx + # not reached. The wakeup causes reset and restart at 0x7c00, and we + # do not save and restore registers as a real kernel would do. + + + # GDT magic from old (GPLv2) Grub startup.S + .p2align 2 /* force 4-byte alignment */ +gdt: + .word 0, 0 + .byte 0, 0, 0, 0 + + /* -- code segment -- + * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present + * type = 32bit code execute/read, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x9A, 0xCF, 0 + + /* -- data segment -- + * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present + * type = 32 bit data read/write, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x92, 0xCF, 0 + +gdtdesc: + .word 0x27 /* limit */ + .long gdt /* addr */ + + /* test launcher can poke a 1 here to exercise suspend */ +suspend_me: + .int 0 + +/* I'm a bootable disk */ +.org 0x7dfe + .byte 0x55 + .byte 0xAA diff --git a/tests/qtest/migration/i386/a-b-bootblock.h b/tests/qtest/migration/i386/a-b-bootblock.h new file mode 100644 index 0000000..c83f871 --- /dev/null +++ b/tests/qtest/migration/i386/a-b-bootblock.h @@ -0,0 +1,61 @@ +/* This file is automatically generated from the assembly file in + * tests/migration/i386. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +unsigned char x86_bootsect[] = { + 0xfa, 0x0f, 0x01, 0x16, 0xb8, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00, + 0x0f, 0x22, 0xc0, 0x66, 0xea, 0x20, 0x7c, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x92, 0x0c, 0x02, + 0xe6, 0x92, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, 0x66, 0xb8, 0x41, + 0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xb3, 0x00, 0xb8, 0x00, 0x00, 0x10, + 0x00, 0xc6, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x40, 0x06, 0x7c, 0xf1, 0xb8, 0x00, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x05, + 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40, 0x06, 0x7c, 0xf2, 0xfe, + 0xc3, 0x80, 0xe3, 0x3f, 0x75, 0xe6, 0x66, 0xb8, 0x42, 0x00, 0x66, 0xba, + 0xf8, 0x03, 0xee, 0xa1, 0xbe, 0x7c, 0x00, 0x00, 0x83, 0xf8, 0x00, 0x74, + 0xd3, 0xb8, 0x04, 0x00, 0x10, 0x00, 0x8b, 0x00, 0x83, 0xf8, 0x01, 0x74, + 0xc7, 0xb0, 0xf1, 0xe6, 0xb2, 0xb8, 0x04, 0x00, 0x10, 0x00, 0xc7, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x66, 0xb8, 0x01, 0x24, 0x66, 0xba, 0x04, 0x06, + 0x66, 0xef, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0xa0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa +}; + +#define SYM_do_zero 0x00007c3d +#define SYM_gdt 0x00007ca0 +#define SYM_gdtdesc 0x00007cb8 +#define SYM_innerloop 0x00007c51 +#define SYM_mainloop 0x00007c4c +#define SYM_pre_zero 0x00007c38 +#define SYM_start 0x00007c00 +#define SYM_suspend_me 0x00007cbe +#define SYM_TEST_MEM_END 0x06400000 +#define SYM_TEST_MEM_START 0x00100000 diff --git a/tests/qtest/migration/migration-test.h b/tests/qtest/migration/migration-test.h new file mode 100644 index 0000000..194df7df6 --- /dev/null +++ b/tests/qtest/migration/migration-test.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Red Hat, Inc. and/or its affiliates + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef MIGRATION_TEST_H +#define MIGRATION_TEST_H + +/* Common */ +#define TEST_MEM_PAGE_SIZE 4096 + +/* x86 */ +#define X86_TEST_MEM_START (1 * 1024 * 1024) +#define X86_TEST_MEM_END (100 * 1024 * 1024) + +/* S390 */ +#define S390_TEST_MEM_START (1 * 1024 * 1024) +#define S390_TEST_MEM_END (100 * 1024 * 1024) + +/* PPC */ +#define PPC_TEST_MEM_START (1 * 1024 * 1024) +#define PPC_TEST_MEM_END (100 * 1024 * 1024) +#define PPC_H_PUT_TERM_CHAR 0x58 + +/* ARM */ +#define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024) +#define ARM_TEST_MEM_END (0x40000000 + 100 * 1024 * 1024) +#define ARM_MACH_VIRT_UART 0x09000000 +/* AArch64 kernel load address is 0x40080000, and the test memory starts at + * 0x40100000. So the maximum allowable kernel size is 512KB. + */ +#define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) + +#endif /* MIGRATION_TEST_H */ diff --git a/tests/qtest/migration/ppc64/Makefile b/tests/qtest/migration/ppc64/Makefile new file mode 100644 index 0000000..a3a2d98 --- /dev/null +++ b/tests/qtest/migration/ppc64/Makefile @@ -0,0 +1,15 @@ +.PHONY: all clean +all: a-b-kernel.h + +a-b-kernel.h: ppc64.kernel + echo "$$__note" > $@ + xxd -i $< | sed -e 's/.*int.*//' >> $@ + +ppc64.kernel: ppc64.elf + $(CROSS_PREFIX)objcopy -O binary -S $< $@ + +ppc64.elf: a-b-kernel.S + $(CROSS_PREFIX)gcc -static -o $@ -nostdlib -Wl,--build-id=none $< + +clean: + $(RM) *.kernel *.elf diff --git a/tests/qtest/migration/ppc64/a-b-kernel.S b/tests/qtest/migration/ppc64/a-b-kernel.S new file mode 100644 index 0000000..0613a8d --- /dev/null +++ b/tests/qtest/migration/ppc64/a-b-kernel.S @@ -0,0 +1,66 @@ +# +# Copyright (c) 2024 IBM, Inc +# +# 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 "../migration-test.h" + +.section .text + +.macro print ch + li %r3,PPC_H_PUT_TERM_CHAR + li %r4,0 + li %r5,1 + li %r6,\ch + sldi %r6,%r6,56 + sc 1 +.endm + + .globl _start +_start: +. = 0x100 + /* + * Enter 64-bit mode. Not necessary because the test uses 32-bit + * addresses, but those constants could easily be changed and break + * in 32-bit mode. + */ + mfmsr %r9 + li %r10,-1 + rldimi %r9,%r10,63,0 + mtmsrd %r9 + + /* + * Set up test memory region. Non-volatiles are used because the + * hcall can clobber regs. + * r20 - start address + * r21 - number of pages + */ + lis %r20,PPC_TEST_MEM_START@h + ori %r20,%r20,PPC_TEST_MEM_START@l + lis %r9,PPC_TEST_MEM_END@h + ori %r9,%r9,PPC_TEST_MEM_END@l + subf %r21,%r20,%r9 + li %r10,TEST_MEM_PAGE_SIZE + divd %r21,%r21,%r10 + + print 'A' + + li %r3,0 + mr %r9,%r20 + mtctr %r21 +1: stb %r3,0(%r9) + addi %r9,%r9,TEST_MEM_PAGE_SIZE + bdnz 1b + +loop: + mr %r9,%r20 + mtctr %r21 +1: lbz %r3,0(%r9) + addi %r3,%r3,1 + stb %r3,0(%r9) + addi %r9,%r9,TEST_MEM_PAGE_SIZE + bdnz 1b + + print 'B' + b loop diff --git a/tests/qtest/migration/ppc64/a-b-kernel.h b/tests/qtest/migration/ppc64/a-b-kernel.h new file mode 100644 index 0000000..673317e --- /dev/null +++ b/tests/qtest/migration/ppc64/a-b-kernel.h @@ -0,0 +1,42 @@ +/* This file is automatically generated from the assembly file in + * tests/migration/ppc64. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +unsigned char ppc64_kernel[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7d, 0x20, 0x00, 0xa6, 0x39, 0x40, 0xff, 0xff, + 0x79, 0x49, 0xf8, 0x0e, 0x7d, 0x20, 0x01, 0x64, 0x3e, 0x80, 0x00, 0x10, + 0x62, 0x94, 0x00, 0x00, 0x3d, 0x20, 0x06, 0x40, 0x61, 0x29, 0x00, 0x00, + 0x7e, 0xb4, 0x48, 0x50, 0x39, 0x40, 0x10, 0x00, 0x7e, 0xb5, 0x53, 0xd2, + 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, 0x38, 0xa0, 0x00, 0x01, + 0x38, 0xc0, 0x00, 0x41, 0x78, 0xc6, 0xc1, 0xc6, 0x44, 0x00, 0x00, 0x22, + 0x38, 0x60, 0x00, 0x00, 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, + 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, 0x42, 0x00, 0xff, 0xf8, + 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, 0x88, 0x69, 0x00, 0x00, + 0x38, 0x63, 0x00, 0x01, 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, + 0x42, 0x00, 0xff, 0xf0, 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, + 0x38, 0xa0, 0x00, 0x01, 0x38, 0xc0, 0x00, 0x42, 0x78, 0xc6, 0xc1, 0xc6, + 0x44, 0x00, 0x00, 0x22, 0x4b, 0xff, 0xff, 0xcc +}; + diff --git a/tests/qtest/migration/s390x/Makefile b/tests/qtest/migration/s390x/Makefile new file mode 100644 index 0000000..6671de2 --- /dev/null +++ b/tests/qtest/migration/s390x/Makefile @@ -0,0 +1,24 @@ +# To specify cross compiler prefix, use CROSS_PREFIX= +# $ make CROSS_PREFIX=s390x-linux-gnu- + +.PHONY: all clean +all: a-b-bios.h +fwdir=../../../pc-bios/s390-ccw + +CFLAGS+=-ffreestanding -fno-delete-null-pointer-checks -fPIE -Os \ + -msoft-float -march=z900 -fno-asynchronous-unwind-tables \ + -fno-stack-protector -Wl,-pie -Wl,--build-id=none -nostdlib + +a-b-bios.h: s390x.elf + echo "$$__note" > header.tmp + xxd -i $< | sed -e 's/.*int.*//' >> header.tmp + mv header.tmp $@ + +# We use common-page-size=16 to avoid big padding in the ELF file +s390x.elf: a-b-bios.c + $(CROSS_PREFIX)gcc $(CFLAGS) -I$(fwdir) $(fwdir)/start.S \ + $(fwdir)/sclp.c -Wl,-zcommon-page-size=16 -o $@ $< + $(CROSS_PREFIX)strip $@ + +clean: + @rm -rf *.elf *.o diff --git a/tests/qtest/migration/s390x/a-b-bios.c b/tests/qtest/migration/s390x/a-b-bios.c new file mode 100644 index 0000000..ff99a3e --- /dev/null +++ b/tests/qtest/migration/s390x/a-b-bios.c @@ -0,0 +1,44 @@ +/* + * S390 guest code used in migration tests + * + * Copyright 2018 Thomas Huth, Red Hat Inc. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define LOADPARM_LEN 8 /* Needed for sclp.h */ + +#include +#include +#include + +char stack[0x8000] __attribute__((aligned(4096))); + +#define START_ADDRESS (1024 * 1024) +#define END_ADDRESS (100 * 1024 * 1024) + +void main(void) +{ + unsigned long addr; + + sclp_setup(); + sclp_print("A"); + + /* + * Make sure all of the pages have consistent contents before incrementing + * the first byte below. + */ + for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) { + *(volatile char *)addr = 0; + } + + while (1) { + for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) { + *(volatile char *)addr += 1; /* Change pages */ + } + sclp_print("B"); + } +} diff --git a/tests/qtest/migration/s390x/a-b-bios.h b/tests/qtest/migration/s390x/a-b-bios.h new file mode 100644 index 0000000..96103da --- /dev/null +++ b/tests/qtest/migration/s390x/a-b-bios.h @@ -0,0 +1,279 @@ +/* This file is automatically generated from the a-b-bios.c file in + * tests/migration/s390x. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +unsigned char s390x_elf[] = { + 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x07, 0x00, 0x40, + 0x00, 0x0d, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x18, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x64, 0x74, 0xe5, 0x51, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x64, 0x74, 0xe5, 0x52, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x36, 0x34, 0x2e, 0x73, 0x6f, + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0xeb, 0xef, 0xf0, 0x70, + 0x00, 0x24, 0xa7, 0xfb, 0xff, 0x60, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x5f, + 0xc0, 0x20, 0x00, 0x00, 0x02, 0xa8, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x75, + 0xa5, 0x2e, 0x00, 0x10, 0xa7, 0x19, 0x63, 0x00, 0x92, 0x00, 0x20, 0x00, + 0xa7, 0x2b, 0x10, 0x00, 0xa7, 0x17, 0xff, 0xfc, 0xa5, 0x1e, 0x00, 0x10, + 0xa7, 0x29, 0x63, 0x00, 0xe3, 0x30, 0x10, 0x00, 0x00, 0x90, 0xa7, 0x3a, + 0x00, 0x01, 0x42, 0x30, 0x10, 0x00, 0xa7, 0x1b, 0x10, 0x00, 0xa7, 0x27, + 0xff, 0xf7, 0xc0, 0x20, 0x00, 0x00, 0x02, 0x8a, 0xc0, 0xe5, 0x00, 0x00, + 0x01, 0x56, 0xa7, 0xf4, 0xff, 0xeb, 0x07, 0x07, 0xc0, 0xf0, 0x00, 0x00, + 0x4e, 0x5c, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x7d, 0xe3, 0x20, 0x20, 0x00, + 0x00, 0x04, 0xc0, 0x30, 0x00, 0x00, 0x96, 0xa3, 0xb9, 0x0b, 0x00, 0x32, + 0xb9, 0x02, 0x00, 0x33, 0xa7, 0x84, 0x00, 0x19, 0xa7, 0x3b, 0xff, 0xff, + 0xeb, 0x43, 0x00, 0x08, 0x00, 0x0c, 0xb9, 0x02, 0x00, 0x44, 0xb9, 0x04, + 0x00, 0x12, 0xa7, 0x84, 0x00, 0x09, 0xd7, 0xff, 0x10, 0x00, 0x10, 0x00, + 0x41, 0x10, 0x11, 0x00, 0xa7, 0x47, 0xff, 0xfb, 0xc0, 0x20, 0x00, 0x00, + 0x00, 0x0d, 0x44, 0x30, 0x20, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x5b, + 0xd2, 0x0f, 0x01, 0xd0, 0x20, 0x00, 0xa7, 0xf4, 0xff, 0xa1, 0xd7, 0x00, + 0x10, 0x00, 0x10, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x50, 0xb2, 0xb2, + 0x10, 0x00, 0xa7, 0xf4, 0x00, 0x00, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x25, + 0x96, 0x02, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x2f, 0xc0, 0x10, + 0x00, 0x00, 0x00, 0x2a, 0xe3, 0x10, 0x01, 0xb8, 0x00, 0x24, 0xc0, 0x10, + 0x00, 0x00, 0x00, 0x4b, 0xd2, 0x07, 0x01, 0xb0, 0x10, 0x00, 0xc0, 0x10, + 0x00, 0x00, 0x00, 0x3d, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x66, 0xf0, 0x00, + 0x00, 0x25, 0x96, 0xff, 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x1a, 0xe3, 0x10, 0x01, 0xf8, 0x00, 0x24, + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x36, 0xd2, 0x07, 0x01, 0xf0, 0x10, 0x00, + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x24, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x00, + 0xf0, 0x00, 0x00, 0x25, 0x94, 0xfd, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, + 0x00, 0x2f, 0x07, 0xfe, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x25, 0x94, 0x00, + 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, 0x07, 0xfe, 0x07, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0x00, 0x02, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0xeb, 0xbf, 0xf0, 0x58, + 0x00, 0x24, 0xc0, 0x10, 0x00, 0x00, 0x4e, 0x0d, 0xa7, 0xfb, 0xff, 0x60, + 0xb2, 0x20, 0x00, 0x21, 0xb2, 0x22, 0x00, 0xb0, 0x88, 0xb0, 0x00, 0x1c, + 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x91, 0xa7, 0xbe, 0x00, 0x03, 0xa7, 0x84, + 0x00, 0x13, 0xa7, 0xbe, 0x00, 0x02, 0xa7, 0x28, 0x00, 0x00, 0xa7, 0x74, + 0x00, 0x04, 0xa7, 0x28, 0xff, 0xfe, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, + 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, + 0xa7, 0x28, 0xff, 0xff, 0xa7, 0xf4, 0xff, 0xf5, 0x07, 0x07, 0x07, 0x07, + 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x01, 0x25, + 0xa7, 0xfb, 0xff, 0x60, 0xa7, 0xb9, 0x00, 0x00, 0xa7, 0x19, 0x00, 0x00, + 0xc0, 0x40, 0x00, 0x00, 0x4d, 0xd8, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0x37, + 0x00, 0x23, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0xd1, 0x18, 0x31, 0xa7, 0x1a, + 0x00, 0x06, 0x40, 0x10, 0x20, 0x08, 0xa7, 0x3a, 0x00, 0x0e, 0xa7, 0x18, + 0x1a, 0x00, 0x40, 0x30, 0x20, 0x00, 0x92, 0x00, 0x20, 0x02, 0x40, 0x10, + 0x20, 0x0a, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, + 0xff, 0xac, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xb9, 0x04, 0x00, 0x2b, + 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0xb9, 0x04, 0x00, 0x51, + 0xa7, 0x5b, 0x00, 0x01, 0xa7, 0x09, 0x0f, 0xf7, 0xb9, 0x21, 0x00, 0x50, + 0xa7, 0x24, 0xff, 0xd7, 0x41, 0xeb, 0x20, 0x00, 0x95, 0x0a, 0xe0, 0x00, + 0xa7, 0x74, 0x00, 0x08, 0x41, 0x11, 0x40, 0x0e, 0x92, 0x0d, 0x10, 0x00, + 0xb9, 0x04, 0x00, 0x15, 0x43, 0x5b, 0x20, 0x00, 0x42, 0x51, 0x40, 0x0e, + 0xa7, 0xbb, 0x00, 0x01, 0x41, 0x10, 0x10, 0x01, 0xa7, 0xf4, 0xff, 0xbf, + 0xc0, 0x50, 0x00, 0x00, 0x00, 0xd8, 0xc0, 0x10, 0x00, 0x00, 0x4d, 0x8d, + 0xa7, 0x48, 0x00, 0x1c, 0x40, 0x40, 0x10, 0x00, 0x50, 0x20, 0x10, 0x0c, + 0xa7, 0x48, 0x00, 0x04, 0xe3, 0x20, 0x50, 0x00, 0x00, 0x04, 0x40, 0x40, + 0x10, 0x0a, 0x50, 0x30, 0x10, 0x10, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x6b, + 0xa7, 0x39, 0x00, 0x40, 0xa7, 0x29, 0x00, 0x00, 0xc0, 0xf4, 0xff, 0xff, + 0xff, 0xe4, 0x07, 0x07, 0xb9, 0x04, 0x00, 0x13, 0xa7, 0x2a, 0xff, 0xff, + 0xb9, 0x04, 0x00, 0x34, 0xa7, 0x48, 0x00, 0x01, 0x15, 0x24, 0xa7, 0x24, + 0x00, 0x07, 0xb9, 0x04, 0x00, 0x21, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x7f, + 0xa7, 0x29, 0xff, 0xff, 0x07, 0xfe, 0x07, 0x07, 0xa7, 0x39, 0x00, 0x00, + 0x41, 0x13, 0x20, 0x00, 0x95, 0x00, 0x10, 0x00, 0xa7, 0x74, 0x00, 0x05, + 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x70, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0xf4, + 0xff, 0xf5, 0x07, 0x07, 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, + 0x00, 0x00, 0x00, 0x95, 0xa7, 0xfb, 0xff, 0x60, 0xb9, 0x04, 0x00, 0xb2, + 0xa7, 0x19, 0x00, 0x20, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0x40, 0x92, 0x00, + 0x20, 0x00, 0xa7, 0x2b, 0x00, 0x01, 0xa7, 0x17, 0xff, 0xfc, 0xc0, 0x10, + 0x00, 0x00, 0x4d, 0x37, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00, + 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x1d, + 0x12, 0x22, 0xa7, 0x74, 0x00, 0x19, 0xa7, 0x19, 0x00, 0x00, 0xc0, 0x40, + 0x00, 0x00, 0x00, 0x79, 0xa7, 0x39, 0x00, 0x08, 0xc0, 0x20, 0x00, 0x00, + 0x4d, 0x2c, 0x41, 0x21, 0x20, 0x00, 0xe3, 0x20, 0x20, 0x00, 0x00, 0x90, + 0x43, 0x22, 0x40, 0x00, 0x42, 0x21, 0xb0, 0x00, 0xa7, 0x1b, 0x00, 0x01, + 0xa7, 0x37, 0xff, 0xf2, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xeb, 0xbf, + 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0x07, 0x07, 0xeb, 0xaf, 0xf0, 0x50, + 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x00, 0x55, 0xa7, 0xfb, 0xff, 0x60, + 0xa7, 0x19, 0x0f, 0xf8, 0xb9, 0x21, 0x00, 0x31, 0xb9, 0x04, 0x00, 0xa2, + 0xa7, 0xc4, 0x00, 0x2a, 0xa7, 0xb9, 0x0f, 0xf8, 0xc0, 0x10, 0x00, 0x00, + 0x4c, 0xf6, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00, 0x92, 0x00, + 0x10, 0x02, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, + 0xfe, 0xda, 0xa7, 0xbb, 0x00, 0x01, 0xa7, 0x19, 0x00, 0x00, 0xa7, 0xb7, + 0x00, 0x17, 0xc0, 0x10, 0x00, 0x00, 0x4c, 0xe1, 0xe3, 0x40, 0xf1, 0x10, + 0x00, 0x04, 0xe3, 0x20, 0x10, 0x08, 0x00, 0x91, 0xa7, 0x2a, 0xff, 0xf9, + 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xaf, 0xf0, 0xf0, 0x00, 0x04, 0x07, 0xf4, + 0xb9, 0x04, 0x00, 0xb3, 0xa7, 0xf4, 0xff, 0xd8, 0xc0, 0x20, 0x00, 0x00, + 0x4c, 0xcc, 0x41, 0x31, 0xa0, 0x00, 0x41, 0x21, 0x20, 0x00, 0xa7, 0x1b, + 0x00, 0x01, 0xd2, 0x00, 0x30, 0x00, 0x20, 0x0f, 0xa7, 0xf4, 0xff, 0xdd, + 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x20, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, 0x26, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x2e, + 0x2d, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2c, + 0x25, 0x5f, 0x3e, 0x3f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, 0x2e, 0x61, 0x62, 0x63, + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x41, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6f, 0xff, 0xfe, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xfb, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x6f, 0xff, 0xff, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, + 0x20, 0x28, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x31, 0x31, 0x2e, + 0x34, 0x2e, 0x30, 0x2d, 0x31, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x31, + 0x7e, 0x32, 0x32, 0x2e, 0x30, 0x34, 0x29, 0x20, 0x31, 0x31, 0x2e, 0x34, + 0x2e, 0x30, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, + 0x62, 0x00, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x00, 0x2e, 0x67, + 0x6e, 0x75, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, + 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, + 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x74, + 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00, + 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, + 0x74, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6f, 0xff, 0xff, 0xf6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4e, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x09, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; -- cgit v1.1 From 537f23fb9e215c4d42ec47055a24cf4670133544 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:48 -0300 Subject: tests/qtest/migration: Move bootfile code to its own file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the code that creates the guest binary out of migration-test and into the qtest/migration/ directory, along with the rest of the a-b-kernel code. That code is part of the basic infrastructure of migration tests, it shouldn't be among the tests themselves. Also take the chance and rename migration-test.h, which is too generic a name for this header which contains only values related to guest memory offsets. Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 8 +++- tests/qtest/migration-helpers.c | 1 + tests/qtest/migration-test.c | 58 ++-------------------------- tests/qtest/migration/bootfile.c | 70 ++++++++++++++++++++++++++++++++++ tests/qtest/migration/bootfile.h | 39 +++++++++++++++++++ tests/qtest/migration/migration-test.h | 36 ----------------- 6 files changed, 119 insertions(+), 93 deletions(-) create mode 100644 tests/qtest/migration/bootfile.c create mode 100644 tests/qtest/migration/bootfile.h delete mode 100644 tests/qtest/migration/migration-test.h (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 89db3ec..c3712a9 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -332,7 +332,11 @@ endif tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] -migration_files = [files('migration-helpers.c')] +migration_files = [files( + 'migration-helpers.c', + 'migration/bootfile.c', +)] + if gnutls.found() migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls] @@ -358,7 +362,7 @@ qtests = { 'tpm-tis-i2c-test': [io, tpmemu_files, 'qtest_aspeed.c'], 'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'], 'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'], - 'virtio-net-failover': files('migration-helpers.c'), + 'virtio-net-failover': migration_files, 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 3f8ba7f..2786f9c 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -21,6 +21,7 @@ #include "qemu/memalign.h" #include "migration-helpers.h" +#include "migration/bootfile.h" /* * Number of seconds we wait when looking for migration diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 82b9170..e104e44 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -24,7 +24,7 @@ #include "ppc-util.h" #include "migration-helpers.h" -#include "migration/migration-test.h" +#include "migration/bootfile.h" #ifdef CONFIG_GNUTLS # include "tests/unit/crypto-tls-psk-helpers.h" # ifdef CONFIG_TASN1 @@ -135,58 +135,6 @@ static bool ufd_version_check(void) static char *tmpfs; static char *bootpath; -/* The boot file modifies memory area in [start_address, end_address) - * repeatedly. It outputs a 'B' at a fixed rate while it's still running. - */ -#include "migration/i386/a-b-bootblock.h" -#include "migration/aarch64/a-b-kernel.h" -#include "migration/ppc64/a-b-kernel.h" -#include "migration/s390x/a-b-bios.h" - -static void bootfile_delete(void) -{ - if (!bootpath) { - return; - } - unlink(bootpath); - g_free(bootpath); - bootpath = NULL; -} - -static void bootfile_create(char *dir, bool suspend_me) -{ - const char *arch = qtest_get_arch(); - unsigned char *content; - size_t len; - - bootfile_delete(); - bootpath = g_strdup_printf("%s/bootsect", dir); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - /* the assembled x86 boot sector should be exactly one sector large */ - g_assert(sizeof(x86_bootsect) == 512); - x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me; - content = x86_bootsect; - len = sizeof(x86_bootsect); - } else if (g_str_equal(arch, "s390x")) { - content = s390x_elf; - len = sizeof(s390x_elf); - } else if (strcmp(arch, "ppc64") == 0) { - content = ppc64_kernel; - len = sizeof(ppc64_kernel); - } else if (strcmp(arch, "aarch64") == 0) { - content = aarch64_kernel; - len = sizeof(aarch64_kernel); - g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); - } else { - g_assert_not_reached(); - } - - FILE *bootfile = fopen(bootpath, "wb"); - - g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); - fclose(bootfile); -} - /* * Wait for some output in the serial output file, * we get an 'A' followed by an endless string of 'B's @@ -737,7 +685,7 @@ static int migrate_start(QTestState **from, QTestState **to, dst_state = (QTestMigrationState) { }; src_state = (QTestMigrationState) { }; - bootfile_create(tmpfs, args->suspend_me); + bootpath = bootfile_create(arch, tmpfs, args->suspend_me); src_state.suspend_me = args->suspend_me; if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { @@ -3488,7 +3436,7 @@ static QTestState *dirtylimit_start_vm(void) QTestState *vm = NULL; g_autofree gchar *cmd = NULL; - bootfile_create(tmpfs, false); + bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " "-name dirtylimit-test,debug-threads=on " "-m 150M -smp 1 " diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c new file mode 100644 index 0000000..8f75f64 --- /dev/null +++ b/tests/qtest/migration/bootfile.c @@ -0,0 +1,70 @@ +/* + * Guest code setup for migration tests + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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" + +/* + * The boot file modifies memory area in [start_address, end_address) + * repeatedly. It outputs a 'B' at a fixed rate while it's still running. + */ +#include "bootfile.h" +#include "i386/a-b-bootblock.h" +#include "aarch64/a-b-kernel.h" +#include "ppc64/a-b-kernel.h" +#include "s390x/a-b-bios.h" + +static char *bootpath; + +void bootfile_delete(void) +{ + if (!bootpath) { + return; + } + unlink(bootpath); + g_free(bootpath); + bootpath = NULL; +} + +char *bootfile_create(const char *arch, char *dir, bool suspend_me) +{ + unsigned char *content; + size_t len; + + bootfile_delete(); + bootpath = g_strdup_printf("%s/bootsect", dir); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + /* the assembled x86 boot sector should be exactly one sector large */ + g_assert(sizeof(x86_bootsect) == 512); + x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me; + content = x86_bootsect; + len = sizeof(x86_bootsect); + } else if (g_str_equal(arch, "s390x")) { + content = s390x_elf; + len = sizeof(s390x_elf); + } else if (strcmp(arch, "ppc64") == 0) { + content = ppc64_kernel; + len = sizeof(ppc64_kernel); + } else if (strcmp(arch, "aarch64") == 0) { + content = aarch64_kernel; + len = sizeof(aarch64_kernel); + g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); + } else { + g_assert_not_reached(); + } + + FILE *bootfile = fopen(bootpath, "wb"); + + g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); + fclose(bootfile); + + return bootpath; +} diff --git a/tests/qtest/migration/bootfile.h b/tests/qtest/migration/bootfile.h new file mode 100644 index 0000000..4f5099d --- /dev/null +++ b/tests/qtest/migration/bootfile.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Red Hat, Inc. and/or its affiliates + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef BOOTFILE_H +#define BOOTFILE_H + +/* Common */ +#define TEST_MEM_PAGE_SIZE 4096 + +/* x86 */ +#define X86_TEST_MEM_START (1 * 1024 * 1024) +#define X86_TEST_MEM_END (100 * 1024 * 1024) + +/* S390 */ +#define S390_TEST_MEM_START (1 * 1024 * 1024) +#define S390_TEST_MEM_END (100 * 1024 * 1024) + +/* PPC */ +#define PPC_TEST_MEM_START (1 * 1024 * 1024) +#define PPC_TEST_MEM_END (100 * 1024 * 1024) +#define PPC_H_PUT_TERM_CHAR 0x58 + +/* ARM */ +#define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024) +#define ARM_TEST_MEM_END (0x40000000 + 100 * 1024 * 1024) +#define ARM_MACH_VIRT_UART 0x09000000 +/* AArch64 kernel load address is 0x40080000, and the test memory starts at + * 0x40100000. So the maximum allowable kernel size is 512KB. + */ +#define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) + +void bootfile_delete(void); +char *bootfile_create(const char *arch, char *dir, bool suspend_me); + +#endif /* BOOTFILE_H */ diff --git a/tests/qtest/migration/migration-test.h b/tests/qtest/migration/migration-test.h deleted file mode 100644 index 194df7df6..0000000 --- a/tests/qtest/migration/migration-test.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018 Red Hat, Inc. and/or its affiliates - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef MIGRATION_TEST_H -#define MIGRATION_TEST_H - -/* Common */ -#define TEST_MEM_PAGE_SIZE 4096 - -/* x86 */ -#define X86_TEST_MEM_START (1 * 1024 * 1024) -#define X86_TEST_MEM_END (100 * 1024 * 1024) - -/* S390 */ -#define S390_TEST_MEM_START (1 * 1024 * 1024) -#define S390_TEST_MEM_END (100 * 1024 * 1024) - -/* PPC */ -#define PPC_TEST_MEM_START (1 * 1024 * 1024) -#define PPC_TEST_MEM_END (100 * 1024 * 1024) -#define PPC_H_PUT_TERM_CHAR 0x58 - -/* ARM */ -#define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024) -#define ARM_TEST_MEM_END (0x40000000 + 100 * 1024 * 1024) -#define ARM_MACH_VIRT_UART 0x09000000 -/* AArch64 kernel load address is 0x40080000, and the test memory starts at - * 0x40100000. So the maximum allowable kernel size is 512KB. - */ -#define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) - -#endif /* MIGRATION_TEST_H */ -- cgit v1.1 From e6c5704043f0cdb3ce567b74d73a99c79457e3e6 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:49 -0300 Subject: tests/qtest/migration: Move qmp helpers to a separate file We current have a bunch of non-test functions in migration-test.c and some others in migration-helpers.c. In order to split migration-test.c into separate test binaries, these helpers need to go somewhere else. To avoid making migration-helpers even larger, move all QMP-related functions into a new migration-qmp.c file and put it under the qtest/migration/ directory. The new file holds everything that has as its main responsibility to call into QMP. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 4 +- tests/qtest/migration-helpers.c | 250 +----------------- tests/qtest/migration-helpers.h | 18 +- tests/qtest/migration-test.c | 237 +---------------- tests/qtest/migration/migration-qmp.c | 485 ++++++++++++++++++++++++++++++++++ tests/qtest/migration/migration-qmp.h | 44 +++ tests/qtest/virtio-net-failover.c | 1 + 7 files changed, 542 insertions(+), 497 deletions(-) create mode 100644 tests/qtest/migration/migration-qmp.c create mode 100644 tests/qtest/migration/migration-qmp.h (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index c3712a9..0b113ad 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -335,6 +335,7 @@ tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] migration_files = [files( 'migration-helpers.c', 'migration/bootfile.c', + 'migration/migration-qmp.c', )] if gnutls.found() @@ -348,7 +349,8 @@ endif qtests = { 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], 'cdrom-test': files('boot-sector.c'), - 'dbus-vmstate-test': files('migration-helpers.c') + dbus_vmstate1, + 'dbus-vmstate-test': files('migration/migration-qmp.c', + 'migration-helpers.c') + dbus_vmstate1, 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], 'migration-test': migration_files, diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 2786f9c..218ee4e 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "qemu/ctype.h" -#include "qapi/qmp/qjson.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" @@ -23,14 +22,6 @@ #include "migration-helpers.h" #include "migration/bootfile.h" -/* - * Number of seconds we wait when looking for migration - * status changes, to avoid test suite hanging forever - * when things go wrong. Needs to be higher enough to - * avoid false positives on loaded hosts. - */ -#define MIGRATION_STATUS_WAIT_TIMEOUT 120 - static char *SocketAddress_to_str(SocketAddress *addr) { switch (addr->type) { @@ -100,8 +91,7 @@ static SocketAddressList *migrate_get_socket_address(QTestState *who) return addrs; } -static char * -migrate_get_connect_uri(QTestState *who) +char *migrate_get_connect_uri(QTestState *who) { SocketAddressList *addrs; char *connect_uri; @@ -126,7 +116,7 @@ migrate_get_connect_qdict(QTestState *who) return connect_qdict; } -static void migrate_set_ports(QTestState *to, QList *channel_list) +void migrate_set_ports(QTestState *to, QList *channel_list) { QDict *addr; QListEntry *entry; @@ -168,222 +158,6 @@ bool migrate_watch_for_events(QTestState *who, const char *name, return false; } -void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...) -{ - va_list ap; - QDict *args, *err; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - if (uri) { - qdict_put_str(args, "uri", uri); - } - - g_assert(!qdict_haskey(args, "channels")); - if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - qdict_put_obj(args, "channels", channels_obj); - } - - err = qtest_qmp_assert_failure_ref( - who, "{ 'execute': 'migrate', 'arguments': %p}", args); - - g_assert(qdict_haskey(err, "desc")); - - qobject_unref(err); -} - -/* - * Send QMP command "migrate". - * Arguments are built from @fmt... (formatted like - * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. - */ -void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...) -{ - va_list ap; - QDict *args; - g_autofree char *connect_uri = NULL; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - if (uri) { - qdict_put_str(args, "uri", uri); - } else if (!channels) { - connect_uri = migrate_get_connect_uri(to); - qdict_put_str(args, "uri", connect_uri); - } - - g_assert(!qdict_haskey(args, "channels")); - if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - QList *channel_list = qobject_to(QList, channels_obj); - migrate_set_ports(to, channel_list); - qdict_put_obj(args, "channels", channels_obj); - } - - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate', 'arguments': %p}", args); -} - -void migrate_set_capability(QTestState *who, const char *capability, - bool value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-capabilities'," - "'arguments': { " - "'capabilities': [ { " - "'capability': %s, 'state': %i } ] } }", - capability, value); -} - -void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) -{ - va_list ap; - QDict *args, *rsp; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); - - /* This function relies on the event to work, make sure it's enabled */ - migrate_set_capability(to, "events", true); - - rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", - args); - - if (!qdict_haskey(rsp, "return")) { - g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); - g_test_message("%s", s->str); - } - - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - - migration_event_wait(to, "setup"); -} - -/* - * Note: caller is responsible to free the returned object via - * qobject_unref() after use - */ -QDict *migrate_query(QTestState *who) -{ - return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); -} - -QDict *migrate_query_not_failed(QTestState *who) -{ - const char *status; - QDict *rsp = migrate_query(who); - status = qdict_get_str(rsp, "status"); - if (g_str_equal(status, "failed")) { - g_printerr("query-migrate shows failed migration: %s\n", - qdict_get_str(rsp, "error-desc")); - } - g_assert(!g_str_equal(status, "failed")); - return rsp; -} - -/* - * Note: caller is responsible to free the returned object via - * g_free() after use - */ -static gchar *migrate_query_status(QTestState *who) -{ - QDict *rsp_return = migrate_query(who); - gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); - - g_assert(status); - qobject_unref(rsp_return); - - return status; -} - -static bool check_migration_status(QTestState *who, const char *goal, - const char **ungoals) -{ - bool ready; - char *current_status; - const char **ungoal; - - current_status = migrate_query_status(who); - ready = strcmp(current_status, goal) == 0; - if (!ungoals) { - g_assert_cmpstr(current_status, !=, "failed"); - /* - * If looking for a state other than completed, - * completion of migration would cause the test to - * hang. - */ - if (strcmp(goal, "completed") != 0) { - g_assert_cmpstr(current_status, !=, "completed"); - } - } else { - for (ungoal = ungoals; *ungoal; ungoal++) { - g_assert_cmpstr(current_status, !=, *ungoal); - } - } - g_free(current_status); - return ready; -} - -void wait_for_migration_status(QTestState *who, - const char *goal, const char **ungoals) -{ - g_test_timer_start(); - while (!check_migration_status(who, goal, ungoals)) { - usleep(1000); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } -} - -void wait_for_migration_complete(QTestState *who) -{ - wait_for_migration_status(who, "completed", NULL); -} - -void wait_for_migration_fail(QTestState *from, bool allow_active) -{ - g_test_timer_start(); - QDict *rsp_return; - char *status; - bool failed; - - do { - status = migrate_query_status(from); - bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || - (allow_active && !strcmp(status, "active")); - if (!result) { - fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", - __func__, status, allow_active); - } - g_assert(result); - failed = !strcmp(status, "failed"); - g_free(status); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } while (!failed); - - /* Is the machine currently running? */ - rsp_return = qtest_qmp_assert_success_ref(from, - "{ 'execute': 'query-status' }"); - g_assert(qdict_haskey(rsp_return, "running")); - g_assert(qdict_get_bool(rsp_return, "running")); - qobject_unref(rsp_return); -} - char *find_common_machine_version(const char *mtype, const char *var1, const char *var2) { @@ -509,23 +283,3 @@ bool probe_o_direct_support(const char *tmpfs) return true; } #endif - -/* - * Wait for a "MIGRATION" event. This is what Libvirt uses to track - * migration status changes. - */ -void migration_event_wait(QTestState *s, const char *target) -{ - QDict *response, *data; - const char *status; - bool found; - - do { - response = qtest_qmp_eventwait_ref(s, "MIGRATION"); - data = qdict_get_qdict(response, "data"); - g_assert(data); - status = qdict_get_str(data, "status"); - found = (strcmp(status, target) == 0); - qobject_unref(response); - } while (!found); -} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 72dba36..2cb1f78 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -25,21 +25,6 @@ typedef struct QTestMigrationState { bool migrate_watch_for_events(QTestState *who, const char *name, QDict *event, void *opaque); -G_GNUC_PRINTF(5, 6) -void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...); - -G_GNUC_PRINTF(3, 4) -void migrate_incoming_qmp(QTestState *who, const char *uri, - const char *fmt, ...); - -G_GNUC_PRINTF(4, 5) -void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...); - -void migrate_set_capability(QTestState *who, const char *capability, - bool value); - QDict *migrate_query(QTestState *who); QDict *migrate_query_not_failed(QTestState *who); @@ -63,6 +48,7 @@ static inline bool probe_o_direct_support(const char *tmpfs) } #endif void migration_test_add(const char *path, void (*fn)(void)); -void migration_event_wait(QTestState *s, const char *target); +char *migrate_get_connect_uri(QTestState *who); +void migrate_set_ports(QTestState *to, QList *channel_list); #endif /* MIGRATION_HELPERS_H */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index e104e44..489fca7 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -13,18 +13,18 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/range.h" #include "qemu/sockets.h" #include "chardev/char.h" #include "crypto/tlscredspsk.h" -#include "qapi/qmp/qlist.h" #include "ppc-util.h" #include "migration-helpers.h" #include "migration/bootfile.h" +#include "migration/migration-qmp.h" #ifdef CONFIG_GNUTLS # include "tests/unit/crypto-tls-psk-helpers.h" # ifdef CONFIG_TASN1 @@ -170,89 +170,6 @@ static void wait_for_serial(const char *side) } while (true); } -static void wait_for_stop(QTestState *who, QTestMigrationState *state) -{ - if (!state->stop_seen) { - qtest_qmp_eventwait(who, "STOP"); - } -} - -static void wait_for_resume(QTestState *who, QTestMigrationState *state) -{ - if (!state->resume_seen) { - qtest_qmp_eventwait(who, "RESUME"); - } -} - -static void wait_for_suspend(QTestState *who, QTestMigrationState *state) -{ - if (state->suspend_me && !state->suspend_seen) { - qtest_qmp_eventwait(who, "SUSPEND"); - } -} - -/* - * It's tricky to use qemu's migration event capability with qtest, - * events suddenly appearing confuse the qmp()/hmp() responses. - */ - -static int64_t read_ram_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return, *rsp_ram; - int64_t result; - - rsp_return = migrate_query_not_failed(who); - if (!qdict_haskey(rsp_return, "ram")) { - /* Still in setup */ - result = 0; - } else { - rsp_ram = qdict_get_qdict(rsp_return, "ram"); - result = qdict_get_try_int(rsp_ram, property, 0); - } - qobject_unref(rsp_return); - return result; -} - -static int64_t read_migrate_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return; - int64_t result; - - rsp_return = migrate_query_not_failed(who); - result = qdict_get_try_int(rsp_return, property, 0); - qobject_unref(rsp_return); - return result; -} - -static uint64_t get_migration_pass(QTestState *who) -{ - return read_ram_property_int(who, "dirty-sync-count"); -} - -static void read_blocktime(QTestState *who) -{ - QDict *rsp_return; - - rsp_return = migrate_query_not_failed(who); - g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); - qobject_unref(rsp_return); -} - -/* - * Wait for two changes in the migration pass count, but bail if we stop. - */ -static void wait_for_migration_pass(QTestState *who) -{ - uint64_t pass, prev_pass = 0, changes = 0; - - while (changes < 2 && !src_state.stop_seen && !src_state.suspend_seen) { - usleep(1000); - pass = get_migration_pass(who); - changes += (pass != prev_pass); - prev_pass = pass; - } -} - static void check_guests_ram(QTestState *who) { /* Our ASM test will have been incrementing one byte from each page from @@ -308,114 +225,6 @@ static void cleanup(const char *filename) unlink(path); } -static long long migrate_get_parameter_int(QTestState *who, - const char *parameter) -{ - QDict *rsp; - long long result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = qdict_get_int(rsp, parameter); - qobject_unref(rsp); - return result; -} - -static void migrate_check_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - long long result; - - result = migrate_get_parameter_int(who, parameter); - g_assert_cmpint(result, ==, value); -} - -static void migrate_set_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %lld } }", - parameter, value); - migrate_check_parameter_int(who, parameter, value); -} - -static char *migrate_get_parameter_str(QTestState *who, - const char *parameter) -{ - QDict *rsp; - char *result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = g_strdup(qdict_get_str(rsp, parameter)); - qobject_unref(rsp); - return result; -} - -static void migrate_check_parameter_str(QTestState *who, const char *parameter, - const char *value) -{ - g_autofree char *result = migrate_get_parameter_str(who, parameter); - g_assert_cmpstr(result, ==, value); -} - -static void migrate_set_parameter_str(QTestState *who, const char *parameter, - const char *value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %s } }", - parameter, value); - migrate_check_parameter_str(who, parameter, value); -} - -static long long migrate_get_parameter_bool(QTestState *who, - const char *parameter) -{ - QDict *rsp; - int result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = qdict_get_bool(rsp, parameter); - qobject_unref(rsp); - return !!result; -} - -static void migrate_check_parameter_bool(QTestState *who, const char *parameter, - int value) -{ - int result; - - result = migrate_get_parameter_bool(who, parameter); - g_assert_cmpint(result, ==, value); -} - -static void migrate_set_parameter_bool(QTestState *who, const char *parameter, - int value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %i } }", - parameter, value); - migrate_check_parameter_bool(who, parameter, value); -} - -static void migrate_ensure_non_converge(QTestState *who) -{ - /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); - migrate_set_parameter_int(who, "downtime-limit", 1); -} - -static void migrate_ensure_converge(QTestState *who) -{ - /* Should converge with 30s downtime + 1 gbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); - migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); -} - /* * Our goal is to ensure that we run a single full migration * iteration, and also dirty memory, ensuring that at least @@ -506,42 +315,6 @@ static void migrate_wait_for_dirty_mem(QTestState *from, } while (qtest_readb(from, watch_address) == watch_byte); } - -static void migrate_pause(QTestState *who) -{ - qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); -} - -static void migrate_continue(QTestState *who, const char *state) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-continue'," - " 'arguments': { 'state': %s } }", - state); -} - -static void migrate_recover(QTestState *who, const char *uri) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-recover', " - " 'id': 'recover-cmd', " - " 'arguments': { 'uri': %s } }", - uri); -} - -static void migrate_cancel(QTestState *who) -{ - qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); -} - -static void migrate_postcopy_start(QTestState *from, QTestState *to) -{ - qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); - - wait_for_stop(from, &src_state); - qtest_qmp_eventwait(to, "RESUME"); -} - typedef struct { /* * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors @@ -1277,7 +1050,7 @@ static void test_postcopy_common(MigrateCommon *args) if (migrate_postcopy_prepare(&from, &to, args)) { return; } - migrate_postcopy_start(from, to); + migrate_postcopy_start(from, to, &src_state); migrate_postcopy_complete(from, to, args); } @@ -1464,7 +1237,7 @@ static void test_postcopy_recovery_common(MigrateCommon *args) migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); /* Now we start the postcopy */ - migrate_postcopy_start(from, to); + migrate_postcopy_start(from, to, &src_state); /* * Wait until postcopy is really started; we can only run the @@ -1704,7 +1477,7 @@ static void test_precopy_common(MigrateCommon *args) * for some dirty mem before switching to converge */ while (args->iterations > 1) { - wait_for_migration_pass(from); + wait_for_migration_pass(from, &src_state); args->iterations--; } migrate_wait_for_dirty_mem(from, to); diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c new file mode 100644 index 0000000..20be46f --- /dev/null +++ b/tests/qtest/migration/migration-qmp.c @@ -0,0 +1,485 @@ +/* + * QTest QMP helpers for migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "libqtest.h" +#include "migration-helpers.h" +#include "migration-qmp.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qlist.h" + +/* + * Number of seconds we wait when looking for migration + * status changes, to avoid test suite hanging forever + * when things go wrong. Needs to be higher enough to + * avoid false positives on loaded hosts. + */ +#define MIGRATION_STATUS_WAIT_TIMEOUT 120 + +/* + * Wait for a "MIGRATION" event. This is what Libvirt uses to track + * migration status changes. + */ +void migration_event_wait(QTestState *s, const char *target) +{ + QDict *response, *data; + const char *status; + bool found; + + do { + response = qtest_qmp_eventwait_ref(s, "MIGRATION"); + data = qdict_get_qdict(response, "data"); + g_assert(data); + status = qdict_get_str(data, "status"); + found = (strcmp(status, target) == 0); + qobject_unref(response); + } while (!found); +} + +void migrate_qmp_fail(QTestState *who, const char *uri, + const char *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args, *err; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + qdict_put_obj(args, "channels", channels_obj); + } + + err = qtest_qmp_assert_failure_ref( + who, "{ 'execute': 'migrate', 'arguments': %p}", args); + + g_assert(qdict_haskey(err, "desc")); + + qobject_unref(err); +} + +/* + * Send QMP command "migrate". + * Arguments are built from @fmt... (formatted like + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. + */ +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + const char *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args; + g_autofree char *connect_uri = NULL; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } else if (!channels) { + connect_uri = migrate_get_connect_uri(to); + qdict_put_str(args, "uri", connect_uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + QList *channel_list = qobject_to(QList, channels_obj); + migrate_set_ports(to, channel_list); + qdict_put_obj(args, "channels", channels_obj); + } + + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate', 'arguments': %p}", args); +} + +void migrate_set_capability(QTestState *who, const char *capability, + bool value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-capabilities'," + "'arguments': { " + "'capabilities': [ { " + "'capability': %s, 'state': %i } ] } }", + capability, value); +} + +void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) +{ + va_list ap; + QDict *args, *rsp; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + qdict_put_str(args, "uri", uri); + + /* This function relies on the event to work, make sure it's enabled */ + migrate_set_capability(to, "events", true); + + rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", + args); + + if (!qdict_haskey(rsp, "return")) { + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); + g_test_message("%s", s->str); + } + + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + + migration_event_wait(to, "setup"); +} + +static bool check_migration_status(QTestState *who, const char *goal, + const char **ungoals) +{ + bool ready; + char *current_status; + const char **ungoal; + + current_status = migrate_query_status(who); + ready = strcmp(current_status, goal) == 0; + if (!ungoals) { + g_assert_cmpstr(current_status, !=, "failed"); + /* + * If looking for a state other than completed, + * completion of migration would cause the test to + * hang. + */ + if (strcmp(goal, "completed") != 0) { + g_assert_cmpstr(current_status, !=, "completed"); + } + } else { + for (ungoal = ungoals; *ungoal; ungoal++) { + g_assert_cmpstr(current_status, !=, *ungoal); + } + } + g_free(current_status); + return ready; +} + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals) +{ + g_test_timer_start(); + while (!check_migration_status(who, goal, ungoals)) { + usleep(1000); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } +} + +void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed", NULL); +} + +void wait_for_migration_fail(QTestState *from, bool allow_active) +{ + g_test_timer_start(); + QDict *rsp_return; + char *status; + bool failed; + + do { + status = migrate_query_status(from); + bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || + (allow_active && !strcmp(status, "active")); + if (!result) { + fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", + __func__, status, allow_active); + } + g_assert(result); + failed = !strcmp(status, "failed"); + g_free(status); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } while (!failed); + + /* Is the machine currently running? */ + rsp_return = qtest_qmp_assert_success_ref(from, + "{ 'execute': 'query-status' }"); + g_assert(qdict_haskey(rsp_return, "running")); + g_assert(qdict_get_bool(rsp_return, "running")); + qobject_unref(rsp_return); +} + +void wait_for_stop(QTestState *who, QTestMigrationState *state) +{ + if (!state->stop_seen) { + qtest_qmp_eventwait(who, "STOP"); + } +} + +void wait_for_resume(QTestState *who, QTestMigrationState *state) +{ + if (!state->resume_seen) { + qtest_qmp_eventwait(who, "RESUME"); + } +} + +void wait_for_suspend(QTestState *who, QTestMigrationState *state) +{ + if (state->suspend_me && !state->suspend_seen) { + qtest_qmp_eventwait(who, "SUSPEND"); + } +} + +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +QDict *migrate_query(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); +} + +QDict *migrate_query_not_failed(QTestState *who) +{ + const char *status; + QDict *rsp = migrate_query(who); + status = qdict_get_str(rsp, "status"); + if (g_str_equal(status, "failed")) { + g_printerr("query-migrate shows failed migration: %s\n", + qdict_get_str(rsp, "error-desc")); + } + g_assert(!g_str_equal(status, "failed")); + return rsp; +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} + +int64_t read_ram_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return, *rsp_ram; + int64_t result; + + rsp_return = migrate_query_not_failed(who); + if (!qdict_haskey(rsp_return, "ram")) { + /* Still in setup */ + result = 0; + } else { + rsp_ram = qdict_get_qdict(rsp_return, "ram"); + result = qdict_get_try_int(rsp_ram, property, 0); + } + qobject_unref(rsp_return); + return result; +} + +int64_t read_migrate_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return; + int64_t result; + + rsp_return = migrate_query_not_failed(who); + result = qdict_get_try_int(rsp_return, property, 0); + qobject_unref(rsp_return); + return result; +} + +uint64_t get_migration_pass(QTestState *who) +{ + return read_ram_property_int(who, "dirty-sync-count"); +} + +void read_blocktime(QTestState *who) +{ + QDict *rsp_return; + + rsp_return = migrate_query_not_failed(who); + g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); + qobject_unref(rsp_return); +} + +/* + * Wait for two changes in the migration pass count, but bail if we stop. + */ +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state) +{ + uint64_t pass, prev_pass = 0, changes = 0; + + while (changes < 2 && !src_state->stop_seen && !src_state->suspend_seen) { + usleep(1000); + pass = get_migration_pass(who); + changes += (pass != prev_pass); + prev_pass = pass; + } +} + +static long long migrate_get_parameter_int(QTestState *who, + const char *parameter) +{ + QDict *rsp; + long long result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_int(rsp, parameter); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + long long result; + + result = migrate_get_parameter_int(who, parameter); + g_assert_cmpint(result, ==, value); +} + +void migrate_set_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %lld } }", + parameter, value); + migrate_check_parameter_int(who, parameter, value); +} + +static char *migrate_get_parameter_str(QTestState *who, const char *parameter) +{ + QDict *rsp; + char *result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = g_strdup(qdict_get_str(rsp, parameter)); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + g_autofree char *result = migrate_get_parameter_str(who, parameter); + g_assert_cmpstr(result, ==, value); +} + +void migrate_set_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %s } }", + parameter, value); + migrate_check_parameter_str(who, parameter, value); +} + +static long long migrate_get_parameter_bool(QTestState *who, + const char *parameter) +{ + QDict *rsp; + int result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_bool(rsp, parameter); + qobject_unref(rsp); + return !!result; +} + +static void migrate_check_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + int result; + + result = migrate_get_parameter_bool(who, parameter); + g_assert_cmpint(result, ==, value); +} + +void migrate_set_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %i } }", + parameter, value); + migrate_check_parameter_bool(who, parameter, value); +} + +void migrate_ensure_non_converge(QTestState *who) +{ + /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 1); +} + +void migrate_ensure_converge(QTestState *who) +{ + /* Should converge with 30s downtime + 1 gbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); +} + +void migrate_pause(QTestState *who) +{ + qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); +} + +void migrate_continue(QTestState *who, const char *state) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-continue'," + " 'arguments': { 'state': %s } }", + state); +} + +void migrate_recover(QTestState *who, const char *uri) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': %s } }", + uri); +} + +void migrate_cancel(QTestState *who) +{ + qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); +} + +void migrate_postcopy_start(QTestState *from, QTestState *to, + QTestMigrationState *src_state) +{ + qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); + + wait_for_stop(from, src_state); + qtest_qmp_eventwait(to, "RESUME"); +} diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h new file mode 100644 index 0000000..ed927cf --- /dev/null +++ b/tests/qtest/migration/migration-qmp.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef MIGRATION_QMP_H +#define MIGRATION_QMP_H + +G_GNUC_PRINTF(4, 5) +void migrate_qmp_fail(QTestState *who, const char *uri, + const char *channels, const char *fmt, ...); + +G_GNUC_PRINTF(5, 6) +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + const char *channels, const char *fmt, ...); + +G_GNUC_PRINTF(3, 4) +void migrate_incoming_qmp(QTestState *who, const char *uri, + const char *fmt, ...); + +void migration_event_wait(QTestState *s, const char *target); +void migrate_set_capability(QTestState *who, const char *capability, + bool value); +int64_t read_ram_property_int(QTestState *who, const char *property); +void migrate_set_parameter_int(QTestState *who, const char *parameter, + long long value); +void wait_for_stop(QTestState *who, QTestMigrationState *state); +void wait_for_resume(QTestState *who, QTestMigrationState *state); +void wait_for_suspend(QTestState *who, QTestMigrationState *state); +gchar *migrate_query_status(QTestState *who); +int64_t read_migrate_property_int(QTestState *who, const char *property); +uint64_t get_migration_pass(QTestState *who); +void read_blocktime(QTestState *who); +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state); +void migrate_set_parameter_str(QTestState *who, const char *parameter, + const char *value); +void migrate_set_parameter_bool(QTestState *who, const char *parameter, + int value); +void migrate_ensure_non_converge(QTestState *who); +void migrate_ensure_converge(QTestState *who); +void migrate_pause(QTestState *who); +void migrate_continue(QTestState *who, const char *state); +void migrate_recover(QTestState *who, const char *uri); +void migrate_cancel(QTestState *who); +void migrate_postcopy_start(QTestState *from, QTestState *to, + QTestMigrationState *src_state); + +#endif /* MIGRATION_QMP_H */ diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 73dfabc..28a6147 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -12,6 +12,7 @@ #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "migration-helpers.h" +#include "migration/migration-qmp.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qjson.h" -- cgit v1.1 From b7d7f723a9853a68a648075a1f2ab96d410e929e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:50 -0300 Subject: tests/qtest/migration: Rename migration-helpers.c Rename migration-helpers.c to migration-util.c to make its purpose more explicit and avoid the "helper" terminology. Move the file to the qtest/migration/ directory along with the rest of the migration files. Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 4 +- tests/qtest/migration-helpers.c | 285 --------------------------------- tests/qtest/migration-helpers.h | 54 ------- tests/qtest/migration-test.c | 2 +- tests/qtest/migration/migration-qmp.c | 2 +- tests/qtest/migration/migration-qmp.h | 2 + tests/qtest/migration/migration-util.c | 285 +++++++++++++++++++++++++++++++++ tests/qtest/migration/migration-util.h | 54 +++++++ tests/qtest/virtio-net-failover.c | 2 +- 9 files changed, 346 insertions(+), 344 deletions(-) delete mode 100644 tests/qtest/migration-helpers.c delete mode 100644 tests/qtest/migration-helpers.h create mode 100644 tests/qtest/migration/migration-util.c create mode 100644 tests/qtest/migration/migration-util.h (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 0b113ad..ec809af 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -333,9 +333,9 @@ endif tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] migration_files = [files( - 'migration-helpers.c', 'migration/bootfile.c', 'migration/migration-qmp.c', + 'migration/migration-util.c', )] if gnutls.found() @@ -350,7 +350,7 @@ qtests = { 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], 'cdrom-test': files('boot-sector.c'), 'dbus-vmstate-test': files('migration/migration-qmp.c', - 'migration-helpers.c') + dbus_vmstate1, + 'migration/migration-util.c') + dbus_vmstate1, 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], 'migration-test': migration_files, diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c deleted file mode 100644 index 218ee4e..0000000 --- a/tests/qtest/migration-helpers.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * QTest migration helpers - * - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * based on the vhost-user-test.c that is: - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * 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 "qemu/ctype.h" -#include "qapi/qapi-visit-sockets.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/error.h" -#include "qapi/qmp/qlist.h" -#include "qemu/cutils.h" -#include "qemu/memalign.h" - -#include "migration-helpers.h" -#include "migration/bootfile.h" - -static char *SocketAddress_to_str(SocketAddress *addr) -{ - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - return g_strdup_printf("tcp:%s:%s", - addr->u.inet.host, - addr->u.inet.port); - case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("unix:%s", - addr->u.q_unix.path); - case SOCKET_ADDRESS_TYPE_FD: - return g_strdup_printf("fd:%s", addr->u.fd.str); - case SOCKET_ADDRESS_TYPE_VSOCK: - return g_strdup_printf("vsock:%s:%s", - addr->u.vsock.cid, - addr->u.vsock.port); - default: - return g_strdup("unknown address type"); - } -} - -static QDict *SocketAddress_to_qdict(SocketAddress *addr) -{ - QDict *dict = qdict_new(); - - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - qdict_put_str(dict, "type", "inet"); - qdict_put_str(dict, "host", addr->u.inet.host); - qdict_put_str(dict, "port", addr->u.inet.port); - break; - case SOCKET_ADDRESS_TYPE_UNIX: - qdict_put_str(dict, "type", "unix"); - qdict_put_str(dict, "path", addr->u.q_unix.path); - break; - case SOCKET_ADDRESS_TYPE_FD: - qdict_put_str(dict, "type", "fd"); - qdict_put_str(dict, "str", addr->u.fd.str); - break; - case SOCKET_ADDRESS_TYPE_VSOCK: - qdict_put_str(dict, "type", "vsock"); - qdict_put_str(dict, "cid", addr->u.vsock.cid); - qdict_put_str(dict, "port", addr->u.vsock.port); - break; - default: - g_assert_not_reached(); - } - - return dict; -} - -static SocketAddressList *migrate_get_socket_address(QTestState *who) -{ - QDict *rsp; - SocketAddressList *addrs; - Visitor *iv = NULL; - QObject *object; - - rsp = migrate_query(who); - object = qdict_get(rsp, "socket-address"); - - iv = qobject_input_visitor_new(object); - visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); - visit_free(iv); - - qobject_unref(rsp); - return addrs; -} - -char *migrate_get_connect_uri(QTestState *who) -{ - SocketAddressList *addrs; - char *connect_uri; - - addrs = migrate_get_socket_address(who); - connect_uri = SocketAddress_to_str(addrs->value); - - qapi_free_SocketAddressList(addrs); - return connect_uri; -} - -static QDict * -migrate_get_connect_qdict(QTestState *who) -{ - SocketAddressList *addrs; - QDict *connect_qdict; - - addrs = migrate_get_socket_address(who); - connect_qdict = SocketAddress_to_qdict(addrs->value); - - qapi_free_SocketAddressList(addrs); - return connect_qdict; -} - -void migrate_set_ports(QTestState *to, QList *channel_list) -{ - QDict *addr; - QListEntry *entry; - const char *addr_port = NULL; - - addr = migrate_get_connect_qdict(to); - - QLIST_FOREACH_ENTRY(channel_list, entry) { - QDict *channel = qobject_to(QDict, qlist_entry_obj(entry)); - QDict *addrdict = qdict_get_qdict(channel, "addr"); - - if (qdict_haskey(addrdict, "port") && - qdict_haskey(addr, "port") && - (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) { - addr_port = qdict_get_str(addr, "port"); - qdict_put_str(addrdict, "port", addr_port); - } - } - - qobject_unref(addr); -} - -bool migrate_watch_for_events(QTestState *who, const char *name, - QDict *event, void *opaque) -{ - QTestMigrationState *state = opaque; - - if (g_str_equal(name, "STOP")) { - state->stop_seen = true; - return true; - } else if (g_str_equal(name, "SUSPEND")) { - state->suspend_seen = true; - return true; - } else if (g_str_equal(name, "RESUME")) { - state->resume_seen = true; - return true; - } - - return false; -} - -char *find_common_machine_version(const char *mtype, const char *var1, - const char *var2) -{ - g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype); - g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype); - - g_assert(type1 && type2); - - if (g_str_equal(type1, type2)) { - /* either can be used */ - return g_strdup(type1); - } - - if (qtest_has_machine_with_env(var2, type1)) { - return g_strdup(type1); - } - - if (qtest_has_machine_with_env(var1, type2)) { - return g_strdup(type2); - } - - g_test_message("No common machine version for machine type '%s' between " - "binaries %s and %s", mtype, getenv(var1), getenv(var2)); - g_assert_not_reached(); -} - -char *resolve_machine_version(const char *alias, const char *var1, - const char *var2) -{ - const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE"); - g_autofree char *machine_name = NULL; - - if (mname) { - const char *dash = strrchr(mname, '-'); - const char *dot = strrchr(mname, '.'); - - machine_name = g_strdup(mname); - - if (dash && dot) { - assert(qtest_has_machine(machine_name)); - return g_steal_pointer(&machine_name); - } - /* else: probably an alias, let it be resolved below */ - } else { - /* use the hardcoded alias */ - machine_name = g_strdup(alias); - } - - return find_common_machine_version(machine_name, var1, var2); -} - -typedef struct { - char *name; - void (*func)(void); -} MigrationTest; - -static void migration_test_destroy(gpointer data) -{ - MigrationTest *test = (MigrationTest *)data; - - g_free(test->name); - g_free(test); -} - -static void migration_test_wrapper(const void *data) -{ - MigrationTest *test = (MigrationTest *)data; - - g_test_message("Running /%s%s", qtest_get_arch(), test->name); - test->func(); -} - -void migration_test_add(const char *path, void (*fn)(void)) -{ - MigrationTest *test = g_new0(MigrationTest, 1); - - test->func = fn; - test->name = g_strdup(path); - - qtest_add_data_func_full(path, test, migration_test_wrapper, - migration_test_destroy); -} - -#ifdef O_DIRECT -/* - * Probe for O_DIRECT support on the filesystem. Since this is used - * for tests, be conservative, if anything fails, assume it's - * unsupported. - */ -bool probe_o_direct_support(const char *tmpfs) -{ - g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs); - int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT; - void *buf; - ssize_t ret, len; - uint64_t offset; - - fd = open(filename, flags, 0660); - if (fd < 0) { - unlink(filename); - return false; - } - - /* - * Using 1MB alignment as conservative choice to satisfy any - * plausible architecture default page size, and/or filesystem - * alignment restrictions. - */ - len = 0x100000; - offset = 0x100000; - - buf = qemu_try_memalign(len, len); - g_assert(buf); - - ret = pwrite(fd, buf, len, offset); - unlink(filename); - g_free(buf); - - if (ret < 0) { - return false; - } - - return true; -} -#endif diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h deleted file mode 100644 index 2cb1f78..0000000 --- a/tests/qtest/migration-helpers.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * QTest migration helpers - * - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * based on the vhost-user-test.c that is: - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef MIGRATION_HELPERS_H -#define MIGRATION_HELPERS_H - -#include "libqtest.h" - -typedef struct QTestMigrationState { - bool stop_seen; - bool resume_seen; - bool suspend_seen; - bool suspend_me; -} QTestMigrationState; - -bool migrate_watch_for_events(QTestState *who, const char *name, - QDict *event, void *opaque); - -QDict *migrate_query(QTestState *who); -QDict *migrate_query_not_failed(QTestState *who); - -void wait_for_migration_status(QTestState *who, - const char *goal, const char **ungoals); - -void wait_for_migration_complete(QTestState *who); - -void wait_for_migration_fail(QTestState *from, bool allow_active); - -char *find_common_machine_version(const char *mtype, const char *var1, - const char *var2); -char *resolve_machine_version(const char *alias, const char *var1, - const char *var2); -#ifdef O_DIRECT -bool probe_o_direct_support(const char *tmpfs); -#else -static inline bool probe_o_direct_support(const char *tmpfs) -{ - return false; -} -#endif -void migration_test_add(const char *path, void (*fn)(void)); -char *migrate_get_connect_uri(QTestState *who); -void migrate_set_ports(QTestState *to, QList *channel_list); - -#endif /* MIGRATION_HELPERS_H */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 489fca7..e295bd3 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -22,9 +22,9 @@ #include "crypto/tlscredspsk.h" #include "ppc-util.h" -#include "migration-helpers.h" #include "migration/bootfile.h" #include "migration/migration-qmp.h" +#include "migration/migration-util.h" #ifdef CONFIG_GNUTLS # include "tests/unit/crypto-tls-psk-helpers.h" # ifdef CONFIG_TASN1 diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 20be46f..71b14b5 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -12,8 +12,8 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "migration-helpers.h" #include "migration-qmp.h" +#include "migration-util.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h index ed927cf..caaa787 100644 --- a/tests/qtest/migration/migration-qmp.h +++ b/tests/qtest/migration/migration-qmp.h @@ -2,6 +2,8 @@ #ifndef MIGRATION_QMP_H #define MIGRATION_QMP_H +#include "migration-util.h" + G_GNUC_PRINTF(4, 5) void migrate_qmp_fail(QTestState *who, const char *uri, const char *channels, const char *fmt, ...); diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c new file mode 100644 index 0000000..8a974de --- /dev/null +++ b/tests/qtest/migration/migration-util.c @@ -0,0 +1,285 @@ +/* + * QTest migration utilities + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "qemu/ctype.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/error.h" +#include "qapi/qmp/qlist.h" +#include "qemu/cutils.h" +#include "qemu/memalign.h" + +#include "migration/bootfile.h" +#include "migration/migration-util.h" + +static char *SocketAddress_to_str(SocketAddress *addr) +{ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("tcp:%s:%s", + addr->u.inet.host, + addr->u.inet.port); + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("unix:%s", + addr->u.q_unix.path); + case SOCKET_ADDRESS_TYPE_FD: + return g_strdup_printf("fd:%s", addr->u.fd.str); + case SOCKET_ADDRESS_TYPE_VSOCK: + return g_strdup_printf("vsock:%s:%s", + addr->u.vsock.cid, + addr->u.vsock.port); + default: + return g_strdup("unknown address type"); + } +} + +static QDict *SocketAddress_to_qdict(SocketAddress *addr) +{ + QDict *dict = qdict_new(); + + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + qdict_put_str(dict, "type", "inet"); + qdict_put_str(dict, "host", addr->u.inet.host); + qdict_put_str(dict, "port", addr->u.inet.port); + break; + case SOCKET_ADDRESS_TYPE_UNIX: + qdict_put_str(dict, "type", "unix"); + qdict_put_str(dict, "path", addr->u.q_unix.path); + break; + case SOCKET_ADDRESS_TYPE_FD: + qdict_put_str(dict, "type", "fd"); + qdict_put_str(dict, "str", addr->u.fd.str); + break; + case SOCKET_ADDRESS_TYPE_VSOCK: + qdict_put_str(dict, "type", "vsock"); + qdict_put_str(dict, "cid", addr->u.vsock.cid); + qdict_put_str(dict, "port", addr->u.vsock.port); + break; + default: + g_assert_not_reached(); + } + + return dict; +} + +static SocketAddressList *migrate_get_socket_address(QTestState *who) +{ + QDict *rsp; + SocketAddressList *addrs; + Visitor *iv = NULL; + QObject *object; + + rsp = migrate_query(who); + object = qdict_get(rsp, "socket-address"); + + iv = qobject_input_visitor_new(object); + visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); + visit_free(iv); + + qobject_unref(rsp); + return addrs; +} + +char *migrate_get_connect_uri(QTestState *who) +{ + SocketAddressList *addrs; + char *connect_uri; + + addrs = migrate_get_socket_address(who); + connect_uri = SocketAddress_to_str(addrs->value); + + qapi_free_SocketAddressList(addrs); + return connect_uri; +} + +static QDict * +migrate_get_connect_qdict(QTestState *who) +{ + SocketAddressList *addrs; + QDict *connect_qdict; + + addrs = migrate_get_socket_address(who); + connect_qdict = SocketAddress_to_qdict(addrs->value); + + qapi_free_SocketAddressList(addrs); + return connect_qdict; +} + +void migrate_set_ports(QTestState *to, QList *channel_list) +{ + QDict *addr; + QListEntry *entry; + const char *addr_port = NULL; + + addr = migrate_get_connect_qdict(to); + + QLIST_FOREACH_ENTRY(channel_list, entry) { + QDict *channel = qobject_to(QDict, qlist_entry_obj(entry)); + QDict *addrdict = qdict_get_qdict(channel, "addr"); + + if (qdict_haskey(addrdict, "port") && + qdict_haskey(addr, "port") && + (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) { + addr_port = qdict_get_str(addr, "port"); + qdict_put_str(addrdict, "port", addr_port); + } + } + + qobject_unref(addr); +} + +bool migrate_watch_for_events(QTestState *who, const char *name, + QDict *event, void *opaque) +{ + QTestMigrationState *state = opaque; + + if (g_str_equal(name, "STOP")) { + state->stop_seen = true; + return true; + } else if (g_str_equal(name, "SUSPEND")) { + state->suspend_seen = true; + return true; + } else if (g_str_equal(name, "RESUME")) { + state->resume_seen = true; + return true; + } + + return false; +} + +char *find_common_machine_version(const char *mtype, const char *var1, + const char *var2) +{ + g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype); + g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype); + + g_assert(type1 && type2); + + if (g_str_equal(type1, type2)) { + /* either can be used */ + return g_strdup(type1); + } + + if (qtest_has_machine_with_env(var2, type1)) { + return g_strdup(type1); + } + + if (qtest_has_machine_with_env(var1, type2)) { + return g_strdup(type2); + } + + g_test_message("No common machine version for machine type '%s' between " + "binaries %s and %s", mtype, getenv(var1), getenv(var2)); + g_assert_not_reached(); +} + +char *resolve_machine_version(const char *alias, const char *var1, + const char *var2) +{ + const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE"); + g_autofree char *machine_name = NULL; + + if (mname) { + const char *dash = strrchr(mname, '-'); + const char *dot = strrchr(mname, '.'); + + machine_name = g_strdup(mname); + + if (dash && dot) { + assert(qtest_has_machine(machine_name)); + return g_steal_pointer(&machine_name); + } + /* else: probably an alias, let it be resolved below */ + } else { + /* use the hardcoded alias */ + machine_name = g_strdup(alias); + } + + return find_common_machine_version(machine_name, var1, var2); +} + +typedef struct { + char *name; + void (*func)(void); +} MigrationTest; + +static void migration_test_destroy(gpointer data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_free(test->name); + g_free(test); +} + +static void migration_test_wrapper(const void *data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_test_message("Running /%s%s", qtest_get_arch(), test->name); + test->func(); +} + +void migration_test_add(const char *path, void (*fn)(void)) +{ + MigrationTest *test = g_new0(MigrationTest, 1); + + test->func = fn; + test->name = g_strdup(path); + + qtest_add_data_func_full(path, test, migration_test_wrapper, + migration_test_destroy); +} + +#ifdef O_DIRECT +/* + * Probe for O_DIRECT support on the filesystem. Since this is used + * for tests, be conservative, if anything fails, assume it's + * unsupported. + */ +bool probe_o_direct_support(const char *tmpfs) +{ + g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs); + int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT; + void *buf; + ssize_t ret, len; + uint64_t offset; + + fd = open(filename, flags, 0660); + if (fd < 0) { + unlink(filename); + return false; + } + + /* + * Using 1MB alignment as conservative choice to satisfy any + * plausible architecture default page size, and/or filesystem + * alignment restrictions. + */ + len = 0x100000; + offset = 0x100000; + + buf = qemu_try_memalign(len, len); + g_assert(buf); + + ret = pwrite(fd, buf, len, offset); + unlink(filename); + g_free(buf); + + if (ret < 0) { + return false; + } + + return true; +} +#endif diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h new file mode 100644 index 0000000..0fa3676 --- /dev/null +++ b/tests/qtest/migration/migration-util.h @@ -0,0 +1,54 @@ +/* + * QTest migration helpers + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef MIGRATION_UTIL_H +#define MIGRATION_UTIL_H + +#include "libqtest.h" + +typedef struct QTestMigrationState { + bool stop_seen; + bool resume_seen; + bool suspend_seen; + bool suspend_me; +} QTestMigrationState; + +bool migrate_watch_for_events(QTestState *who, const char *name, + QDict *event, void *opaque); + +QDict *migrate_query(QTestState *who); +QDict *migrate_query_not_failed(QTestState *who); + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals); + +void wait_for_migration_complete(QTestState *who); + +void wait_for_migration_fail(QTestState *from, bool allow_active); + +char *find_common_machine_version(const char *mtype, const char *var1, + const char *var2); +char *resolve_machine_version(const char *alias, const char *var1, + const char *var2); +#ifdef O_DIRECT +bool probe_o_direct_support(const char *tmpfs); +#else +static inline bool probe_o_direct_support(const char *tmpfs) +{ + return false; +} +#endif +void migration_test_add(const char *path, void (*fn)(void)); +char *migrate_get_connect_uri(QTestState *who); +void migrate_set_ports(QTestState *to, QList *channel_list); + +#endif /* MIGRATION_UTIL_H */ diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 28a6147..08365ff 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -11,8 +11,8 @@ #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" -#include "migration-helpers.h" #include "migration/migration-qmp.h" +#include "migration/migration-util.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qjson.h" -- cgit v1.1 From 124a3c58b82ad8e3662711f4deca281d81d5d56a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:51 -0300 Subject: tests/qtest/migration: Move ufd_version_check to utils Move ufd_version_check() to migration-util.c file. This is a helper function that is used during tests definition so it should be available outside of migration-test.c Since the function is moving to another file, change it to stop accessing the global uffd_feature_thread_id and take it as argument instead. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 47 ++------------------------------- tests/qtest/migration/migration-util.c | 48 ++++++++++++++++++++++++++++++++++ tests/qtest/migration/migration-util.h | 2 ++ 3 files changed, 52 insertions(+), 45 deletions(-) (limited to 'tests') diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index e295bd3..f1c1ca3 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -35,6 +35,7 @@ /* For dirty ring test; so far only x86_64 is supported */ #if defined(__linux__) && defined(HOST_X86_64) #include "linux/kvm.h" +#include #endif unsigned start_address; @@ -88,50 +89,6 @@ typedef enum PostcopyRecoveryFailStage { #include #endif -#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) -#include -#include -#include "qemu/userfaultfd.h" - -static bool ufd_version_check(void) -{ - struct uffdio_api api_struct; - uint64_t ioctl_mask; - - int ufd = uffd_open(O_CLOEXEC); - - if (ufd == -1) { - g_test_message("Skipping test: userfaultfd not available"); - return false; - } - - api_struct.api = UFFD_API; - api_struct.features = 0; - if (ioctl(ufd, UFFDIO_API, &api_struct)) { - g_test_message("Skipping test: UFFDIO_API failed"); - return false; - } - uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; - - ioctl_mask = (1ULL << _UFFDIO_REGISTER | - 1ULL << _UFFDIO_UNREGISTER); - if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { - g_test_message("Skipping test: Missing userfault feature"); - return false; - } - - return true; -} - -#else -static bool ufd_version_check(void) -{ - g_test_message("Skipping test: Userfault not available (builtdtime)"); - return false; -} - -#endif - static char *tmpfs; static char *bootpath; @@ -3532,7 +3489,7 @@ int main(int argc, char **argv) return 0; } - has_uffd = ufd_version_check(); + has_uffd = ufd_version_check(&uffd_feature_thread_id); arch = qtest_get_arch(); is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64"); diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 8a974de..2ff8870 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -22,6 +22,13 @@ #include "migration/bootfile.h" #include "migration/migration-util.h" +/* for uffd_version_check() */ +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +#include +#include "qemu/userfaultfd.h" +#endif + + static char *SocketAddress_to_str(SocketAddress *addr) { switch (addr->type) { @@ -283,3 +290,44 @@ bool probe_o_direct_support(const char *tmpfs) return true; } #endif + +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +bool ufd_version_check(bool *uffd_feature_thread_id) +{ + struct uffdio_api api_struct; + uint64_t ioctl_mask; + + int ufd = uffd_open(O_CLOEXEC); + + if (ufd == -1) { + g_test_message("Skipping test: userfaultfd not available"); + return false; + } + + api_struct.api = UFFD_API; + api_struct.features = 0; + if (ioctl(ufd, UFFDIO_API, &api_struct)) { + g_test_message("Skipping test: UFFDIO_API failed"); + return false; + } + + if (uffd_feature_thread_id) { + *uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; + } + + ioctl_mask = (1ULL << _UFFDIO_REGISTER | + 1ULL << _UFFDIO_UNREGISTER); + if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { + g_test_message("Skipping test: Missing userfault feature"); + return false; + } + + return true; +} +#else +bool ufd_version_check(bool *uffd_feature_thread_id) +{ + g_test_message("Skipping test: Userfault not available (builtdtime)"); + return false; +} +#endif diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h index 0fa3676..2ab22e3 100644 --- a/tests/qtest/migration/migration-util.h +++ b/tests/qtest/migration/migration-util.h @@ -47,6 +47,8 @@ static inline bool probe_o_direct_support(const char *tmpfs) return false; } #endif + +bool ufd_version_check(bool *uffd_feature_thread_id); void migration_test_add(const char *path, void (*fn)(void)); char *migrate_get_connect_uri(QTestState *who); void migrate_set_ports(QTestState *to, QList *channel_list); -- cgit v1.1 From 2fa72ba79bc84bd44385af260dbacc223f7a6181 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:52 -0300 Subject: tests/qtest/migration: Move kvm_dirty_ring_supported to utils Move kvm_dirty_ring_supported() to migration-util.c. Similarly to ufd_version_check(), this function is used during test definition so put it in the utils file. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 29 ----------------------------- tests/qtest/migration/migration-util.c | 29 +++++++++++++++++++++++++++++ tests/qtest/migration/migration-util.h | 1 + 3 files changed, 30 insertions(+), 29 deletions(-) (limited to 'tests') diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index f1c1ca3..ab7cbb8 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -32,12 +32,6 @@ # endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ -/* For dirty ring test; so far only x86_64 is supported */ -#if defined(__linux__) && defined(HOST_X86_64) -#include "linux/kvm.h" -#include -#endif - unsigned start_address; unsigned end_address; static bool uffd_feature_thread_id; @@ -3435,29 +3429,6 @@ static void test_dirty_limit(void) migrate_end(from, to, true); } -static bool kvm_dirty_ring_supported(void) -{ -#if defined(__linux__) && defined(HOST_X86_64) - int ret, kvm_fd = open("/dev/kvm", O_RDONLY); - - if (kvm_fd < 0) { - return false; - } - - ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); - close(kvm_fd); - - /* We test with 4096 slots */ - if (ret < 4096) { - return false; - } - - return true; -#else - return false; -#endif -} - int main(int argc, char **argv) { bool has_kvm, has_tcg; diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 2ff8870..525bf1e 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -28,6 +28,12 @@ #include "qemu/userfaultfd.h" #endif +/* For dirty ring test; so far only x86_64 is supported */ +#if defined(__linux__) && defined(HOST_X86_64) +#include "linux/kvm.h" +#include +#endif + static char *SocketAddress_to_str(SocketAddress *addr) { @@ -331,3 +337,26 @@ bool ufd_version_check(bool *uffd_feature_thread_id) return false; } #endif + +bool kvm_dirty_ring_supported(void) +{ +#if defined(__linux__) && defined(HOST_X86_64) + int ret, kvm_fd = open("/dev/kvm", O_RDONLY); + + if (kvm_fd < 0) { + return false; + } + + ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); + close(kvm_fd); + + /* We test with 4096 slots */ + if (ret < 4096) { + return false; + } + + return true; +#else + return false; +#endif +} diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h index 2ab22e3..f5f2e46 100644 --- a/tests/qtest/migration/migration-util.h +++ b/tests/qtest/migration/migration-util.h @@ -49,6 +49,7 @@ static inline bool probe_o_direct_support(const char *tmpfs) #endif bool ufd_version_check(bool *uffd_feature_thread_id); +bool kvm_dirty_ring_supported(void); void migration_test_add(const char *path, void (*fn)(void)); char *migrate_get_connect_uri(QTestState *who); void migrate_set_ports(QTestState *to, QList *channel_list); -- cgit v1.1 From d4bedc3514b6ffde449bcf967b04a3cafe7e89c7 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:53 -0300 Subject: tests/qtest/migration: Isolate test initialization We currently have some environment validation to perform and flags to set during the initialization of the tests. To be able to add more migration test binaries, we'll need these tasks to be in their own function so they can be called from more than one place. Move the initialization code to a function and introduce the MigrationTestEnv structure to hold the flags that are accessed during test registration. Make the env object static to avoid have to change all the code to pass it around. Similarly with the tmpfs variable, which is used extensively. Note: I'm keeping the new functions in migration-test.c because they are going to be moved in the next patch to the correct place. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 122 ++++++++++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 36 deletions(-) (limited to 'tests') diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index ab7cbb8..5617445 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -34,7 +34,6 @@ unsigned start_address; unsigned end_address; -static bool uffd_feature_thread_id; static QTestMigrationState src_state; static QTestMigrationState dst_state; @@ -86,6 +85,22 @@ typedef enum PostcopyRecoveryFailStage { static char *tmpfs; static char *bootpath; +typedef struct MigrationTestEnv { + bool has_kvm; + bool has_tcg; + bool has_uffd; + bool uffd_feature_thread_id; + bool has_dirty_ring; + bool is_x86; + const char *arch; + const char *qemu_src; + const char *qemu_dst; + char *tmpfs; +} MigrationTestEnv; + + +MigrationTestEnv *migration_get_env(void); + /* * Wait for some output in the serial output file, * we get an 'A' followed by an endless string of 'B's @@ -972,6 +987,8 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, static void migrate_postcopy_complete(QTestState *from, QTestState *to, MigrateCommon *args) { + MigrationTestEnv *env = migration_get_env(); + wait_for_migration_complete(from); if (args->start.suspend_me) { @@ -982,7 +999,7 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to, /* Make sure we get at least one "B" on destination */ wait_for_serial("dest_serial"); - if (uffd_feature_thread_id) { + if (env->uffd_feature_thread_id) { read_blocktime(to); } @@ -3429,63 +3446,99 @@ static void test_dirty_limit(void) migrate_end(from, to, true); } -int main(int argc, char **argv) +MigrationTestEnv *migration_get_env(void) { - bool has_kvm, has_tcg; - bool has_uffd, is_x86; - const char *arch; + static MigrationTestEnv *env; g_autoptr(GError) err = NULL; - const char *qemu_src = getenv(QEMU_ENV_SRC); - const char *qemu_dst = getenv(QEMU_ENV_DST); - int ret; - g_test_init(&argc, &argv, NULL); + if (env) { + return env; + } + + env = g_new0(MigrationTestEnv, 1); + env->qemu_src = getenv(QEMU_ENV_SRC); + env->qemu_dst = getenv(QEMU_ENV_DST); /* * The default QTEST_QEMU_BINARY must always be provided because * that is what helpers use to query the accel type and * architecture. */ - if (qemu_src && qemu_dst) { + if (env->qemu_src && env->qemu_dst) { g_test_message("Only one of %s, %s is allowed", QEMU_ENV_SRC, QEMU_ENV_DST); exit(1); } - has_kvm = qtest_has_accel("kvm"); - has_tcg = qtest_has_accel("tcg"); + env->has_kvm = qtest_has_accel("kvm"); + env->has_tcg = qtest_has_accel("tcg"); - if (!has_tcg && !has_kvm) { + if (!env->has_tcg && !env->has_kvm) { g_test_skip("No KVM or TCG accelerator available"); - return 0; + return env; } - has_uffd = ufd_version_check(&uffd_feature_thread_id); - arch = qtest_get_arch(); - is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64"); + env->has_dirty_ring = kvm_dirty_ring_supported(); + env->has_uffd = ufd_version_check(&env->uffd_feature_thread_id); + env->arch = qtest_get_arch(); + env->is_x86 = !strcmp(env->arch, "i386") || !strcmp(env->arch, "x86_64"); - tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); - if (!tmpfs) { + env->tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); + if (!env->tmpfs) { g_test_message("Can't create temporary directory in %s: %s", g_get_tmp_dir(), err->message); } - g_assert(tmpfs); + g_assert(env->tmpfs); + return env; +} + +static int migration_env_clean(MigrationTestEnv *env) +{ + char *tmpfs; + int ret = 0; + + if (!env) { + return ret; + } + + bootfile_delete(); + + tmpfs = env->tmpfs; + ret = rmdir(tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s", + tmpfs, strerror(errno)); + } + g_free(tmpfs); + + return ret; +} + +int main(int argc, char **argv) +{ + MigrationTestEnv *env; + int ret; + + g_test_init(&argc, &argv, NULL); + env = migration_get_env(); module_call_init(MODULE_INIT_QOM); + tmpfs = env->tmpfs; + migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); #endif - if (is_x86) { + if (env->is_x86) { migration_test_add("/migration/precopy/unix/suspend/live", test_precopy_unix_suspend_live); migration_test_add("/migration/precopy/unix/suspend/notlive", test_precopy_unix_suspend_notlive); } - if (has_uffd) { + if (env->has_uffd) { migration_test_add("/migration/postcopy/plain", test_postcopy); migration_test_add("/migration/postcopy/recovery/plain", test_postcopy_recovery); @@ -3497,7 +3550,7 @@ int main(int argc, char **argv) test_postcopy_recovery_fail_handshake); migration_test_add("/migration/postcopy/recovery/double-failures/reconnect", test_postcopy_recovery_fail_reconnect); - if (is_x86) { + if (env->is_x86) { migration_test_add("/migration/postcopy/suspend", test_postcopy_suspend); } @@ -3552,7 +3605,7 @@ int main(int argc, char **argv) migration_test_add("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); - if (has_uffd) { + if (env->has_uffd) { /* * NOTE: psk test is enough for postcopy, as other types of TLS * channels are tested under precopy. Here what we want to test is the @@ -3627,9 +3680,9 @@ int main(int argc, char **argv) if (g_test_slow()) { migration_test_add("/migration/auto_converge", test_auto_converge); - if (g_str_equal(arch, "x86_64") && - has_kvm && kvm_dirty_ring_supported()) { - migration_test_add("/migration/dirty_limit", + if (g_str_equal(env->arch, "x86_64") && + env->has_kvm && env->has_dirty_ring) { + migration_test_add("/dirty_limit", test_dirty_limit); } } @@ -3680,7 +3733,9 @@ int main(int argc, char **argv) #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ - if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) { + if (g_str_equal(env->arch, "x86_64") && + env->has_kvm && env->has_dirty_ring) { + migration_test_add("/migration/dirty_ring", test_precopy_unix_dirty_ring); if (qtest_has_machine("pc") && g_test_slow()) { @@ -3693,13 +3748,8 @@ int main(int argc, char **argv) g_assert_cmpint(ret, ==, 0); - bootfile_delete(); - ret = rmdir(tmpfs); - if (ret != 0) { - g_test_message("unable to rmdir: path (%s): %s", - tmpfs, strerror(errno)); - } - g_free(tmpfs); + tmpfs = NULL; + ret = migration_env_clean(env); return ret; } -- cgit v1.1 From e1803dabdcbd26f2cf72e586f22b03e2e90d6110 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:54 -0300 Subject: tests/qtest/migration: Move common test code The migration tests have a set of core infrastructure routines. These are functions that are called by (almost) all tests and centralize the common operations of: starting migration on both sides, waiting for guests to boot, performing guest initialization and teardown, guest memory validation, etc. Move this basic framework code (and a few static helpers) into a separate file. Leave only individual test functions (and their own static helpers) in migration-test.c. Acked-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 1140 +------------------------------------ tests/qtest/migration/bootfile.c | 2 +- tests/qtest/migration/bootfile.h | 2 +- tests/qtest/migration/framework.c | 971 +++++++++++++++++++++++++++++++ tests/qtest/migration/framework.h | 218 +++++++ 6 files changed, 1205 insertions(+), 1129 deletions(-) create mode 100644 tests/qtest/migration/framework.c create mode 100644 tests/qtest/migration/framework.h (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index ec809af..c5cc09f 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -334,6 +334,7 @@ tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] migration_files = [files( 'migration/bootfile.c', + 'migration/framework.c', 'migration/migration-qmp.c', 'migration/migration-util.c', )] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 5617445..a601504 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -23,6 +23,7 @@ #include "ppc-util.h" #include "migration/bootfile.h" +#include "migration/framework.h" #include "migration/migration-qmp.h" #include "migration/migration-util.h" #ifdef CONFIG_GNUTLS @@ -32,25 +33,6 @@ # endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ -unsigned start_address; -unsigned end_address; -static QTestMigrationState src_state; -static QTestMigrationState dst_state; - -/* - * An initial 3 MB offset is used as that corresponds - * to ~1 sec of data transfer with our bandwidth setting. - */ -#define MAGIC_OFFSET_BASE (3 * 1024 * 1024) -/* - * A further 1k is added to ensure we're not a multiple - * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes - * from the migration guest workload. - */ -#define MAGIC_OFFSET_SHUFFLE 1024 -#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE) -#define MAGIC_MARKER 0xFEED12345678CAFEULL - /* * Dirtylimit stop working if dirty page rate error * value less than DIRTYLIMIT_TOLERANCE_RANGE @@ -59,543 +41,13 @@ static QTestMigrationState dst_state; #define ANALYZE_SCRIPT "scripts/analyze-migration.py" -#define QEMU_VM_FILE_MAGIC 0x5145564d -#define FILE_TEST_FILENAME "migfile" -#define FILE_TEST_OFFSET 0x1000 -#define FILE_TEST_MARKER 'X' -#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" -#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" - -typedef enum PostcopyRecoveryFailStage { - /* - * "no failure" must be 0 as it's the default. OTOH, real failure - * cases must be >0 to make sure they trigger by a "if" test. - */ - POSTCOPY_FAIL_NONE = 0, - POSTCOPY_FAIL_CHANNEL_ESTABLISH, - POSTCOPY_FAIL_RECOVERY, - POSTCOPY_FAIL_MAX -} PostcopyRecoveryFailStage; - #if defined(__linux__) +#include #include #include #endif static char *tmpfs; -static char *bootpath; - -typedef struct MigrationTestEnv { - bool has_kvm; - bool has_tcg; - bool has_uffd; - bool uffd_feature_thread_id; - bool has_dirty_ring; - bool is_x86; - const char *arch; - const char *qemu_src; - const char *qemu_dst; - char *tmpfs; -} MigrationTestEnv; - - -MigrationTestEnv *migration_get_env(void); - -/* - * Wait for some output in the serial output file, - * we get an 'A' followed by an endless string of 'B's - * but on the destination we won't have the A (unless we enabled suspend/resume) - */ -static void wait_for_serial(const char *side) -{ - g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); - FILE *serialfile = fopen(serialpath, "r"); - - do { - int readvalue = fgetc(serialfile); - - switch (readvalue) { - case 'A': - /* Fine */ - break; - - case 'B': - /* It's alive! */ - fclose(serialfile); - return; - - case EOF: - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - - default: - fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); - g_assert_not_reached(); - } - } while (true); -} - -static void check_guests_ram(QTestState *who) -{ - /* Our ASM test will have been incrementing one byte from each page from - * start_address to < end_address in order. This gives us a constraint - * that any page's byte should be equal or less than the previous pages - * byte (mod 256); and they should all be equal except for one transition - * at the point where we meet the incrementer. (We're running this with - * the guest stopped). - */ - unsigned address; - uint8_t first_byte; - uint8_t last_byte; - bool hit_edge = false; - int bad = 0; - - qtest_memread(who, start_address, &first_byte, 1); - last_byte = first_byte; - - for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; - address += TEST_MEM_PAGE_SIZE) - { - uint8_t b; - qtest_memread(who, address, &b, 1); - if (b != last_byte) { - if (((b + 1) % 256) == last_byte && !hit_edge) { - /* This is OK, the guest stopped at the point of - * incrementing the previous page but didn't get - * to us yet. - */ - hit_edge = true; - last_byte = b; - } else { - bad++; - if (bad <= 10) { - fprintf(stderr, "Memory content inconsistency at %x" - " first_byte = %x last_byte = %x current = %x" - " hit_edge = %x\n", - address, first_byte, last_byte, b, hit_edge); - } - } - } - } - if (bad >= 10) { - fprintf(stderr, "and in another %d pages", bad - 10); - } - g_assert(bad == 0); -} - -static void cleanup(const char *filename) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename); - - unlink(path); -} - -/* - * Our goal is to ensure that we run a single full migration - * iteration, and also dirty memory, ensuring that at least - * one further iteration is required. - * - * We can't directly synchronize with the start of a migration - * so we have to apply some tricks monitoring memory that is - * transferred. - * - * Initially we set the migration bandwidth to an insanely - * low value, with tiny max downtime too. This basically - * guarantees migration will never complete. - * - * This will result in a test that is unacceptably slow though, - * so we can't let the entire migration pass run at this speed. - * Our intent is to let it run just long enough that we can - * prove data prior to the marker has been transferred *AND* - * also prove this transferred data is dirty again. - * - * Before migration starts, we write a 64-bit magic marker - * into a fixed location in the src VM RAM. - * - * Then watch dst memory until the marker appears. This is - * proof that start_address -> MAGIC_OFFSET_BASE has been - * transferred. - * - * Finally we go back to the source and read a byte just - * before the marker until we see it flip in value. This - * is proof that start_address -> MAGIC_OFFSET_BASE - * is now dirty again. - * - * IOW, we're guaranteed at least a 2nd migration pass - * at this point. - * - * We can now let migration run at full speed to finish - * the test - */ -static void migrate_prepare_for_dirty_mem(QTestState *from) -{ - /* - * The guest workflow iterates from start_address to - * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE - * bytes. - * - * IOW, if we write to mem at a point which is NOT - * a multiple of TEST_MEM_PAGE_SIZE, our write won't - * conflict with the migration workflow. - * - * We put in a marker here, that we'll use to determine - * when the data has been transferred to the dst. - */ - qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER); -} - -static void migrate_wait_for_dirty_mem(QTestState *from, - QTestState *to) -{ - uint64_t watch_address = start_address + MAGIC_OFFSET_BASE; - uint64_t marker_address = start_address + MAGIC_OFFSET; - uint8_t watch_byte; - - /* - * Wait for the MAGIC_MARKER to get transferred, as an - * indicator that a migration pass has made some known - * amount of progress. - */ - do { - usleep(1000 * 10); - } while (qtest_readq(to, marker_address) != MAGIC_MARKER); - - - /* If suspended, src only iterates once, and watch_byte may never change */ - if (src_state.suspend_me) { - return; - } - - /* - * Now ensure that already transferred bytes are - * dirty again from the guest workload. Note the - * guest byte value will wrap around and by chance - * match the original watch_byte. This is harmless - * as we'll eventually see a different value if we - * keep watching - */ - watch_byte = qtest_readb(from, watch_address); - do { - usleep(1000 * 10); - } while (qtest_readb(from, watch_address) == watch_byte); -} - -typedef struct { - /* - * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors - * unconditionally, because it means the user would like to be verbose. - */ - bool hide_stderr; - bool use_shmem; - /* only launch the target process */ - bool only_target; - /* Use dirty ring if true; dirty logging otherwise */ - bool use_dirty_ring; - const char *opts_source; - const char *opts_target; - /* suspend the src before migrating to dest. */ - bool suspend_me; -} MigrateStart; - -/* - * A hook that runs after the src and dst QEMUs have been - * created, but before the migration is started. This can - * be used to set migration parameters and capabilities. - * - * Returns: NULL, or a pointer to opaque state to be - * later passed to the TestMigrateFinishHook - */ -typedef void * (*TestMigrateStartHook)(QTestState *from, - QTestState *to); - -/* - * A hook that runs after the migration has finished, - * regardless of whether it succeeded or failed, but - * before QEMU has terminated (unless it self-terminated - * due to migration error) - * - * @opaque is a pointer to state previously returned - * by the TestMigrateStartHook if any, or NULL. - */ -typedef void (*TestMigrateEndHook)(QTestState *from, - QTestState *to, - void *opaque); - -typedef struct { - /* Optional: fine tune start parameters */ - MigrateStart start; - - /* Required: the URI for the dst QEMU to listen on */ - const char *listen_uri; - - /* - * Optional: the URI for the src QEMU to connect to - * If NULL, then it will query the dst QEMU for its actual - * listening address and use that as the connect address. - * This allows for dynamically picking a free TCP port. - */ - const char *connect_uri; - - /* - * Optional: JSON-formatted list of src QEMU URIs. If a port is - * defined as '0' in any QDict key a value of '0' will be - * automatically converted to the correct destination port. - */ - const char *connect_channels; - - /* Optional: callback to run at start to set migration parameters */ - TestMigrateStartHook start_hook; - /* Optional: callback to run at finish to cleanup */ - TestMigrateEndHook end_hook; - - /* - * Optional: normally we expect the migration process to complete. - * - * There can be a variety of reasons and stages in which failure - * can happen during tests. - * - * If a failure is expected to happen at time of establishing - * the connection, then MIG_TEST_FAIL will indicate that the dst - * QEMU is expected to stay running and accept future migration - * connections. - * - * If a failure is expected to happen while processing the - * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate - * that the dst QEMU is expected to quit with non-zero exit status - */ - enum { - /* This test should succeed, the default */ - MIG_TEST_SUCCEED = 0, - /* This test should fail, dest qemu should keep alive */ - MIG_TEST_FAIL, - /* This test should fail, dest qemu should fail with abnormal status */ - MIG_TEST_FAIL_DEST_QUIT_ERR, - /* The QMP command for this migration should fail with an error */ - MIG_TEST_QMP_ERROR, - } result; - - /* - * Optional: set number of migration passes to wait for, if live==true. - * If zero, then merely wait for a few MB of dirty data - */ - unsigned int iterations; - - /* - * Optional: whether the guest CPUs should be running during a precopy - * migration test. We used to always run with live but it took much - * longer so we reduced live tests to only the ones that have solid - * reason to be tested live-only. For each of the new test cases for - * precopy please provide justifications to use live explicitly (please - * refer to existing ones with live=true), or use live=off by default. - */ - bool live; - - /* Postcopy specific fields */ - void *postcopy_data; - bool postcopy_preempt; - PostcopyRecoveryFailStage postcopy_recovery_fail_stage; -} MigrateCommon; - -static int migrate_start(QTestState **from, QTestState **to, - const char *uri, MigrateStart *args) -{ - g_autofree gchar *arch_source = NULL; - g_autofree gchar *arch_target = NULL; - /* options for source and target */ - g_autofree gchar *arch_opts = NULL; - g_autofree gchar *cmd_source = NULL; - g_autofree gchar *cmd_target = NULL; - const gchar *ignore_stderr; - g_autofree char *shmem_opts = NULL; - g_autofree char *shmem_path = NULL; - const char *kvm_opts = NULL; - const char *arch = qtest_get_arch(); - const char *memory_size; - const char *machine_alias, *machine_opts = ""; - g_autofree char *machine = NULL; - - if (args->use_shmem) { - if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { - g_test_skip("/dev/shm is not supported"); - return -1; - } - } - - dst_state = (QTestMigrationState) { }; - src_state = (QTestMigrationState) { }; - bootpath = bootfile_create(arch, tmpfs, args->suspend_me); - src_state.suspend_me = args->suspend_me; - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - memory_size = "150M"; - - if (g_str_equal(arch, "i386")) { - machine_alias = "pc"; - } else { - machine_alias = "q35"; - } - arch_opts = g_strdup_printf( - "-drive if=none,id=d0,file=%s,format=raw " - "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath); - start_address = X86_TEST_MEM_START; - end_address = X86_TEST_MEM_END; - } else if (g_str_equal(arch, "s390x")) { - memory_size = "128M"; - machine_alias = "s390-ccw-virtio"; - arch_opts = g_strdup_printf("-bios %s", bootpath); - start_address = S390_TEST_MEM_START; - end_address = S390_TEST_MEM_END; - } else if (strcmp(arch, "ppc64") == 0) { - memory_size = "256M"; - start_address = PPC_TEST_MEM_START; - end_address = PPC_TEST_MEM_END; - machine_alias = "pseries"; - machine_opts = "vsmt=8"; - arch_opts = g_strdup_printf( - "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " " - "-bios %s", bootpath); - } else if (strcmp(arch, "aarch64") == 0) { - memory_size = "150M"; - machine_alias = "virt"; - machine_opts = "gic-version=3"; - arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath); - start_address = ARM_TEST_MEM_START; - end_address = ARM_TEST_MEM_END; - } else { - g_assert_not_reached(); - } - - if (!getenv("QTEST_LOG") && args->hide_stderr) { -#ifndef _WIN32 - ignore_stderr = "2>/dev/null"; -#else - /* - * On Windows the QEMU executable is created via CreateProcess() and - * IO redirection does not work, so don't bother adding IO redirection - * to the command line. - */ - ignore_stderr = ""; -#endif - } else { - ignore_stderr = ""; - } - - if (args->use_shmem) { - shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); - shmem_opts = g_strdup_printf( - "-object memory-backend-file,id=mem0,size=%s" - ",mem-path=%s,share=on -numa node,memdev=mem0", - memory_size, shmem_path); - } - - if (args->use_dirty_ring) { - kvm_opts = ",dirty-ring-size=4096"; - } - - if (!qtest_has_machine(machine_alias)) { - g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); - g_test_skip(msg); - return -1; - } - - machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, - QEMU_ENV_DST); - - g_test_message("Using machine type: %s", machine); - - cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " - "-machine %s,%s " - "-name source,debug-threads=on " - "-m %s " - "-serial file:%s/src_serial " - "%s %s %s %s %s", - kvm_opts ? kvm_opts : "", - machine, machine_opts, - memory_size, tmpfs, - arch_opts ? arch_opts : "", - arch_source ? arch_source : "", - shmem_opts ? shmem_opts : "", - args->opts_source ? args->opts_source : "", - ignore_stderr); - if (!args->only_target) { - *from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source); - qtest_qmp_set_event_callback(*from, - migrate_watch_for_events, - &src_state); - } - - cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " - "-machine %s,%s " - "-name target,debug-threads=on " - "-m %s " - "-serial file:%s/dest_serial " - "-incoming %s " - "%s %s %s %s %s", - kvm_opts ? kvm_opts : "", - machine, machine_opts, - memory_size, tmpfs, uri, - arch_opts ? arch_opts : "", - arch_target ? arch_target : "", - shmem_opts ? shmem_opts : "", - args->opts_target ? args->opts_target : "", - ignore_stderr); - *to = qtest_init_with_env(QEMU_ENV_DST, cmd_target); - qtest_qmp_set_event_callback(*to, - migrate_watch_for_events, - &dst_state); - - /* - * Remove shmem file immediately to avoid memory leak in test failed case. - * It's valid because QEMU has already opened this file - */ - if (args->use_shmem) { - unlink(shmem_path); - } - - /* - * Always enable migration events. Libvirt always uses it, let's try - * to mimic as closer as that. - */ - migrate_set_capability(*from, "events", true); - migrate_set_capability(*to, "events", true); - - return 0; -} - -static void migrate_end(QTestState *from, QTestState *to, bool test_dest) -{ - unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; - - qtest_quit(from); - - if (test_dest) { - qtest_memread(to, start_address, &dest_byte_a, 1); - - /* Destination still running, wait for a byte to change */ - do { - qtest_memread(to, start_address, &dest_byte_b, 1); - usleep(1000 * 10); - } while (dest_byte_a == dest_byte_b); - - qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); - - /* With it stopped, check nothing changes */ - qtest_memread(to, start_address, &dest_byte_c, 1); - usleep(1000 * 200); - qtest_memread(to, start_address, &dest_byte_d, 1); - g_assert_cmpint(dest_byte_c, ==, dest_byte_d); - - check_guests_ram(to); - } - - qtest_quit(to); - - cleanup("migsocket"); - cleanup("src_serial"); - cleanup("dest_serial"); - cleanup(FILE_TEST_FILENAME); -} #ifdef CONFIG_GNUTLS struct TestMigrateTLSPSKData { @@ -936,92 +388,6 @@ migrate_hook_end_tls_x509(QTestState *from, #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ -static int migrate_postcopy_prepare(QTestState **from_ptr, - QTestState **to_ptr, - MigrateCommon *args) -{ - QTestState *from, *to; - - if (migrate_start(&from, &to, "defer", &args->start)) { - return -1; - } - - if (args->start_hook) { - args->postcopy_data = args->start_hook(from, to); - } - - migrate_set_capability(from, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-blocktime", true); - - if (args->postcopy_preempt) { - migrate_set_capability(from, "postcopy-preempt", true); - migrate_set_capability(to, "postcopy-preempt", true); - } - - migrate_ensure_non_converge(from); - - migrate_prepare_for_dirty_mem(from); - qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { " - " 'channels': [ { 'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ] } }"); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - wait_for_suspend(from, &src_state); - - migrate_qmp(from, to, NULL, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - *from_ptr = from; - *to_ptr = to; - - return 0; -} - -static void migrate_postcopy_complete(QTestState *from, QTestState *to, - MigrateCommon *args) -{ - MigrationTestEnv *env = migration_get_env(); - - wait_for_migration_complete(from); - - if (args->start.suspend_me) { - /* wakeup succeeds only if guest is suspended */ - qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); - } - - /* Make sure we get at least one "B" on destination */ - wait_for_serial("dest_serial"); - - if (env->uffd_feature_thread_id) { - read_blocktime(to); - } - - if (args->end_hook) { - args->end_hook(from, to, args->postcopy_data); - args->postcopy_data = NULL; - } - - migrate_end(from, to, true); -} - -static void test_postcopy_common(MigrateCommon *args) -{ - QTestState *from, *to; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - migrate_postcopy_start(from, to, &src_state); - migrate_postcopy_complete(from, to, args); -} - static void test_postcopy(void) { MigrateCommon args = { }; @@ -1070,192 +436,6 @@ static void test_postcopy_preempt_tls_psk(void) } #endif -static void wait_for_postcopy_status(QTestState *one, const char *status) -{ - wait_for_migration_status(one, status, - (const char * []) { - "failed", "active", - "completed", NULL - }); -} - -static void postcopy_recover_fail(QTestState *from, QTestState *to, - PostcopyRecoveryFailStage stage) -{ -#ifndef _WIN32 - bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH); - int ret, pair1[2], pair2[2]; - char c; - - g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX); - - /* Create two unrelated socketpairs */ - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); - g_assert_cmpint(ret, ==, 0); - - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); - g_assert_cmpint(ret, ==, 0); - - /* - * Give the guests unpaired ends of the sockets, so they'll all blocked - * at reading. This mimics a wrong channel established. - */ - qtest_qmp_fds_assert_success(from, &pair1[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - qtest_qmp_fds_assert_success(to, &pair2[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - - /* - * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to - * emulate the 1st byte of a real recovery, but stops from there to - * keep dest QEMU in RECOVER. This is needed so that we can kick off - * the recover process on dest QEMU (by triggering the G_IO_IN event). - * - * NOTE: this trick is not needed on src QEMUs, because src doesn't - * rely on an pre-existing G_IO_IN event, so it will always trigger the - * upcoming recovery anyway even if it can read nothing. - */ -#define QEMU_VM_COMMAND 0x08 - c = QEMU_VM_COMMAND; - ret = send(pair2[1], &c, 1, 0); - g_assert_cmpint(ret, ==, 1); - - if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) { - /* - * This will make src QEMU to fail at an early stage when trying to - * resume later, where it shouldn't reach RECOVER stage at all. - */ - close(pair1[1]); - } - - migrate_recover(to, "fd:fd-mig"); - migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); - - /* - * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it. - * Make sure it appears along the way. - */ - migration_event_wait(from, "postcopy-recover-setup"); - - if (fail_early) { - /* - * When fails at reconnection, src QEMU will automatically goes - * back to PAUSED state. Making sure there is an event in this - * case: Libvirt relies on this to detect early reconnection - * errors. - */ - migration_event_wait(from, "postcopy-paused"); - } else { - /* - * We want to test "fail later" at RECOVER stage here. Make sure - * both QEMU instances will go into RECOVER stage first, then test - * kicking them out using migrate-pause. - * - * Explicitly check the RECOVER event on src, that's what Libvirt - * relies on, rather than polling. - */ - migration_event_wait(from, "postcopy-recover"); - wait_for_postcopy_status(from, "postcopy-recover"); - - /* Need an explicit kick on src QEMU in this case */ - migrate_pause(from); - } - - /* - * For all failure cases, we'll reach such states on both sides now. - * Check them. - */ - wait_for_postcopy_status(from, "postcopy-paused"); - wait_for_postcopy_status(to, "postcopy-recover"); - - /* - * Kick dest QEMU out too. This is normally not needed in reality - * because when the channel is shutdown it should also happen on src. - * However here we used separate socket pairs so we need to do that - * explicitly. - */ - migrate_pause(to); - wait_for_postcopy_status(to, "postcopy-paused"); - - close(pair1[0]); - close(pair2[0]); - close(pair2[1]); - - if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) { - close(pair1[1]); - } -#endif -} - -static void test_postcopy_recovery_common(MigrateCommon *args) -{ - QTestState *from, *to; - g_autofree char *uri = NULL; - - /* Always hide errors for postcopy recover tests since they're expected */ - args->start.hide_stderr = true; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - - /* Turn postcopy speed down, 4K/s is slow enough on any machines */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); - - /* Now we start the postcopy */ - migrate_postcopy_start(from, to, &src_state); - - /* - * Wait until postcopy is really started; we can only run the - * migrate-pause command during a postcopy - */ - wait_for_migration_status(from, "postcopy-active", NULL); - - /* - * Manually stop the postcopy migration. This emulates a network - * failure with the migration socket - */ - migrate_pause(from); - - /* - * Wait for destination side to reach postcopy-paused state. The - * migrate-recover command can only succeed if destination machine - * is in the paused state - */ - wait_for_postcopy_status(to, "postcopy-paused"); - wait_for_postcopy_status(from, "postcopy-paused"); - - if (args->postcopy_recovery_fail_stage) { - /* - * Test when a wrong socket specified for recover, and then the - * ability to kick it out, and continue with a correct socket. - */ - postcopy_recover_fail(from, to, args->postcopy_recovery_fail_stage); - /* continue with a good recovery */ - } - - /* - * Create a new socket to emulate a new channel that is different - * from the broken migration channel; tell the destination to - * listen to the new port - */ - uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); - migrate_recover(to, uri); - - /* - * Try to rebuild the migration channel using the resume flag and - * the newly created channel - */ - migrate_qmp(from, to, uri, NULL, "{'resume': true}"); - - /* Restore the postcopy bandwidth to unlimited */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); - - migrate_postcopy_complete(from, to, args); -} - static void test_postcopy_recovery(void) { MigrateCommon args = { }; @@ -1383,218 +563,10 @@ static void test_analyze_script(void) g_test_fail(); } migrate_end(from, to, false); - cleanup("migfile"); + unlink(file); } #endif -static void test_precopy_common(MigrateCommon *args) -{ - QTestState *from, *to; - void *data_hook = NULL; - - if (migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - if (args->start_hook) { - data_hook = args->start_hook(from, to); - } - - /* Wait for the first serial output from the source */ - if (args->result == MIG_TEST_SUCCEED) { - wait_for_serial("src_serial"); - wait_for_suspend(from, &src_state); - } - - if (args->live) { - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); - } else { - /* - * Testing non-live migration, we allow it to run at - * full speed to ensure short test case duration. - * For tests expected to fail, we don't need to - * change anything. - */ - if (args->result == MIG_TEST_SUCCEED) { - qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); - wait_for_stop(from, &src_state); - migrate_ensure_converge(from); - } - } - - if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); - goto finish; - } - - migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); - - if (args->result != MIG_TEST_SUCCEED) { - bool allow_active = args->result == MIG_TEST_FAIL; - wait_for_migration_fail(from, allow_active); - - if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { - qtest_set_expected_status(to, EXIT_FAILURE); - } - } else { - if (args->live) { - /* - * For initial iteration(s) we must do a full pass, - * but for the final iteration, we need only wait - * for some dirty mem before switching to converge - */ - while (args->iterations > 1) { - wait_for_migration_pass(from, &src_state); - args->iterations--; - } - migrate_wait_for_dirty_mem(from, to); - - migrate_ensure_converge(from); - - /* - * We do this first, as it has a timeout to stop us - * hanging forever if migration didn't converge - */ - wait_for_migration_complete(from); - - wait_for_stop(from, &src_state); - - } else { - wait_for_migration_complete(from); - /* - * Must wait for dst to finish reading all incoming - * data on the socket before issuing 'cont' otherwise - * it'll be ignored - */ - wait_for_migration_complete(to); - - qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); - } - - wait_for_resume(to, &dst_state); - - if (args->start.suspend_me) { - /* wakeup succeeds only if guest is suspended */ - qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); - } - - wait_for_serial("dest_serial"); - } - -finish: - if (args->end_hook) { - args->end_hook(from, to, data_hook); - } - - migrate_end(from, to, args->result == MIG_TEST_SUCCEED); -} - -static void file_dirty_offset_region(void) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - size_t size = FILE_TEST_OFFSET; - g_autofree char *data = g_new0(char, size); - - memset(data, FILE_TEST_MARKER, size); - g_assert(g_file_set_contents(path, data, size, NULL)); -} - -static void file_check_offset_region(void) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - size_t size = FILE_TEST_OFFSET; - g_autofree char *expected = g_new0(char, size); - g_autofree char *actual = NULL; - uint64_t *stream_start; - - /* - * Ensure the skipped offset region's data has not been touched - * and the migration stream starts at the right place. - */ - - memset(expected, FILE_TEST_MARKER, size); - - g_assert(g_file_get_contents(path, &actual, NULL, NULL)); - g_assert(!memcmp(actual, expected, size)); - - stream_start = (uint64_t *)(actual + size); - g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC); -} - -static void test_file_common(MigrateCommon *args, bool stop_src) -{ - QTestState *from, *to; - void *data_hook = NULL; - bool check_offset = false; - - if (migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - /* - * File migration is never live. We can keep the source VM running - * during migration, but the destination will not be running - * concurrently. - */ - g_assert_false(args->live); - - if (g_strrstr(args->connect_uri, "offset=")) { - check_offset = true; - /* - * This comes before the start_hook because it's equivalent to - * a management application creating the file and writing to - * it so hooks should expect the file to be already present. - */ - file_dirty_offset_region(); - } - - if (args->start_hook) { - data_hook = args->start_hook(from, to); - } - - migrate_ensure_converge(from); - wait_for_serial("src_serial"); - - if (stop_src) { - qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); - wait_for_stop(from, &src_state); - } - - if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, NULL, "{}"); - goto finish; - } - - migrate_qmp(from, to, args->connect_uri, NULL, "{}"); - wait_for_migration_complete(from); - - /* - * We need to wait for the source to finish before starting the - * destination. - */ - migrate_incoming_qmp(to, args->connect_uri, "{}"); - wait_for_migration_complete(to); - - if (stop_src) { - qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); - } - wait_for_resume(to, &dst_state); - - wait_for_serial("dest_serial"); - - if (check_offset) { - file_check_offset_region(); - } - -finish: - if (args->end_hook) { - args->end_hook(from, to, data_hook); - } - - migrate_end(from, to, args->result == MIG_TEST_SUCCEED); -} - static void test_precopy_unix_plain(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); @@ -1730,7 +702,7 @@ static void test_ignore_shared(void) migrate_wait_for_dirty_mem(from, to); - wait_for_stop(from, &src_state); + wait_for_stop(from, get_src()); qtest_qmp_eventwait(to, "RESUME"); @@ -2524,7 +1496,7 @@ static void test_auto_converge(void) break; } usleep(20); - g_assert_false(src_state.stop_seen); + g_assert_false(get_src()->stop_seen); } while (true); /* The first percentage of throttling should be at least init_pct */ g_assert_cmpint(percentage, >=, init_pct); @@ -2581,26 +1553,6 @@ static void test_auto_converge(void) } static void * -migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, - QTestState *to, - const char *method) -{ - migrate_set_parameter_int(from, "multifd-channels", 16); - migrate_set_parameter_int(to, "multifd-channels", 16); - - migrate_set_parameter_str(from, "multifd-compression", method); - migrate_set_parameter_str(to, "multifd-compression", method); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); - - return NULL; -} - -static void * migrate_hook_start_precopy_tcp_multifd(QTestState *from, QTestState *to) { @@ -3031,7 +1983,7 @@ static void test_multifd_tcp_cancel(void) migrate_ensure_converge(from); - wait_for_stop(from, &src_state); + wait_for_stop(from, get_src()); qtest_qmp_eventwait(to2, "RESUME"); wait_for_serial("dest_serial"); @@ -3176,6 +2128,7 @@ static QTestState *dirtylimit_start_vm(void) { QTestState *vm = NULL; g_autofree gchar *cmd = NULL; + const char *bootpath; bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " @@ -3191,8 +2144,10 @@ static QTestState *dirtylimit_start_vm(void) static void dirtylimit_stop_vm(QTestState *vm) { + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, "vm_serial"); + qtest_quit(vm); - cleanup("vm_serial"); + unlink(path); } static void test_vcpu_dirty_limit(void) @@ -3370,7 +2325,7 @@ static void test_dirty_limit(void) read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); usleep(100); - g_assert_false(src_state.stop_seen); + g_assert_false(get_src()->stop_seen); } /* Now cancel migrate and wait for dirty limit throttle switch off */ @@ -3383,7 +2338,7 @@ static void test_dirty_limit(void) read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); usleep(100); - g_assert_false(src_state.stop_seen); + g_assert_false(get_src()->stop_seen); } while (throttle_us_per_full != 0 && --max_try_count); /* Assert dirty limit is not in service */ @@ -3413,7 +2368,7 @@ static void test_dirty_limit(void) read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); usleep(100); - g_assert_false(src_state.stop_seen); + g_assert_false(get_src()->stop_seen); } /* @@ -3446,75 +2401,6 @@ static void test_dirty_limit(void) migrate_end(from, to, true); } -MigrationTestEnv *migration_get_env(void) -{ - static MigrationTestEnv *env; - g_autoptr(GError) err = NULL; - - if (env) { - return env; - } - - env = g_new0(MigrationTestEnv, 1); - env->qemu_src = getenv(QEMU_ENV_SRC); - env->qemu_dst = getenv(QEMU_ENV_DST); - - /* - * The default QTEST_QEMU_BINARY must always be provided because - * that is what helpers use to query the accel type and - * architecture. - */ - if (env->qemu_src && env->qemu_dst) { - g_test_message("Only one of %s, %s is allowed", - QEMU_ENV_SRC, QEMU_ENV_DST); - exit(1); - } - - env->has_kvm = qtest_has_accel("kvm"); - env->has_tcg = qtest_has_accel("tcg"); - - if (!env->has_tcg && !env->has_kvm) { - g_test_skip("No KVM or TCG accelerator available"); - return env; - } - - env->has_dirty_ring = kvm_dirty_ring_supported(); - env->has_uffd = ufd_version_check(&env->uffd_feature_thread_id); - env->arch = qtest_get_arch(); - env->is_x86 = !strcmp(env->arch, "i386") || !strcmp(env->arch, "x86_64"); - - env->tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); - if (!env->tmpfs) { - g_test_message("Can't create temporary directory in %s: %s", - g_get_tmp_dir(), err->message); - } - g_assert(env->tmpfs); - - return env; -} - -static int migration_env_clean(MigrationTestEnv *env) -{ - char *tmpfs; - int ret = 0; - - if (!env) { - return ret; - } - - bootfile_delete(); - - tmpfs = env->tmpfs; - ret = rmdir(tmpfs); - if (ret != 0) { - g_test_message("unable to rmdir: path (%s): %s", - tmpfs, strerror(errno)); - } - g_free(tmpfs); - - return ret; -} - int main(int argc, char **argv) { MigrationTestEnv *env; diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c index 8f75f64..fac059d 100644 --- a/tests/qtest/migration/bootfile.c +++ b/tests/qtest/migration/bootfile.c @@ -34,7 +34,7 @@ void bootfile_delete(void) bootpath = NULL; } -char *bootfile_create(const char *arch, char *dir, bool suspend_me) +char *bootfile_create(const char *arch, const char *dir, bool suspend_me) { unsigned char *content; size_t len; diff --git a/tests/qtest/migration/bootfile.h b/tests/qtest/migration/bootfile.h index 4f5099d..6d6a673 100644 --- a/tests/qtest/migration/bootfile.h +++ b/tests/qtest/migration/bootfile.h @@ -34,6 +34,6 @@ #define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) void bootfile_delete(void); -char *bootfile_create(const char *arch, char *dir, bool suspend_me); +char *bootfile_create(const char *arch, const char *dir, bool suspend_me); #endif /* BOOTFILE_H */ diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c new file mode 100644 index 0000000..a902936 --- /dev/null +++ b/tests/qtest/migration/framework.c @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "chardev/char.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/bootfile.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "ppc-util.h" +#include "qapi/qmp/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + + +#define QEMU_VM_FILE_MAGIC 0x5145564d +#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" +#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" + +unsigned start_address; +unsigned end_address; +static QTestMigrationState src_state; +static QTestMigrationState dst_state; +static char *tmpfs; + +/* + * An initial 3 MB offset is used as that corresponds + * to ~1 sec of data transfer with our bandwidth setting. + */ +#define MAGIC_OFFSET_BASE (3 * 1024 * 1024) +/* + * A further 1k is added to ensure we're not a multiple + * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes + * from the migration guest workload. + */ +#define MAGIC_OFFSET_SHUFFLE 1024 +#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE) +#define MAGIC_MARKER 0xFEED12345678CAFEULL + + +/* + * Wait for some output in the serial output file, + * we get an 'A' followed by an endless string of 'B's + * but on the destination we won't have the A (unless we enabled suspend/resume) + */ +void wait_for_serial(const char *side) +{ + g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); + FILE *serialfile = fopen(serialpath, "r"); + + do { + int readvalue = fgetc(serialfile); + + switch (readvalue) { + case 'A': + /* Fine */ + break; + + case 'B': + /* It's alive! */ + fclose(serialfile); + return; + + case EOF: + fseek(serialfile, 0, SEEK_SET); + usleep(1000); + break; + + default: + fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); + g_assert_not_reached(); + } + } while (true); +} + +void migrate_prepare_for_dirty_mem(QTestState *from) +{ + /* + * The guest workflow iterates from start_address to + * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE + * bytes. + * + * IOW, if we write to mem at a point which is NOT + * a multiple of TEST_MEM_PAGE_SIZE, our write won't + * conflict with the migration workflow. + * + * We put in a marker here, that we'll use to determine + * when the data has been transferred to the dst. + */ + qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER); +} + +void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to) +{ + uint64_t watch_address = start_address + MAGIC_OFFSET_BASE; + uint64_t marker_address = start_address + MAGIC_OFFSET; + uint8_t watch_byte; + + /* + * Wait for the MAGIC_MARKER to get transferred, as an + * indicator that a migration pass has made some known + * amount of progress. + */ + do { + usleep(1000 * 10); + } while (qtest_readq(to, marker_address) != MAGIC_MARKER); + + + /* If suspended, src only iterates once, and watch_byte may never change */ + if (src_state.suspend_me) { + return; + } + + /* + * Now ensure that already transferred bytes are + * dirty again from the guest workload. Note the + * guest byte value will wrap around and by chance + * match the original watch_byte. This is harmless + * as we'll eventually see a different value if we + * keep watching + */ + watch_byte = qtest_readb(from, watch_address); + do { + usleep(1000 * 10); + } while (qtest_readb(from, watch_address) == watch_byte); +} + +static void check_guests_ram(QTestState *who) +{ + /* + * Our ASM test will have been incrementing one byte from each page from + * start_address to < end_address in order. This gives us a constraint + * that any page's byte should be equal or less than the previous pages + * byte (mod 256); and they should all be equal except for one transition + * at the point where we meet the incrementer. (We're running this with + * the guest stopped). + */ + unsigned address; + uint8_t first_byte; + uint8_t last_byte; + bool hit_edge = false; + int bad = 0; + + qtest_memread(who, start_address, &first_byte, 1); + last_byte = first_byte; + + for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; + address += TEST_MEM_PAGE_SIZE) + { + uint8_t b; + qtest_memread(who, address, &b, 1); + if (b != last_byte) { + if (((b + 1) % 256) == last_byte && !hit_edge) { + /* + * This is OK, the guest stopped at the point of + * incrementing the previous page but didn't get + * to us yet. + */ + hit_edge = true; + last_byte = b; + } else { + bad++; + if (bad <= 10) { + fprintf(stderr, "Memory content inconsistency at %x" + " first_byte = %x last_byte = %x current = %x" + " hit_edge = %x\n", + address, first_byte, last_byte, b, hit_edge); + } + } + } + } + if (bad >= 10) { + fprintf(stderr, "and in another %d pages", bad - 10); + } + g_assert(bad == 0); +} + +static void cleanup(const char *filename) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename); + + unlink(path); +} + +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args) +{ + g_autofree gchar *arch_source = NULL; + g_autofree gchar *arch_target = NULL; + /* options for source and target */ + g_autofree gchar *arch_opts = NULL; + g_autofree gchar *cmd_source = NULL; + g_autofree gchar *cmd_target = NULL; + const gchar *ignore_stderr; + g_autofree char *shmem_opts = NULL; + g_autofree char *shmem_path = NULL; + const char *kvm_opts = NULL; + const char *arch = qtest_get_arch(); + const char *memory_size; + const char *machine_alias, *machine_opts = ""; + g_autofree char *machine = NULL; + const char *bootpath; + + if (args->use_shmem) { + if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { + g_test_skip("/dev/shm is not supported"); + return -1; + } + } + + dst_state = (QTestMigrationState) { }; + src_state = (QTestMigrationState) { }; + bootpath = bootfile_create(arch, tmpfs, args->suspend_me); + src_state.suspend_me = args->suspend_me; + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + memory_size = "150M"; + + if (g_str_equal(arch, "i386")) { + machine_alias = "pc"; + } else { + machine_alias = "q35"; + } + arch_opts = g_strdup_printf( + "-drive if=none,id=d0,file=%s,format=raw " + "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath); + start_address = X86_TEST_MEM_START; + end_address = X86_TEST_MEM_END; + } else if (g_str_equal(arch, "s390x")) { + memory_size = "128M"; + machine_alias = "s390-ccw-virtio"; + arch_opts = g_strdup_printf("-bios %s", bootpath); + start_address = S390_TEST_MEM_START; + end_address = S390_TEST_MEM_END; + } else if (strcmp(arch, "ppc64") == 0) { + memory_size = "256M"; + start_address = PPC_TEST_MEM_START; + end_address = PPC_TEST_MEM_END; + machine_alias = "pseries"; + machine_opts = "vsmt=8"; + arch_opts = g_strdup_printf( + "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " " + "-bios %s", bootpath); + } else if (strcmp(arch, "aarch64") == 0) { + memory_size = "150M"; + machine_alias = "virt"; + machine_opts = "gic-version=3"; + arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath); + start_address = ARM_TEST_MEM_START; + end_address = ARM_TEST_MEM_END; + } else { + g_assert_not_reached(); + } + + if (!getenv("QTEST_LOG") && args->hide_stderr) { +#ifndef _WIN32 + ignore_stderr = "2>/dev/null"; +#else + /* + * On Windows the QEMU executable is created via CreateProcess() and + * IO redirection does not work, so don't bother adding IO redirection + * to the command line. + */ + ignore_stderr = ""; +#endif + } else { + ignore_stderr = ""; + } + + if (args->use_shmem) { + shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); + shmem_opts = g_strdup_printf( + "-object memory-backend-file,id=mem0,size=%s" + ",mem-path=%s,share=on -numa node,memdev=mem0", + memory_size, shmem_path); + } + + if (args->use_dirty_ring) { + kvm_opts = ",dirty-ring-size=4096"; + } + + if (!qtest_has_machine(machine_alias)) { + g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); + g_test_skip(msg); + return -1; + } + + machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, + QEMU_ENV_DST); + + g_test_message("Using machine type: %s", machine); + + cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " + "-machine %s,%s " + "-name source,debug-threads=on " + "-m %s " + "-serial file:%s/src_serial " + "%s %s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, + memory_size, tmpfs, + arch_opts ? arch_opts : "", + arch_source ? arch_source : "", + shmem_opts ? shmem_opts : "", + args->opts_source ? args->opts_source : "", + ignore_stderr); + if (!args->only_target) { + *from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source); + qtest_qmp_set_event_callback(*from, + migrate_watch_for_events, + &src_state); + } + + cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " + "-machine %s,%s " + "-name target,debug-threads=on " + "-m %s " + "-serial file:%s/dest_serial " + "-incoming %s " + "%s %s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, + memory_size, tmpfs, uri, + arch_opts ? arch_opts : "", + arch_target ? arch_target : "", + shmem_opts ? shmem_opts : "", + args->opts_target ? args->opts_target : "", + ignore_stderr); + *to = qtest_init_with_env(QEMU_ENV_DST, cmd_target); + qtest_qmp_set_event_callback(*to, + migrate_watch_for_events, + &dst_state); + + /* + * Remove shmem file immediately to avoid memory leak in test failed case. + * It's valid because QEMU has already opened this file + */ + if (args->use_shmem) { + unlink(shmem_path); + } + + /* + * Always enable migration events. Libvirt always uses it, let's try + * to mimic as closer as that. + */ + migrate_set_capability(*from, "events", true); + migrate_set_capability(*to, "events", true); + + return 0; +} + +void migrate_end(QTestState *from, QTestState *to, bool test_dest) +{ + unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; + + qtest_quit(from); + + if (test_dest) { + qtest_memread(to, start_address, &dest_byte_a, 1); + + /* Destination still running, wait for a byte to change */ + do { + qtest_memread(to, start_address, &dest_byte_b, 1); + usleep(1000 * 10); + } while (dest_byte_a == dest_byte_b); + + qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); + + /* With it stopped, check nothing changes */ + qtest_memread(to, start_address, &dest_byte_c, 1); + usleep(1000 * 200); + qtest_memread(to, start_address, &dest_byte_d, 1); + g_assert_cmpint(dest_byte_c, ==, dest_byte_d); + + check_guests_ram(to); + } + + qtest_quit(to); + + cleanup("migsocket"); + cleanup("src_serial"); + cleanup("dest_serial"); + cleanup(FILE_TEST_FILENAME); +} + +static int migrate_postcopy_prepare(QTestState **from_ptr, + QTestState **to_ptr, + MigrateCommon *args) +{ + QTestState *from, *to; + + if (migrate_start(&from, &to, "defer", &args->start)) { + return -1; + } + + if (args->start_hook) { + args->postcopy_data = args->start_hook(from, to); + } + + migrate_set_capability(from, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-blocktime", true); + + if (args->postcopy_preempt) { + migrate_set_capability(from, "postcopy-preempt", true); + migrate_set_capability(to, "postcopy-preempt", true); + } + + migrate_ensure_non_converge(from); + + migrate_prepare_for_dirty_mem(from); + qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { " + " 'channels': [ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ] } }"); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + wait_for_suspend(from, &src_state); + + migrate_qmp(from, to, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + *from_ptr = from; + *to_ptr = to; + + return 0; +} + +static void migrate_postcopy_complete(QTestState *from, QTestState *to, + MigrateCommon *args) +{ + MigrationTestEnv *env = migration_get_env(); + + wait_for_migration_complete(from); + + if (args->start.suspend_me) { + /* wakeup succeeds only if guest is suspended */ + qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); + } + + /* Make sure we get at least one "B" on destination */ + wait_for_serial("dest_serial"); + + if (env->uffd_feature_thread_id) { + read_blocktime(to); + } + + if (args->end_hook) { + args->end_hook(from, to, args->postcopy_data); + args->postcopy_data = NULL; + } + + migrate_end(from, to, true); +} + +void test_postcopy_common(MigrateCommon *args) +{ + QTestState *from, *to; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + migrate_postcopy_start(from, to, &src_state); + migrate_postcopy_complete(from, to, args); +} + +static void wait_for_postcopy_status(QTestState *one, const char *status) +{ + wait_for_migration_status(one, status, + (const char * []) { + "failed", "active", + "completed", NULL + }); +} + +static void postcopy_recover_fail(QTestState *from, QTestState *to, + PostcopyRecoveryFailStage stage) +{ +#ifndef _WIN32 + bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH); + int ret, pair1[2], pair2[2]; + char c; + + g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX); + + /* Create two unrelated socketpairs */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); + g_assert_cmpint(ret, ==, 0); + + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); + g_assert_cmpint(ret, ==, 0); + + /* + * Give the guests unpaired ends of the sockets, so they'll all blocked + * at reading. This mimics a wrong channel established. + */ + qtest_qmp_fds_assert_success(from, &pair1[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + qtest_qmp_fds_assert_success(to, &pair2[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + /* + * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to + * emulate the 1st byte of a real recovery, but stops from there to + * keep dest QEMU in RECOVER. This is needed so that we can kick off + * the recover process on dest QEMU (by triggering the G_IO_IN event). + * + * NOTE: this trick is not needed on src QEMUs, because src doesn't + * rely on an pre-existing G_IO_IN event, so it will always trigger the + * upcoming recovery anyway even if it can read nothing. + */ +#define QEMU_VM_COMMAND 0x08 + c = QEMU_VM_COMMAND; + ret = send(pair2[1], &c, 1, 0); + g_assert_cmpint(ret, ==, 1); + + if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + /* + * This will make src QEMU to fail at an early stage when trying to + * resume later, where it shouldn't reach RECOVER stage at all. + */ + close(pair1[1]); + } + + migrate_recover(to, "fd:fd-mig"); + migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); + + /* + * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it. + * Make sure it appears along the way. + */ + migration_event_wait(from, "postcopy-recover-setup"); + + if (fail_early) { + /* + * When fails at reconnection, src QEMU will automatically goes + * back to PAUSED state. Making sure there is an event in this + * case: Libvirt relies on this to detect early reconnection + * errors. + */ + migration_event_wait(from, "postcopy-paused"); + } else { + /* + * We want to test "fail later" at RECOVER stage here. Make sure + * both QEMU instances will go into RECOVER stage first, then test + * kicking them out using migrate-pause. + * + * Explicitly check the RECOVER event on src, that's what Libvirt + * relies on, rather than polling. + */ + migration_event_wait(from, "postcopy-recover"); + wait_for_postcopy_status(from, "postcopy-recover"); + + /* Need an explicit kick on src QEMU in this case */ + migrate_pause(from); + } + + /* + * For all failure cases, we'll reach such states on both sides now. + * Check them. + */ + wait_for_postcopy_status(from, "postcopy-paused"); + wait_for_postcopy_status(to, "postcopy-recover"); + + /* + * Kick dest QEMU out too. This is normally not needed in reality + * because when the channel is shutdown it should also happen on src. + * However here we used separate socket pairs so we need to do that + * explicitly. + */ + migrate_pause(to); + wait_for_postcopy_status(to, "postcopy-paused"); + + close(pair1[0]); + close(pair2[0]); + close(pair2[1]); + + if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + close(pair1[1]); + } +#endif +} + +void test_postcopy_recovery_common(MigrateCommon *args) +{ + QTestState *from, *to; + g_autofree char *uri = NULL; + + /* Always hide errors for postcopy recover tests since they're expected */ + args->start.hide_stderr = true; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + + /* Turn postcopy speed down, 4K/s is slow enough on any machines */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); + + /* Now we start the postcopy */ + migrate_postcopy_start(from, to, &src_state); + + /* + * Wait until postcopy is really started; we can only run the + * migrate-pause command during a postcopy + */ + wait_for_migration_status(from, "postcopy-active", NULL); + + /* + * Manually stop the postcopy migration. This emulates a network + * failure with the migration socket + */ + migrate_pause(from); + + /* + * Wait for destination side to reach postcopy-paused state. The + * migrate-recover command can only succeed if destination machine + * is in the paused state + */ + wait_for_postcopy_status(to, "postcopy-paused"); + wait_for_postcopy_status(from, "postcopy-paused"); + + if (args->postcopy_recovery_fail_stage) { + /* + * Test when a wrong socket specified for recover, and then the + * ability to kick it out, and continue with a correct socket. + */ + postcopy_recover_fail(from, to, args->postcopy_recovery_fail_stage); + /* continue with a good recovery */ + } + + /* + * Create a new socket to emulate a new channel that is different + * from the broken migration channel; tell the destination to + * listen to the new port + */ + uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); + migrate_recover(to, uri); + + /* + * Try to rebuild the migration channel using the resume flag and + * the newly created channel + */ + migrate_qmp(from, to, uri, NULL, "{'resume': true}"); + + /* Restore the postcopy bandwidth to unlimited */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); + + migrate_postcopy_complete(from, to, args); +} + +void test_precopy_common(MigrateCommon *args) +{ + QTestState *from, *to; + void *data_hook = NULL; + + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + if (args->start_hook) { + data_hook = args->start_hook(from, to); + } + + /* Wait for the first serial output from the source */ + if (args->result == MIG_TEST_SUCCEED) { + wait_for_serial("src_serial"); + wait_for_suspend(from, &src_state); + } + + if (args->live) { + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + } else { + /* + * Testing non-live migration, we allow it to run at + * full speed to ensure short test case duration. + * For tests expected to fail, we don't need to + * change anything. + */ + if (args->result == MIG_TEST_SUCCEED) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + wait_for_stop(from, &src_state); + migrate_ensure_converge(from); + } + } + + if (args->result == MIG_TEST_QMP_ERROR) { + migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + goto finish; + } + + migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); + + if (args->result != MIG_TEST_SUCCEED) { + bool allow_active = args->result == MIG_TEST_FAIL; + wait_for_migration_fail(from, allow_active); + + if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { + qtest_set_expected_status(to, EXIT_FAILURE); + } + } else { + if (args->live) { + /* + * For initial iteration(s) we must do a full pass, + * but for the final iteration, we need only wait + * for some dirty mem before switching to converge + */ + while (args->iterations > 1) { + wait_for_migration_pass(from, &src_state); + args->iterations--; + } + migrate_wait_for_dirty_mem(from, to); + + migrate_ensure_converge(from); + + /* + * We do this first, as it has a timeout to stop us + * hanging forever if migration didn't converge + */ + wait_for_migration_complete(from); + + wait_for_stop(from, &src_state); + + } else { + wait_for_migration_complete(from); + /* + * Must wait for dst to finish reading all incoming + * data on the socket before issuing 'cont' otherwise + * it'll be ignored + */ + wait_for_migration_complete(to); + + qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); + } + + wait_for_resume(to, &dst_state); + + if (args->start.suspend_me) { + /* wakeup succeeds only if guest is suspended */ + qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); + } + + wait_for_serial("dest_serial"); + } + +finish: + if (args->end_hook) { + args->end_hook(from, to, data_hook); + } + + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +static void file_dirty_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *data = g_new0(char, size); + + memset(data, FILE_TEST_MARKER, size); + g_assert(g_file_set_contents(path, data, size, NULL)); +} + +static void file_check_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *expected = g_new0(char, size); + g_autofree char *actual = NULL; + uint64_t *stream_start; + + /* + * Ensure the skipped offset region's data has not been touched + * and the migration stream starts at the right place. + */ + + memset(expected, FILE_TEST_MARKER, size); + + g_assert(g_file_get_contents(path, &actual, NULL, NULL)); + g_assert(!memcmp(actual, expected, size)); + + stream_start = (uint64_t *)(actual + size); + g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC); +} + +void test_file_common(MigrateCommon *args, bool stop_src) +{ + QTestState *from, *to; + void *data_hook = NULL; + bool check_offset = false; + + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + /* + * File migration is never live. We can keep the source VM running + * during migration, but the destination will not be running + * concurrently. + */ + g_assert_false(args->live); + + if (g_strrstr(args->connect_uri, "offset=")) { + check_offset = true; + /* + * This comes before the start_hook because it's equivalent to + * a management application creating the file and writing to + * it so hooks should expect the file to be already present. + */ + file_dirty_offset_region(); + } + + if (args->start_hook) { + data_hook = args->start_hook(from, to); + } + + migrate_ensure_converge(from); + wait_for_serial("src_serial"); + + if (stop_src) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + wait_for_stop(from, &src_state); + } + + if (args->result == MIG_TEST_QMP_ERROR) { + migrate_qmp_fail(from, args->connect_uri, NULL, "{}"); + goto finish; + } + + migrate_qmp(from, to, args->connect_uri, NULL, "{}"); + wait_for_migration_complete(from); + + /* + * We need to wait for the source to finish before starting the + * destination. + */ + migrate_incoming_qmp(to, args->connect_uri, "{}"); + wait_for_migration_complete(to); + + if (stop_src) { + qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); + } + wait_for_resume(to, &dst_state); + + wait_for_serial("dest_serial"); + + if (check_offset) { + file_check_offset_region(); + } + +finish: + if (args->end_hook) { + args->end_hook(from, to, data_hook); + } + + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, + QTestState *to, + const char *method) +{ + migrate_set_parameter_int(from, "multifd-channels", 16); + migrate_set_parameter_int(to, "multifd-channels", 16); + + migrate_set_parameter_str(from, "multifd-compression", method); + migrate_set_parameter_str(to, "multifd-compression", method); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); + + return NULL; +} + +QTestMigrationState *get_src(void) +{ + return &src_state; +} + +MigrationTestEnv *migration_get_env(void) +{ + static MigrationTestEnv *env; + g_autoptr(GError) err = NULL; + + if (env) { + return env; + } + + env = g_new0(MigrationTestEnv, 1); + env->qemu_src = getenv(QEMU_ENV_SRC); + env->qemu_dst = getenv(QEMU_ENV_DST); + + /* + * The default QTEST_QEMU_BINARY must always be provided because + * that is what helpers use to query the accel type and + * architecture. + */ + if (env->qemu_src && env->qemu_dst) { + g_test_message("Only one of %s, %s is allowed", + QEMU_ENV_SRC, QEMU_ENV_DST); + exit(1); + } + + env->has_kvm = qtest_has_accel("kvm"); + env->has_tcg = qtest_has_accel("tcg"); + + if (!env->has_tcg && !env->has_kvm) { + g_test_skip("No KVM or TCG accelerator available"); + return env; + } + + env->has_dirty_ring = kvm_dirty_ring_supported(); + env->has_uffd = ufd_version_check(&env->uffd_feature_thread_id); + env->arch = qtest_get_arch(); + env->is_x86 = !strcmp(env->arch, "i386") || !strcmp(env->arch, "x86_64"); + + env->tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); + if (!env->tmpfs) { + g_test_message("Can't create temporary directory in %s: %s", + g_get_tmp_dir(), err->message); + } + g_assert(env->tmpfs); + + tmpfs = env->tmpfs; + + return env; +} + +int migration_env_clean(MigrationTestEnv *env) +{ + char *tmpfs; + int ret = 0; + + if (!env) { + return ret; + } + + bootfile_delete(); + + tmpfs = env->tmpfs; + ret = rmdir(tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s", + tmpfs, strerror(errno)); + } + g_free(tmpfs); + + return ret; +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h new file mode 100644 index 0000000..6d5176b --- /dev/null +++ b/tests/qtest/migration/framework.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef TEST_FRAMEWORK_H +#define TEST_FRAMEWORK_H + +#include "libqtest.h" + +#define FILE_TEST_FILENAME "migfile" +#define FILE_TEST_OFFSET 0x1000 +#define FILE_TEST_MARKER 'X' + +typedef struct MigrationTestEnv { + bool has_kvm; + bool has_tcg; + bool has_uffd; + bool uffd_feature_thread_id; + bool has_dirty_ring; + bool is_x86; + const char *arch; + const char *qemu_src; + const char *qemu_dst; + char *tmpfs; +} MigrationTestEnv; + +MigrationTestEnv *migration_get_env(void); +int migration_env_clean(MigrationTestEnv *env); + +/* + * A hook that runs after the src and dst QEMUs have been + * created, but before the migration is started. This can + * be used to set migration parameters and capabilities. + * + * Returns: NULL, or a pointer to opaque state to be + * later passed to the TestMigrateEndHook + */ +typedef void * (*TestMigrateStartHook)(QTestState *from, + QTestState *to); + +/* + * A hook that runs after the migration has finished, + * regardless of whether it succeeded or failed, but + * before QEMU has terminated (unless it self-terminated + * due to migration error) + * + * @opaque is a pointer to state previously returned + * by the TestMigrateStartHook if any, or NULL. + */ +typedef void (*TestMigrateEndHook)(QTestState *from, + QTestState *to, + void *opaque); + +/* + * Our goal is to ensure that we run a single full migration + * iteration, and also dirty memory, ensuring that at least + * one further iteration is required. + * + * We can't directly synchronize with the start of a migration + * so we have to apply some tricks monitoring memory that is + * transferred. + * + * Initially we set the migration bandwidth to an insanely + * low value, with tiny max downtime too. This basically + * guarantees migration will never complete. + * + * This will result in a test that is unacceptably slow though, + * so we can't let the entire migration pass run at this speed. + * Our intent is to let it run just long enough that we can + * prove data prior to the marker has been transferred *AND* + * also prove this transferred data is dirty again. + * + * Before migration starts, we write a 64-bit magic marker + * into a fixed location in the src VM RAM. + * + * Then watch dst memory until the marker appears. This is + * proof that start_address -> MAGIC_OFFSET_BASE has been + * transferred. + * + * Finally we go back to the source and read a byte just + * before the marker until we see it flip in value. This + * is proof that start_address -> MAGIC_OFFSET_BASE + * is now dirty again. + * + * IOW, we're guaranteed at least a 2nd migration pass + * at this point. + * + * We can now let migration run at full speed to finish + * the test + */ +typedef struct { + /* + * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors + * unconditionally, because it means the user would like to be verbose. + */ + bool hide_stderr; + bool use_shmem; + /* only launch the target process */ + bool only_target; + /* Use dirty ring if true; dirty logging otherwise */ + bool use_dirty_ring; + const char *opts_source; + const char *opts_target; + /* suspend the src before migrating to dest. */ + bool suspend_me; +} MigrateStart; + +typedef enum PostcopyRecoveryFailStage { + /* + * "no failure" must be 0 as it's the default. OTOH, real failure + * cases must be >0 to make sure they trigger by a "if" test. + */ + POSTCOPY_FAIL_NONE = 0, + POSTCOPY_FAIL_CHANNEL_ESTABLISH, + POSTCOPY_FAIL_RECOVERY, + POSTCOPY_FAIL_MAX +} PostcopyRecoveryFailStage; + +typedef struct { + /* Optional: fine tune start parameters */ + MigrateStart start; + + /* Required: the URI for the dst QEMU to listen on */ + const char *listen_uri; + + /* + * Optional: the URI for the src QEMU to connect to + * If NULL, then it will query the dst QEMU for its actual + * listening address and use that as the connect address. + * This allows for dynamically picking a free TCP port. + */ + const char *connect_uri; + + /* + * Optional: JSON-formatted list of src QEMU URIs. If a port is + * defined as '0' in any QDict key a value of '0' will be + * automatically converted to the correct destination port. + */ + const char *connect_channels; + + /* Optional: callback to run at start to set migration parameters */ + TestMigrateStartHook start_hook; + /* Optional: callback to run at finish to cleanup */ + TestMigrateEndHook end_hook; + + /* + * Optional: normally we expect the migration process to complete. + * + * There can be a variety of reasons and stages in which failure + * can happen during tests. + * + * If a failure is expected to happen at time of establishing + * the connection, then MIG_TEST_FAIL will indicate that the dst + * QEMU is expected to stay running and accept future migration + * connections. + * + * If a failure is expected to happen while processing the + * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate + * that the dst QEMU is expected to quit with non-zero exit status + */ + enum { + /* This test should succeed, the default */ + MIG_TEST_SUCCEED = 0, + /* This test should fail, dest qemu should keep alive */ + MIG_TEST_FAIL, + /* This test should fail, dest qemu should fail with abnormal status */ + MIG_TEST_FAIL_DEST_QUIT_ERR, + /* The QMP command for this migration should fail with an error */ + MIG_TEST_QMP_ERROR, + } result; + + /* + * Optional: set number of migration passes to wait for, if live==true. + * If zero, then merely wait for a few MB of dirty data + */ + unsigned int iterations; + + /* + * Optional: whether the guest CPUs should be running during a precopy + * migration test. We used to always run with live but it took much + * longer so we reduced live tests to only the ones that have solid + * reason to be tested live-only. For each of the new test cases for + * precopy please provide justifications to use live explicitly (please + * refer to existing ones with live=true), or use live=off by default. + */ + bool live; + + /* Postcopy specific fields */ + void *postcopy_data; + bool postcopy_preempt; + PostcopyRecoveryFailStage postcopy_recovery_fail_stage; +} MigrateCommon; + +void wait_for_serial(const char *side); +void migrate_prepare_for_dirty_mem(QTestState *from); +void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to); +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args); +void migrate_end(QTestState *from, QTestState *to, bool test_dest); + +void test_postcopy_common(MigrateCommon *args); +void test_postcopy_recovery_common(MigrateCommon *args); +void test_precopy_common(MigrateCommon *args); +void test_file_common(MigrateCommon *args, bool stop_src); +void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, + QTestState *to, + const char *method); + +typedef struct QTestMigrationState QTestMigrationState; +QTestMigrationState *get_src(void); + +#endif /* TEST_FRAMEWORK_H */ -- cgit v1.1 From 979ee2a76feb3bf8dea4624482f7399077e39a87 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:55 -0300 Subject: tests/qtest/migration: Split TLS tests from migration-test.c The migration-test.c file has become unwieldy large. It's quite confusing to navigate with all the test definitions mixed with hook definitions. The TLS tests make this worse with ifdef'ery. Since we're planning on having a smaller set of tests to run as smoke testing on all architectures, I'm taking the time to split some tests into their own file. Move the TLS tests into a file of their own. Acked-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 8 +- tests/qtest/migration-test.c | 788 +------------------------------------ tests/qtest/migration/framework.h | 6 + tests/qtest/migration/tls-tests.c | 791 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 804 insertions(+), 789 deletions(-) create mode 100644 tests/qtest/migration/tls-tests.c (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index c5cc09f..6dc4ba9 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -339,11 +339,13 @@ migration_files = [files( 'migration/migration-util.c', )] +migration_tls_files = [] if gnutls.found() - migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls] + migration_tls_files = [files('migration/tls-tests.c', + '../unit/crypto-tls-psk-helpers.c'), gnutls] if tasn1.found() - migration_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1] + migration_tls_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1] endif endif @@ -354,7 +356,7 @@ qtests = { 'migration/migration-util.c') + dbus_vmstate1, 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], - 'migration-test': migration_files, + 'migration-test': migration_files + migration_tls_files, 'pxe-test': files('boot-sector.c'), 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c'), 'qos-test': [chardev, io, qos_test_ss.apply({}).sources()], diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index a601504..7395403 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -26,12 +26,6 @@ #include "migration/framework.h" #include "migration/migration-qmp.h" #include "migration/migration-util.h" -#ifdef CONFIG_GNUTLS -# include "tests/unit/crypto-tls-psk-helpers.h" -# ifdef CONFIG_TASN1 -# include "tests/unit/crypto-tls-x509-helpers.h" -# endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ /* * Dirtylimit stop working if dirty page rate error @@ -49,345 +43,6 @@ static char *tmpfs; -#ifdef CONFIG_GNUTLS -struct TestMigrateTLSPSKData { - char *workdir; - char *workdiralt; - char *pskfile; - char *pskfilealt; -}; - -static void * -migrate_hook_start_tls_psk_common(QTestState *from, - QTestState *to, - bool mismatch) -{ - struct TestMigrateTLSPSKData *data = - g_new0(struct TestMigrateTLSPSKData, 1); - - data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); - data->pskfile = g_strdup_printf("%s/%s", data->workdir, - QCRYPTO_TLS_CREDS_PSKFILE); - g_mkdir_with_parents(data->workdir, 0700); - test_tls_psk_init(data->pskfile); - - if (mismatch) { - data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); - data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, - QCRYPTO_TLS_CREDS_PSKFILE); - g_mkdir_with_parents(data->workdiralt, 0700); - test_tls_psk_init_alt(data->pskfilealt); - } - - qtest_qmp_assert_success(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'username': 'qemu'} }", - data->workdir); - - qtest_qmp_assert_success(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'server'," - " 'dir': %s } }", - mismatch ? data->workdiralt : data->workdir); - - migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0"); - migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0"); - - return data; -} - -static void * -migrate_hook_start_tls_psk_match(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_tls_psk_common(from, to, false); -} - -static void * -migrate_hook_start_tls_psk_mismatch(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_tls_psk_common(from, to, true); -} - -static void -migrate_hook_end_tls_psk(QTestState *from, - QTestState *to, - void *opaque) -{ - struct TestMigrateTLSPSKData *data = opaque; - - test_tls_psk_cleanup(data->pskfile); - if (data->pskfilealt) { - test_tls_psk_cleanup(data->pskfilealt); - } - rmdir(data->workdir); - if (data->workdiralt) { - rmdir(data->workdiralt); - } - - g_free(data->workdiralt); - g_free(data->pskfilealt); - g_free(data->workdir); - g_free(data->pskfile); - g_free(data); -} - -#ifdef CONFIG_TASN1 -typedef struct { - char *workdir; - char *keyfile; - char *cacert; - char *servercert; - char *serverkey; - char *clientcert; - char *clientkey; -} TestMigrateTLSX509Data; - -typedef struct { - bool verifyclient; - bool clientcert; - bool hostileclient; - bool authzclient; - const char *certhostname; - const char *certipaddr; -} TestMigrateTLSX509; - -static void * -migrate_hook_start_tls_x509_common(QTestState *from, - QTestState *to, - TestMigrateTLSX509 *args) -{ - TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); - - data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); - data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); - - data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir); - data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir); - data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir); - if (args->clientcert) { - data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir); - data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); - } - - g_mkdir_with_parents(data->workdir, 0700); - - test_tls_init(data->keyfile); -#ifndef _WIN32 - g_assert(link(data->keyfile, data->serverkey) == 0); -#else - g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); -#endif - if (args->clientcert) { -#ifndef _WIN32 - g_assert(link(data->keyfile, data->clientkey) == 0); -#else - g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); -#endif - } - - TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); - if (args->clientcert) { - TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq, - args->hostileclient ? - QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : - QCRYPTO_TLS_TEST_CLIENT_NAME, - data->clientcert); - test_tls_deinit_cert(&servercertreq); - } - - TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, - data->servercert, - args->certhostname, - args->certipaddr); - test_tls_deinit_cert(&clientcertreq); - test_tls_deinit_cert(&cacertreq); - - qtest_qmp_assert_success(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509client0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': true} }", - data->workdir); - migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0"); - if (args->certhostname) { - migrate_set_parameter_str(from, "tls-hostname", args->certhostname); - } - - qtest_qmp_assert_success(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509server0'," - " 'endpoint': 'server'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': %i} }", - data->workdir, args->verifyclient); - migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0"); - - if (args->authzclient) { - qtest_qmp_assert_success(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'authz-simple'," - " 'id': 'tlsauthz0'," - " 'identity': %s} }", - "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); - migrate_set_parameter_str(to, "tls-authz", "tlsauthz0"); - } - - return data; -} - -/* - * The normal case: match server's cert hostname against - * whatever host we were telling QEMU to connect to (if any) - */ -static void * -migrate_hook_start_tls_x509_default_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certipaddr = "127.0.0.1" - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -/* - * The unusual case: the server's cert is different from - * the address we're telling QEMU to connect to (if any), - * so we must give QEMU an explicit hostname to validate - */ -static void * -migrate_hook_start_tls_x509_override_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certhostname = "qemu.org", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -/* - * The unusual case: the server's cert is different from - * the address we're telling QEMU to connect to, and so we - * expect the client to reject the server - */ -static void * -migrate_hook_start_tls_x509_mismatch_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certipaddr = "10.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -static void * -migrate_hook_start_tls_x509_friendly_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .authzclient = true, - .certipaddr = "127.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -static void * -migrate_hook_start_tls_x509_hostile_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .hostileclient = true, - .authzclient = true, - .certipaddr = "127.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -/* - * The case with no client certificate presented, - * and no server verification - */ -static void * -migrate_hook_start_tls_x509_allow_anon_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .certipaddr = "127.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -/* - * The case with no client certificate presented, - * and server verification rejecting - */ -static void * -migrate_hook_start_tls_x509_reject_anon_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .certipaddr = "127.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -static void -migrate_hook_end_tls_x509(QTestState *from, - QTestState *to, - void *opaque) -{ - TestMigrateTLSX509Data *data = opaque; - - test_tls_cleanup(data->keyfile); - g_free(data->keyfile); - - unlink(data->cacert); - g_free(data->cacert); - unlink(data->servercert); - g_free(data->servercert); - unlink(data->serverkey); - g_free(data->serverkey); - - if (data->clientcert) { - unlink(data->clientcert); - g_free(data->clientcert); - } - if (data->clientkey) { - unlink(data->clientkey); - g_free(data->clientkey); - } - - rmdir(data->workdir); - g_free(data->workdir); - - g_free(data); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - static void test_postcopy(void) { MigrateCommon args = { }; @@ -413,29 +68,6 @@ static void test_postcopy_preempt(void) test_postcopy_common(&args); } -#ifdef CONFIG_GNUTLS -static void test_postcopy_tls_psk(void) -{ - MigrateCommon args = { - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_preempt_tls_psk(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_postcopy_common(&args); -} -#endif - static void test_postcopy_recovery(void) { MigrateCommon args = { }; @@ -461,18 +93,6 @@ static void test_postcopy_recovery_fail_reconnect(void) test_postcopy_recovery_common(&args); } -#ifdef CONFIG_GNUTLS -static void test_postcopy_recovery_tls_psk(void) -{ - MigrateCommon args = { - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_postcopy_recovery_common(&args); -} -#endif - static void test_postcopy_preempt_recovery(void) { MigrateCommon args = { @@ -482,21 +102,6 @@ static void test_postcopy_preempt_recovery(void) test_postcopy_recovery_common(&args); } -#ifdef CONFIG_GNUTLS -/* This contains preempt+recovery+tls test altogether */ -static void test_postcopy_preempt_all(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_postcopy_recovery_common(&args); -} - -#endif - static void test_baddest(void) { MigrateStart args = { @@ -631,53 +236,6 @@ static void test_precopy_unix_dirty_ring(void) test_precopy_common(&args); } -#ifdef CONFIG_GNUTLS -static void test_precopy_unix_tls_psk(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_precopy_unix_tls_x509_default_host(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_tls_x509_default_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_tls_x509_override_host(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_tls_x509_override_host, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - #if 0 /* Currently upset on aarch64 TCG */ static void test_ignore_shared(void) @@ -1086,125 +644,6 @@ static void test_precopy_tcp_switchover_ack(void) test_precopy_common(&args); } -#ifdef CONFIG_GNUTLS -static void test_precopy_tcp_tls_psk_match(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_psk_mismatch(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_psk_mismatch, - .end_hook = migrate_hook_end_tls_psk, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_precopy_tcp_tls_x509_default_host(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_default_host, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_override_host(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_override_host, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_mismatch_host(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_mismatch_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_friendly_client(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_friendly_client, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_hostile_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_hostile_client, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_allow_anon_client(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_allow_anon_client, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_reject_anon_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_reject_anon_client, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - #ifndef _WIN32 static void *migrate_hook_start_fd(QTestState *from, QTestState *to) @@ -1745,163 +1184,6 @@ static void test_multifd_tcp_uadk(void) } #endif -#ifdef CONFIG_GNUTLS -static void * -migrate_hook_start_multifd_tcp_tls_psk_match(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_psk_match(from, to); -} - -static void * -migrate_hook_start_multifd_tcp_tls_psk_mismatch(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_psk_mismatch(from, to); -} - -#ifdef CONFIG_TASN1 -static void * -migrate_hook_start_multifd_tls_x509_default_host(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_default_host(from, to); -} - -static void * -migrate_hook_start_multifd_tls_x509_override_host(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_override_host(from, to); -} - -static void * -migrate_hook_start_multifd_tls_x509_mismatch_host(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_mismatch_host(from, to); -} - -static void * -migrate_hook_start_multifd_tls_x509_allow_anon_client(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_allow_anon_client(from, to); -} - -static void * -migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_reject_anon_client(from, to); -} -#endif /* CONFIG_TASN1 */ - -static void test_multifd_tcp_tls_psk_match(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_psk_mismatch(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, - .end_hook = migrate_hook_end_tls_psk, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_multifd_tcp_tls_x509_default_host(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_default_host, - .end_hook = migrate_hook_end_tls_x509, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_override_host(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_override_host, - .end_hook = migrate_hook_end_tls_x509, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_mismatch_host(void) -{ - /* - * This has different behaviour to the non-multifd case. - * - * In non-multifd case when client aborts due to mismatched - * cert host, the server has already started trying to load - * migration state, and so it exits with I/O failure. - * - * In multifd case when client aborts due to mismatched - * cert host, the server is still waiting for the other - * multifd connections to arrive so hasn't started trying - * to load migration state, and thus just aborts the migration - * without exiting. - */ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_allow_anon_client(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, - .end_hook = migrate_hook_end_tls_x509, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_reject_anon_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - /* * This test does: * source target @@ -2412,6 +1694,8 @@ int main(int argc, char **argv) tmpfs = env->tmpfs; + migration_test_add_tls(env); + migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); @@ -2487,61 +1771,11 @@ int main(int argc, char **argv) test_multifd_file_mapped_ram_fdset_dio); #endif -#ifdef CONFIG_GNUTLS - migration_test_add("/migration/precopy/unix/tls/psk", - test_precopy_unix_tls_psk); - - if (env->has_uffd) { - /* - * NOTE: psk test is enough for postcopy, as other types of TLS - * channels are tested under precopy. Here what we want to test is the - * general postcopy path that has TLS channel enabled. - */ - migration_test_add("/migration/postcopy/tls/psk", - test_postcopy_tls_psk); - migration_test_add("/migration/postcopy/recovery/tls/psk", - test_postcopy_recovery_tls_psk); - migration_test_add("/migration/postcopy/preempt/tls/psk", - test_postcopy_preempt_tls_psk); - migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", - test_postcopy_preempt_all); - } -#ifdef CONFIG_TASN1 - migration_test_add("/migration/precopy/unix/tls/x509/default-host", - test_precopy_unix_tls_x509_default_host); - migration_test_add("/migration/precopy/unix/tls/x509/override-host", - test_precopy_unix_tls_x509_override_host); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); migration_test_add("/migration/precopy/tcp/plain/switchover-ack", test_precopy_tcp_switchover_ack); -#ifdef CONFIG_GNUTLS - migration_test_add("/migration/precopy/tcp/tls/psk/match", - test_precopy_tcp_tls_psk_match); - migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", - test_precopy_tcp_tls_psk_mismatch); -#ifdef CONFIG_TASN1 - migration_test_add("/migration/precopy/tcp/tls/x509/default-host", - test_precopy_tcp_tls_x509_default_host); - migration_test_add("/migration/precopy/tcp/tls/x509/override-host", - test_precopy_tcp_tls_x509_override_host); - migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host", - test_precopy_tcp_tls_x509_mismatch_host); - migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client", - test_precopy_tcp_tls_x509_friendly_client); - migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client", - test_precopy_tcp_tls_x509_hostile_client); - migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client", - test_precopy_tcp_tls_x509_allow_anon_client); - migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client", - test_precopy_tcp_tls_x509_reject_anon_client); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ #ifndef _WIN32 migration_test_add("/migration/precopy/fd/tcp", @@ -2600,24 +1834,6 @@ int main(int argc, char **argv) migration_test_add("/migration/multifd/tcp/plain/uadk", test_multifd_tcp_uadk); #endif -#ifdef CONFIG_GNUTLS - migration_test_add("/migration/multifd/tcp/tls/psk/match", - test_multifd_tcp_tls_psk_match); - migration_test_add("/migration/multifd/tcp/tls/psk/mismatch", - test_multifd_tcp_tls_psk_mismatch); -#ifdef CONFIG_TASN1 - migration_test_add("/migration/multifd/tcp/tls/x509/default-host", - test_multifd_tcp_tls_x509_default_host); - migration_test_add("/migration/multifd/tcp/tls/x509/override-host", - test_multifd_tcp_tls_x509_override_host); - migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host", - test_multifd_tcp_tls_x509_mismatch_host); - migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client", - test_multifd_tcp_tls_x509_allow_anon_client); - migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client", - test_multifd_tcp_tls_x509_reject_anon_client); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ if (g_str_equal(env->arch, "x86_64") && env->has_kvm && env->has_dirty_ring) { diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 6d5176b..1aad4da 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -215,4 +215,10 @@ void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, typedef struct QTestMigrationState QTestMigrationState; QTestMigrationState *get_src(void); +#ifdef CONFIG_GNUTLS +void migration_test_add_tls(MigrationTestEnv *env); +#else +static inline void migration_test_add_tls(MigrationTestEnv *env) {}; +#endif + #endif /* TEST_FRAMEWORK_H */ diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c new file mode 100644 index 0000000..5704a1f --- /dev/null +++ b/tests/qtest/migration/tls-tests.c @@ -0,0 +1,791 @@ +/* + * QTest testcases for TLS migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + +#include "tests/unit/crypto-tls-psk-helpers.h" +#ifdef CONFIG_TASN1 +# include "tests/unit/crypto-tls-x509-helpers.h" +#endif /* CONFIG_TASN1 */ + + +struct TestMigrateTLSPSKData { + char *workdir; + char *workdiralt; + char *pskfile; + char *pskfilealt; +}; + +static char *tmpfs; + +static void * +migrate_hook_start_tls_psk_common(QTestState *from, + QTestState *to, + bool mismatch) +{ + struct TestMigrateTLSPSKData *data = + g_new0(struct TestMigrateTLSPSKData, 1); + + data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); + data->pskfile = g_strdup_printf("%s/%s", data->workdir, + QCRYPTO_TLS_CREDS_PSKFILE); + g_mkdir_with_parents(data->workdir, 0700); + test_tls_psk_init(data->pskfile); + + if (mismatch) { + data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); + data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, + QCRYPTO_TLS_CREDS_PSKFILE); + g_mkdir_with_parents(data->workdiralt, 0700); + test_tls_psk_init_alt(data->pskfilealt); + } + + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'username': 'qemu'} }", + data->workdir); + + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'server'," + " 'dir': %s } }", + mismatch ? data->workdiralt : data->workdir); + + migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0"); + migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0"); + + return data; +} + +static void * +migrate_hook_start_tls_psk_match(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_tls_psk_common(from, to, false); +} + +static void * +migrate_hook_start_tls_psk_mismatch(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_tls_psk_common(from, to, true); +} + +static void +migrate_hook_end_tls_psk(QTestState *from, + QTestState *to, + void *opaque) +{ + struct TestMigrateTLSPSKData *data = opaque; + + test_tls_psk_cleanup(data->pskfile); + if (data->pskfilealt) { + test_tls_psk_cleanup(data->pskfilealt); + } + rmdir(data->workdir); + if (data->workdiralt) { + rmdir(data->workdiralt); + } + + g_free(data->workdiralt); + g_free(data->pskfilealt); + g_free(data->workdir); + g_free(data->pskfile); + g_free(data); +} + +#ifdef CONFIG_TASN1 +typedef struct { + char *workdir; + char *keyfile; + char *cacert; + char *servercert; + char *serverkey; + char *clientcert; + char *clientkey; +} TestMigrateTLSX509Data; + +typedef struct { + bool verifyclient; + bool clientcert; + bool hostileclient; + bool authzclient; + const char *certhostname; + const char *certipaddr; +} TestMigrateTLSX509; + +static void * +migrate_hook_start_tls_x509_common(QTestState *from, + QTestState *to, + TestMigrateTLSX509 *args) +{ + TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); + + data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); + data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); + + data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir); + data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir); + data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir); + if (args->clientcert) { + data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir); + data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); + } + + g_mkdir_with_parents(data->workdir, 0700); + + test_tls_init(data->keyfile); +#ifndef _WIN32 + g_assert(link(data->keyfile, data->serverkey) == 0); +#else + g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); +#endif + if (args->clientcert) { +#ifndef _WIN32 + g_assert(link(data->keyfile, data->clientkey) == 0); +#else + g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); +#endif + } + + TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); + if (args->clientcert) { + TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq, + args->hostileclient ? + QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : + QCRYPTO_TLS_TEST_CLIENT_NAME, + data->clientcert); + test_tls_deinit_cert(&servercertreq); + } + + TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, + data->servercert, + args->certhostname, + args->certipaddr); + test_tls_deinit_cert(&clientcertreq); + test_tls_deinit_cert(&cacertreq); + + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509client0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': true} }", + data->workdir); + migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0"); + if (args->certhostname) { + migrate_set_parameter_str(from, "tls-hostname", args->certhostname); + } + + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509server0'," + " 'endpoint': 'server'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': %i} }", + data->workdir, args->verifyclient); + migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0"); + + if (args->authzclient) { + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'authz-simple'," + " 'id': 'tlsauthz0'," + " 'identity': %s} }", + "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); + migrate_set_parameter_str(to, "tls-authz", "tlsauthz0"); + } + + return data; +} + +/* + * The normal case: match server's cert hostname against + * whatever host we were telling QEMU to connect to (if any) + */ +static void * +migrate_hook_start_tls_x509_default_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certipaddr = "127.0.0.1" + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The unusual case: the server's cert is different from + * the address we're telling QEMU to connect to (if any), + * so we must give QEMU an explicit hostname to validate + */ +static void * +migrate_hook_start_tls_x509_override_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certhostname = "qemu.org", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The unusual case: the server's cert is different from + * the address we're telling QEMU to connect to, and so we + * expect the client to reject the server + */ +static void * +migrate_hook_start_tls_x509_mismatch_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certipaddr = "10.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void * +migrate_hook_start_tls_x509_friendly_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .authzclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void * +migrate_hook_start_tls_x509_hostile_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .hostileclient = true, + .authzclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The case with no client certificate presented, + * and no server verification + */ +static void * +migrate_hook_start_tls_x509_allow_anon_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The case with no client certificate presented, + * and server verification rejecting + */ +static void * +migrate_hook_start_tls_x509_reject_anon_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void +migrate_hook_end_tls_x509(QTestState *from, + QTestState *to, + void *opaque) +{ + TestMigrateTLSX509Data *data = opaque; + + test_tls_cleanup(data->keyfile); + g_free(data->keyfile); + + unlink(data->cacert); + g_free(data->cacert); + unlink(data->servercert); + g_free(data->servercert); + unlink(data->serverkey); + g_free(data->serverkey); + + if (data->clientcert) { + unlink(data->clientcert); + g_free(data->clientcert); + } + if (data->clientkey) { + unlink(data->clientkey); + g_free(data->clientkey); + } + + rmdir(data->workdir); + g_free(data->workdir); + + g_free(data); +} +#endif /* CONFIG_TASN1 */ + +static void test_postcopy_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_preempt_tls_psk(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_recovery_common(&args); +} + +/* This contains preempt+recovery+tls test altogether */ +static void test_postcopy_preempt_all(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_precopy_unix_tls_psk(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_precopy_unix_tls_x509_default_host(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL_DEST_QUIT_ERR, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_tls_x509_override_host(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void test_precopy_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_psk_mismatch(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_precopy_tcp_tls_x509_default_host(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_override_host(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_mismatch_host(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL_DEST_QUIT_ERR, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_friendly_client(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_friendly_client, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_hostile_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_hostile_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_allow_anon_client(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_reject_anon_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void * +migrate_hook_start_multifd_tcp_tls_psk_match(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_match(from, to); +} + +static void * +migrate_hook_start_multifd_tcp_tls_psk_mismatch(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_mismatch(from, to); +} + +#ifdef CONFIG_TASN1 +static void * +migrate_hook_start_multifd_tls_x509_default_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_default_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_override_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_override_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_mismatch_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_mismatch_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_allow_anon_client(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_allow_anon_client(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_reject_anon_client(from, to); +} +#endif /* CONFIG_TASN1 */ + +static void test_multifd_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_psk_mismatch(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_multifd_tcp_tls_x509_default_host(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_override_host(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_mismatch_host(void) +{ + /* + * This has different behaviour to the non-multifd case. + * + * In non-multifd case when client aborts due to mismatched + * cert host, the server has already started trying to load + * migration state, and so it exits with I/O failure. + * + * In multifd case when client aborts due to mismatched + * cert host, the server is still waiting for the other + * multifd connections to arrive so hasn't started trying + * to load migration state, and thus just aborts the migration + * without exiting. + */ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_allow_anon_client(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_reject_anon_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +void migration_test_add_tls(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add("/migration/precopy/unix/tls/psk", + test_precopy_unix_tls_psk); + + if (env->has_uffd) { + /* + * NOTE: psk test is enough for postcopy, as other types of TLS + * channels are tested under precopy. Here what we want to test is the + * general postcopy path that has TLS channel enabled. + */ + migration_test_add("/migration/postcopy/tls/psk", + test_postcopy_tls_psk); + migration_test_add("/migration/postcopy/recovery/tls/psk", + test_postcopy_recovery_tls_psk); + migration_test_add("/migration/postcopy/preempt/tls/psk", + test_postcopy_preempt_tls_psk); + migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", + test_postcopy_preempt_all); + } +#ifdef CONFIG_TASN1 + migration_test_add("/migration/precopy/unix/tls/x509/default-host", + test_precopy_unix_tls_x509_default_host); + migration_test_add("/migration/precopy/unix/tls/x509/override-host", + test_precopy_unix_tls_x509_override_host); +#endif /* CONFIG_TASN1 */ + + migration_test_add("/migration/precopy/tcp/tls/psk/match", + test_precopy_tcp_tls_psk_match); + migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", + test_precopy_tcp_tls_psk_mismatch); +#ifdef CONFIG_TASN1 + migration_test_add("/migration/precopy/tcp/tls/x509/default-host", + test_precopy_tcp_tls_x509_default_host); + migration_test_add("/migration/precopy/tcp/tls/x509/override-host", + test_precopy_tcp_tls_x509_override_host); + migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host", + test_precopy_tcp_tls_x509_mismatch_host); + migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client", + test_precopy_tcp_tls_x509_friendly_client); + migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client", + test_precopy_tcp_tls_x509_hostile_client); + migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client", + test_precopy_tcp_tls_x509_allow_anon_client); + migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client", + test_precopy_tcp_tls_x509_reject_anon_client); +#endif /* CONFIG_TASN1 */ + + migration_test_add("/migration/multifd/tcp/tls/psk/match", + test_multifd_tcp_tls_psk_match); + migration_test_add("/migration/multifd/tcp/tls/psk/mismatch", + test_multifd_tcp_tls_psk_mismatch); +#ifdef CONFIG_TASN1 + migration_test_add("/migration/multifd/tcp/tls/x509/default-host", + test_multifd_tcp_tls_x509_default_host); + migration_test_add("/migration/multifd/tcp/tls/x509/override-host", + test_multifd_tcp_tls_x509_override_host); + migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host", + test_multifd_tcp_tls_x509_mismatch_host); + migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client", + test_multifd_tcp_tls_x509_allow_anon_client); + migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client", + test_multifd_tcp_tls_x509_reject_anon_client); +#endif /* CONFIG_TASN1 */ +} -- cgit v1.1 From 932f74f3fe6ef6b17a642f91bed1ac76350e363c Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:56 -0300 Subject: tests/qtest/migration: Split compression tests from migration-test.c Continuing the split of groups of tests from migration-test.c, split the compression tests into their own file. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 161 +------------------- tests/qtest/migration/compression-tests.c | 239 ++++++++++++++++++++++++++++++ tests/qtest/migration/framework.h | 1 + 4 files changed, 242 insertions(+), 160 deletions(-) create mode 100644 tests/qtest/migration/compression-tests.c (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 6dc4ba9..eeb8ecf 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -337,6 +337,7 @@ migration_files = [files( 'migration/framework.c', 'migration/migration-qmp.c', 'migration/migration-util.c', + 'migration/compression-tests.c', )] migration_tls_files = [] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 7395403..3528676 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -274,36 +274,6 @@ static void test_ignore_shared(void) } #endif -static void * -migrate_hook_start_xbzrle(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); - - migrate_set_capability(from, "xbzrle", true); - migrate_set_capability(to, "xbzrle", true); - - return NULL; -} - -static void test_precopy_unix_xbzrle(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_xbzrle, - .iterations = 2, - /* - * XBZRLE needs pages to be modified when doing the 2nd+ round - * iteration to have real data pushed to the stream. - */ - .live = true, - }; - - test_precopy_common(&args); -} - static void test_precopy_file(void) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, @@ -1016,61 +986,6 @@ migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, return NULL; } -static void * -migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, - QTestState *to) -{ - /* - * Overloading this test to also check that set_parameter does not error. - * This is also done in the tests for the other compression methods. - */ - migrate_set_parameter_int(from, "multifd-zlib-level", 2); - migrate_set_parameter_int(to, "multifd-zlib-level", 2); - - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); -} - -#ifdef CONFIG_ZSTD -static void * -migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "multifd-zstd-level", 2); - migrate_set_parameter_int(to, "multifd-zstd-level", 2); - - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd"); -} -#endif /* CONFIG_ZSTD */ - -#ifdef CONFIG_QATZIP -static void * -migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "multifd-qatzip-level", 2); - migrate_set_parameter_int(to, "multifd-qatzip-level", 2); - - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip"); -} -#endif - -#ifdef CONFIG_QPL -static void * -migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl"); -} -#endif /* CONFIG_QPL */ -#ifdef CONFIG_UADK -static void * -migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); -} -#endif /* CONFIG_UADK */ - static void test_multifd_tcp_uri_none(void) { MigrateCommon args = { @@ -1131,59 +1046,6 @@ static void test_multifd_tcp_channels_none(void) test_precopy_common(&args); } -static void test_multifd_tcp_zlib(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, - }; - test_precopy_common(&args); -} - -#ifdef CONFIG_ZSTD -static void test_multifd_tcp_zstd(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_QATZIP -static void test_multifd_tcp_qatzip(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_QPL -static void test_multifd_tcp_qpl(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_UADK -static void test_multifd_tcp_uadk(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, - }; - test_precopy_common(&args); -} -#endif - /* * This test does: * source target @@ -1695,6 +1557,7 @@ int main(int argc, char **argv) tmpfs = env->tmpfs; migration_test_add_tls(env); + migration_test_add_compression(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 @@ -1728,10 +1591,6 @@ int main(int argc, char **argv) migration_test_add("/migration/precopy/unix/plain", test_precopy_unix_plain); - if (g_test_slow()) { - migration_test_add("/migration/precopy/unix/xbzrle", - test_precopy_unix_xbzrle); - } migration_test_add("/migration/precopy/file", test_precopy_file); migration_test_add("/migration/precopy/file/offset", @@ -1816,24 +1675,6 @@ int main(int argc, char **argv) test_multifd_tcp_no_zero_page); migration_test_add("/migration/multifd/tcp/plain/cancel", test_multifd_tcp_cancel); - migration_test_add("/migration/multifd/tcp/plain/zlib", - test_multifd_tcp_zlib); -#ifdef CONFIG_ZSTD - migration_test_add("/migration/multifd/tcp/plain/zstd", - test_multifd_tcp_zstd); -#endif -#ifdef CONFIG_QATZIP - migration_test_add("/migration/multifd/tcp/plain/qatzip", - test_multifd_tcp_qatzip); -#endif -#ifdef CONFIG_QPL - migration_test_add("/migration/multifd/tcp/plain/qpl", - test_multifd_tcp_qpl); -#endif -#ifdef CONFIG_UADK - migration_test_add("/migration/multifd/tcp/plain/uadk", - test_multifd_tcp_uadk); -#endif if (g_str_equal(env->arch, "x86_64") && env->has_kvm && env->has_dirty_ring) { diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c new file mode 100644 index 0000000..6de87bc --- /dev/null +++ b/tests/qtest/migration/compression-tests.c @@ -0,0 +1,239 @@ +/* + * QTest testcases for migration compression + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qemu/module.h" + + +static char *tmpfs; + +#ifdef CONFIG_ZSTD +static void * +migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "multifd-zstd-level", 2); + migrate_set_parameter_int(to, "multifd-zstd-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd"); +} + +static void test_multifd_tcp_zstd(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_ZSTD */ + +#ifdef CONFIG_QATZIP +static void * +migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "multifd-qatzip-level", 2); + migrate_set_parameter_int(to, "multifd-qatzip-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip"); +} + +static void test_multifd_tcp_qatzip(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, + }; + test_precopy_common(&args); +} +#endif + +#ifdef CONFIG_QPL +static void * +migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl"); +} + +static void test_multifd_tcp_qpl(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_QPL */ + +#ifdef CONFIG_UADK +static void * +migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); +} + +static void * +migrate_hook_start_xbzrle(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); + + migrate_set_capability(from, "xbzrle", true); + migrate_set_capability(to, "xbzrle", true); + + return NULL; +} + +static void test_precopy_unix_xbzrle(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_xbzrle, + .iterations = 2, + /* + * XBZRLE needs pages to be modified when doing the 2nd+ round + * iteration to have real data pushed to the stream. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void * +migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, + QTestState *to) +{ + /* + * Overloading this test to also check that set_parameter does not error. + * This is also done in the tests for the other compression methods. + */ + migrate_set_parameter_int(from, "multifd-zlib-level", 2); + migrate_set_parameter_int(to, "multifd-zlib-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); +} + +static void test_multifd_tcp_zlib(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_uadk(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_UADK */ + + +static void * +migrate_hook_start_xbzrle(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); + + migrate_set_capability(from, "xbzrle", true); + migrate_set_capability(to, "xbzrle", true); + + return NULL; +} + +static void test_precopy_unix_xbzrle(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_xbzrle, + .iterations = 2, + /* + * XBZRLE needs pages to be modified when doing the 2nd+ round + * iteration to have real data pushed to the stream. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void * +migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, + QTestState *to) +{ + /* + * Overloading this test to also check that set_parameter does not error. + * This is also done in the tests for the other compression methods. + */ + migrate_set_parameter_int(from, "multifd-zlib-level", 2); + migrate_set_parameter_int(to, "multifd-zlib-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); +} + +static void test_multifd_tcp_zlib(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, + }; + test_precopy_common(&args); +} + +void migration_test_add_compression(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + +#ifdef CONFIG_ZSTD + migration_test_add("/migration/multifd/tcp/plain/zstd", + test_multifd_tcp_zstd); +#endif + +#ifdef CONFIG_QATZIP + migration_test_add("/migration/multifd/tcp/plain/qatzip", + test_multifd_tcp_qatzip); +#endif + +#ifdef CONFIG_QPL + migration_test_add("/migration/multifd/tcp/plain/qpl", + test_multifd_tcp_qpl); +#endif + +#ifdef CONFIG_UADK + migration_test_add("/migration/multifd/tcp/plain/uadk", + test_multifd_tcp_uadk); +#endif + + if (g_test_slow()) { + migration_test_add("/migration/precopy/unix/xbzrle", + test_precopy_unix_xbzrle); + } + + migration_test_add("/migration/multifd/tcp/plain/zlib", + test_multifd_tcp_zlib); +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 1aad4da..bc978d4 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -220,5 +220,6 @@ void migration_test_add_tls(MigrationTestEnv *env); #else static inline void migration_test_add_tls(MigrationTestEnv *env) {}; #endif +void migration_test_add_compression(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ -- cgit v1.1 From 5e87cf322f98e4b791fdcbe1d35eafd14036dba9 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:57 -0300 Subject: tests/qtest/migration: Split postcopy tests Split the next group of tests from migration-test.c, the postcopy tests. This is another well-defined group of tests and postcopy is a unique enough feature that it deserves it's own file. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 78 +----------------------- tests/qtest/migration/framework.h | 1 + tests/qtest/migration/postcopy-tests.c | 106 +++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 77 deletions(-) create mode 100644 tests/qtest/migration/postcopy-tests.c (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index eeb8ecf..ff33034 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -338,6 +338,7 @@ migration_files = [files( 'migration/migration-qmp.c', 'migration/migration-util.c', 'migration/compression-tests.c', + 'migration/postcopy-tests.c', )] migration_tls_files = [] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 3528676..fa75902 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -43,65 +43,6 @@ static char *tmpfs; -static void test_postcopy(void) -{ - MigrateCommon args = { }; - - test_postcopy_common(&args); -} - -static void test_postcopy_suspend(void) -{ - MigrateCommon args = { - .start.suspend_me = true, - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_preempt(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_recovery(void) -{ - MigrateCommon args = { }; - - test_postcopy_recovery_common(&args); -} - -static void test_postcopy_recovery_fail_handshake(void) -{ - MigrateCommon args = { - .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY, - }; - - test_postcopy_recovery_common(&args); -} - -static void test_postcopy_recovery_fail_reconnect(void) -{ - MigrateCommon args = { - .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH, - }; - - test_postcopy_recovery_common(&args); -} - -static void test_postcopy_preempt_recovery(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - }; - - test_postcopy_recovery_common(&args); -} - static void test_baddest(void) { MigrateStart args = { @@ -1558,6 +1499,7 @@ int main(int argc, char **argv) migration_test_add_tls(env); migration_test_add_compression(env); + migration_test_add_postcopy(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 @@ -1571,24 +1513,6 @@ int main(int argc, char **argv) test_precopy_unix_suspend_notlive); } - if (env->has_uffd) { - migration_test_add("/migration/postcopy/plain", test_postcopy); - migration_test_add("/migration/postcopy/recovery/plain", - test_postcopy_recovery); - migration_test_add("/migration/postcopy/preempt/plain", - test_postcopy_preempt); - migration_test_add("/migration/postcopy/preempt/recovery/plain", - test_postcopy_preempt_recovery); - migration_test_add("/migration/postcopy/recovery/double-failures/handshake", - test_postcopy_recovery_fail_handshake); - migration_test_add("/migration/postcopy/recovery/double-failures/reconnect", - test_postcopy_recovery_fail_reconnect); - if (env->is_x86) { - migration_test_add("/migration/postcopy/suspend", - test_postcopy_suspend); - } - } - migration_test_add("/migration/precopy/unix/plain", test_precopy_unix_plain); migration_test_add("/migration/precopy/file", diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index bc978d4..1ac3aea 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -221,5 +221,6 @@ void migration_test_add_tls(MigrationTestEnv *env); static inline void migration_test_add_tls(MigrationTestEnv *env) {}; #endif void migration_test_add_compression(MigrationTestEnv *env); +void migration_test_add_postcopy(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c new file mode 100644 index 0000000..daf7449 --- /dev/null +++ b/tests/qtest/migration/postcopy-tests.c @@ -0,0 +1,106 @@ +/* + * QTest testcases for postcopy migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-util.h" +#include "qapi/qmp/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + +static void test_postcopy(void) +{ + MigrateCommon args = { }; + + test_postcopy_common(&args); +} + +static void test_postcopy_suspend(void) +{ + MigrateCommon args = { + .start.suspend_me = true, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_preempt(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_recovery(void) +{ + MigrateCommon args = { }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_recovery_fail_handshake(void) +{ + MigrateCommon args = { + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_recovery_fail_reconnect(void) +{ + MigrateCommon args = { + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_preempt_recovery(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + }; + + test_postcopy_recovery_common(&args); +} + +void migration_test_add_postcopy(MigrationTestEnv *env) +{ + if (env->has_uffd) { + migration_test_add("/migration/postcopy/plain", test_postcopy); + migration_test_add("/migration/postcopy/recovery/plain", + test_postcopy_recovery); + migration_test_add("/migration/postcopy/preempt/plain", + test_postcopy_preempt); + migration_test_add("/migration/postcopy/preempt/recovery/plain", + test_postcopy_preempt_recovery); + + migration_test_add( + "/migration/postcopy/recovery/double-failures/handshake", + test_postcopy_recovery_fail_handshake); + + migration_test_add( + "/migration/postcopy/recovery/double-failures/reconnect", + test_postcopy_recovery_fail_reconnect); + + if (env->is_x86) { + migration_test_add("/migration/postcopy/suspend", + test_postcopy_suspend); + } + } +} -- cgit v1.1 From d8057eb3059e06e451fe3be71786525acc588ef3 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:58 -0300 Subject: tests/qtest/migration: Split file tests Split the file tests from migration-test.c. These are being moved to their own file due to being special enough compared with the regular stream migration. There is also the entire mapped-ram feature which depends on file migration. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 311 +--------------------------------- tests/qtest/migration/file-tests.c | 338 +++++++++++++++++++++++++++++++++++++ tests/qtest/migration/framework.h | 1 + 4 files changed, 341 insertions(+), 310 deletions(-) create mode 100644 tests/qtest/migration/file-tests.c (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index ff33034..ede5a9f 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -338,6 +338,7 @@ migration_files = [files( 'migration/migration-qmp.c', 'migration/migration-util.c', 'migration/compression-tests.c', + 'migration/file-tests.c', 'migration/postcopy-tests.c', )] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index fa75902..a2ce1c6 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -215,93 +215,6 @@ static void test_ignore_shared(void) } #endif -static void test_precopy_file(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - }; - - test_file_common(&args, true); -} - -#ifndef _WIN32 -static void fdset_add_fds(QTestState *qts, const char *file, int flags, - int num_fds, bool direct_io) -{ - for (int i = 0; i < num_fds; i++) { - int fd; - -#ifdef O_DIRECT - /* only secondary channels can use direct-io */ - if (direct_io && i != 0) { - flags |= O_DIRECT; - } -#endif - - fd = open(file, flags, 0660); - assert(fd != -1); - - qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', " - "'arguments': {'fdset-id': 1}}"); - close(fd); - } -} - -static void *migrate_hook_start_file_offset_fdset(QTestState *from, - QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - - fdset_add_fds(from, file, O_WRONLY, 1, false); - fdset_add_fds(to, file, O_RDONLY, 1, false); - - return NULL; -} - -static void test_precopy_file_offset_fdset(void) -{ - g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_file_offset_fdset, - }; - - test_file_common(&args, false); -} -#endif - -static void test_precopy_file_offset(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, - FILE_TEST_FILENAME, - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - }; - - test_file_common(&args, false); -} - -static void test_precopy_file_offset_bad(void) -{ - /* using a value not supported by qemu_strtosz() */ - g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M", - tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .result = MIG_TEST_QMP_ERROR, - }; - - test_file_common(&args, false); -} - static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) { migrate_set_parameter_str(from, "mode", "cpr-reboot"); @@ -313,14 +226,6 @@ static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) return NULL; } -static void *migrate_hook_start_mapped_ram(QTestState *from, QTestState *to) -{ - migrate_set_capability(from, "mapped-ram", true); - migrate_set_capability(to, "mapped-ram", true); - - return NULL; -} - static void test_mode_reboot(void) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, @@ -335,190 +240,6 @@ static void test_mode_reboot(void) test_file_common(&args, true); } -static void test_precopy_file_mapped_ram_live(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_mapped_ram, - }; - - test_file_common(&args, false); -} - -static void test_precopy_file_mapped_ram(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_mapped_ram, - }; - - test_file_common(&args, true); -} - -static void *migrate_hook_start_multifd_mapped_ram(QTestState *from, - QTestState *to) -{ - migrate_hook_start_mapped_ram(from, to); - - migrate_set_parameter_int(from, "multifd-channels", 4); - migrate_set_parameter_int(to, "multifd-channels", 4); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - return NULL; -} - -static void test_multifd_file_mapped_ram_live(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram, - }; - - test_file_common(&args, false); -} - -static void test_multifd_file_mapped_ram(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram, - }; - - test_file_common(&args, true); -} - -static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, - QTestState *to) -{ - migrate_hook_start_multifd_mapped_ram(from, to); - - migrate_set_parameter_bool(from, "direct-io", true); - migrate_set_parameter_bool(to, "direct-io", true); - - return NULL; -} - -static void test_multifd_file_mapped_ram_dio(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram_dio, - }; - - if (!probe_o_direct_support(tmpfs)) { - g_test_skip("Filesystem does not support O_DIRECT"); - return; - } - - test_file_common(&args, true); -} - -#ifndef _WIN32 -static void migrate_hook_end_multifd_mapped_ram_fdset(QTestState *from, - QTestState *to, - void *opaque) -{ - QDict *resp; - QList *fdsets; - - /* - * Remove the fdsets after migration, otherwise a second migration - * would fail due fdset reuse. - */ - qtest_qmp_assert_success(from, "{'execute': 'remove-fd', " - "'arguments': { 'fdset-id': 1}}"); - - /* - * Make sure no fdsets are left after migration, otherwise a - * second migration would fail due fdset reuse. - */ - resp = qtest_qmp(from, "{'execute': 'query-fdsets', " - "'arguments': {}}"); - g_assert(qdict_haskey(resp, "return")); - fdsets = qdict_get_qlist(resp, "return"); - g_assert(fdsets && qlist_empty(fdsets)); - qobject_unref(resp); -} - -static void *migrate_hook_start_multifd_mapped_ram_fdset_dio(QTestState *from, - QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - - fdset_add_fds(from, file, O_WRONLY, 2, true); - fdset_add_fds(to, file, O_RDONLY, 2, true); - - migrate_hook_start_multifd_mapped_ram(from, to); - migrate_set_parameter_bool(from, "direct-io", true); - migrate_set_parameter_bool(to, "direct-io", true); - - return NULL; -} - -static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from, - QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - - fdset_add_fds(from, file, O_WRONLY, 2, false); - fdset_add_fds(to, file, O_RDONLY, 2, false); - - migrate_hook_start_multifd_mapped_ram(from, to); - - return NULL; -} - -static void test_multifd_file_mapped_ram_fdset(void) -{ - g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram_fdset, - .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, - }; - - test_file_common(&args, true); -} - -static void test_multifd_file_mapped_ram_fdset_dio(void) -{ - g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio, - .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, - }; - - if (!probe_o_direct_support(tmpfs)) { - g_test_skip("Filesystem does not support O_DIRECT"); - return; - } - - test_file_common(&args, true); -} -#endif /* !_WIN32 */ - static void test_precopy_tcp_plain(void) { MigrateCommon args = { @@ -1500,6 +1221,7 @@ int main(int argc, char **argv) migration_test_add_tls(env); migration_test_add_compression(env); migration_test_add_postcopy(env); + migration_test_add_file(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 @@ -1515,17 +1237,6 @@ int main(int argc, char **argv) migration_test_add("/migration/precopy/unix/plain", test_precopy_unix_plain); - migration_test_add("/migration/precopy/file", - test_precopy_file); - migration_test_add("/migration/precopy/file/offset", - test_precopy_file_offset); -#ifndef _WIN32 - migration_test_add("/migration/precopy/file/offset/fdset", - test_precopy_file_offset_fdset); -#endif - migration_test_add("/migration/precopy/file/offset/bad", - test_precopy_file_offset_bad); - /* * Our CI system has problems with shared memory. * Don't run this test until we find a workaround. @@ -1534,26 +1245,6 @@ int main(int argc, char **argv) migration_test_add("/migration/mode/reboot", test_mode_reboot); } - migration_test_add("/migration/precopy/file/mapped-ram", - test_precopy_file_mapped_ram); - migration_test_add("/migration/precopy/file/mapped-ram/live", - test_precopy_file_mapped_ram_live); - - migration_test_add("/migration/multifd/file/mapped-ram", - test_multifd_file_mapped_ram); - migration_test_add("/migration/multifd/file/mapped-ram/live", - test_multifd_file_mapped_ram_live); - - migration_test_add("/migration/multifd/file/mapped-ram/dio", - test_multifd_file_mapped_ram_dio); - -#ifndef _WIN32 - migration_test_add("/migration/multifd/file/mapped-ram/fdset", - test_multifd_file_mapped_ram_fdset); - migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio", - test_multifd_file_mapped_ram_fdset_dio); -#endif - migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); migration_test_add("/migration/precopy/tcp/plain/switchover-ack", diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c new file mode 100644 index 0000000..84225c8 --- /dev/null +++ b/tests/qtest/migration/file-tests.c @@ -0,0 +1,338 @@ +/* + * QTest testcases for migration to file + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qapi/qmp/qlist.h" + + +static char *tmpfs; + +static void test_precopy_file(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + }; + + test_file_common(&args, true); +} + +#ifndef _WIN32 +static void fdset_add_fds(QTestState *qts, const char *file, int flags, + int num_fds, bool direct_io) +{ + for (int i = 0; i < num_fds; i++) { + int fd; + +#ifdef O_DIRECT + /* only secondary channels can use direct-io */ + if (direct_io && i != 0) { + flags |= O_DIRECT; + } +#endif + + fd = open(file, flags, 0660); + assert(fd != -1); + + qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', " + "'arguments': {'fdset-id': 1}}"); + close(fd); + } +} + +static void *migrate_hook_start_file_offset_fdset(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 1, false); + fdset_add_fds(to, file, O_RDONLY, 1, false); + + return NULL; +} + +static void test_precopy_file_offset_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_file_offset_fdset, + }; + + test_file_common(&args, false); +} +#endif + +static void test_precopy_file_offset(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, + FILE_TEST_FILENAME, + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + }; + + test_file_common(&args, false); +} + +static void test_precopy_file_offset_bad(void) +{ + /* using a value not supported by qemu_strtosz() */ + g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M", + tmpfs, FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .result = MIG_TEST_QMP_ERROR, + }; + + test_file_common(&args, false); +} + +static void *migrate_hook_start_mapped_ram(QTestState *from, + QTestState *to) +{ + migrate_set_capability(from, "mapped-ram", true); + migrate_set_capability(to, "mapped-ram", true); + + return NULL; +} + +static void test_precopy_file_mapped_ram_live(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_mapped_ram, + }; + + test_file_common(&args, false); +} + +static void test_precopy_file_mapped_ram(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_mapped_ram, + }; + + test_file_common(&args, true); +} + +static void *migrate_hook_start_multifd_mapped_ram(QTestState *from, + QTestState *to) +{ + migrate_hook_start_mapped_ram(from, to); + + migrate_set_parameter_int(from, "multifd-channels", 4); + migrate_set_parameter_int(to, "multifd-channels", 4); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + return NULL; +} + +static void test_multifd_file_mapped_ram_live(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram, + }; + + test_file_common(&args, false); +} + +static void test_multifd_file_mapped_ram(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram, + }; + + test_file_common(&args, true); +} + +static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, + QTestState *to) +{ + migrate_hook_start_multifd_mapped_ram(from, to); + + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void test_multifd_file_mapped_ram_dio(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram_dio, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} + +#ifndef _WIN32 +static void migrate_hook_end_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to, + void *opaque) +{ + QDict *resp; + QList *fdsets; + + /* + * Remove the fdsets after migration, otherwise a second migration + * would fail due fdset reuse. + */ + qtest_qmp_assert_success(from, "{'execute': 'remove-fd', " + "'arguments': { 'fdset-id': 1}}"); + + /* + * Make sure no fdsets are left after migration, otherwise a + * second migration would fail due fdset reuse. + */ + resp = qtest_qmp(from, "{'execute': 'query-fdsets', " + "'arguments': {}}"); + g_assert(qdict_haskey(resp, "return")); + fdsets = qdict_get_qlist(resp, "return"); + g_assert(fdsets && qlist_empty(fdsets)); + qobject_unref(resp); +} + +static void *migrate_hook_start_multifd_mapped_ram_fdset_dio(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, true); + fdset_add_fds(to, file, O_RDONLY, 2, true); + + migrate_hook_start_multifd_mapped_ram(from, to); + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, false); + fdset_add_fds(to, file, O_RDONLY, 2, false); + + migrate_hook_start_multifd_mapped_ram(from, to); + + return NULL; +} + +static void test_multifd_file_mapped_ram_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + }; + + test_file_common(&args, true); +} + +static void test_multifd_file_mapped_ram_fdset_dio(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} +#endif /* !_WIN32 */ + +void migration_test_add_file(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add("/migration/precopy/file", + test_precopy_file); + + migration_test_add("/migration/precopy/file/offset", + test_precopy_file_offset); +#ifndef _WIN32 + migration_test_add("/migration/precopy/file/offset/fdset", + test_precopy_file_offset_fdset); +#endif + migration_test_add("/migration/precopy/file/offset/bad", + test_precopy_file_offset_bad); + + migration_test_add("/migration/precopy/file/mapped-ram", + test_precopy_file_mapped_ram); + migration_test_add("/migration/precopy/file/mapped-ram/live", + test_precopy_file_mapped_ram_live); + + migration_test_add("/migration/multifd/file/mapped-ram", + test_multifd_file_mapped_ram); + migration_test_add("/migration/multifd/file/mapped-ram/live", + test_multifd_file_mapped_ram_live); + + migration_test_add("/migration/multifd/file/mapped-ram/dio", + test_multifd_file_mapped_ram_dio); + +#ifndef _WIN32 + migration_test_add("/migration/multifd/file/mapped-ram/fdset", + test_multifd_file_mapped_ram_fdset); + migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio", + test_multifd_file_mapped_ram_fdset_dio); +#endif +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 1ac3aea..6be434c 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -222,5 +222,6 @@ static inline void migration_test_add_tls(MigrationTestEnv *env) {}; #endif void migration_test_add_compression(MigrationTestEnv *env); void migration_test_add_postcopy(MigrationTestEnv *env); +void migration_test_add_file(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ -- cgit v1.1 From 8a645544565c14cf1ac1b57ad2ef4f77a9446b7d Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:59 -0300 Subject: tests/qtest/migration: Split precopy tests Split the precopy tests from migration-test.c. This is the largest group of tests and the more difficult one to break into smaller groups, so move all of it. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 970 +------------------------------ tests/qtest/migration/framework.h | 1 + tests/qtest/migration/precopy-tests.c | 1007 +++++++++++++++++++++++++++++++++ 4 files changed, 1011 insertions(+), 968 deletions(-) create mode 100644 tests/qtest/migration/precopy-tests.c (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index ede5a9f..fc38c80 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -339,6 +339,7 @@ migration_files = [files( 'migration/migration-util.c', 'migration/compression-tests.c', 'migration/file-tests.c', + 'migration/precopy-tests.c', 'migration/postcopy-tests.c', )] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index a2ce1c6..76ba820 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -27,12 +27,6 @@ #include "migration/migration-qmp.h" #include "migration/migration-util.h" -/* - * Dirtylimit stop working if dirty page rate error - * value less than DIRTYLIMIT_TOLERANCE_RANGE - */ -#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ - #define ANALYZE_SCRIPT "scripts/analyze-migration.py" #if defined(__linux__) @@ -113,70 +107,6 @@ static void test_analyze_script(void) } #endif -static void test_precopy_unix_plain(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - /* - * The simplest use case of precopy, covering smoke tests of - * get-dirty-log dirty tracking. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_suspend_live(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - /* - * despite being live, the test is fast because the src - * suspends immediately. - */ - .live = true, - .start.suspend_me = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_suspend_notlive(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - .start.suspend_me = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_dirty_ring(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - /* - * Besides the precopy/unix basic test, cover dirty ring interface - * rather than get-dirty-log. - */ - .live = true, - }; - - test_precopy_common(&args); -} - #if 0 /* Currently upset on aarch64 TCG */ static void test_ignore_shared(void) @@ -240,149 +170,6 @@ static void test_mode_reboot(void) test_file_common(&args, true); } -static void test_precopy_tcp_plain(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - }; - - test_precopy_common(&args); -} - -static void *migrate_hook_start_switchover_ack(QTestState *from, QTestState *to) -{ - - migrate_set_capability(from, "return-path", true); - migrate_set_capability(to, "return-path", true); - - migrate_set_capability(from, "switchover-ack", true); - migrate_set_capability(to, "switchover-ack", true); - - return NULL; -} - -static void test_precopy_tcp_switchover_ack(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_switchover_ack, - /* - * Source VM must be running in order to consider the switchover ACK - * when deciding to do switchover or not. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -#ifndef _WIN32 -static void *migrate_hook_start_fd(QTestState *from, - QTestState *to) -{ - int ret; - int pair[2]; - - /* Create two connected sockets for migration */ - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); - g_assert_cmpint(ret, ==, 0); - - /* Send the 1st socket to the target */ - qtest_qmp_fds_assert_success(to, &pair[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - close(pair[0]); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "fd:fd-mig", "{}"); - - /* Send the 2nd socket to the target */ - qtest_qmp_fds_assert_success(from, &pair[1], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - close(pair[1]); - - return NULL; -} - -static void migrate_hook_end_fd(QTestState *from, - QTestState *to, - void *opaque) -{ - QDict *rsp; - const char *error_desc; - - /* Test closing fds */ - /* We assume, that QEMU removes named fd from its list, - * so this should fail */ - rsp = qtest_qmp(from, - "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - g_assert_true(qdict_haskey(rsp, "error")); - error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); - g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); - qobject_unref(rsp); - - rsp = qtest_qmp(to, - "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - g_assert_true(qdict_haskey(rsp, "error")); - error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); - g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); - qobject_unref(rsp); -} - -static void test_precopy_fd_socket(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .connect_uri = "fd:fd-mig", - .start_hook = migrate_hook_start_fd, - .end_hook = migrate_hook_end_fd, - }; - test_precopy_common(&args); -} - -static void *migrate_hook_start_precopy_fd_file(QTestState *from, QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - int src_flags = O_CREAT | O_RDWR; - int dst_flags = O_CREAT | O_RDWR; - int fds[2]; - - fds[0] = open(file, src_flags, 0660); - assert(fds[0] != -1); - - fds[1] = open(file, dst_flags, 0660); - assert(fds[1] != -1); - - - qtest_qmp_fds_assert_success(to, &fds[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - - qtest_qmp_fds_assert_success(from, &fds[1], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - - close(fds[0]); - close(fds[1]); - - return NULL; -} - -static void test_precopy_fd_file(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .connect_uri = "fd:fd-mig", - .start_hook = migrate_hook_start_precopy_fd_file, - .end_hook = migrate_hook_end_fd, - }; - test_file_common(&args, true); -} -#endif /* _WIN32 */ - static void do_test_validate_uuid(MigrateStart *args, bool should_fail) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); @@ -505,708 +292,6 @@ static void test_validate_uri_channels_none_set(void) do_test_validate_uri_channel(&args); } -/* - * The way auto_converge works, we need to do too many passes to - * run this test. Auto_converge logic is only run once every - * three iterations, so: - * - * - 3 iterations without auto_converge enabled - * - 3 iterations with pct = 5 - * - 3 iterations with pct = 30 - * - 3 iterations with pct = 55 - * - 3 iterations with pct = 80 - * - 3 iterations with pct = 95 (max(95, 80 + 25)) - * - * To make things even worse, we need to run the initial stage at - * 3MB/s so we enter autoconverge even when host is (over)loaded. - */ -static void test_auto_converge(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateStart args = {}; - QTestState *from, *to; - int64_t percentage; - - /* - * We want the test to be stable and as fast as possible. - * E.g., with 1Gb/s bandwidth migration may pass without throttling, - * so we need to decrease a bandwidth. - */ - const int64_t init_pct = 5, inc_pct = 25, max_pct = 95; - uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; - int max_try_count, hit = 0; - - if (migrate_start(&from, &to, uri, &args)) { - return; - } - - migrate_set_capability(from, "auto-converge", true); - migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); - migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); - migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); - - /* - * Set the initial parameters so that the migration could not converge - * without throttling. - */ - migrate_ensure_non_converge(from); - - /* To check remaining size after precopy */ - migrate_set_capability(from, "pause-before-switchover", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, uri, NULL, "{}"); - - /* Wait for throttling begins */ - percentage = 0; - do { - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - if (percentage != 0) { - break; - } - usleep(20); - g_assert_false(get_src()->stop_seen); - } while (true); - /* The first percentage of throttling should be at least init_pct */ - g_assert_cmpint(percentage, >=, init_pct); - - /* - * End the loop when the dirty sync count greater than 1. - */ - while ((dirty_sync_cnt = get_migration_pass(from)) < 2) { - usleep(1000 * 1000); - } - - prev_dirty_sync_cnt = dirty_sync_cnt; - - /* - * The RAMBlock dirty sync count must changes in 5 seconds, here we set - * the timeout to 10 seconds to ensure it changes. - * - * Note that migrate_ensure_non_converge set the max-bandwidth to 3MB/s, - * while the qtest mem is >= 100MB, one iteration takes at least 33s (100/3) - * to complete; this ensures that the RAMBlock dirty sync occurs. - */ - max_try_count = 10; - while (--max_try_count) { - dirty_sync_cnt = get_migration_pass(from); - if (dirty_sync_cnt != prev_dirty_sync_cnt) { - hit = 1; - break; - } - prev_dirty_sync_cnt = dirty_sync_cnt; - sleep(1); - } - g_assert_cmpint(hit, ==, 1); - - /* Now, when we tested that throttling works, let it converge */ - migrate_ensure_converge(from); - - /* - * Wait for pre-switchover status to check last throttle percentage - * and remaining. These values will be zeroed later - */ - wait_for_migration_status(from, "pre-switchover", NULL); - - /* The final percentage of throttling shouldn't be greater than max_pct */ - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - g_assert_cmpint(percentage, <=, max_pct); - migrate_continue(from, "pre-switchover"); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - migrate_end(from, to, true); -} - -static void * -migrate_hook_start_precopy_tcp_multifd(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); -} - -static void * -migrate_hook_start_precopy_tcp_multifd_zero_page_legacy(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - migrate_set_parameter_str(from, "zero-page-detection", "legacy"); - return NULL; -} - -static void * -migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - migrate_set_parameter_str(from, "zero-page-detection", "none"); - return NULL; -} - -static void test_multifd_tcp_uri_none(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_zero_page_legacy(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_no_zero_page(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_channels_none(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd, - .live = true, - .connect_channels = ("[ { 'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ]"), - }; - test_precopy_common(&args); -} - -/* - * This test does: - * source target - * migrate_incoming - * migrate - * migrate_cancel - * launch another target - * migrate - * - * And see that it works - */ -static void test_multifd_tcp_cancel(void) -{ - MigrateStart args = { - .hide_stderr = true, - }; - QTestState *from, *to, *to2; - - if (migrate_start(&from, &to, "defer", &args)) { - return; - } - - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); - - migrate_set_parameter_int(from, "multifd-channels", 16); - migrate_set_parameter_int(to, "multifd-channels", 16); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, NULL, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - migrate_cancel(from); - - /* Make sure QEMU process "to" exited */ - qtest_set_expected_status(to, EXIT_FAILURE); - qtest_wait_qemu(to); - qtest_quit(to); - - /* - * Ensure the source QEMU finishes its cancellation process before we - * proceed with the setup of the next migration. The migrate_start() - * function and others might want to interact with the source in a way that - * is not possible while the migration is not canceled properly. For - * example, setting migration capabilities when the migration is still - * running leads to an error. - */ - wait_for_migration_status(from, "cancelled", NULL); - - args = (MigrateStart){ - .only_target = true, - }; - - if (migrate_start(&from, &to2, "defer", &args)) { - return; - } - - migrate_set_parameter_int(to2, "multifd-channels", 16); - - migrate_set_capability(to2, "multifd", true); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); - - migrate_ensure_non_converge(from); - - migrate_qmp(from, to2, NULL, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to2); - - migrate_ensure_converge(from); - - wait_for_stop(from, get_src()); - qtest_qmp_eventwait(to2, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - migrate_end(from, to2, true); -} - -static void calc_dirty_rate(QTestState *who, uint64_t calc_time) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'calc-dirty-rate'," - "'arguments': { " - "'calc-time': %" PRIu64 "," - "'mode': 'dirty-ring' }}", - calc_time); -} - -static QDict *query_dirty_rate(QTestState *who) -{ - return qtest_qmp_assert_success_ref(who, - "{ 'execute': 'query-dirty-rate' }"); -} - -static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'set-vcpu-dirty-limit'," - "'arguments': { " - "'dirty-rate': %" PRIu64 " } }", - dirtyrate); -} - -static void cancel_vcpu_dirty_limit(QTestState *who) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'cancel-vcpu-dirty-limit' }"); -} - -static QDict *query_vcpu_dirty_limit(QTestState *who) -{ - QDict *rsp; - - rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); - g_assert(!qdict_haskey(rsp, "error")); - g_assert(qdict_haskey(rsp, "return")); - - return rsp; -} - -static bool calc_dirtyrate_ready(QTestState *who) -{ - QDict *rsp_return; - const char *status; - bool ready; - - rsp_return = query_dirty_rate(who); - g_assert(rsp_return); - - status = qdict_get_str(rsp_return, "status"); - g_assert(status); - ready = g_strcmp0(status, "measuring"); - qobject_unref(rsp_return); - - return ready; -} - -static void wait_for_calc_dirtyrate_complete(QTestState *who, - int64_t time_s) -{ - int max_try_count = 10000; - usleep(time_s * 1000000); - - while (!calc_dirtyrate_ready(who) && max_try_count--) { - usleep(1000); - } - - /* - * Set the timeout with 10 s(max_try_count * 1000us), - * if dirtyrate measurement not complete, fail test. - */ - g_assert_cmpint(max_try_count, !=, 0); -} - -static int64_t get_dirty_rate(QTestState *who) -{ - QDict *rsp_return; - const char *status; - QList *rates; - const QListEntry *entry; - QDict *rate; - int64_t dirtyrate; - - rsp_return = query_dirty_rate(who); - g_assert(rsp_return); - - status = qdict_get_str(rsp_return, "status"); - g_assert(status); - g_assert_cmpstr(status, ==, "measured"); - - rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); - g_assert(rates && !qlist_empty(rates)); - - entry = qlist_first(rates); - g_assert(entry); - - rate = qobject_to(QDict, qlist_entry_obj(entry)); - g_assert(rate); - - dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); - - qobject_unref(rsp_return); - return dirtyrate; -} - -static int64_t get_limit_rate(QTestState *who) -{ - QDict *rsp_return; - QList *rates; - const QListEntry *entry; - QDict *rate; - int64_t dirtyrate; - - rsp_return = query_vcpu_dirty_limit(who); - g_assert(rsp_return); - - rates = qdict_get_qlist(rsp_return, "return"); - g_assert(rates && !qlist_empty(rates)); - - entry = qlist_first(rates); - g_assert(entry); - - rate = qobject_to(QDict, qlist_entry_obj(entry)); - g_assert(rate); - - dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); - - qobject_unref(rsp_return); - return dirtyrate; -} - -static QTestState *dirtylimit_start_vm(void) -{ - QTestState *vm = NULL; - g_autofree gchar *cmd = NULL; - const char *bootpath; - - bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); - cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " - "-name dirtylimit-test,debug-threads=on " - "-m 150M -smp 1 " - "-serial file:%s/vm_serial " - "-drive file=%s,format=raw ", - tmpfs, bootpath); - - vm = qtest_init(cmd); - return vm; -} - -static void dirtylimit_stop_vm(QTestState *vm) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, "vm_serial"); - - qtest_quit(vm); - unlink(path); -} - -static void test_vcpu_dirty_limit(void) -{ - QTestState *vm; - int64_t origin_rate; - int64_t quota_rate; - int64_t rate ; - int max_try_count = 20; - int hit = 0; - - /* Start vm for vcpu dirtylimit test */ - vm = dirtylimit_start_vm(); - - /* Wait for the first serial output from the vm*/ - wait_for_serial("vm_serial"); - - /* Do dirtyrate measurement with calc time equals 1s */ - calc_dirty_rate(vm, 1); - - /* Sleep calc time and wait for calc dirtyrate complete */ - wait_for_calc_dirtyrate_complete(vm, 1); - - /* Query original dirty page rate */ - origin_rate = get_dirty_rate(vm); - - /* VM booted from bootsect should dirty memory steadily */ - assert(origin_rate != 0); - - /* Setup quota dirty page rate at half of origin */ - quota_rate = origin_rate / 2; - - /* Set dirtylimit */ - dirtylimit_set_all(vm, quota_rate); - - /* - * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit - * works literally - */ - g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); - - /* Sleep a bit to check if it take effect */ - usleep(2000000); - - /* - * Check if dirtylimit take effect realistically, set the - * timeout with 20 s(max_try_count * 1s), if dirtylimit - * doesn't take effect, fail test. - */ - while (--max_try_count) { - calc_dirty_rate(vm, 1); - wait_for_calc_dirtyrate_complete(vm, 1); - rate = get_dirty_rate(vm); - - /* - * Assume hitting if current rate is less - * than quota rate (within accepting error) - */ - if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { - hit = 1; - break; - } - } - - g_assert_cmpint(hit, ==, 1); - - hit = 0; - max_try_count = 20; - - /* Check if dirtylimit cancellation take effect */ - cancel_vcpu_dirty_limit(vm); - while (--max_try_count) { - calc_dirty_rate(vm, 1); - wait_for_calc_dirtyrate_complete(vm, 1); - rate = get_dirty_rate(vm); - - /* - * Assume dirtylimit be canceled if current rate is - * greater than quota rate (within accepting error) - */ - if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { - hit = 1; - break; - } - } - - g_assert_cmpint(hit, ==, 1); - dirtylimit_stop_vm(vm); -} - -static void migrate_dirty_limit_wait_showup(QTestState *from, - const int64_t period, - const int64_t value) -{ - /* Enable dirty limit capability */ - migrate_set_capability(from, "dirty-limit", true); - - /* Set dirty limit parameters */ - migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); - migrate_set_parameter_int(from, "vcpu-dirty-limit", value); - - /* Make sure migrate can't converge */ - migrate_ensure_non_converge(from); - - /* To check limit rate after precopy */ - migrate_set_capability(from, "pause-before-switchover", true); - - /* Wait for the serial output from the source */ - wait_for_serial("src_serial"); -} - -/* - * This test does: - * source destination - * start vm - * start incoming vm - * migrate - * wait dirty limit to begin - * cancel migrate - * cancellation check - * restart incoming vm - * migrate - * wait dirty limit to begin - * wait pre-switchover event - * convergence condition check - * - * And see if dirty limit migration works correctly. - * This test case involves many passes, so it runs in slow mode only. - */ -static void test_dirty_limit(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - int64_t remaining; - uint64_t throttle_us_per_full; - /* - * We want the test to be stable and as fast as possible. - * E.g., with 1Gb/s bandwidth migration may pass without dirty limit, - * so we need to decrease a bandwidth. - */ - const int64_t dirtylimit_period = 1000, dirtylimit_value = 50; - const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ - const int64_t downtime_limit = 250; /* 250ms */ - /* - * We migrate through unix-socket (> 500Mb/s). - * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). - * So, we can predict expected_threshold - */ - const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; - int max_try_count = 10; - MigrateCommon args = { - .start = { - .hide_stderr = true, - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - }; - - /* Start src, dst vm */ - if (migrate_start(&from, &to, args.listen_uri, &args.start)) { - return; - } - - /* Prepare for dirty limit migration and wait src vm show up */ - migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); - - /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, NULL, "{}"); - - /* Wait for dirty limit throttle begin */ - throttle_us_per_full = 0; - while (throttle_us_per_full == 0) { - throttle_us_per_full = - read_migrate_property_int(from, - "dirty-limit-throttle-time-per-round"); - usleep(100); - g_assert_false(get_src()->stop_seen); - } - - /* Now cancel migrate and wait for dirty limit throttle switch off */ - migrate_cancel(from); - wait_for_migration_status(from, "cancelled", NULL); - - /* Check if dirty limit throttle switched off, set timeout 1ms */ - do { - throttle_us_per_full = - read_migrate_property_int(from, - "dirty-limit-throttle-time-per-round"); - usleep(100); - g_assert_false(get_src()->stop_seen); - } while (throttle_us_per_full != 0 && --max_try_count); - - /* Assert dirty limit is not in service */ - g_assert_cmpint(throttle_us_per_full, ==, 0); - - args = (MigrateCommon) { - .start = { - .only_target = true, - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - }; - - /* Restart dst vm, src vm already show up so we needn't wait anymore */ - if (migrate_start(&from, &to, args.listen_uri, &args.start)) { - return; - } - - /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, NULL, "{}"); - - /* Wait for dirty limit throttle begin */ - throttle_us_per_full = 0; - while (throttle_us_per_full == 0) { - throttle_us_per_full = - read_migrate_property_int(from, - "dirty-limit-throttle-time-per-round"); - usleep(100); - g_assert_false(get_src()->stop_seen); - } - - /* - * The dirty limit rate should equals the return value of - * query-vcpu-dirty-limit if dirty limit cap set - */ - g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from)); - - /* Now, we have tested if dirty limit works, let it converge */ - migrate_set_parameter_int(from, "downtime-limit", downtime_limit); - migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); - - /* - * Wait for pre-switchover status to check if migration - * satisfy the convergence condition - */ - wait_for_migration_status(from, "pre-switchover", NULL); - - remaining = read_ram_property_int(from, "remaining"); - g_assert_cmpint(remaining, <, - (expected_threshold + expected_threshold / 100)); - - migrate_continue(from, "pre-switchover"); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - migrate_end(from, to, true); -} - int main(int argc, char **argv) { MigrationTestEnv *env; @@ -1222,21 +307,13 @@ int main(int argc, char **argv) migration_test_add_compression(env); migration_test_add_postcopy(env); migration_test_add_file(env); + migration_test_add_precopy(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); #endif - if (env->is_x86) { - migration_test_add("/migration/precopy/unix/suspend/live", - test_precopy_unix_suspend_live); - migration_test_add("/migration/precopy/unix/suspend/notlive", - test_precopy_unix_suspend_notlive); - } - - migration_test_add("/migration/precopy/unix/plain", - test_precopy_unix_plain); /* * Our CI system has problems with shared memory. * Don't run this test until we find a workaround. @@ -1245,18 +322,8 @@ int main(int argc, char **argv) migration_test_add("/migration/mode/reboot", test_mode_reboot); } - migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); - - migration_test_add("/migration/precopy/tcp/plain/switchover-ack", - test_precopy_tcp_switchover_ack); - /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ -#ifndef _WIN32 - migration_test_add("/migration/precopy/fd/tcp", - test_precopy_fd_socket); - migration_test_add("/migration/precopy/fd/file", - test_precopy_fd_file); -#endif + migration_test_add("/migration/validate_uuid", test_validate_uuid); migration_test_add("/migration/validate_uuid_error", test_validate_uuid_error); @@ -1268,39 +335,6 @@ int main(int argc, char **argv) test_validate_uri_channels_both_set); migration_test_add("/migration/validate_uri/channels/none_set", test_validate_uri_channels_none_set); - /* - * See explanation why this test is slow on function definition - */ - if (g_test_slow()) { - migration_test_add("/migration/auto_converge", - test_auto_converge); - if (g_str_equal(env->arch, "x86_64") && - env->has_kvm && env->has_dirty_ring) { - migration_test_add("/dirty_limit", - test_dirty_limit); - } - } - migration_test_add("/migration/multifd/tcp/uri/plain/none", - test_multifd_tcp_uri_none); - migration_test_add("/migration/multifd/tcp/channels/plain/none", - test_multifd_tcp_channels_none); - migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy", - test_multifd_tcp_zero_page_legacy); - migration_test_add("/migration/multifd/tcp/plain/zero-page/none", - test_multifd_tcp_no_zero_page); - migration_test_add("/migration/multifd/tcp/plain/cancel", - test_multifd_tcp_cancel); - - if (g_str_equal(env->arch, "x86_64") && - env->has_kvm && env->has_dirty_ring) { - - migration_test_add("/migration/dirty_ring", - test_precopy_unix_dirty_ring); - if (qtest_has_machine("pc") && g_test_slow()) { - migration_test_add("/migration/vcpu_dirty_limit", - test_vcpu_dirty_limit); - } - } ret = g_test_run(); diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 6be434c..52c7157 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -223,5 +223,6 @@ static inline void migration_test_add_tls(MigrationTestEnv *env) {}; void migration_test_add_compression(MigrationTestEnv *env); void migration_test_add_postcopy(MigrationTestEnv *env); void migration_test_add_file(MigrationTestEnv *env); +void migration_test_add_precopy(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c new file mode 100644 index 0000000..b709d90 --- /dev/null +++ b/tests/qtest/migration/precopy-tests.c @@ -0,0 +1,1007 @@ +/* + * QTest testcase for precopy migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "chardev/char.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/bootfile.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "ppc-util.h" +#include "qapi/qmp/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + + +/* + * Dirtylimit stop working if dirty page rate error + * value less than DIRTYLIMIT_TOLERANCE_RANGE + */ +#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ + +static char *tmpfs; + +static void test_precopy_unix_plain(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + /* + * The simplest use case of precopy, covering smoke tests of + * get-dirty-log dirty tracking. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_suspend_live(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + /* + * despite being live, the test is fast because the src + * suspends immediately. + */ + .live = true, + .start.suspend_me = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_suspend_notlive(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + .start.suspend_me = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_dirty_ring(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .start = { + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + /* + * Besides the precopy/unix basic test, cover dirty ring interface + * rather than get-dirty-log. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_plain(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + }; + + test_precopy_common(&args); +} + +static void *migrate_hook_start_switchover_ack(QTestState *from, QTestState *to) +{ + + migrate_set_capability(from, "return-path", true); + migrate_set_capability(to, "return-path", true); + + migrate_set_capability(from, "switchover-ack", true); + migrate_set_capability(to, "switchover-ack", true); + + return NULL; +} + +static void test_precopy_tcp_switchover_ack(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_switchover_ack, + /* + * Source VM must be running in order to consider the switchover ACK + * when deciding to do switchover or not. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +#ifndef _WIN32 +static void *migrate_hook_start_fd(QTestState *from, + QTestState *to) +{ + int ret; + int pair[2]; + + /* Create two connected sockets for migration */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); + g_assert_cmpint(ret, ==, 0); + + /* Send the 1st socket to the target */ + qtest_qmp_fds_assert_success(to, &pair[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + close(pair[0]); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "fd:fd-mig", "{}"); + + /* Send the 2nd socket to the target */ + qtest_qmp_fds_assert_success(from, &pair[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + close(pair[1]); + + return NULL; +} + +static void migrate_hook_end_fd(QTestState *from, + QTestState *to, + void *opaque) +{ + QDict *rsp; + const char *error_desc; + + /* Test closing fds */ + /* + * We assume, that QEMU removes named fd from its list, + * so this should fail. + */ + rsp = qtest_qmp(from, + "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + g_assert_true(qdict_haskey(rsp, "error")); + error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); + g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); + qobject_unref(rsp); + + rsp = qtest_qmp(to, + "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + g_assert_true(qdict_haskey(rsp, "error")); + error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); + g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); + qobject_unref(rsp); +} + +static void test_precopy_fd_socket(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = migrate_hook_start_fd, + .end_hook = migrate_hook_end_fd, + }; + test_precopy_common(&args); +} + +static void *migrate_hook_start_precopy_fd_file(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + int src_flags = O_CREAT | O_RDWR; + int dst_flags = O_CREAT | O_RDWR; + int fds[2]; + + fds[0] = open(file, src_flags, 0660); + assert(fds[0] != -1); + + fds[1] = open(file, dst_flags, 0660); + assert(fds[1] != -1); + + + qtest_qmp_fds_assert_success(to, &fds[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + qtest_qmp_fds_assert_success(from, &fds[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + close(fds[0]); + close(fds[1]); + + return NULL; +} + +static void test_precopy_fd_file(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = migrate_hook_start_precopy_fd_file, + .end_hook = migrate_hook_end_fd, + }; + test_file_common(&args, true); +} +#endif /* _WIN32 */ + +/* + * The way auto_converge works, we need to do too many passes to + * run this test. Auto_converge logic is only run once every + * three iterations, so: + * + * - 3 iterations without auto_converge enabled + * - 3 iterations with pct = 5 + * - 3 iterations with pct = 30 + * - 3 iterations with pct = 55 + * - 3 iterations with pct = 80 + * - 3 iterations with pct = 95 (max(95, 80 + 25)) + * + * To make things even worse, we need to run the initial stage at + * 3MB/s so we enter autoconverge even when host is (over)loaded. + */ +static void test_auto_converge(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateStart args = {}; + QTestState *from, *to; + int64_t percentage; + + /* + * We want the test to be stable and as fast as possible. + * E.g., with 1Gb/s bandwidth migration may pass without throttling, + * so we need to decrease a bandwidth. + */ + const int64_t init_pct = 5, inc_pct = 25, max_pct = 95; + uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; + int max_try_count, hit = 0; + + if (migrate_start(&from, &to, uri, &args)) { + return; + } + + migrate_set_capability(from, "auto-converge", true); + migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); + migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); + migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); + + /* + * Set the initial parameters so that the migration could not converge + * without throttling. + */ + migrate_ensure_non_converge(from); + + /* To check remaining size after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + /* Wait for throttling begins */ + percentage = 0; + do { + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + if (percentage != 0) { + break; + } + usleep(20); + g_assert_false(get_src()->stop_seen); + } while (true); + /* The first percentage of throttling should be at least init_pct */ + g_assert_cmpint(percentage, >=, init_pct); + + /* + * End the loop when the dirty sync count greater than 1. + */ + while ((dirty_sync_cnt = get_migration_pass(from)) < 2) { + usleep(1000 * 1000); + } + + prev_dirty_sync_cnt = dirty_sync_cnt; + + /* + * The RAMBlock dirty sync count must changes in 5 seconds, here we set + * the timeout to 10 seconds to ensure it changes. + * + * Note that migrate_ensure_non_converge set the max-bandwidth to 3MB/s, + * while the qtest mem is >= 100MB, one iteration takes at least 33s (100/3) + * to complete; this ensures that the RAMBlock dirty sync occurs. + */ + max_try_count = 10; + while (--max_try_count) { + dirty_sync_cnt = get_migration_pass(from); + if (dirty_sync_cnt != prev_dirty_sync_cnt) { + hit = 1; + break; + } + prev_dirty_sync_cnt = dirty_sync_cnt; + sleep(1); + } + g_assert_cmpint(hit, ==, 1); + + /* Now, when we tested that throttling works, let it converge */ + migrate_ensure_converge(from); + + /* + * Wait for pre-switchover status to check last throttle percentage + * and remaining. These values will be zeroed later + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + /* The final percentage of throttling shouldn't be greater than max_pct */ + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + g_assert_cmpint(percentage, <=, max_pct); + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + migrate_end(from, to, true); +} + +static void * +migrate_hook_start_precopy_tcp_multifd(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); +} + +static void * +migrate_hook_start_precopy_tcp_multifd_zero_page_legacy(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "legacy"); + return NULL; +} + +static void * +migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "none"); + return NULL; +} + +static void test_multifd_tcp_uri_none(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_zero_page_legacy(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_no_zero_page(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_channels_none(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd, + .live = true, + .connect_channels = ("[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), + }; + test_precopy_common(&args); +} + +/* + * This test does: + * source target + * migrate_incoming + * migrate + * migrate_cancel + * launch another target + * migrate + * + * And see that it works + */ +static void test_multifd_tcp_cancel(void) +{ + MigrateStart args = { + .hide_stderr = true, + }; + QTestState *from, *to, *to2; + + if (migrate_start(&from, &to, "defer", &args)) { + return; + } + + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + + migrate_set_parameter_int(from, "multifd-channels", 16); + migrate_set_parameter_int(to, "multifd-channels", 16); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + migrate_cancel(from); + + /* Make sure QEMU process "to" exited */ + qtest_set_expected_status(to, EXIT_FAILURE); + qtest_wait_qemu(to); + qtest_quit(to); + + /* + * Ensure the source QEMU finishes its cancellation process before we + * proceed with the setup of the next migration. The migrate_start() + * function and others might want to interact with the source in a way that + * is not possible while the migration is not canceled properly. For + * example, setting migration capabilities when the migration is still + * running leads to an error. + */ + wait_for_migration_status(from, "cancelled", NULL); + + args = (MigrateStart){ + .only_target = true, + }; + + if (migrate_start(&from, &to2, "defer", &args)) { + return; + } + + migrate_set_parameter_int(to2, "multifd-channels", 16); + + migrate_set_capability(to2, "multifd", true); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); + + migrate_ensure_non_converge(from); + + migrate_qmp(from, to2, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to2); + + migrate_ensure_converge(from); + + wait_for_stop(from, get_src()); + qtest_qmp_eventwait(to2, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + migrate_end(from, to2, true); +} + +static void calc_dirty_rate(QTestState *who, uint64_t calc_time) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'calc-dirty-rate'," + "'arguments': { " + "'calc-time': %" PRIu64 "," + "'mode': 'dirty-ring' }}", + calc_time); +} + +static QDict *query_dirty_rate(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, + "{ 'execute': 'query-dirty-rate' }"); +} + +static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'set-vcpu-dirty-limit'," + "'arguments': { " + "'dirty-rate': %" PRIu64 " } }", + dirtyrate); +} + +static void cancel_vcpu_dirty_limit(QTestState *who) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'cancel-vcpu-dirty-limit' }"); +} + +static QDict *query_vcpu_dirty_limit(QTestState *who) +{ + QDict *rsp; + + rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); + g_assert(!qdict_haskey(rsp, "error")); + g_assert(qdict_haskey(rsp, "return")); + + return rsp; +} + +static bool calc_dirtyrate_ready(QTestState *who) +{ + QDict *rsp_return; + const char *status; + bool ready; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = qdict_get_str(rsp_return, "status"); + g_assert(status); + ready = g_strcmp0(status, "measuring"); + qobject_unref(rsp_return); + + return ready; +} + +static void wait_for_calc_dirtyrate_complete(QTestState *who, + int64_t time_s) +{ + int max_try_count = 10000; + usleep(time_s * 1000000); + + while (!calc_dirtyrate_ready(who) && max_try_count--) { + usleep(1000); + } + + /* + * Set the timeout with 10 s(max_try_count * 1000us), + * if dirtyrate measurement not complete, fail test. + */ + g_assert_cmpint(max_try_count, !=, 0); +} + +static int64_t get_dirty_rate(QTestState *who) +{ + QDict *rsp_return; + const char *status; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = qdict_get_str(rsp_return, "status"); + g_assert(status); + g_assert_cmpstr(status, ==, "measured"); + + rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static int64_t get_limit_rate(QTestState *who) +{ + QDict *rsp_return; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_vcpu_dirty_limit(who); + g_assert(rsp_return); + + rates = qdict_get_qlist(rsp_return, "return"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static QTestState *dirtylimit_start_vm(void) +{ + QTestState *vm = NULL; + g_autofree gchar *cmd = NULL; + const char *bootpath; + + bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); + cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " + "-name dirtylimit-test,debug-threads=on " + "-m 150M -smp 1 " + "-serial file:%s/vm_serial " + "-drive file=%s,format=raw ", + tmpfs, bootpath); + + vm = qtest_init(cmd); + return vm; +} + +static void dirtylimit_stop_vm(QTestState *vm) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, "vm_serial"); + + qtest_quit(vm); + unlink(path); +} + +static void test_vcpu_dirty_limit(void) +{ + QTestState *vm; + int64_t origin_rate; + int64_t quota_rate; + int64_t rate ; + int max_try_count = 20; + int hit = 0; + + /* Start vm for vcpu dirtylimit test */ + vm = dirtylimit_start_vm(); + + /* Wait for the first serial output from the vm*/ + wait_for_serial("vm_serial"); + + /* Do dirtyrate measurement with calc time equals 1s */ + calc_dirty_rate(vm, 1); + + /* Sleep calc time and wait for calc dirtyrate complete */ + wait_for_calc_dirtyrate_complete(vm, 1); + + /* Query original dirty page rate */ + origin_rate = get_dirty_rate(vm); + + /* VM booted from bootsect should dirty memory steadily */ + assert(origin_rate != 0); + + /* Setup quota dirty page rate at half of origin */ + quota_rate = origin_rate / 2; + + /* Set dirtylimit */ + dirtylimit_set_all(vm, quota_rate); + + /* + * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit + * works literally + */ + g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); + + /* Sleep a bit to check if it take effect */ + usleep(2000000); + + /* + * Check if dirtylimit take effect realistically, set the + * timeout with 20 s(max_try_count * 1s), if dirtylimit + * doesn't take effect, fail test. + */ + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume hitting if current rate is less + * than quota rate (within accepting error) + */ + if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + + hit = 0; + max_try_count = 20; + + /* Check if dirtylimit cancellation take effect */ + cancel_vcpu_dirty_limit(vm); + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume dirtylimit be canceled if current rate is + * greater than quota rate (within accepting error) + */ + if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + dirtylimit_stop_vm(vm); +} + +static void migrate_dirty_limit_wait_showup(QTestState *from, + const int64_t period, + const int64_t value) +{ + /* Enable dirty limit capability */ + migrate_set_capability(from, "dirty-limit", true); + + /* Set dirty limit parameters */ + migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); + migrate_set_parameter_int(from, "vcpu-dirty-limit", value); + + /* Make sure migrate can't converge */ + migrate_ensure_non_converge(from); + + /* To check limit rate after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the serial output from the source */ + wait_for_serial("src_serial"); +} + +/* + * This test does: + * source destination + * start vm + * start incoming vm + * migrate + * wait dirty limit to begin + * cancel migrate + * cancellation check + * restart incoming vm + * migrate + * wait dirty limit to begin + * wait pre-switchover event + * convergence condition check + * + * And see if dirty limit migration works correctly. + * This test case involves many passes, so it runs in slow mode only. + */ +static void test_dirty_limit(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + int64_t remaining; + uint64_t throttle_us_per_full; + /* + * We want the test to be stable and as fast as possible. + * E.g., with 1Gb/s bandwidth migration may pass without dirty limit, + * so we need to decrease a bandwidth. + */ + const int64_t dirtylimit_period = 1000, dirtylimit_value = 50; + const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ + const int64_t downtime_limit = 250; /* 250ms */ + /* + * We migrate through unix-socket (> 500Mb/s). + * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). + * So, we can predict expected_threshold + */ + const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; + int max_try_count = 10; + MigrateCommon args = { + .start = { + .hide_stderr = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Start src, dst vm */ + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Prepare for dirty limit migration and wait src vm show up */ + migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); + + /* Start migrate */ + migrate_qmp(from, to, args.connect_uri, NULL, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, + "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(get_src()->stop_seen); + } + + /* Now cancel migrate and wait for dirty limit throttle switch off */ + migrate_cancel(from); + wait_for_migration_status(from, "cancelled", NULL); + + /* Check if dirty limit throttle switched off, set timeout 1ms */ + do { + throttle_us_per_full = + read_migrate_property_int(from, + "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(get_src()->stop_seen); + } while (throttle_us_per_full != 0 && --max_try_count); + + /* Assert dirty limit is not in service */ + g_assert_cmpint(throttle_us_per_full, ==, 0); + + args = (MigrateCommon) { + .start = { + .only_target = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Restart dst vm, src vm already show up so we needn't wait anymore */ + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Start migrate */ + migrate_qmp(from, to, args.connect_uri, NULL, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, + "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(get_src()->stop_seen); + } + + /* + * The dirty limit rate should equals the return value of + * query-vcpu-dirty-limit if dirty limit cap set + */ + g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from)); + + /* Now, we have tested if dirty limit works, let it converge */ + migrate_set_parameter_int(from, "downtime-limit", downtime_limit); + migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); + + /* + * Wait for pre-switchover status to check if migration + * satisfy the convergence condition + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + remaining = read_ram_property_int(from, "remaining"); + g_assert_cmpint(remaining, <, + (expected_threshold + expected_threshold / 100)); + + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + migrate_end(from, to, true); +} + +void migration_test_add_precopy(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + if (env->is_x86) { + migration_test_add("/migration/precopy/unix/suspend/live", + test_precopy_unix_suspend_live); + migration_test_add("/migration/precopy/unix/suspend/notlive", + test_precopy_unix_suspend_notlive); + } + + migration_test_add("/migration/precopy/unix/plain", + test_precopy_unix_plain); + + migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); + + migration_test_add("/migration/precopy/tcp/plain/switchover-ack", + test_precopy_tcp_switchover_ack); + +#ifndef _WIN32 + migration_test_add("/migration/precopy/fd/tcp", + test_precopy_fd_socket); + migration_test_add("/migration/precopy/fd/file", + test_precopy_fd_file); +#endif + + /* + * See explanation why this test is slow on function definition + */ + if (g_test_slow()) { + migration_test_add("/migration/auto_converge", + test_auto_converge); + if (g_str_equal(env->arch, "x86_64") && + env->has_kvm && env->has_dirty_ring) { + migration_test_add("/dirty_limit", + test_dirty_limit); + } + } + migration_test_add("/migration/multifd/tcp/uri/plain/none", + test_multifd_tcp_uri_none); + migration_test_add("/migration/multifd/tcp/channels/plain/none", + test_multifd_tcp_channels_none); + migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy", + test_multifd_tcp_zero_page_legacy); + migration_test_add("/migration/multifd/tcp/plain/zero-page/none", + test_multifd_tcp_no_zero_page); + migration_test_add("/migration/multifd/tcp/plain/cancel", + test_multifd_tcp_cancel); + if (g_str_equal(env->arch, "x86_64") + && env->has_kvm && env->has_dirty_ring) { + + migration_test_add("/migration/dirty_ring", + test_precopy_unix_dirty_ring); + if (qtest_has_machine("pc") && g_test_slow()) { + migration_test_add("/migration/vcpu_dirty_limit", + test_vcpu_dirty_limit); + } + } +} -- cgit v1.1 From 44bd87a500dabf65d21b704c9ce61a44e8ff60eb Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:29:00 -0300 Subject: tests/qtest/migration: Split CPR tests Move the mode/reboot test into a separate file to hold all the CPR tests. Currently there's just one test, but we're adding more CPR modes and the feature is different enough from live migration that it's worth it to have a separate file for it. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 34 +---------------------- tests/qtest/migration/cpr-tests.c | 58 +++++++++++++++++++++++++++++++++++++++ tests/qtest/migration/framework.h | 1 + 4 files changed, 61 insertions(+), 33 deletions(-) create mode 100644 tests/qtest/migration/cpr-tests.c (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index fc38c80..cda1a84 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -338,6 +338,7 @@ migration_files = [files( 'migration/migration-qmp.c', 'migration/migration-util.c', 'migration/compression-tests.c', + 'migration/cpr-tests.c', 'migration/file-tests.c', 'migration/precopy-tests.c', 'migration/postcopy-tests.c', diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 76ba820..0d1c815 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -145,31 +145,6 @@ static void test_ignore_shared(void) } #endif -static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) -{ - migrate_set_parameter_str(from, "mode", "cpr-reboot"); - migrate_set_parameter_str(to, "mode", "cpr-reboot"); - - migrate_set_capability(from, "x-ignore-shared", true); - migrate_set_capability(to, "x-ignore-shared", true); - - return NULL; -} - -static void test_mode_reboot(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .start.use_shmem = true, - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_mode_reboot, - }; - - test_file_common(&args, true); -} - static void do_test_validate_uuid(MigrateStart *args, bool should_fail) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); @@ -308,20 +283,13 @@ int main(int argc, char **argv) migration_test_add_postcopy(env); migration_test_add_file(env); migration_test_add_precopy(env); + migration_test_add_cpr(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); #endif - /* - * Our CI system has problems with shared memory. - * Don't run this test until we find a workaround. - */ - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - migration_test_add("/migration/mode/reboot", test_mode_reboot); - } - /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ migration_test_add("/migration/validate_uuid", test_validate_uuid); diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c new file mode 100644 index 0000000..44ce89a --- /dev/null +++ b/tests/qtest/migration/cpr-tests.c @@ -0,0 +1,58 @@ +/* + * QTest testcases for CPR + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + + +static char *tmpfs; + +static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-reboot"); + migrate_set_parameter_str(to, "mode", "cpr-reboot"); + + migrate_set_capability(from, "x-ignore-shared", true); + migrate_set_capability(to, "x-ignore-shared", true); + + return NULL; +} + +static void test_mode_reboot(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .start.use_shmem = true, + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_mode_reboot, + }; + + test_file_common(&args, true); +} + +void migration_test_add_cpr(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/mode/reboot", test_mode_reboot); + } +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 52c7157..b264be7 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -224,5 +224,6 @@ void migration_test_add_compression(MigrationTestEnv *env); void migration_test_add_postcopy(MigrationTestEnv *env); void migration_test_add_file(MigrationTestEnv *env); void migration_test_add_precopy(MigrationTestEnv *env); +void migration_test_add_cpr(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ -- cgit v1.1 From 0610e2c268fbc7939163347c41541165d116da02 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 30 May 2024 19:54:06 +1000 Subject: tests/qtest/migration-test: Fix and enable test_ignore_shared This test is already starting to bitrot, so first remove it from ifdef and fix compile issues. ppc64 transfers about 2MB, so bump the size threshold too. It was said to be broken on aarch64 but it may have been due to the limited shm size under Gitlab CI. Cc: Yury Kotov Cc: Dr. David Alan Gilbert Signed-off-by: Nicholas Piggin Reviewed-by: Dr. David Alan Gilbert [put it under flaky tests, we still don't have a solution for the CI] Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 0d1c815..4b366a9 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -107,14 +107,15 @@ static void test_analyze_script(void) } #endif -#if 0 -/* Currently upset on aarch64 TCG */ static void test_ignore_shared(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; + MigrateStart args = { + .use_shmem = true, + }; - if (migrate_start(&from, &to, uri, false, true, NULL, NULL)) { + if (migrate_start(&from, &to, uri, &args)) { return; } @@ -139,11 +140,11 @@ static void test_ignore_shared(void) wait_for_migration_complete(from); /* Check whether shared RAM has been really skipped */ - g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); + g_assert_cmpint( + read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); migrate_end(from, to, true); } -#endif static void do_test_validate_uuid(MigrateStart *args, bool should_fail) { @@ -290,7 +291,13 @@ int main(int argc, char **argv) migration_test_add("/migration/analyze-script", test_analyze_script); #endif - /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/ignore-shared", test_ignore_shared); + } migration_test_add("/migration/validate_uuid", test_validate_uuid); migration_test_add("/migration/validate_uuid_error", -- cgit v1.1 From de8bc62cc4871fa97d1891ebb96e782b0c75b56a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:29:01 -0300 Subject: tests/qtest/migration: Split validation tests + misc Move the remaining tests into a misc-tests.c file. These tests are mostly about validation of input and should be in the future replaced by unit testing. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 285 +------------------------------------ tests/qtest/migration/framework.h | 1 + tests/qtest/migration/misc-tests.c | 282 ++++++++++++++++++++++++++++++++++++ 4 files changed, 286 insertions(+), 283 deletions(-) create mode 100644 tests/qtest/migration/misc-tests.c (limited to 'tests') diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index cda1a84..c5a7002 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -340,6 +340,7 @@ migration_files = [files( 'migration/compression-tests.c', 'migration/cpr-tests.c', 'migration/file-tests.c', + 'migration/misc-tests.c', 'migration/precopy-tests.c', 'migration/postcopy-tests.c', )] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 4b366a9..5cad506 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -11,262 +11,8 @@ */ #include "qemu/osdep.h" - -#include "libqtest.h" -#include "qapi/qmp/qlist.h" -#include "qemu/module.h" -#include "qemu/option.h" -#include "qemu/range.h" -#include "qemu/sockets.h" -#include "chardev/char.h" -#include "crypto/tlscredspsk.h" -#include "ppc-util.h" - -#include "migration/bootfile.h" #include "migration/framework.h" -#include "migration/migration-qmp.h" -#include "migration/migration-util.h" - -#define ANALYZE_SCRIPT "scripts/analyze-migration.py" - -#if defined(__linux__) -#include -#include -#include -#endif - -static char *tmpfs; - -static void test_baddest(void) -{ - MigrateStart args = { - .hide_stderr = true - }; - QTestState *from, *to; - - if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { - return; - } - migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); - wait_for_migration_fail(from, false); - migrate_end(from, to, false); -} - -#ifndef _WIN32 -static void test_analyze_script(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - }; - QTestState *from, *to; - g_autofree char *uri = NULL; - g_autofree char *file = NULL; - int pid, wstatus; - const char *python = g_getenv("PYTHON"); - - if (!python) { - g_test_skip("PYTHON variable not set"); - return; - } - - /* dummy url */ - if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { - return; - } - - /* - * Setting these two capabilities causes the "configuration" - * vmstate to include subsections for them. The script needs to - * parse those subsections properly. - */ - migrate_set_capability(from, "validate-uuid", true); - migrate_set_capability(from, "x-ignore-shared", true); - - file = g_strdup_printf("%s/migfile", tmpfs); - uri = g_strdup_printf("exec:cat > %s", file); - - migrate_ensure_converge(from); - migrate_qmp(from, to, uri, NULL, "{}"); - wait_for_migration_complete(from); - - pid = fork(); - if (!pid) { - close(1); - open("/dev/null", O_WRONLY); - execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); - g_assert_not_reached(); - } - - g_assert(waitpid(pid, &wstatus, 0) == pid); - if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { - g_test_message("Failed to analyze the migration stream"); - g_test_fail(); - } - migrate_end(from, to, false); - unlink(file); -} -#endif - -static void test_ignore_shared(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - MigrateStart args = { - .use_shmem = true, - }; - - if (migrate_start(&from, &to, uri, &args)) { - return; - } - - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); - - migrate_set_capability(from, "x-ignore-shared", true); - migrate_set_capability(to, "x-ignore-shared", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, uri, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - wait_for_stop(from, get_src()); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - /* Check whether shared RAM has been really skipped */ - g_assert_cmpint( - read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); - - migrate_end(from, to, true); -} - -static void do_test_validate_uuid(MigrateStart *args, bool should_fail) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - - if (migrate_start(&from, &to, uri, args)) { - return; - } - - /* - * UUID validation is at the begin of migration. So, the main process of - * migration is not interesting for us here. Thus, set huge downtime for - * very fast migration. - */ - migrate_set_parameter_int(from, "downtime-limit", 1000000); - migrate_set_capability(from, "validate-uuid", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, uri, NULL, "{}"); - - if (should_fail) { - qtest_set_expected_status(to, EXIT_FAILURE); - wait_for_migration_fail(from, true); - } else { - wait_for_migration_complete(from); - } - - migrate_end(from, to, false); -} - -static void test_validate_uuid(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", - }; - - do_test_validate_uuid(&args, false); -} - -static void test_validate_uuid_error(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, true); -} - -static void test_validate_uuid_src_not_set(void) -{ - MigrateStart args = { - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, false); -} - -static void test_validate_uuid_dst_not_set(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, false); -} - -static void do_test_validate_uri_channel(MigrateCommon *args) -{ - QTestState *from, *to; - - if (migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - /* - * 'uri' and 'channels' validation is checked even before the migration - * starts. - */ - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); - migrate_end(from, to, false); -} - -static void test_validate_uri_channels_both_set(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .connect_uri = "tcp:127.0.0.1:0", - .connect_channels = ("[ { ""'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ]"), - }; - - do_test_validate_uri_channel(&args); -} - -static void test_validate_uri_channels_none_set(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - }; - - do_test_validate_uri_channel(&args); -} +#include "qemu/module.h" int main(int argc, char **argv) { @@ -277,45 +23,18 @@ int main(int argc, char **argv) env = migration_get_env(); module_call_init(MODULE_INIT_QOM); - tmpfs = env->tmpfs; - migration_test_add_tls(env); migration_test_add_compression(env); migration_test_add_postcopy(env); migration_test_add_file(env); migration_test_add_precopy(env); migration_test_add_cpr(env); - - migration_test_add("/migration/bad_dest", test_baddest); -#ifndef _WIN32 - migration_test_add("/migration/analyze-script", test_analyze_script); -#endif - - /* - * Our CI system has problems with shared memory. - * Don't run this test until we find a workaround. - */ - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - migration_test_add("/migration/ignore-shared", test_ignore_shared); - } - - migration_test_add("/migration/validate_uuid", test_validate_uuid); - migration_test_add("/migration/validate_uuid_error", - test_validate_uuid_error); - migration_test_add("/migration/validate_uuid_src_not_set", - test_validate_uuid_src_not_set); - migration_test_add("/migration/validate_uuid_dst_not_set", - test_validate_uuid_dst_not_set); - migration_test_add("/migration/validate_uri/channels/both_set", - test_validate_uri_channels_both_set); - migration_test_add("/migration/validate_uri/channels/none_set", - test_validate_uri_channels_none_set); + migration_test_add_misc(env); ret = g_test_run(); g_assert_cmpint(ret, ==, 0); - tmpfs = NULL; ret = migration_env_clean(env); return ret; diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index b264be7..e9fc4ec 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -225,5 +225,6 @@ void migration_test_add_postcopy(MigrationTestEnv *env); void migration_test_add_file(MigrationTestEnv *env); void migration_test_add_precopy(MigrationTestEnv *env); void migration_test_add_cpr(MigrationTestEnv *env); +void migration_test_add_misc(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c new file mode 100644 index 0000000..6173430 --- /dev/null +++ b/tests/qtest/migration/misc-tests.c @@ -0,0 +1,282 @@ +/* + * QTest testcases for migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + +#define ANALYZE_SCRIPT "scripts/analyze-migration.py" + +static char *tmpfs; + +static void test_baddest(void) +{ + MigrateStart args = { + .hide_stderr = true + }; + QTestState *from, *to; + + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + return; + } + migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); + wait_for_migration_fail(from, false); + migrate_end(from, to, false); +} + +#ifndef _WIN32 +static void test_analyze_script(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + }; + QTestState *from, *to; + g_autofree char *uri = NULL; + g_autofree char *file = NULL; + int pid, wstatus; + const char *python = g_getenv("PYTHON"); + + if (!python) { + g_test_skip("PYTHON variable not set"); + return; + } + + /* dummy url */ + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + return; + } + + /* + * Setting these two capabilities causes the "configuration" + * vmstate to include subsections for them. The script needs to + * parse those subsections properly. + */ + migrate_set_capability(from, "validate-uuid", true); + migrate_set_capability(from, "x-ignore-shared", true); + + file = g_strdup_printf("%s/migfile", tmpfs); + uri = g_strdup_printf("exec:cat > %s", file); + + migrate_ensure_converge(from); + migrate_qmp(from, to, uri, NULL, "{}"); + wait_for_migration_complete(from); + + pid = fork(); + if (!pid) { + close(1); + open("/dev/null", O_WRONLY); + execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); + g_assert_not_reached(); + } + + g_assert(waitpid(pid, &wstatus, 0) == pid); + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { + g_test_message("Failed to analyze the migration stream"); + g_test_fail(); + } + migrate_end(from, to, false); + unlink(file); +} +#endif + +static void test_ignore_shared(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + MigrateStart args = { + .use_shmem = true, + }; + + if (migrate_start(&from, &to, uri, &args)) { + return; + } + + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + + migrate_set_capability(from, "x-ignore-shared", true); + migrate_set_capability(to, "x-ignore-shared", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + wait_for_stop(from, get_src()); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + /* Check whether shared RAM has been really skipped */ + g_assert_cmpint( + read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); + + migrate_end(from, to, true); +} + +static void do_test_validate_uuid(MigrateStart *args, bool should_fail) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + if (migrate_start(&from, &to, uri, args)) { + return; + } + + /* + * UUID validation is at the begin of migration. So, the main process of + * migration is not interesting for us here. Thus, set huge downtime for + * very fast migration. + */ + migrate_set_parameter_int(from, "downtime-limit", 1000000); + migrate_set_capability(from, "validate-uuid", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + if (should_fail) { + qtest_set_expected_status(to, EXIT_FAILURE); + wait_for_migration_fail(from, true); + } else { + wait_for_migration_complete(from); + } + + migrate_end(from, to, false); +} + +static void test_validate_uuid(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", + }; + + do_test_validate_uuid(&args, false); +} + +static void test_validate_uuid_error(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, true); +} + +static void test_validate_uuid_src_not_set(void) +{ + MigrateStart args = { + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, false); +} + +static void test_validate_uuid_dst_not_set(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, false); +} + +static void do_test_validate_uri_channel(MigrateCommon *args) +{ + QTestState *from, *to; + + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + /* + * 'uri' and 'channels' validation is checked even before the migration + * starts. + */ + migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + migrate_end(from, to, false); +} + +static void test_validate_uri_channels_both_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .connect_uri = "tcp:127.0.0.1:0", + .connect_channels = ("[ { ""'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), + }; + + do_test_validate_uri_channel(&args); +} + +static void test_validate_uri_channels_none_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + }; + + do_test_validate_uri_channel(&args); +} + +void migration_test_add_misc(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add("/migration/bad_dest", test_baddest); +#ifndef _WIN32 + migration_test_add("/migration/analyze-script", test_analyze_script); +#endif + + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/ignore-shared", test_ignore_shared); + } + + migration_test_add("/migration/validate_uuid", test_validate_uuid); + migration_test_add("/migration/validate_uuid_error", + test_validate_uuid_error); + migration_test_add("/migration/validate_uuid_src_not_set", + test_validate_uuid_src_not_set); + migration_test_add("/migration/validate_uuid_dst_not_set", + test_validate_uuid_dst_not_set); + migration_test_add("/migration/validate_uri/channels/both_set", + test_validate_uri_channels_both_set); + migration_test_add("/migration/validate_uri/channels/none_set", + test_validate_uri_channels_none_set); +} -- cgit v1.1