* [PATCH v2 01/22] tests/qtest/migration: Fix indentations
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-13 19:46 ` [PATCH v2 02/22] tests/qtest/migration: Standardize hook names Fabiano Rosas
` (21 subsequent siblings)
22 siblings, 0 replies; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
Select all the code and hit tab. I'll be moving functions around quite
a lot in the next patches, so make sure all indentation is correct
now.
Add parentheses around some expressions to preserve readability.
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/migration-helpers.c | 4 +--
tests/qtest/migration-test.c | 59 ++++++++++++++++++---------------
2 files changed, 35 insertions(+), 28 deletions(-)
diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c
index 0025933883..3f8ba7fa8e 100644
--- a/tests/qtest/migration-helpers.c
+++ b/tests/qtest/migration-helpers.c
@@ -140,8 +140,8 @@ static void migrate_set_ports(QTestState *to, QList *channel_list)
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);
+ addr_port = qdict_get_str(addr, "port");
+ qdict_put_str(addrdict, "port", addr_port);
}
}
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index e6a2803e71..74d3000198 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -113,8 +113,8 @@ static bool ufd_version_check(void)
}
uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID;
- ioctl_mask = 1ULL << _UFFDIO_REGISTER |
- 1ULL << _UFFDIO_UNREGISTER;
+ 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;
@@ -423,7 +423,7 @@ static void migrate_set_parameter_str(QTestState *who, const char *parameter,
}
static long long migrate_get_parameter_bool(QTestState *who,
- const char *parameter)
+ const char *parameter)
{
QDict *rsp;
int result;
@@ -436,7 +436,7 @@ static long long migrate_get_parameter_bool(QTestState *who,
}
static void migrate_check_parameter_bool(QTestState *who, const char *parameter,
- int value)
+ int value)
{
int result;
@@ -445,7 +445,7 @@ static void migrate_check_parameter_bool(QTestState *who, const char *parameter,
}
static void migrate_set_parameter_bool(QTestState *who, const char *parameter,
- int value)
+ int value)
{
qtest_qmp_assert_success(who,
"{ 'execute': 'migrate-set-parameters',"
@@ -1384,8 +1384,10 @@ static void test_postcopy_preempt_tls_psk(void)
static void wait_for_postcopy_status(QTestState *one, const char *status)
{
wait_for_migration_status(one, status,
- (const char * []) { "failed", "active",
- "completed", NULL });
+ (const char * []) {
+ "failed", "active",
+ "completed", NULL
+ });
}
static void postcopy_recover_fail(QTestState *from, QTestState *to,
@@ -2575,15 +2577,17 @@ static void test_migrate_fd_finish_hook(QTestState *from,
/* 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' }}");
+ 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' }}");
+ 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");
@@ -2741,11 +2745,11 @@ static void test_validate_uri_channels_both_set(void)
},
.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' } } ]",
+ .connect_channels = ("[ { ""'channel-type': 'main',"
+ " 'addr': { 'transport': 'socket',"
+ " 'type': 'inet',"
+ " 'host': '127.0.0.1',"
+ " 'port': '0' } } ]"),
};
do_test_validate_uri_channel(&args);
@@ -2967,7 +2971,7 @@ test_migrate_precopy_tcp_multifd_qatzip_start(QTestState *from,
#ifdef CONFIG_QPL
static void *
test_migrate_precopy_tcp_multifd_qpl_start(QTestState *from,
- QTestState *to)
+ QTestState *to)
{
return test_migrate_precopy_tcp_multifd_start_common(from, to, "qpl");
}
@@ -3032,11 +3036,11 @@ static void test_multifd_tcp_channels_none(void)
.listen_uri = "defer",
.start_hook = test_migrate_precopy_tcp_multifd_start,
.live = true,
- .connect_channels = "[ { 'channel-type': 'main',"
- " 'addr': { 'transport': 'socket',"
- " 'type': 'inet',"
- " 'host': '127.0.0.1',"
- " 'port': '0' } } ]",
+ .connect_channels = ("[ { 'channel-type': 'main',"
+ " 'addr': { 'transport': 'socket',"
+ " 'type': 'inet',"
+ " 'host': '127.0.0.1',"
+ " 'port': '0' } } ]"),
};
test_precopy_common(&args);
}
@@ -3668,7 +3672,8 @@ static void test_migrate_dirty_limit(void)
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");
+ read_migrate_property_int(from,
+ "dirty-limit-throttle-time-per-round");
usleep(100);
g_assert_false(src_state.stop_seen);
}
@@ -3680,7 +3685,8 @@ static void test_migrate_dirty_limit(void)
/* 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");
+ read_migrate_property_int(from,
+ "dirty-limit-throttle-time-per-round");
usleep(100);
g_assert_false(src_state.stop_seen);
} while (throttle_us_per_full != 0 && --max_try_count);
@@ -3709,7 +3715,8 @@ static void test_migrate_dirty_limit(void)
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");
+ read_migrate_property_int(from,
+ "dirty-limit-throttle-time-per-round");
usleep(100);
g_assert_false(src_state.stop_seen);
}
@@ -3989,7 +3996,7 @@ int main(int argc, char **argv)
#endif
#ifdef CONFIG_QATZIP
migration_test_add("/migration/multifd/tcp/plain/qatzip",
- test_multifd_tcp_qatzip);
+ test_multifd_tcp_qatzip);
#endif
#ifdef CONFIG_QPL
migration_test_add("/migration/multifd/tcp/plain/qpl",
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* [PATCH v2 02/22] tests/qtest/migration: Standardize hook names
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
2024-11-13 19:46 ` [PATCH v2 01/22] tests/qtest/migration: Fix indentations Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 20:51 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 03/22] tests/qtest/migration: Stop calling everything "test" Fabiano Rosas
` (20 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
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
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/migration-test.c | 299 ++++++++++++++++++-----------------
1 file changed, 150 insertions(+), 149 deletions(-)
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 74d3000198..68fa24edb4 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,7 @@ 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 *file_offset_fdset_start(QTestState *from, QTestState *to)
{
g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
@@ -2137,7 +2137,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 = file_offset_fdset_start,
};
test_file_common(&args, false);
@@ -2323,7 +2323,8 @@ 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 *multifd_mapped_ram_fdset_dio_start(QTestState *from,
+ QTestState *to)
{
g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
@@ -2337,7 +2338,7 @@ static void *multifd_mapped_ram_fdset_dio(QTestState *from, QTestState *to)
return NULL;
}
-static void *multifd_mapped_ram_fdset(QTestState *from, QTestState *to)
+static void *multifd_mapped_ram_fdset_start(QTestState *from, QTestState *to)
{
g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
@@ -2356,8 +2357,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 = multifd_mapped_ram_fdset_start,
+ .end_hook = multifd_mapped_ram_fdset_end,
};
test_file_common(&args, true);
@@ -2370,8 +2371,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 = multifd_mapped_ram_fdset_dio_start,
+ .end_hook = multifd_mapped_ram_fdset_end,
};
if (!probe_o_direct_support(tmpfs)) {
@@ -2392,7 +2393,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 +2409,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 +2425,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 +2439,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 +2452,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 +2463,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 +2477,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 +2489,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 +2503,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 +2515,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 +2529,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 +2540,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 +2568,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,8 +2600,8 @@ 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);
}
@@ -2639,7 +2640,7 @@ static void test_migrate_precopy_fd_file(void)
.listen_uri = "defer",
.connect_uri = "fd:fd-mig",
.start_hook = migrate_precopy_fd_file_start,
- .finish_hook = test_migrate_fd_finish_hook
+ .end_hook = migrate_hook_end_fd,
};
test_file_common(&args, true);
}
@@ -2886,7 +2887,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 +2907,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,
+test_migration_precopy_tcp_multifd_no_zero_page_start(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 +2942,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 +2990,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 +3005,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 +3020,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 = test_migration_precopy_tcp_multifd_no_zero_page_start,
/*
* Multifd is more complicated than most of the features, it
* directly takes guest page buffers when sending, make sure
@@ -3034,7 +3035,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 +3050,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 +3060,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 +3071,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 +3082,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 +3093,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 +3101,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 +3162,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 +3175,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 +3187,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 +3197,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 +3223,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 +3234,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 +3247,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);
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 02/22] tests/qtest/migration: Standardize hook names
2024-11-13 19:46 ` [PATCH v2 02/22] tests/qtest/migration: Standardize hook names Fabiano Rosas
@ 2024-11-25 20:51 ` Peter Xu
2024-11-25 21:03 ` Fabiano Rosas
0 siblings, 1 reply; 55+ messages in thread
From: Peter Xu @ 2024-11-25 20:51 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
On Wed, Nov 13, 2024 at 04:46:10PM -0300, Fabiano Rosas wrote:
> static void *
> -test_migration_precopy_tcp_multifd_start_no_zero_page(QTestState *from,
> +test_migration_precopy_tcp_multifd_no_zero_page_start(QTestState *from,
Looks like this one is leftover to do s/test_migration/migrate_hook/ (and
below when referenced)?
Other than that:
Reviewed-by: Peter Xu <peterx@redhat.com>
> 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;
> }
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread* Re: [PATCH v2 02/22] tests/qtest/migration: Standardize hook names
2024-11-25 20:51 ` Peter Xu
@ 2024-11-25 21:03 ` Fabiano Rosas
0 siblings, 0 replies; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-25 21:03 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
Peter Xu <peterx@redhat.com> writes:
> On Wed, Nov 13, 2024 at 04:46:10PM -0300, Fabiano Rosas wrote:
>> static void *
>> -test_migration_precopy_tcp_multifd_start_no_zero_page(QTestState *from,
>> +test_migration_precopy_tcp_multifd_no_zero_page_start(QTestState *from,
>
> Looks like this one is leftover to do s/test_migration/migrate_hook/ (and
> below when referenced)?
Good catch! Thanks
>
> Other than that:
>
> Reviewed-by: Peter Xu <peterx@redhat.com>
>
>> 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;
>> }
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 03/22] tests/qtest/migration: Stop calling everything "test"
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
2024-11-13 19:46 ` [PATCH v2 01/22] tests/qtest/migration: Fix indentations Fabiano Rosas
2024-11-13 19:46 ` [PATCH v2 02/22] tests/qtest/migration: Standardize hook names Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 20:47 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 04/22] tests/migration: Disambiguate guestperf vs. a-b Fabiano Rosas
` (19 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
note: I would prefer the prefix "mig_" to avoid using "migrate_" which
is a verb, but several functions such as migrate_qmp() would have to
be given an entirely new name to keep expressiveness and I want to
keep this a mechanical change.
---
tests/qtest/migration-test.c | 72 ++++++++++++++++++------------------
1 file changed, 36 insertions(+), 36 deletions(-)
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 68fa24edb4..74df687c78 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
@@ -2595,7 +2595,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",
@@ -2634,7 +2634,7 @@ static void *migrate_precopy_fd_file_start(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",
@@ -2651,7 +2651,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;
}
@@ -2675,7 +2675,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)
@@ -2723,7 +2723,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;
}
@@ -2735,7 +2735,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)
@@ -2783,7 +2783,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 = {};
@@ -2799,7 +2799,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;
}
@@ -2883,7 +2883,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 *
@@ -3274,7 +3274,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;
}
@@ -3306,7 +3306,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
@@ -3318,7 +3318,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;
}
@@ -3342,7 +3342,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)
@@ -3628,7 +3628,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;
@@ -3659,7 +3659,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;
}
@@ -3705,7 +3705,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;
}
@@ -3749,7 +3749,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)
@@ -3952,9 +3952,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",
@@ -3972,11 +3972,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",
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 03/22] tests/qtest/migration: Stop calling everything "test"
2024-11-13 19:46 ` [PATCH v2 03/22] tests/qtest/migration: Stop calling everything "test" Fabiano Rosas
@ 2024-11-25 20:47 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 20:47 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
On Wed, Nov 13, 2024 at 04:46:11PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 04/22] tests/migration: Disambiguate guestperf vs. a-b
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (2 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 03/22] tests/qtest/migration: Stop calling everything "test" Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-21 21:05 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 05/22] tests/qtest/migration: Move bootfile code to its own file Fabiano Rosas
` (18 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
MAINTAINERS | 5 +++--
tests/meson.build | 2 +-
.../{migration => migration-stress}/guestperf-batch.py | 0
.../{migration => migration-stress}/guestperf-plot.py | 0
tests/{migration => migration-stress}/guestperf.py | 0
.../guestperf/__init__.py | 0
.../guestperf/comparison.py | 0
.../guestperf/engine.py | 0
.../guestperf/hardware.py | 0
.../{migration => migration-stress}/guestperf/plot.py | 0
.../guestperf/progress.py | 0
.../guestperf/report.py | 0
.../guestperf/scenario.py | 0
.../{migration => migration-stress}/guestperf/shell.py | 0
.../guestperf/timings.py | 0
tests/{migration => migration-stress}/initrd-stress.sh | 0
tests/{migration => migration-stress}/meson.build | 0
tests/{migration => migration-stress}/stress.c | 0
tests/qtest/migration-test.c | 10 +++++-----
tests/{ => qtest}/migration/Makefile | 0
tests/{ => qtest}/migration/aarch64/Makefile | 0
tests/{ => qtest}/migration/aarch64/a-b-kernel.S | 0
tests/{ => qtest}/migration/aarch64/a-b-kernel.h | 0
tests/{ => qtest}/migration/i386/Makefile | 0
tests/{ => qtest}/migration/i386/a-b-bootblock.S | 0
tests/{ => qtest}/migration/i386/a-b-bootblock.h | 0
tests/{ => qtest}/migration/migration-test.h | 0
tests/{ => qtest}/migration/ppc64/Makefile | 0
tests/{ => qtest}/migration/ppc64/a-b-kernel.S | 0
tests/{ => qtest}/migration/ppc64/a-b-kernel.h | 0
tests/{ => qtest}/migration/s390x/Makefile | 0
tests/{ => qtest}/migration/s390x/a-b-bios.c | 0
tests/{ => qtest}/migration/s390x/a-b-bios.h | 0
33 files changed, 9 insertions(+), 8 deletions(-)
rename tests/{migration => migration-stress}/guestperf-batch.py (100%)
rename tests/{migration => migration-stress}/guestperf-plot.py (100%)
rename tests/{migration => migration-stress}/guestperf.py (100%)
rename tests/{migration => migration-stress}/guestperf/__init__.py (100%)
rename tests/{migration => migration-stress}/guestperf/comparison.py (100%)
rename tests/{migration => migration-stress}/guestperf/engine.py (100%)
rename tests/{migration => migration-stress}/guestperf/hardware.py (100%)
rename tests/{migration => migration-stress}/guestperf/plot.py (100%)
rename tests/{migration => migration-stress}/guestperf/progress.py (100%)
rename tests/{migration => migration-stress}/guestperf/report.py (100%)
rename tests/{migration => migration-stress}/guestperf/scenario.py (100%)
rename tests/{migration => migration-stress}/guestperf/shell.py (100%)
rename tests/{migration => migration-stress}/guestperf/timings.py (100%)
rename tests/{migration => migration-stress}/initrd-stress.sh (100%)
rename tests/{migration => migration-stress}/meson.build (100%)
rename tests/{migration => migration-stress}/stress.c (100%)
rename tests/{ => qtest}/migration/Makefile (100%)
rename tests/{ => qtest}/migration/aarch64/Makefile (100%)
rename tests/{ => qtest}/migration/aarch64/a-b-kernel.S (100%)
rename tests/{ => qtest}/migration/aarch64/a-b-kernel.h (100%)
rename tests/{ => qtest}/migration/i386/Makefile (100%)
rename tests/{ => qtest}/migration/i386/a-b-bootblock.S (100%)
rename tests/{ => qtest}/migration/i386/a-b-bootblock.h (100%)
rename tests/{ => qtest}/migration/migration-test.h (100%)
rename tests/{ => qtest}/migration/ppc64/Makefile (100%)
rename tests/{ => qtest}/migration/ppc64/a-b-kernel.S (100%)
rename tests/{ => qtest}/migration/ppc64/a-b-kernel.h (100%)
rename tests/{ => qtest}/migration/s390x/Makefile (100%)
rename tests/{ => qtest}/migration/s390x/a-b-bios.c (100%)
rename tests/{ => qtest}/migration/s390x/a-b-bios.h (100%)
diff --git a/MAINTAINERS b/MAINTAINERS
index 095420f8b0..068234d2c3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -118,7 +118,7 @@ F: pc-bios/s390-ccw.img
F: target/s390x/
F: docs/system/target-s390x.rst
F: docs/system/s390x/
-F: tests/migration/s390x/
+F: tests/qtest/migration/s390x/
K: ^Subject:.*(?i)s390x?
L: qemu-s390x@nongnu.org
@@ -3416,10 +3416,11 @@ F: include/qemu/userfaultfd.h
F: migration/
F: scripts/vmstate-static-checker.py
F: tests/vmstate-static-checker-data/
+F: tests/qtest/migration/
F: tests/qtest/migration-*
F: docs/devel/migration/
F: qapi/migration.json
-F: tests/migration/
+F: tests/migration-stress/
F: util/userfaultfd.c
X: migration/rdma*
diff --git a/tests/meson.build b/tests/meson.build
index 907a4c1c98..f96c1be574 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/guestperf-batch.py b/tests/migration-stress/guestperf-batch.py
similarity index 100%
rename from tests/migration/guestperf-batch.py
rename to tests/migration-stress/guestperf-batch.py
diff --git a/tests/migration/guestperf-plot.py b/tests/migration-stress/guestperf-plot.py
similarity index 100%
rename from tests/migration/guestperf-plot.py
rename to tests/migration-stress/guestperf-plot.py
diff --git a/tests/migration/guestperf.py b/tests/migration-stress/guestperf.py
similarity index 100%
rename from tests/migration/guestperf.py
rename to tests/migration-stress/guestperf.py
diff --git a/tests/migration/guestperf/__init__.py b/tests/migration-stress/guestperf/__init__.py
similarity index 100%
rename from tests/migration/guestperf/__init__.py
rename to tests/migration-stress/guestperf/__init__.py
diff --git a/tests/migration/guestperf/comparison.py b/tests/migration-stress/guestperf/comparison.py
similarity index 100%
rename from tests/migration/guestperf/comparison.py
rename to tests/migration-stress/guestperf/comparison.py
diff --git a/tests/migration/guestperf/engine.py b/tests/migration-stress/guestperf/engine.py
similarity index 100%
rename from tests/migration/guestperf/engine.py
rename to tests/migration-stress/guestperf/engine.py
diff --git a/tests/migration/guestperf/hardware.py b/tests/migration-stress/guestperf/hardware.py
similarity index 100%
rename from tests/migration/guestperf/hardware.py
rename to tests/migration-stress/guestperf/hardware.py
diff --git a/tests/migration/guestperf/plot.py b/tests/migration-stress/guestperf/plot.py
similarity index 100%
rename from tests/migration/guestperf/plot.py
rename to tests/migration-stress/guestperf/plot.py
diff --git a/tests/migration/guestperf/progress.py b/tests/migration-stress/guestperf/progress.py
similarity index 100%
rename from tests/migration/guestperf/progress.py
rename to tests/migration-stress/guestperf/progress.py
diff --git a/tests/migration/guestperf/report.py b/tests/migration-stress/guestperf/report.py
similarity index 100%
rename from tests/migration/guestperf/report.py
rename to tests/migration-stress/guestperf/report.py
diff --git a/tests/migration/guestperf/scenario.py b/tests/migration-stress/guestperf/scenario.py
similarity index 100%
rename from tests/migration/guestperf/scenario.py
rename to tests/migration-stress/guestperf/scenario.py
diff --git a/tests/migration/guestperf/shell.py b/tests/migration-stress/guestperf/shell.py
similarity index 100%
rename from tests/migration/guestperf/shell.py
rename to tests/migration-stress/guestperf/shell.py
diff --git a/tests/migration/guestperf/timings.py b/tests/migration-stress/guestperf/timings.py
similarity index 100%
rename from tests/migration/guestperf/timings.py
rename to tests/migration-stress/guestperf/timings.py
diff --git a/tests/migration/initrd-stress.sh b/tests/migration-stress/initrd-stress.sh
similarity index 100%
rename from tests/migration/initrd-stress.sh
rename to tests/migration-stress/initrd-stress.sh
diff --git a/tests/migration/meson.build b/tests/migration-stress/meson.build
similarity index 100%
rename from tests/migration/meson.build
rename to tests/migration-stress/meson.build
diff --git a/tests/migration/stress.c b/tests/migration-stress/stress.c
similarity index 100%
rename from tests/migration/stress.c
rename to tests/migration-stress/stress.c
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 74df687c78..ff778ac659 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/migration/Makefile b/tests/qtest/migration/Makefile
similarity index 100%
rename from tests/migration/Makefile
rename to tests/qtest/migration/Makefile
diff --git a/tests/migration/aarch64/Makefile b/tests/qtest/migration/aarch64/Makefile
similarity index 100%
rename from tests/migration/aarch64/Makefile
rename to tests/qtest/migration/aarch64/Makefile
diff --git a/tests/migration/aarch64/a-b-kernel.S b/tests/qtest/migration/aarch64/a-b-kernel.S
similarity index 100%
rename from tests/migration/aarch64/a-b-kernel.S
rename to tests/qtest/migration/aarch64/a-b-kernel.S
diff --git a/tests/migration/aarch64/a-b-kernel.h b/tests/qtest/migration/aarch64/a-b-kernel.h
similarity index 100%
rename from tests/migration/aarch64/a-b-kernel.h
rename to tests/qtest/migration/aarch64/a-b-kernel.h
diff --git a/tests/migration/i386/Makefile b/tests/qtest/migration/i386/Makefile
similarity index 100%
rename from tests/migration/i386/Makefile
rename to tests/qtest/migration/i386/Makefile
diff --git a/tests/migration/i386/a-b-bootblock.S b/tests/qtest/migration/i386/a-b-bootblock.S
similarity index 100%
rename from tests/migration/i386/a-b-bootblock.S
rename to tests/qtest/migration/i386/a-b-bootblock.S
diff --git a/tests/migration/i386/a-b-bootblock.h b/tests/qtest/migration/i386/a-b-bootblock.h
similarity index 100%
rename from tests/migration/i386/a-b-bootblock.h
rename to tests/qtest/migration/i386/a-b-bootblock.h
diff --git a/tests/migration/migration-test.h b/tests/qtest/migration/migration-test.h
similarity index 100%
rename from tests/migration/migration-test.h
rename to tests/qtest/migration/migration-test.h
diff --git a/tests/migration/ppc64/Makefile b/tests/qtest/migration/ppc64/Makefile
similarity index 100%
rename from tests/migration/ppc64/Makefile
rename to tests/qtest/migration/ppc64/Makefile
diff --git a/tests/migration/ppc64/a-b-kernel.S b/tests/qtest/migration/ppc64/a-b-kernel.S
similarity index 100%
rename from tests/migration/ppc64/a-b-kernel.S
rename to tests/qtest/migration/ppc64/a-b-kernel.S
diff --git a/tests/migration/ppc64/a-b-kernel.h b/tests/qtest/migration/ppc64/a-b-kernel.h
similarity index 100%
rename from tests/migration/ppc64/a-b-kernel.h
rename to tests/qtest/migration/ppc64/a-b-kernel.h
diff --git a/tests/migration/s390x/Makefile b/tests/qtest/migration/s390x/Makefile
similarity index 100%
rename from tests/migration/s390x/Makefile
rename to tests/qtest/migration/s390x/Makefile
diff --git a/tests/migration/s390x/a-b-bios.c b/tests/qtest/migration/s390x/a-b-bios.c
similarity index 100%
rename from tests/migration/s390x/a-b-bios.c
rename to tests/qtest/migration/s390x/a-b-bios.c
diff --git a/tests/migration/s390x/a-b-bios.h b/tests/qtest/migration/s390x/a-b-bios.h
similarity index 100%
rename from tests/migration/s390x/a-b-bios.h
rename to tests/qtest/migration/s390x/a-b-bios.h
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 04/22] tests/migration: Disambiguate guestperf vs. a-b
2024-11-13 19:46 ` [PATCH v2 04/22] tests/migration: Disambiguate guestperf vs. a-b Fabiano Rosas
@ 2024-11-21 21:05 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-21 21:05 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
On Wed, Nov 13, 2024 at 04:46:12PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
Generally makes sense to me.
Reviewed-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 05/22] tests/qtest/migration: Move bootfile code to its own file
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (3 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 04/22] tests/migration: Disambiguate guestperf vs. a-b Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-13 19:46 ` [PATCH v2 06/22] tests/qtest/migration: Move qmp helpers to a separate file Fabiano Rosas
` (17 subsequent siblings)
22 siblings, 0 replies; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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é <berrange@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 8 ++-
tests/qtest/migration-helpers.c | 1 +
tests/qtest/migration-test.c | 58 +--------------
tests/qtest/migration/bootfile.c | 70 +++++++++++++++++++
.../{migration-test.h => bootfile.h} | 9 ++-
5 files changed, 86 insertions(+), 60 deletions(-)
create mode 100644 tests/qtest/migration/bootfile.c
rename tests/qtest/migration/{migration-test.h => bootfile.h} (85%)
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index aa93e98418..16823a9202 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -330,7 +330,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]
@@ -356,7 +360,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'),
}
diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c
index 3f8ba7fa8e..2786f9c860 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 ff778ac659..0059e1bbbb 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) {
@@ -3483,7 +3431,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 0000000000..8f75f64093
--- /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/migration-test.h b/tests/qtest/migration/bootfile.h
similarity index 85%
rename from tests/qtest/migration/migration-test.h
rename to tests/qtest/migration/bootfile.h
index 194df7df6f..4f5099d765 100644
--- a/tests/qtest/migration/migration-test.h
+++ b/tests/qtest/migration/bootfile.h
@@ -5,8 +5,8 @@
* See the COPYING file in the top-level directory.
*/
-#ifndef MIGRATION_TEST_H
-#define MIGRATION_TEST_H
+#ifndef BOOTFILE_H
+#define BOOTFILE_H
/* Common */
#define TEST_MEM_PAGE_SIZE 4096
@@ -33,4 +33,7 @@
*/
#define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024)
-#endif /* MIGRATION_TEST_H */
+void bootfile_delete(void);
+char *bootfile_create(const char *arch, char *dir, bool suspend_me);
+
+#endif /* BOOTFILE_H */
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* [PATCH v2 06/22] tests/qtest/migration: Move qmp helpers to a separate file
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (4 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 05/22] tests/qtest/migration: Move bootfile code to its own file Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-21 23:00 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 07/22] tests/qtest/migration: Rename migration-helpers.c Fabiano Rosas
` (16 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 1 +
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, 540 insertions(+), 496 deletions(-)
create mode 100644 tests/qtest/migration/migration-qmp.c
create mode 100644 tests/qtest/migration/migration-qmp.h
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 16823a9202..ca199b9491 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -333,6 +333,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()
diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c
index 2786f9c860..218ee4e59f 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 72dba369fb..2cb1f78d9e 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 0059e1bbbb..a0c63026ed 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 0000000000..20be46fdf6
--- /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 0000000000..ed927cf408
--- /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 73dfabc272..28a6147d9a 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"
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 06/22] tests/qtest/migration: Move qmp helpers to a separate file
2024-11-13 19:46 ` [PATCH v2 06/22] tests/qtest/migration: Move qmp helpers to a separate file Fabiano Rosas
@ 2024-11-21 23:00 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-21 23:00 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:14PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
> tests/qtest/meson.build | 1 +
> 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, 540 insertions(+), 496 deletions(-)
> create mode 100644 tests/qtest/migration/migration-qmp.c
> create mode 100644 tests/qtest/migration/migration-qmp.h
>
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index 16823a9202..ca199b9491 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -333,6 +333,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',
> )]
Not relevant to this patch alone, but ... this looks like a nested list. I
assume meson does some auto-flatten things.
Reviewed-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 07/22] tests/qtest/migration: Rename migration-helpers.c
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (5 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 06/22] tests/qtest/migration: Move qmp helpers to a separate file Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-21 23:04 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 08/22] tests/qtest/migration: Move ufd_version_check to utils Fabiano Rosas
` (15 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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 <farosas@suse.de>
---
tests/qtest/meson.build | 4 ++--
tests/qtest/migration-test.c | 2 +-
tests/qtest/migration/migration-qmp.c | 2 +-
tests/qtest/migration/migration-qmp.h | 2 ++
.../{migration-helpers.c => migration/migration-util.c} | 4 ++--
.../{migration-helpers.h => migration/migration-util.h} | 9 ++++++---
tests/qtest/virtio-net-failover.c | 2 +-
7 files changed, 15 insertions(+), 10 deletions(-)
rename tests/qtest/{migration-helpers.c => migration/migration-util.c} (99%)
rename tests/qtest/{migration-helpers.h => migration/migration-util.h} (89%)
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index ca199b9491..b9f70ac32f 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -331,9 +331,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()
@@ -347,7 +347,7 @@ 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/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-test.c b/tests/qtest/migration-test.c
index a0c63026ed..59f02f7815 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 20be46fdf6..71b14b51b2 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 ed927cf408..caaa78722a 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-helpers.c b/tests/qtest/migration/migration-util.c
similarity index 99%
rename from tests/qtest/migration-helpers.c
rename to tests/qtest/migration/migration-util.c
index 218ee4e59f..8a974ded22 100644
--- a/tests/qtest/migration-helpers.c
+++ b/tests/qtest/migration/migration-util.c
@@ -1,5 +1,5 @@
/*
- * QTest migration helpers
+ * QTest migration utilities
*
* Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
* based on the vhost-user-test.c that is:
@@ -19,8 +19,8 @@
#include "qemu/cutils.h"
#include "qemu/memalign.h"
-#include "migration-helpers.h"
#include "migration/bootfile.h"
+#include "migration/migration-util.h"
static char *SocketAddress_to_str(SocketAddress *addr)
{
diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration/migration-util.h
similarity index 89%
rename from tests/qtest/migration-helpers.h
rename to tests/qtest/migration/migration-util.h
index 2cb1f78d9e..de1bc0ad0c 100644
--- a/tests/qtest/migration-helpers.h
+++ b/tests/qtest/migration/migration-util.h
@@ -10,11 +10,14 @@
*
*/
-#ifndef MIGRATION_HELPERS_H
-#define MIGRATION_HELPERS_H
+#ifndef MIGRATION_UTIL_H
+#define MIGRATION_UTIL_H
#include "libqtest.h"
+#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC"
+#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST"
+
typedef struct QTestMigrationState {
bool stop_seen;
bool resume_seen;
@@ -51,4 +54,4 @@ 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 */
+#endif /* MIGRATION_UTIL_H */
diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c
index 28a6147d9a..08365ffa11 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"
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 07/22] tests/qtest/migration: Rename migration-helpers.c
2024-11-13 19:46 ` [PATCH v2 07/22] tests/qtest/migration: Rename migration-helpers.c Fabiano Rosas
@ 2024-11-21 23:04 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-21 23:04 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:15PM -0300, Fabiano Rosas wrote:
> 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 <farosas@suse.de>
> ---
> tests/qtest/meson.build | 4 ++--
> tests/qtest/migration-test.c | 2 +-
> tests/qtest/migration/migration-qmp.c | 2 +-
> tests/qtest/migration/migration-qmp.h | 2 ++
> .../{migration-helpers.c => migration/migration-util.c} | 4 ++--
> .../{migration-helpers.h => migration/migration-util.h} | 9 ++++++---
> tests/qtest/virtio-net-failover.c | 2 +-
> 7 files changed, 15 insertions(+), 10 deletions(-)
> rename tests/qtest/{migration-helpers.c => migration/migration-util.c} (99%)
> rename tests/qtest/{migration-helpers.h => migration/migration-util.h} (89%)
>
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index ca199b9491..b9f70ac32f 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -331,9 +331,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()
> @@ -347,7 +347,7 @@ 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/migration-util.c') + dbus_vmstate1,
Seems like the -qmp.c part should belong to previous patch.
> '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-test.c b/tests/qtest/migration-test.c
> index a0c63026ed..59f02f7815 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 20be46fdf6..71b14b51b2 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 ed927cf408..caaa78722a 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-helpers.c b/tests/qtest/migration/migration-util.c
> similarity index 99%
> rename from tests/qtest/migration-helpers.c
> rename to tests/qtest/migration/migration-util.c
> index 218ee4e59f..8a974ded22 100644
> --- a/tests/qtest/migration-helpers.c
> +++ b/tests/qtest/migration/migration-util.c
> @@ -1,5 +1,5 @@
> /*
> - * QTest migration helpers
> + * QTest migration utilities
> *
> * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
> * based on the vhost-user-test.c that is:
> @@ -19,8 +19,8 @@
> #include "qemu/cutils.h"
> #include "qemu/memalign.h"
>
> -#include "migration-helpers.h"
> #include "migration/bootfile.h"
> +#include "migration/migration-util.h"
>
> static char *SocketAddress_to_str(SocketAddress *addr)
> {
> diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration/migration-util.h
> similarity index 89%
> rename from tests/qtest/migration-helpers.h
> rename to tests/qtest/migration/migration-util.h
> index 2cb1f78d9e..de1bc0ad0c 100644
> --- a/tests/qtest/migration-helpers.h
> +++ b/tests/qtest/migration/migration-util.h
> @@ -10,11 +10,14 @@
> *
> */
>
> -#ifndef MIGRATION_HELPERS_H
> -#define MIGRATION_HELPERS_H
> +#ifndef MIGRATION_UTIL_H
> +#define MIGRATION_UTIL_H
>
> #include "libqtest.h"
>
> +#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC"
> +#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST"
Are these re-defined in migration-test.c?
> +
> typedef struct QTestMigrationState {
> bool stop_seen;
> bool resume_seen;
> @@ -51,4 +54,4 @@ 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 */
> +#endif /* MIGRATION_UTIL_H */
> diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c
> index 28a6147d9a..08365ffa11 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"
> --
> 2.35.3
>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 08/22] tests/qtest/migration: Move ufd_version_check to utils
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (6 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 07/22] tests/qtest/migration: Rename migration-helpers.c Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:17 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 09/22] tests/qtest/migration: Move kvm_dirty_ring_supported " Fabiano Rosas
` (14 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
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(-)
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 59f02f7815..40e11e2af0 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 <sys/ioctl.h>
#endif
unsigned start_address;
@@ -88,50 +89,6 @@ typedef enum PostcopyRecoveryFailStage {
#include <sys/vfs.h>
#endif
-#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
-#include <sys/eventfd.h>
-#include <sys/ioctl.h>
-#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;
@@ -3527,7 +3484,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 8a974ded22..2ff8870fd5 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 <sys/eventfd.h>
+#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 de1bc0ad0c..e6bfd25244 100644
--- a/tests/qtest/migration/migration-util.h
+++ b/tests/qtest/migration/migration-util.h
@@ -50,6 +50,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);
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* [PATCH v2 09/22] tests/qtest/migration: Move kvm_dirty_ring_supported to utils
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (7 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 08/22] tests/qtest/migration: Move ufd_version_check to utils Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:18 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 10/22] tests/qtest/migration: Isolate test initialization Fabiano Rosas
` (13 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
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(-)
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 40e11e2af0..01ef0014bd 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 <sys/ioctl.h>
-#endif
-
unsigned start_address;
unsigned end_address;
static bool uffd_feature_thread_id;
@@ -3430,29 +3424,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 2ff8870fd5..525bf1eed4 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 <sys/ioctl.h>
+#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 e6bfd25244..9127702e70 100644
--- a/tests/qtest/migration/migration-util.h
+++ b/tests/qtest/migration/migration-util.h
@@ -52,6 +52,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);
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 09/22] tests/qtest/migration: Move kvm_dirty_ring_supported to utils
2024-11-13 19:46 ` [PATCH v2 09/22] tests/qtest/migration: Move kvm_dirty_ring_supported " Fabiano Rosas
@ 2024-11-25 17:18 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 17:18 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:17PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 10/22] tests/qtest/migration: Isolate test initialization
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (8 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 09/22] tests/qtest/migration: Move kvm_dirty_ring_supported " Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:29 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 11/22] tests/qtest/migration: Move common test code Fabiano Rosas
` (12 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/migration-test.c | 122 ++++++++++++++++++++++++-----------
1 file changed, 86 insertions(+), 36 deletions(-)
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 01ef0014bd..5e426a8885 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);
}
@@ -3424,63 +3441,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);
@@ -3492,7 +3545,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);
}
@@ -3547,7 +3600,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
@@ -3622,9 +3675,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);
}
}
@@ -3675,7 +3728,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()) {
@@ -3688,13 +3743,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;
}
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 10/22] tests/qtest/migration: Isolate test initialization
2024-11-13 19:46 ` [PATCH v2 10/22] tests/qtest/migration: Isolate test initialization Fabiano Rosas
@ 2024-11-25 17:29 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 17:29 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
On Wed, Nov 13, 2024 at 04:46:18PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 11/22] tests/qtest/migration: Move common test code
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (9 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 10/22] tests/qtest/migration: Isolate test initialization Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:31 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 12/22] tests/qtest/migration: Split TLS tests from migration-test.c Fabiano Rosas
` (11 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
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/test-framework.c | 969 ++++++++++++++++++++
tests/qtest/migration/test-framework.h | 216 +++++
6 files changed, 1201 insertions(+), 1129 deletions(-)
create mode 100644 tests/qtest/migration/test-framework.c
create mode 100644 tests/qtest/migration/test-framework.h
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index b9f70ac32f..bdb9512510 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -332,6 +332,7 @@ tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c']
migration_files = [files(
'migration/bootfile.c',
+ 'migration/test-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 5e426a8885..8d3b61cf30 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/test-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 <sys/ioctl.h>
#include <sys/syscall.h>
#include <sys/vfs.h>
#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");
@@ -2519,7 +1491,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);
@@ -2575,26 +1547,6 @@ static void test_auto_converge(void)
migrate_end(from, to, true);
}
-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)
@@ -3026,7 +1978,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");
@@ -3171,6 +2123,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 "
@@ -3186,8 +2139,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)
@@ -3365,7 +2320,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 */
@@ -3378,7 +2333,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 */
@@ -3408,7 +2363,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);
}
/*
@@ -3441,75 +2396,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 8f75f64093..fac059d11d 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 4f5099d765..6d6a67386e 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/test-framework.c b/tests/qtest/migration/test-framework.c
new file mode 100644
index 0000000000..3b9ca6c133
--- /dev/null
+++ b/tests/qtest/migration/test-framework.c
@@ -0,0 +1,969 @@
+/*
+ * 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/test-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
+
+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/test-framework.h b/tests/qtest/migration/test-framework.h
new file mode 100644
index 0000000000..121078cf4f
--- /dev/null
+++ b/tests/qtest/migration/test-framework.h
@@ -0,0 +1,216 @@
+/*
+ * 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
+
+#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 */
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 11/22] tests/qtest/migration: Move common test code
2024-11-13 19:46 ` [PATCH v2 11/22] tests/qtest/migration: Move common test code Fabiano Rosas
@ 2024-11-25 17:31 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 17:31 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:19PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
> 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/test-framework.c | 969 ++++++++++++++++++++
> tests/qtest/migration/test-framework.h | 216 +++++
> 6 files changed, 1201 insertions(+), 1129 deletions(-)
> create mode 100644 tests/qtest/migration/test-framework.c
> create mode 100644 tests/qtest/migration/test-framework.h
>
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index b9f70ac32f..bdb9512510 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -332,6 +332,7 @@ tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c']
>
> migration_files = [files(
> 'migration/bootfile.c',
> + 'migration/test-framework.c',
Nit: considering your previous ways of renaming things (where you tend to
drop "test" prefixes all over the places), maybe framework.c suites more.
Acked-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 12/22] tests/qtest/migration: Split TLS tests from migration-test.c
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (10 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 11/22] tests/qtest/migration: Move common test code Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:48 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 13/22] tests/qtest/migration: Split compression " Fabiano Rosas
` (10 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 8 +-
tests/qtest/migration-test.c | 788 +-----------------------
tests/qtest/migration/test-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
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index bdb9512510..9ad9f0dc65 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -337,11 +337,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'),
+ files('../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
@@ -351,7 +353,7 @@ qtests = {
'dbus-vmstate-test': files('migration/migration-qmp.c', '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 8d3b61cf30..1cd14529a1 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -26,12 +26,6 @@
#include "migration/test-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)
@@ -1081,125 +639,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)
@@ -1740,163 +1179,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
@@ -2407,6 +1689,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);
@@ -2482,61 +1766,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",
@@ -2595,24 +1829,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/test-framework.h b/tests/qtest/migration/test-framework.h
index 121078cf4f..c9008a3227 100644
--- a/tests/qtest/migration/test-framework.h
+++ b/tests/qtest/migration/test-framework.h
@@ -213,4 +213,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 0000000000..7609183474
--- /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/test-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 */
+}
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 12/22] tests/qtest/migration: Split TLS tests from migration-test.c
2024-11-13 19:46 ` [PATCH v2 12/22] tests/qtest/migration: Split TLS tests from migration-test.c Fabiano Rosas
@ 2024-11-25 17:48 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 17:48 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:20PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
> tests/qtest/meson.build | 8 +-
> tests/qtest/migration-test.c | 788 +-----------------------
> tests/qtest/migration/test-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
>
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index bdb9512510..9ad9f0dc65 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -337,11 +337,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'),
> + files('../unit/crypto-tls-psk-helpers.c'), gnutls]
Nit: not a meson expert, but I think we could simply use files(A, B)
instead of files(A), files(B)..
Acked-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 13/22] tests/qtest/migration: Split compression tests from migration-test.c
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (11 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 12/22] tests/qtest/migration: Split TLS tests from migration-test.c Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:50 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 14/22] tests/qtest/migration: Split postcopy tests Fabiano Rosas
` (9 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
Continuing the split of groups of tests from migration-test.c, split
the compression tests into their own file.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 1 +
tests/qtest/migration-test.c | 161 +--------------
tests/qtest/migration/compression-tests.c | 239 ++++++++++++++++++++++
tests/qtest/migration/test-framework.h | 1 +
4 files changed, 242 insertions(+), 160 deletions(-)
create mode 100644 tests/qtest/migration/compression-tests.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 9ad9f0dc65..5e77eb09f2 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -335,6 +335,7 @@ migration_files = [files(
'migration/test-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 1cd14529a1..4a468079e8 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,
@@ -1011,61 +981,6 @@ test_migration_precopy_tcp_multifd_no_zero_page_start(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 = {
@@ -1126,59 +1041,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
@@ -1690,6 +1552,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
@@ -1723,10 +1586,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",
@@ -1811,24 +1670,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 0000000000..1b4c59338c
--- /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/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "migration/test-framework.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/test-framework.h b/tests/qtest/migration/test-framework.h
index c9008a3227..0f16f37426 100644
--- a/tests/qtest/migration/test-framework.h
+++ b/tests/qtest/migration/test-framework.h
@@ -218,5 +218,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 */
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 13/22] tests/qtest/migration: Split compression tests from migration-test.c
2024-11-13 19:46 ` [PATCH v2 13/22] tests/qtest/migration: Split compression " Fabiano Rosas
@ 2024-11-25 17:50 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 17:50 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:21PM -0300, Fabiano Rosas wrote:
> Continuing the split of groups of tests from migration-test.c, split
> the compression tests into their own file.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 14/22] tests/qtest/migration: Split postcopy tests
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (12 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 13/22] tests/qtest/migration: Split compression " Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:51 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 15/22] tests/qtest/migration: Split file tests Fabiano Rosas
` (8 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 1 +
tests/qtest/migration-test.c | 78 +-----------------
tests/qtest/migration/postcopy-tests.c | 106 +++++++++++++++++++++++++
tests/qtest/migration/test-framework.h | 1 +
4 files changed, 109 insertions(+), 77 deletions(-)
create mode 100644 tests/qtest/migration/postcopy-tests.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 5e77eb09f2..23b600e6ec 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -336,6 +336,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 4a468079e8..d1d67fc42f 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 = {
@@ -1553,6 +1494,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
@@ -1566,24 +1508,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/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c
new file mode 100644
index 0000000000..9e2032bbf3
--- /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/test-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);
+ }
+ }
+}
diff --git a/tests/qtest/migration/test-framework.h b/tests/qtest/migration/test-framework.h
index 0f16f37426..d5197e8da1 100644
--- a/tests/qtest/migration/test-framework.h
+++ b/tests/qtest/migration/test-framework.h
@@ -219,5 +219,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 */
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 14/22] tests/qtest/migration: Split postcopy tests
2024-11-13 19:46 ` [PATCH v2 14/22] tests/qtest/migration: Split postcopy tests Fabiano Rosas
@ 2024-11-25 17:51 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 17:51 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:22PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 15/22] tests/qtest/migration: Split file tests
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (13 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 14/22] tests/qtest/migration: Split postcopy tests Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:52 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 16/22] tests/qtest/migration: Split precopy tests Fabiano Rosas
` (7 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 1 +
tests/qtest/migration-test.c | 306 +----------------------
tests/qtest/migration/file-tests.c | 332 +++++++++++++++++++++++++
tests/qtest/migration/test-framework.h | 1 +
4 files changed, 335 insertions(+), 305 deletions(-)
create mode 100644 tests/qtest/migration/file-tests.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 23b600e6ec..ba07ca89bb 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -336,6 +336,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 d1d67fc42f..4138a2ebd8 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -215,92 +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 *file_offset_fdset_start(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 = file_offset_fdset_start,
- };
-
- 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 *test_mode_reboot_start(QTestState *from, QTestState *to)
{
migrate_set_parameter_str(from, "mode", "cpr-reboot");
@@ -312,14 +226,6 @@ static void *test_mode_reboot_start(QTestState *from, QTestState *to)
return NULL;
}
-static void *migrate_mapped_ram_start(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,
@@ -334,186 +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_mapped_ram_start,
- };
-
- 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_mapped_ram_start,
- };
-
- test_file_common(&args, true);
-}
-
-static void *migrate_multifd_mapped_ram_start(QTestState *from, QTestState *to)
-{
- migrate_mapped_ram_start(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_multifd_mapped_ram_start,
- };
-
- 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_multifd_mapped_ram_start,
- };
-
- test_file_common(&args, true);
-}
-
-static void *multifd_mapped_ram_dio_start(QTestState *from, QTestState *to)
-{
- migrate_multifd_mapped_ram_start(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 = multifd_mapped_ram_dio_start,
- };
-
- 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 multifd_mapped_ram_fdset_end(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 *multifd_mapped_ram_fdset_dio_start(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_set_parameter_bool(from, "direct-io", true);
- migrate_set_parameter_bool(to, "direct-io", true);
-
- return NULL;
-}
-
-static void *multifd_mapped_ram_fdset_start(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);
-
- 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 = multifd_mapped_ram_fdset_start,
- .end_hook = multifd_mapped_ram_fdset_end,
- };
-
- 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 = multifd_mapped_ram_fdset_dio_start,
- .end_hook = multifd_mapped_ram_fdset_end,
- };
-
- 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 = {
@@ -1495,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
@@ -1510,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.
@@ -1529,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 0000000000..90b0386f58
--- /dev/null
+++ b/tests/qtest/migration/file-tests.c
@@ -0,0 +1,332 @@
+/*
+ * 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/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "migration/test-framework.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 *file_offset_fdset_start(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 = file_offset_fdset_start,
+ };
+
+ 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_mapped_ram_start(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_mapped_ram_start,
+ };
+
+ 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_mapped_ram_start,
+ };
+
+ test_file_common(&args, true);
+}
+
+static void *migrate_multifd_mapped_ram_start(QTestState *from, QTestState *to)
+{
+ migrate_mapped_ram_start(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_multifd_mapped_ram_start,
+ };
+
+ 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_multifd_mapped_ram_start,
+ };
+
+ test_file_common(&args, true);
+}
+
+static void *multifd_mapped_ram_dio_start(QTestState *from, QTestState *to)
+{
+ migrate_multifd_mapped_ram_start(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 = multifd_mapped_ram_dio_start,
+ };
+
+ 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 multifd_mapped_ram_fdset_end(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 *multifd_mapped_ram_fdset_dio_start(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_set_parameter_bool(from, "direct-io", true);
+ migrate_set_parameter_bool(to, "direct-io", true);
+
+ return NULL;
+}
+
+static void *multifd_mapped_ram_fdset_start(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);
+
+ 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 = multifd_mapped_ram_fdset_start,
+ .end_hook = multifd_mapped_ram_fdset_end,
+ };
+
+ 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 = multifd_mapped_ram_fdset_dio_start,
+ .end_hook = multifd_mapped_ram_fdset_end,
+ };
+
+ 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/test-framework.h b/tests/qtest/migration/test-framework.h
index d5197e8da1..59f9da18ed 100644
--- a/tests/qtest/migration/test-framework.h
+++ b/tests/qtest/migration/test-framework.h
@@ -220,5 +220,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 */
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* [PATCH v2 16/22] tests/qtest/migration: Split precopy tests
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (14 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 15/22] tests/qtest/migration: Split file tests Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:53 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 17/22] tests/qtest/migration: Split CPR tests Fabiano Rosas
` (6 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 1 +
tests/qtest/migration-test.c | 970 +----------------------
tests/qtest/migration/precopy-tests.c | 1007 ++++++++++++++++++++++++
tests/qtest/migration/test-framework.h | 1 +
4 files changed, 1011 insertions(+), 968 deletions(-)
create mode 100644 tests/qtest/migration/precopy-tests.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index ba07ca89bb..c1dcc67363 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -337,6 +337,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 4138a2ebd8..fa2fd0c672 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_precopy_fd_file_start(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_precopy_fd_file_start,
- .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 *
-test_migration_precopy_tcp_multifd_no_zero_page_start(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 = test_migration_precopy_tcp_multifd_no_zero_page_start,
- /*
- * 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/precopy-tests.c b/tests/qtest/migration/precopy-tests.c
new file mode 100644
index 0000000000..c7c802f812
--- /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/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "migration/test-framework.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 *
+test_migration_precopy_tcp_multifd_no_zero_page_start(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 = test_migration_precopy_tcp_multifd_no_zero_page_start,
+ /*
+ * 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);
+ }
+ }
+}
diff --git a/tests/qtest/migration/test-framework.h b/tests/qtest/migration/test-framework.h
index 59f9da18ed..4571326028 100644
--- a/tests/qtest/migration/test-framework.h
+++ b/tests/qtest/migration/test-framework.h
@@ -221,5 +221,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 */
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 16/22] tests/qtest/migration: Split precopy tests
2024-11-13 19:46 ` [PATCH v2 16/22] tests/qtest/migration: Split precopy tests Fabiano Rosas
@ 2024-11-25 17:53 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 17:53 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:24PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 17/22] tests/qtest/migration: Split CPR tests
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (15 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 16/22] tests/qtest/migration: Split precopy tests Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:54 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 18/22] tests/qtest/migration: Split validation tests + misc Fabiano Rosas
` (5 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 1 +
tests/qtest/migration-test.c | 34 +--------------
tests/qtest/migration/cpr-tests.c | 58 ++++++++++++++++++++++++++
tests/qtest/migration/test-framework.h | 1 +
4 files changed, 61 insertions(+), 33 deletions(-)
create mode 100644 tests/qtest/migration/cpr-tests.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index c1dcc67363..05d0e33d78 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -336,6 +336,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 fa2fd0c672..93f156f7af 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 *test_mode_reboot_start(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 = test_mode_reboot_start
- };
-
- 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 0000000000..92bd42e61a
--- /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/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "migration/test-framework.h"
+
+
+static char *tmpfs;
+
+static void *test_mode_reboot_start(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 = test_mode_reboot_start
+ };
+
+ 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/test-framework.h b/tests/qtest/migration/test-framework.h
index 4571326028..c1747a4e16 100644
--- a/tests/qtest/migration/test-framework.h
+++ b/tests/qtest/migration/test-framework.h
@@ -222,5 +222,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 */
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 17/22] tests/qtest/migration: Split CPR tests
2024-11-13 19:46 ` [PATCH v2 17/22] tests/qtest/migration: Split CPR tests Fabiano Rosas
@ 2024-11-25 17:54 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 17:54 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:25PM -0300, Fabiano Rosas wrote:
> 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.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Peter Xu <peterx@redhat.com>
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 18/22] tests/qtest/migration: Split validation tests + misc
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (16 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 17/22] tests/qtest/migration: Split CPR tests Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 17:55 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke Fabiano Rosas
` (4 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
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.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 1 +
tests/qtest/migration-test.c | 275 +------------------------
tests/qtest/migration/misc-tests.c | 275 +++++++++++++++++++++++++
tests/qtest/migration/test-framework.h | 1 +
4 files changed, 279 insertions(+), 273 deletions(-)
create mode 100644 tests/qtest/migration/misc-tests.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 05d0e33d78..2b12a4d263 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -338,6 +338,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 93f156f7af..4c8ea397ec 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -11,261 +11,11 @@
*/
#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/test-framework.h"
#include "migration/migration-qmp.h"
#include "migration/migration-util.h"
-
-#define ANALYZE_SCRIPT "scripts/analyze-migration.py"
-
-#if defined(__linux__)
-#include <sys/ioctl.h>
-#include <sys/syscall.h>
-#include <sys/vfs.h>
-#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
-
-#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;
-
- if (migrate_start(&from, &to, uri, false, true, NULL, NULL)) {
- 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"), <, 1024 * 1024);
-
- migrate_end(from, to, true);
-}
-#endif
-
-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)
{
@@ -276,39 +26,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
-
- /* 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/misc-tests.c b/tests/qtest/migration/misc-tests.c
new file mode 100644
index 0000000000..6f2bc5cca1
--- /dev/null
+++ b/tests/qtest/migration/misc-tests.c
@@ -0,0 +1,275 @@
+/*
+ * 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/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "migration/test-framework.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
+
+#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;
+
+ if (migrate_start(&from, &to, uri, false, true, NULL, NULL)) {
+ 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"), <, 1024 * 1024);
+
+ migrate_end(from, to, true);
+}
+#endif
+
+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
+
+ /* 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);
+}
diff --git a/tests/qtest/migration/test-framework.h b/tests/qtest/migration/test-framework.h
index c1747a4e16..207a11edb9 100644
--- a/tests/qtest/migration/test-framework.h
+++ b/tests/qtest/migration/test-framework.h
@@ -223,5 +223,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 */
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (17 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 18/22] tests/qtest/migration: Split validation tests + misc Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-12-18 17:46 ` Peter Xu
2024-11-13 19:46 ` [PATCH v2 20/22] tests/qtest/migration: Pick smoke tests Fabiano Rosas
` (3 subsequent siblings)
22 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
Add a new migration test to be ran as smoke test for each QEMU
target. This test will run only when the QEMU binary being used has no
KVM support, i.e. smoke tests run only on TCG.
Also modify the existing migration-test to run only when KVM is
present, i.e. the full set of tests will only run on a KVM host. To
still enable the full set to be ran anywhere for debug, ignore the
accel restriction when -m thorough is used.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/meson.build | 7 +++-
tests/qtest/migration-test-smoke.c | 39 +++++++++++++++++++++++
tests/qtest/migration-test.c | 12 +++++++
tests/qtest/migration/compression-tests.c | 8 ++++-
tests/qtest/migration/cpr-tests.c | 8 ++++-
tests/qtest/migration/file-tests.c | 8 ++++-
tests/qtest/migration/misc-tests.c | 8 ++++-
tests/qtest/migration/postcopy-tests.c | 7 ++++
tests/qtest/migration/precopy-tests.c | 8 ++++-
tests/qtest/migration/test-framework.h | 8 +++++
tests/qtest/migration/tls-tests.c | 7 +++-
11 files changed, 113 insertions(+), 7 deletions(-)
create mode 100644 tests/qtest/migration-test-smoke.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 2b12a4d263..811117d264 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -5,6 +5,7 @@ slow_qtests = {
'cdrom-test' : 610,
'device-introspect-test' : 720,
'ide-test' : 120,
+ 'migration-test-smoke' : 480,
'migration-test' : 480,
'npcm7xx_pwm-test': 300,
'npcm7xx_watchdog_timer-test': 120,
@@ -111,6 +112,7 @@ qtests_i386 = \
'device-plug-test',
'drive_del-test',
'cpu-plug-test',
+ 'migration-test-smoke',
'migration-test',
]
@@ -185,7 +187,7 @@ qtests_ppc64 = \
(slirp.found() ? ['pxe-test'] : []) + \
(config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \
(config_all_devices.has_key('CONFIG_USB_XHCI_NEC') ? ['usb-hcd-xhci-test'] : []) + \
- qtests_pci + ['migration-test', 'cpu-plug-test', 'drive_del-test']
+ qtests_pci + ['migration-test-smoke', 'migration-test', 'cpu-plug-test', 'drive_del-test']
qtests_sh4 = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : [])
qtests_sh4eb = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : [])
@@ -257,6 +259,7 @@ qtests_aarch64 = \
['arm-cpu-features',
'numa-test',
'boot-serial-test',
+ 'migration-test-smoke',
'migration-test']
qtests_s390x = \
@@ -266,6 +269,7 @@ qtests_s390x = \
'device-plug-test',
'virtio-ccw-test',
'cpu-plug-test',
+ 'migration-test-smoke',
'migration-test']
qtests_riscv32 = \
@@ -359,6 +363,7 @@ qtests = {
'dbus-vmstate-test': files('migration/migration-qmp.c', 'migration/migration-util.c') + dbus_vmstate1,
'erst-test': files('erst-test.c'),
'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'],
+ 'migration-test-smoke': migration_files + migration_tls_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'),
diff --git a/tests/qtest/migration-test-smoke.c b/tests/qtest/migration-test-smoke.c
new file mode 100644
index 0000000000..ff2d72881f
--- /dev/null
+++ b/tests/qtest/migration-test-smoke.c
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "migration/test-framework.h"
+#include "qemu/module.h"
+
+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);
+
+ if (env->has_kvm) {
+ g_test_message(
+ "Smoke tests already run as part of the full suite on KVM hosts");
+ goto out;
+ }
+
+ migration_test_add_tls_smoke(env);
+ migration_test_add_compression_smoke(env);
+ migration_test_add_postcopy_smoke(env);
+ migration_test_add_file_smoke(env);
+ migration_test_add_precopy_smoke(env);
+ migration_test_add_cpr_smoke(env);
+ migration_test_add_misc_smoke(env);
+
+out:
+ ret = g_test_run();
+
+ g_assert_cmpint(ret, ==, 0);
+
+ ret = migration_env_clean(env);
+
+ return ret;
+}
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 4c8ea397ec..73cb0bdfe6 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -26,6 +26,17 @@ int main(int argc, char **argv)
env = migration_get_env();
module_call_init(MODULE_INIT_QOM);
+ /*
+ * Restrict the full set of tests to KVM hosts only. For tests
+ * that run in all platforms, see migration-test-smoke.c. Ignore
+ * the restriction if -m thorough was passed in the command line.
+ */
+ if (!g_test_thorough() && !env->has_kvm) {
+ g_test_message("Full test suite only runs on KVM hosts "
+ "(override with -m thorough)");
+ goto out;
+ }
+
migration_test_add_tls(env);
migration_test_add_compression(env);
migration_test_add_postcopy(env);
@@ -34,6 +45,7 @@ int main(int argc, char **argv)
migration_test_add_cpr(env);
migration_test_add_misc(env);
+out:
ret = g_test_run();
g_assert_cmpint(ret, ==, 0);
diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c
index 1b4c59338c..b48dc87239 100644
--- a/tests/qtest/migration/compression-tests.c
+++ b/tests/qtest/migration/compression-tests.c
@@ -205,9 +205,15 @@ static void test_multifd_tcp_zlib(void)
test_precopy_common(&args);
}
-void migration_test_add_compression(MigrationTestEnv *env)
+void migration_test_add_compression_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ /* TODO: add smoke tests */
+}
+
+void migration_test_add_compression(MigrationTestEnv *env)
+{
+ migration_test_add_compression_smoke(env);
#ifdef CONFIG_ZSTD
migration_test_add("/migration/multifd/tcp/plain/zstd",
diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
index 92bd42e61a..4fe6eefe86 100644
--- a/tests/qtest/migration/cpr-tests.c
+++ b/tests/qtest/migration/cpr-tests.c
@@ -44,9 +44,15 @@ static void test_mode_reboot(void)
test_file_common(&args, true);
}
-void migration_test_add_cpr(MigrationTestEnv *env)
+void migration_test_add_cpr_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ /* TODO: add smoke tests */
+}
+
+void migration_test_add_cpr(MigrationTestEnv *env)
+{
+ migration_test_add_cpr_smoke(env);
/*
* Our CI system has problems with shared memory.
diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c
index 90b0386f58..10a5cb648d 100644
--- a/tests/qtest/migration/file-tests.c
+++ b/tests/qtest/migration/file-tests.c
@@ -294,9 +294,15 @@ static void test_multifd_file_mapped_ram_fdset_dio(void)
}
#endif /* !_WIN32 */
-void migration_test_add_file(MigrationTestEnv *env)
+void migration_test_add_file_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ /* TODO: add smoke tests */
+}
+
+void migration_test_add_file(MigrationTestEnv *env)
+{
+ migration_test_add_file_smoke(env);
migration_test_add("/migration/precopy/file",
test_precopy_file);
diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
index 6f2bc5cca1..480fbda1c9 100644
--- a/tests/qtest/migration/misc-tests.c
+++ b/tests/qtest/migration/misc-tests.c
@@ -250,9 +250,15 @@ static void test_validate_uri_channels_none_set(void)
do_test_validate_uri_channel(&args);
}
-void migration_test_add_misc(MigrationTestEnv *env)
+void migration_test_add_misc_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ /* TODO: add smoke tests */
+}
+
+void migration_test_add_misc(MigrationTestEnv *env)
+{
+ migration_test_add_misc_smoke(env);
migration_test_add("/migration/bad_dest", test_baddest);
#ifndef _WIN32
diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c
index 9e2032bbf3..90d2d0820c 100644
--- a/tests/qtest/migration/postcopy-tests.c
+++ b/tests/qtest/migration/postcopy-tests.c
@@ -79,8 +79,15 @@ static void test_postcopy_preempt_recovery(void)
test_postcopy_recovery_common(&args);
}
+void migration_test_add_postcopy_smoke(MigrationTestEnv *env)
+{
+ /* TODO: add smoke tests */
+}
+
void migration_test_add_postcopy(MigrationTestEnv *env)
{
+ migration_test_add_postcopy_smoke(env);
+
if (env->has_uffd) {
migration_test_add("/migration/postcopy/plain", test_postcopy);
migration_test_add("/migration/postcopy/recovery/plain",
diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c
index c7c802f812..393c7e226a 100644
--- a/tests/qtest/migration/precopy-tests.c
+++ b/tests/qtest/migration/precopy-tests.c
@@ -946,9 +946,15 @@ static void test_dirty_limit(void)
migrate_end(from, to, true);
}
-void migration_test_add_precopy(MigrationTestEnv *env)
+void migration_test_add_precopy_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ /* TODO: add smoke tests */
+}
+
+void migration_test_add_precopy(MigrationTestEnv *env)
+{
+ migration_test_add_precopy_smoke(env);
if (env->is_x86) {
migration_test_add("/migration/precopy/unix/suspend/live",
diff --git a/tests/qtest/migration/test-framework.h b/tests/qtest/migration/test-framework.h
index 207a11edb9..10cc4e524c 100644
--- a/tests/qtest/migration/test-framework.h
+++ b/tests/qtest/migration/test-framework.h
@@ -215,14 +215,22 @@ QTestMigrationState *get_src(void);
#ifdef CONFIG_GNUTLS
void migration_test_add_tls(MigrationTestEnv *env);
+void migration_test_add_tls_smoke(MigrationTestEnv *env);
#else
static inline void migration_test_add_tls(MigrationTestEnv *env) {};
+static inline void migration_test_add_tls_smoke(MigrationTestEnv *env) {}
#endif
void migration_test_add_compression(MigrationTestEnv *env);
+void migration_test_add_compression_smoke(MigrationTestEnv *env);
void migration_test_add_postcopy(MigrationTestEnv *env);
+void migration_test_add_postcopy_smoke(MigrationTestEnv *env);
void migration_test_add_file(MigrationTestEnv *env);
+void migration_test_add_file_smoke(MigrationTestEnv *env);
void migration_test_add_precopy(MigrationTestEnv *env);
+void migration_test_add_precopy_smoke(MigrationTestEnv *env);
void migration_test_add_cpr(MigrationTestEnv *env);
+void migration_test_add_cpr_smoke(MigrationTestEnv *env);
void migration_test_add_misc(MigrationTestEnv *env);
+void migration_test_add_misc_smoke(MigrationTestEnv *env);
#endif /* TEST_FRAMEWORK_H */
diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c
index 7609183474..264b54f352 100644
--- a/tests/qtest/migration/tls-tests.c
+++ b/tests/qtest/migration/tls-tests.c
@@ -722,10 +722,15 @@ static void test_multifd_tcp_tls_x509_reject_anon_client(void)
}
#endif /* CONFIG_TASN1 */
-void migration_test_add_tls(MigrationTestEnv *env)
+void migration_test_add_tls_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ /* TODO: add smoke tests */
+}
+void migration_test_add_tls(MigrationTestEnv *env)
+{
+ migration_test_add_tls_smoke(env);
migration_test_add("/migration/precopy/unix/tls/psk",
test_precopy_unix_tls_psk);
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-11-13 19:46 ` [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke Fabiano Rosas
@ 2024-12-18 17:46 ` Peter Xu
2024-12-18 18:13 ` Fabiano Rosas
0 siblings, 1 reply; 55+ messages in thread
From: Peter Xu @ 2024-12-18 17:46 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Nov 13, 2024 at 04:46:27PM -0300, Fabiano Rosas wrote:
> diff --git a/tests/qtest/migration-test-smoke.c b/tests/qtest/migration-test-smoke.c
> new file mode 100644
> index 0000000000..ff2d72881f
> --- /dev/null
> +++ b/tests/qtest/migration-test-smoke.c
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#include "qemu/osdep.h"
> +#include "libqtest.h"
> +#include "migration/test-framework.h"
> +#include "qemu/module.h"
> +
> +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);
> +
> + if (env->has_kvm) {
> + g_test_message(
> + "Smoke tests already run as part of the full suite on KVM hosts");
> + goto out;
> + }
So the "smoke" here is almost "tcg".. and if i want to run a smoke test on
a kvm-enabled host, it's noop.. which isn't easy to understand why.
If to rethink our goal, we have two requirements:
(1) We want to categorize migration tests, so some are quick, some are
slow, some might be flacky. Maybe more, but it's about putting one
test into only one bucket, and there're >1 buckets.
(2) We want to run only a small portion of tests on tcg, more tests on
kvm.
Ideally, we don't need two separate main test files, do we?
I mean, we can do (1) with the existing migration-test.c, with the help of
either gtest's "-m" or something we invent. The only unfortunate part is
qtest only have quick/slow, afaiu the "thorough" mode is the same as
"slow".. while we don't yet have real "perf" tests. It means we only have
two buckets if we want to reuse gtest's "-m".
Maybe it's enough? If not, we can implement >2 categories in whatever
form, either custom argv/argc cmdline, or env variable.
Then, if we always categorize one test (let me try to not reuse glib's
terms to be clear) into any of: FAST|NORMAL|SLOW|..., then we have a single
migration-test that have different level of tests. We can invoke
"migration-test --mode FAST" if kvm is not supported, and invoke the same
"migration-test --mode SLOW" if kvm is supported.
Would this be nicer? At least we can still run a pretty fast smoke / FAST
test even on kvm. Basically, untangle accel v.s. "test category".
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-18 17:46 ` Peter Xu
@ 2024-12-18 18:13 ` Fabiano Rosas
2024-12-18 20:22 ` Peter Xu
0 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-12-18 18:13 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
Peter Xu <peterx@redhat.com> writes:
> On Wed, Nov 13, 2024 at 04:46:27PM -0300, Fabiano Rosas wrote:
>> diff --git a/tests/qtest/migration-test-smoke.c b/tests/qtest/migration-test-smoke.c
>> new file mode 100644
>> index 0000000000..ff2d72881f
>> --- /dev/null
>> +++ b/tests/qtest/migration-test-smoke.c
>> @@ -0,0 +1,39 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +
>> +#include "qemu/osdep.h"
>> +#include "libqtest.h"
>> +#include "migration/test-framework.h"
>> +#include "qemu/module.h"
>> +
>> +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);
>> +
>> + if (env->has_kvm) {
>> + g_test_message(
>> + "Smoke tests already run as part of the full suite on KVM hosts");
>> + goto out;
>> + }
>
> So the "smoke" here is almost "tcg".. and if i want to run a smoke test on
> a kvm-enabled host, it's noop.. which isn't easy to understand why.
>
> If to rethink our goal, we have two requirements:
>
> (1) We want to categorize migration tests, so some are quick, some are
> slow, some might be flacky. Maybe more, but it's about putting one
> test into only one bucket, and there're >1 buckets.
It's true that the smoke test should never have slow or flaky tests, but
we can't use this categorization for anything else. IOW, what you
describe here is not a goal. If a test is found to be slow we put it
under slow and it will only run with -m slow/thorough, that's it. We can
just ignore this.
>
> (2) We want to run only a small portion of tests on tcg, more tests on
> kvm.
Yes. Guests are fast with KVM and slow with TCG (generally) and the KVM
hosts are the ones where it's actually important to ensure all migration
features work OK. Non-KVM will only care about save/restore of
snapshots. Therefore we don't need to have all tests running with TCG,
only the smoke set.
And "smoke set" is arbitrary, not tied to speed, but of course no slow
tests please (which already happens because we don't pass -m slow to
migration-test-smoke).
>
> Ideally, we don't need two separate main test files, do we?
>
> I mean, we can do (1) with the existing migration-test.c, with the help of
> either gtest's "-m" or something we invent. The only unfortunate part is
> qtest only have quick/slow, afaiu the "thorough" mode is the same as
> "slow".. while we don't yet have real "perf" tests. It means we only have
> two buckets if we want to reuse gtest's "-m".
>
> Maybe it's enough? If not, we can implement >2 categories in whatever
> form, either custom argv/argc cmdline, or env variable.
>
> Then, if we always categorize one test (let me try to not reuse glib's
> terms to be clear) into any of: FAST|NORMAL|SLOW|..., then we have a single
It's either normal or slow. Because we only know a test is only after it
bothers us.
> migration-test that have different level of tests. We can invoke
> "migration-test --mode FAST" if kvm is not supported, and invoke the same
> "migration-test --mode SLOW" if kvm is supported.
This is messy due to how qtest/meson.build works. Having two tests is
the clean change. Otherwise we'll have to add "if migration-test" or
create artificial test names to be able to restrict the arguments that
are passed to the test per arch.
I also *think* we cannot have anything extra in argv because gtester
expects to be able to parse those.
>
> Would this be nicer? At least we can still run a pretty fast smoke / FAST
> test even on kvm. Basically, untangle accel v.s. "test category".
We could just remove the restriction from migration-test-smoke if that's
an issue.
^ permalink raw reply [flat|nested] 55+ messages in thread* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-18 18:13 ` Fabiano Rosas
@ 2024-12-18 20:22 ` Peter Xu
2024-12-18 21:08 ` Fabiano Rosas
0 siblings, 1 reply; 55+ messages in thread
From: Peter Xu @ 2024-12-18 20:22 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Dec 18, 2024 at 03:13:08PM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
>
> > On Wed, Nov 13, 2024 at 04:46:27PM -0300, Fabiano Rosas wrote:
> >> diff --git a/tests/qtest/migration-test-smoke.c b/tests/qtest/migration-test-smoke.c
> >> new file mode 100644
> >> index 0000000000..ff2d72881f
> >> --- /dev/null
> >> +++ b/tests/qtest/migration-test-smoke.c
> >> @@ -0,0 +1,39 @@
> >> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> >> +
> >> +#include "qemu/osdep.h"
> >> +#include "libqtest.h"
> >> +#include "migration/test-framework.h"
> >> +#include "qemu/module.h"
> >> +
> >> +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);
> >> +
> >> + if (env->has_kvm) {
> >> + g_test_message(
> >> + "Smoke tests already run as part of the full suite on KVM hosts");
> >> + goto out;
> >> + }
> >
> > So the "smoke" here is almost "tcg".. and if i want to run a smoke test on
> > a kvm-enabled host, it's noop.. which isn't easy to understand why.
> >
> > If to rethink our goal, we have two requirements:
> >
> > (1) We want to categorize migration tests, so some are quick, some are
> > slow, some might be flacky. Maybe more, but it's about putting one
> > test into only one bucket, and there're >1 buckets.
>
> It's true that the smoke test should never have slow or flaky tests, but
> we can't use this categorization for anything else. IOW, what you
> describe here is not a goal. If a test is found to be slow we put it
> under slow and it will only run with -m slow/thorough, that's it. We can
> just ignore this.
I could have missed something, but I still think it's the same issue. In
general, I think we want to provide different levels of tests, like:
- Level 1: the minimum set of tests (aka, the "smoke" idea here)
- Level 2: normal set of tests (aka, whatever we used to run by default)
- Level 3: slow tests (aka, only ran with '-m slow' before)
- Level 4: flaky tests (aka, only ran when QEMU_TEST_FLAKY_TESTS set)
Then we want to run level1 test only in tcg, and level1+2 in kvm. We can
only trigger level 1-3 or level 1-4 in manual tests.
We used to have different way to provide the level idea, now I think we can
consider provide that level in migration-test in one shot. Obviously it's
more than quick/slow so I don't think we can reuse "-m", but we can add our
own test level "--level" parameter, so --level N means run all tests lower
than level N, for example.
>
> >
> > (2) We want to run only a small portion of tests on tcg, more tests on
> > kvm.
>
> Yes. Guests are fast with KVM and slow with TCG (generally) and the KVM
> hosts are the ones where it's actually important to ensure all migration
> features work OK. Non-KVM will only care about save/restore of
> snapshots. Therefore we don't need to have all tests running with TCG,
> only the smoke set.
>
> And "smoke set" is arbitrary, not tied to speed, but of course no slow
> tests please (which already happens because we don't pass -m slow to
> migration-test-smoke).
>
> >
> > Ideally, we don't need two separate main test files, do we?
> >
> > I mean, we can do (1) with the existing migration-test.c, with the help of
> > either gtest's "-m" or something we invent. The only unfortunate part is
> > qtest only have quick/slow, afaiu the "thorough" mode is the same as
> > "slow".. while we don't yet have real "perf" tests. It means we only have
> > two buckets if we want to reuse gtest's "-m".
> >
> > Maybe it's enough? If not, we can implement >2 categories in whatever
> > form, either custom argv/argc cmdline, or env variable.
> >
> > Then, if we always categorize one test (let me try to not reuse glib's
> > terms to be clear) into any of: FAST|NORMAL|SLOW|..., then we have a single
>
> It's either normal or slow. Because we only know a test is only after it
> bothers us.
So I wonder if we can provide four levels, as above.. and define it for
each test in migration-test.
>
> > migration-test that have different level of tests. We can invoke
> > "migration-test --mode FAST" if kvm is not supported, and invoke the same
> > "migration-test --mode SLOW" if kvm is supported.
>
> This is messy due to how qtest/meson.build works. Having two tests is
> the clean change. Otherwise we'll have to add "if migration-test" or
> create artificial test names to be able to restrict the arguments that
> are passed to the test per arch.
Indeed it'll need a few extra lines in meson, but it doesn't look too bad,
but yeah if anyone is not happy with it we can rethink. I just want to
know whether it's still acceptable.
I tried to code it up, it looks like this:
====8<====
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index c5a70021c5..5bec33b627 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -392,6 +392,12 @@ if dbus_display
qtests += {'dbus-display-test': [dbus_display1, gio]}
endif
+if run_command('test', '-e', '/dev/kvm', check: false).returncode() == 0
+ has_kvm = true
+else
+ has_kvm =false
+endif
+
qtest_executables = {}
foreach dir : target_dirs
if not dir.endswith('-softmmu')
@@ -434,11 +440,21 @@ foreach dir : target_dirs
test: executable(test, src, dependencies: deps)
}
endif
+ test_args = ['--tap', '-k']
+ if test == 'migration-test'
+ if host_os == 'linux' and cpu == target_base and has_kvm
+ # Only run full migration test if host kvm supported
+ test_args += ['-m', 'thorough']
+ else
+ test_args += ['-m', 'quick']
+ endif
+ endif
+
test('qtest-@0@/@1@'.format(target_base, test),
qtest_executables[test],
depends: [test_deps, qtest_emulator, emulator_modules],
env: qtest_env,
- args: ['--tap', '-k'],
+ args: test_args,
protocol: 'tap',
timeout: slow_qtests.get(test, 60),
priority: slow_qtests.get(test, 60),
====8<====
I still used "-m" but just to show the idea. I also wonder whether other
tests would have similar demands.. otherwise are we destined to not be able
to use qtest cmdline at all as long as we use meson?
>
> I also *think* we cannot have anything extra in argv because gtester
> expects to be able to parse those.
We can definitely hijack argv/argc before passing it over to glib.
>
> >
> > Would this be nicer? At least we can still run a pretty fast smoke / FAST
> > test even on kvm. Basically, untangle accel v.s. "test category".
>
> We could just remove the restriction from migration-test-smoke if that's
> an issue.
Not the only issue, but the idea of it. In general, IMHO it'll be good we
don't attach host info to a test program.
IOW, I want to keep the test in a way so that we'll be able to run whatever
level of test on whatever host, at least when when I run migration-test
manually.
So e.g. I also want to be able to run full set of tests on TCG too whenever
needed. So I still feel like we mangled these two issues together which
might be unnecessarily.
--
Peter Xu
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-18 20:22 ` Peter Xu
@ 2024-12-18 21:08 ` Fabiano Rosas
2024-12-18 22:04 ` Peter Xu
0 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-12-18 21:08 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
Peter Xu <peterx@redhat.com> writes:
> On Wed, Dec 18, 2024 at 03:13:08PM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>>
>> > On Wed, Nov 13, 2024 at 04:46:27PM -0300, Fabiano Rosas wrote:
>> >> diff --git a/tests/qtest/migration-test-smoke.c b/tests/qtest/migration-test-smoke.c
>> >> new file mode 100644
>> >> index 0000000000..ff2d72881f
>> >> --- /dev/null
>> >> +++ b/tests/qtest/migration-test-smoke.c
>> >> @@ -0,0 +1,39 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> >> +
>> >> +#include "qemu/osdep.h"
>> >> +#include "libqtest.h"
>> >> +#include "migration/test-framework.h"
>> >> +#include "qemu/module.h"
>> >> +
>> >> +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);
>> >> +
>> >> + if (env->has_kvm) {
>> >> + g_test_message(
>> >> + "Smoke tests already run as part of the full suite on KVM hosts");
>> >> + goto out;
>> >> + }
>> >
>> > So the "smoke" here is almost "tcg".. and if i want to run a smoke test on
>> > a kvm-enabled host, it's noop.. which isn't easy to understand why.
>> >
>> > If to rethink our goal, we have two requirements:
>> >
>> > (1) We want to categorize migration tests, so some are quick, some are
>> > slow, some might be flacky. Maybe more, but it's about putting one
>> > test into only one bucket, and there're >1 buckets.
>>
>> It's true that the smoke test should never have slow or flaky tests, but
>> we can't use this categorization for anything else. IOW, what you
>> describe here is not a goal. If a test is found to be slow we put it
>> under slow and it will only run with -m slow/thorough, that's it. We can
>> just ignore this.
>
> I could have missed something, but I still think it's the same issue. In
> general, I think we want to provide different levels of tests, like:
>
> - Level 1: the minimum set of tests (aka, the "smoke" idea here)
> - Level 2: normal set of tests (aka, whatever we used to run by default)
> - Level 3: slow tests (aka, only ran with '-m slow' before)
How are you going to make this one work? 'migration-test --level 3'
vs. 'migration-test --level 3 -m slow' vs. 'migration-test -m slow'
The only way I can see is to not have a level 3 at all and just use -m
slow.
> - Level 4: flaky tests (aka, only ran when QEMU_TEST_FLAKY_TESTS set)
>
> Then we want to run level1 test only in tcg, and level1+2 in kvm. We can
> only trigger level 1-3 or level 1-4 in manual tests.
>
> We used to have different way to provide the level idea, now I think we can
> consider provide that level in migration-test in one shot. Obviously it's
> more than quick/slow so I don't think we can reuse "-m", but we can add our
>
> own test level "--level" parameter, so --level N means run all tests lower
> than level N, for example.
>
I'm not sure that works semantically for level 4. Because the reason one
runs flaky tests is different from the reason one runs the other
tests. So we probably don't want to run a bunch of tests just to get to
the broken ones.
But we don't need to spend too much time on this. I hate the idea of
flaky tests anyway. Whatever we choose they'll just sit there doing
nothing.
>>
>> >
>> > (2) We want to run only a small portion of tests on tcg, more tests on
>> > kvm.
>>
>> Yes. Guests are fast with KVM and slow with TCG (generally) and the KVM
>> hosts are the ones where it's actually important to ensure all migration
>> features work OK. Non-KVM will only care about save/restore of
>> snapshots. Therefore we don't need to have all tests running with TCG,
>> only the smoke set.
>>
>> And "smoke set" is arbitrary, not tied to speed, but of course no slow
>> tests please (which already happens because we don't pass -m slow to
>> migration-test-smoke).
>>
>> >
>> > Ideally, we don't need two separate main test files, do we?
>> >
>> > I mean, we can do (1) with the existing migration-test.c, with the help of
>> > either gtest's "-m" or something we invent. The only unfortunate part is
>> > qtest only have quick/slow, afaiu the "thorough" mode is the same as
>> > "slow".. while we don't yet have real "perf" tests. It means we only have
>> > two buckets if we want to reuse gtest's "-m".
>> >
>> > Maybe it's enough? If not, we can implement >2 categories in whatever
>> > form, either custom argv/argc cmdline, or env variable.
>> >
>> > Then, if we always categorize one test (let me try to not reuse glib's
>> > terms to be clear) into any of: FAST|NORMAL|SLOW|..., then we have a single
>>
>> It's either normal or slow. Because we only know a test is only after it
>> bothers us.
>
> So I wonder if we can provide four levels, as above.. and define it for
> each test in migration-test.
>
>>
>> > migration-test that have different level of tests. We can invoke
>> > "migration-test --mode FAST" if kvm is not supported, and invoke the same
>> > "migration-test --mode SLOW" if kvm is supported.
>>
>> This is messy due to how qtest/meson.build works. Having two tests is
>> the clean change. Otherwise we'll have to add "if migration-test" or
>> create artificial test names to be able to restrict the arguments that
>> are passed to the test per arch.
>
> Indeed it'll need a few extra lines in meson, but it doesn't look too bad,
> but yeah if anyone is not happy with it we can rethink. I just want to
> know whether it's still acceptable.
>
> I tried to code it up, it looks like this:
>
> ====8<====
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index c5a70021c5..5bec33b627 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -392,6 +392,12 @@ if dbus_display
> qtests += {'dbus-display-test': [dbus_display1, gio]}
> endif
>
> +if run_command('test', '-e', '/dev/kvm', check: false).returncode() == 0
> + has_kvm = true
> +else
> + has_kvm =false
> +endif
This is not right. Checking /dev/kvm at configure time doesn't ensure it
will be present at test runtime. It also doesn't account for builds with
CONFIG_KVM=n or builds without both KVM and TCG. This needs to be done
inside the test.
I think the best we can do is have a qtest_migration_level_<ARCH> and
set it for every arch.
Also note that we must keep plain 'migration-test' invocation working
because of the compat test.
> +
> qtest_executables = {}
> foreach dir : target_dirs
> if not dir.endswith('-softmmu')
> @@ -434,11 +440,21 @@ foreach dir : target_dirs
> test: executable(test, src, dependencies: deps)
> }
> endif
> + test_args = ['--tap', '-k']
> + if test == 'migration-test'
> + if host_os == 'linux' and cpu == target_base and has_kvm
> + # Only run full migration test if host kvm supported
> + test_args += ['-m', 'thorough']
> + else
> + test_args += ['-m', 'quick']
> + endif
> + endif
> +
> test('qtest-@0@/@1@'.format(target_base, test),
> qtest_executables[test],
> depends: [test_deps, qtest_emulator, emulator_modules],
> env: qtest_env,
> - args: ['--tap', '-k'],
> + args: test_args,
> protocol: 'tap',
> timeout: slow_qtests.get(test, 60),
> priority: slow_qtests.get(test, 60),
> ====8<====
>
> I still used "-m" but just to show the idea. I also wonder whether other
> tests would have similar demands.. otherwise are we destined to not be able
> to use qtest cmdline at all as long as we use meson?
>
>>
>> I also *think* we cannot have anything extra in argv because gtester
>> expects to be able to parse those.
>
> We can definitely hijack argv/argc before passing it over to glib.
>
>>
>> >
>> > Would this be nicer? At least we can still run a pretty fast smoke / FAST
>> > test even on kvm. Basically, untangle accel v.s. "test category".
>>
>> We could just remove the restriction from migration-test-smoke if that's
>> an issue.
>
> Not the only issue, but the idea of it. In general, IMHO it'll be good we
> don't attach host info to a test program.
>
> IOW, I want to keep the test in a way so that we'll be able to run whatever
> level of test on whatever host, at least when when I run migration-test
> manually.
>
> So e.g. I also want to be able to run full set of tests on TCG too whenever
> needed. So I still feel like we mangled these two issues together which
> might be unnecessarily.
^ permalink raw reply [flat|nested] 55+ messages in thread* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-18 21:08 ` Fabiano Rosas
@ 2024-12-18 22:04 ` Peter Xu
2024-12-19 15:38 ` Fabiano Rosas
0 siblings, 1 reply; 55+ messages in thread
From: Peter Xu @ 2024-12-18 22:04 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Wed, Dec 18, 2024 at 06:08:01PM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
>
> > On Wed, Dec 18, 2024 at 03:13:08PM -0300, Fabiano Rosas wrote:
> >> Peter Xu <peterx@redhat.com> writes:
> >>
> >> > On Wed, Nov 13, 2024 at 04:46:27PM -0300, Fabiano Rosas wrote:
> >> >> diff --git a/tests/qtest/migration-test-smoke.c b/tests/qtest/migration-test-smoke.c
> >> >> new file mode 100644
> >> >> index 0000000000..ff2d72881f
> >> >> --- /dev/null
> >> >> +++ b/tests/qtest/migration-test-smoke.c
> >> >> @@ -0,0 +1,39 @@
> >> >> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> >> >> +
> >> >> +#include "qemu/osdep.h"
> >> >> +#include "libqtest.h"
> >> >> +#include "migration/test-framework.h"
> >> >> +#include "qemu/module.h"
> >> >> +
> >> >> +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);
> >> >> +
> >> >> + if (env->has_kvm) {
> >> >> + g_test_message(
> >> >> + "Smoke tests already run as part of the full suite on KVM hosts");
> >> >> + goto out;
> >> >> + }
> >> >
> >> > So the "smoke" here is almost "tcg".. and if i want to run a smoke test on
> >> > a kvm-enabled host, it's noop.. which isn't easy to understand why.
> >> >
> >> > If to rethink our goal, we have two requirements:
> >> >
> >> > (1) We want to categorize migration tests, so some are quick, some are
> >> > slow, some might be flacky. Maybe more, but it's about putting one
> >> > test into only one bucket, and there're >1 buckets.
> >>
> >> It's true that the smoke test should never have slow or flaky tests, but
> >> we can't use this categorization for anything else. IOW, what you
> >> describe here is not a goal. If a test is found to be slow we put it
> >> under slow and it will only run with -m slow/thorough, that's it. We can
> >> just ignore this.
> >
> > I could have missed something, but I still think it's the same issue. In
> > general, I think we want to provide different levels of tests, like:
> >
> > - Level 1: the minimum set of tests (aka, the "smoke" idea here)
> > - Level 2: normal set of tests (aka, whatever we used to run by default)
> > - Level 3: slow tests (aka, only ran with '-m slow' before)
>
> How are you going to make this one work? 'migration-test --level 3'
> vs. 'migration-test --level 3 -m slow' vs. 'migration-test -m slow'
>
> The only way I can see is to not have a level 3 at all and just use -m
> slow.
I meant remove "-m" and remove QEMU_TEST_FLAKY_TESTS, instead replacing all
of them using --level. Then migration-test ignores '-m' in the future
because it's simply not enough for us.
>
> > - Level 4: flaky tests (aka, only ran when QEMU_TEST_FLAKY_TESTS set)
> >
> > Then we want to run level1 test only in tcg, and level1+2 in kvm. We can
> > only trigger level 1-3 or level 1-4 in manual tests.
> >
> > We used to have different way to provide the level idea, now I think we can
> > consider provide that level in migration-test in one shot. Obviously it's
> > more than quick/slow so I don't think we can reuse "-m", but we can add our
> >
> > own test level "--level" parameter, so --level N means run all tests lower
> > than level N, for example.
> >
>
> I'm not sure that works semantically for level 4. Because the reason one
> runs flaky tests is different from the reason one runs the other
> tests. So we probably don't want to run a bunch of tests just to get to
> the broken ones.
>
> But we don't need to spend too much time on this. I hate the idea of
> flaky tests anyway. Whatever we choose they'll just sit there doing
> nothing.
Yes how to treat flaky tests isn't important yet. If we don't care about
QEMU_TEST_FLAKY_TESTS then we make it three levels. The idea is the same.
>
> >>
> >> >
> >> > (2) We want to run only a small portion of tests on tcg, more tests on
> >> > kvm.
> >>
> >> Yes. Guests are fast with KVM and slow with TCG (generally) and the KVM
> >> hosts are the ones where it's actually important to ensure all migration
> >> features work OK. Non-KVM will only care about save/restore of
> >> snapshots. Therefore we don't need to have all tests running with TCG,
> >> only the smoke set.
> >>
> >> And "smoke set" is arbitrary, not tied to speed, but of course no slow
> >> tests please (which already happens because we don't pass -m slow to
> >> migration-test-smoke).
> >>
> >> >
> >> > Ideally, we don't need two separate main test files, do we?
> >> >
> >> > I mean, we can do (1) with the existing migration-test.c, with the help of
> >> > either gtest's "-m" or something we invent. The only unfortunate part is
> >> > qtest only have quick/slow, afaiu the "thorough" mode is the same as
> >> > "slow".. while we don't yet have real "perf" tests. It means we only have
> >> > two buckets if we want to reuse gtest's "-m".
> >> >
> >> > Maybe it's enough? If not, we can implement >2 categories in whatever
> >> > form, either custom argv/argc cmdline, or env variable.
> >> >
> >> > Then, if we always categorize one test (let me try to not reuse glib's
> >> > terms to be clear) into any of: FAST|NORMAL|SLOW|..., then we have a single
> >>
> >> It's either normal or slow. Because we only know a test is only after it
> >> bothers us.
> >
> > So I wonder if we can provide four levels, as above.. and define it for
> > each test in migration-test.
> >
> >>
> >> > migration-test that have different level of tests. We can invoke
> >> > "migration-test --mode FAST" if kvm is not supported, and invoke the same
> >> > "migration-test --mode SLOW" if kvm is supported.
> >>
> >> This is messy due to how qtest/meson.build works. Having two tests is
> >> the clean change. Otherwise we'll have to add "if migration-test" or
> >> create artificial test names to be able to restrict the arguments that
> >> are passed to the test per arch.
> >
> > Indeed it'll need a few extra lines in meson, but it doesn't look too bad,
> > but yeah if anyone is not happy with it we can rethink. I just want to
> > know whether it's still acceptable.
> >
> > I tried to code it up, it looks like this:
> >
> > ====8<====
> > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> > index c5a70021c5..5bec33b627 100644
> > --- a/tests/qtest/meson.build
> > +++ b/tests/qtest/meson.build
> > @@ -392,6 +392,12 @@ if dbus_display
> > qtests += {'dbus-display-test': [dbus_display1, gio]}
> > endif
> >
> > +if run_command('test', '-e', '/dev/kvm', check: false).returncode() == 0
> > + has_kvm = true
> > +else
> > + has_kvm =false
> > +endif
>
> This is not right. Checking /dev/kvm at configure time doesn't ensure it
> will be present at test runtime. It also doesn't account for builds with
Why the test runtime would be a different host versus whoever setup the
meson build?
> CONFIG_KVM=n or builds without both KVM and TCG. This needs to be done
> inside the test.
This is true, but IIUC that's not a blocker, as we can use (btw, I found
fs.exists() a better alternative than my previous hack):
if fs.exists('/dev/kvm') and 'CONFIG_KVM' in config_all_accel
has_kvm = true
else
has_kvm = false
endif
>
> I think the best we can do is have a qtest_migration_level_<ARCH> and
> set it for every arch.
>
> Also note that we must keep plain 'migration-test' invocation working
> because of the compat test.
We won't break it if we only switch to levels, right?
Btw, I also don't know why we need to. IIRC the compat test runs the test
in previous release (but only feeds the new QEMU binary to the old
migration-test)? I think that's one reason why we decided to use the old
migration-test (so we won't have new tests ran on compat tests, which is a
loss), just to avoid any change in migration-test will break the compat
test.. so I assume that should be fine regardless..
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-18 22:04 ` Peter Xu
@ 2024-12-19 15:38 ` Fabiano Rosas
2024-12-19 17:42 ` Peter Xu
0 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-12-19 15:38 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
Peter Xu <peterx@redhat.com> writes:
> On Wed, Dec 18, 2024 at 06:08:01PM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>>
>> > On Wed, Dec 18, 2024 at 03:13:08PM -0300, Fabiano Rosas wrote:
>> >> Peter Xu <peterx@redhat.com> writes:
>> >>
>> >> > On Wed, Nov 13, 2024 at 04:46:27PM -0300, Fabiano Rosas wrote:
>> >> >> diff --git a/tests/qtest/migration-test-smoke.c b/tests/qtest/migration-test-smoke.c
>> >> >> new file mode 100644
>> >> >> index 0000000000..ff2d72881f
>> >> >> --- /dev/null
>> >> >> +++ b/tests/qtest/migration-test-smoke.c
>> >> >> @@ -0,0 +1,39 @@
>> >> >> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> >> >> +
>> >> >> +#include "qemu/osdep.h"
>> >> >> +#include "libqtest.h"
>> >> >> +#include "migration/test-framework.h"
>> >> >> +#include "qemu/module.h"
>> >> >> +
>> >> >> +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);
>> >> >> +
>> >> >> + if (env->has_kvm) {
>> >> >> + g_test_message(
>> >> >> + "Smoke tests already run as part of the full suite on KVM hosts");
>> >> >> + goto out;
>> >> >> + }
>> >> >
>> >> > So the "smoke" here is almost "tcg".. and if i want to run a smoke test on
>> >> > a kvm-enabled host, it's noop.. which isn't easy to understand why.
>> >> >
>> >> > If to rethink our goal, we have two requirements:
>> >> >
>> >> > (1) We want to categorize migration tests, so some are quick, some are
>> >> > slow, some might be flacky. Maybe more, but it's about putting one
>> >> > test into only one bucket, and there're >1 buckets.
>> >>
>> >> It's true that the smoke test should never have slow or flaky tests, but
>> >> we can't use this categorization for anything else. IOW, what you
>> >> describe here is not a goal. If a test is found to be slow we put it
>> >> under slow and it will only run with -m slow/thorough, that's it. We can
>> >> just ignore this.
>> >
>> > I could have missed something, but I still think it's the same issue. In
>> > general, I think we want to provide different levels of tests, like:
>> >
>> > - Level 1: the minimum set of tests (aka, the "smoke" idea here)
>> > - Level 2: normal set of tests (aka, whatever we used to run by default)
>> > - Level 3: slow tests (aka, only ran with '-m slow' before)
>>
>> How are you going to make this one work? 'migration-test --level 3'
>> vs. 'migration-test --level 3 -m slow' vs. 'migration-test -m slow'
>>
>> The only way I can see is to not have a level 3 at all and just use -m
>> slow.
>
> I meant remove "-m" and remove QEMU_TEST_FLAKY_TESTS, instead replacing all
> of them using --level. Then migration-test ignores '-m' in the future
> because it's simply not enough for us.
>
Even if we remove -m, it will still be passed in when people run make
SPEED=slow check and people who know about glib/qtests will continue to
try to use it.
Ignoring -m and FLAKY will create a diversion from the rest of qtest and
QEMU tests in general. I think the best approach is to drop levels 3 and
4 from this proposal and just ignore those options altogether.
If we're going to have a single binary as you suggest, then there's no
harm still using -m slow and FLAKY as usual. We call level 1: "smoke
tests", level 2: "full set" and we put in some code to prevent
registering a smoke test as slow or flaky and that's it.
... and of course, if there's only two levels, then we only need a
boolean flag: --full
IOW, I still don't think slow and flaky have anything to do with what
we're trying to do here. I do appreciate that a single binary is better
than two.
>>
>> > - Level 4: flaky tests (aka, only ran when QEMU_TEST_FLAKY_TESTS set)
>> >
>> > Then we want to run level1 test only in tcg, and level1+2 in kvm. We can
>> > only trigger level 1-3 or level 1-4 in manual tests.
>> >
>> > We used to have different way to provide the level idea, now I think we can
>> > consider provide that level in migration-test in one shot. Obviously it's
>> > more than quick/slow so I don't think we can reuse "-m", but we can add our
>> >
>> > own test level "--level" parameter, so --level N means run all tests lower
>> > than level N, for example.
>> >
>>
>> I'm not sure that works semantically for level 4. Because the reason one
>> runs flaky tests is different from the reason one runs the other
>> tests. So we probably don't want to run a bunch of tests just to get to
>> the broken ones.
>>
>> But we don't need to spend too much time on this. I hate the idea of
>> flaky tests anyway. Whatever we choose they'll just sit there doing
>> nothing.
>
> Yes how to treat flaky tests isn't important yet. If we don't care about
> QEMU_TEST_FLAKY_TESTS then we make it three levels. The idea is the same.
>
>>
>> >>
>> >> >
>> >> > (2) We want to run only a small portion of tests on tcg, more tests on
>> >> > kvm.
>> >>
>> >> Yes. Guests are fast with KVM and slow with TCG (generally) and the KVM
>> >> hosts are the ones where it's actually important to ensure all migration
>> >> features work OK. Non-KVM will only care about save/restore of
>> >> snapshots. Therefore we don't need to have all tests running with TCG,
>> >> only the smoke set.
>> >>
>> >> And "smoke set" is arbitrary, not tied to speed, but of course no slow
>> >> tests please (which already happens because we don't pass -m slow to
>> >> migration-test-smoke).
>> >>
>> >> >
>> >> > Ideally, we don't need two separate main test files, do we?
>> >> >
>> >> > I mean, we can do (1) with the existing migration-test.c, with the help of
>> >> > either gtest's "-m" or something we invent. The only unfortunate part is
>> >> > qtest only have quick/slow, afaiu the "thorough" mode is the same as
>> >> > "slow".. while we don't yet have real "perf" tests. It means we only have
>> >> > two buckets if we want to reuse gtest's "-m".
>> >> >
>> >> > Maybe it's enough? If not, we can implement >2 categories in whatever
>> >> > form, either custom argv/argc cmdline, or env variable.
>> >> >
>> >> > Then, if we always categorize one test (let me try to not reuse glib's
>> >> > terms to be clear) into any of: FAST|NORMAL|SLOW|..., then we have a single
>> >>
>> >> It's either normal or slow. Because we only know a test is only after it
>> >> bothers us.
>> >
>> > So I wonder if we can provide four levels, as above.. and define it for
>> > each test in migration-test.
>> >
>> >>
>> >> > migration-test that have different level of tests. We can invoke
>> >> > "migration-test --mode FAST" if kvm is not supported, and invoke the same
>> >> > "migration-test --mode SLOW" if kvm is supported.
>> >>
>> >> This is messy due to how qtest/meson.build works. Having two tests is
>> >> the clean change. Otherwise we'll have to add "if migration-test" or
>> >> create artificial test names to be able to restrict the arguments that
>> >> are passed to the test per arch.
>> >
>> > Indeed it'll need a few extra lines in meson, but it doesn't look too bad,
>> > but yeah if anyone is not happy with it we can rethink. I just want to
>> > know whether it's still acceptable.
>> >
>> > I tried to code it up, it looks like this:
>> >
>> > ====8<====
>> > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
>> > index c5a70021c5..5bec33b627 100644
>> > --- a/tests/qtest/meson.build
>> > +++ b/tests/qtest/meson.build
>> > @@ -392,6 +392,12 @@ if dbus_display
>> > qtests += {'dbus-display-test': [dbus_display1, gio]}
>> > endif
>> >
>> > +if run_command('test', '-e', '/dev/kvm', check: false).returncode() == 0
>> > + has_kvm = true
>> > +else
>> > + has_kvm =false
>> > +endif
>>
>> This is not right. Checking /dev/kvm at configure time doesn't ensure it
>> will be present at test runtime. It also doesn't account for builds with
>
> Why the test runtime would be a different host versus whoever setup the
> meson build?
>
User permissions, containers, configuration changes in between build
time and runtime, etc.
Also, it's quite convenient to be able to pass any QEMU binary to any
version of the tests, I do that with downstream builds sometimes.
>> CONFIG_KVM=n or builds without both KVM and TCG. This needs to be done
>> inside the test.
>
> This is true, but IIUC that's not a blocker, as we can use (btw, I found
> fs.exists() a better alternative than my previous hack):
>
> if fs.exists('/dev/kvm') and 'CONFIG_KVM' in config_all_accel
> has_kvm = true
> else
> has_kvm = false
> endif
>
I dislike this, however I'm thinking what's the worse that could happen
if there's a mismatch between configure and runtime? We'd just run a
different set of tests.
Can we make it:
meson.build:
# If there's KVM support, run the full set of migration tests as KVM
# hosts tend to use more migration features than just save/restore.
if fs.exists('/dev/kvm')
migration_test_args = "--full"
endif
cmdline invocations:
./migration-test # runs smoke, i.e. level 1
./migration-test -m slow # runs smoke only, no slow tests in the smoke set
FLAKY=1 ./migration-test # runs smoke only, no flaky tests in the smoke set
./migration-test --full # runs full set, i.e. level 2
./migration-test --full -m slow # runs full set + slow tests
FLAKY=1 ./migration-test --full # runs full set + flaky tests
I made the first one like that so the compat tests in CI now run less
tests. We don't need full set during compat because that job is about
catching changes in device code. It would also make the argument easier
to enable the compat job for all migration-test-supported archs.
>>
>> I think the best we can do is have a qtest_migration_level_<ARCH> and
>> set it for every arch.
>>
>> Also note that we must keep plain 'migration-test' invocation working
>> because of the compat test.
>
> We won't break it if we only switch to levels, right?
>
> Btw, I also don't know why we need to. IIRC the compat test runs the test
> in previous release (but only feeds the new QEMU binary to the old
> migration-test)? I think that's one reason why we decided to use the old
> migration-test (so we won't have new tests ran on compat tests, which is a
> loss), just to avoid any change in migration-test will break the compat
> test.. so I assume that should be fine regardless..
I meant we shouldn't break the command line invocation:
./tests/qtest/migration-test -p <test_name>
As in, we cannot change the test name or add mandatory flags. Otherwise
we have a discrepancy betweem what the CI job is calling vs. what the
build actually provides. We run the tests from the previous build, but
the CI job is current.
Another point that is more of an annoyance is that the migration-test
invocation not being stable affectes debug, bisect, etc. When debugging
the recent multifd regression I already had to keep changing from
/multifd/tcp/... to /multifd/tcp/uri/... when changing QEMU versions.
^ permalink raw reply [flat|nested] 55+ messages in thread* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-19 15:38 ` Fabiano Rosas
@ 2024-12-19 17:42 ` Peter Xu
2024-12-19 19:31 ` Fabiano Rosas
0 siblings, 1 reply; 55+ messages in thread
From: Peter Xu @ 2024-12-19 17:42 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Thu, Dec 19, 2024 at 12:38:05PM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
>
> > On Wed, Dec 18, 2024 at 06:08:01PM -0300, Fabiano Rosas wrote:
> >> Peter Xu <peterx@redhat.com> writes:
> >>
> >> > On Wed, Dec 18, 2024 at 03:13:08PM -0300, Fabiano Rosas wrote:
> >> >> Peter Xu <peterx@redhat.com> writes:
> >> >>
> >> >> > On Wed, Nov 13, 2024 at 04:46:27PM -0300, Fabiano Rosas wrote:
> >> >> >> diff --git a/tests/qtest/migration-test-smoke.c b/tests/qtest/migration-test-smoke.c
> >> >> >> new file mode 100644
> >> >> >> index 0000000000..ff2d72881f
> >> >> >> --- /dev/null
> >> >> >> +++ b/tests/qtest/migration-test-smoke.c
> >> >> >> @@ -0,0 +1,39 @@
> >> >> >> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> >> >> >> +
> >> >> >> +#include "qemu/osdep.h"
> >> >> >> +#include "libqtest.h"
> >> >> >> +#include "migration/test-framework.h"
> >> >> >> +#include "qemu/module.h"
> >> >> >> +
> >> >> >> +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);
> >> >> >> +
> >> >> >> + if (env->has_kvm) {
> >> >> >> + g_test_message(
> >> >> >> + "Smoke tests already run as part of the full suite on KVM hosts");
> >> >> >> + goto out;
> >> >> >> + }
> >> >> >
> >> >> > So the "smoke" here is almost "tcg".. and if i want to run a smoke test on
> >> >> > a kvm-enabled host, it's noop.. which isn't easy to understand why.
> >> >> >
> >> >> > If to rethink our goal, we have two requirements:
> >> >> >
> >> >> > (1) We want to categorize migration tests, so some are quick, some are
> >> >> > slow, some might be flacky. Maybe more, but it's about putting one
> >> >> > test into only one bucket, and there're >1 buckets.
> >> >>
> >> >> It's true that the smoke test should never have slow or flaky tests, but
> >> >> we can't use this categorization for anything else. IOW, what you
> >> >> describe here is not a goal. If a test is found to be slow we put it
> >> >> under slow and it will only run with -m slow/thorough, that's it. We can
> >> >> just ignore this.
> >> >
> >> > I could have missed something, but I still think it's the same issue. In
> >> > general, I think we want to provide different levels of tests, like:
> >> >
> >> > - Level 1: the minimum set of tests (aka, the "smoke" idea here)
> >> > - Level 2: normal set of tests (aka, whatever we used to run by default)
> >> > - Level 3: slow tests (aka, only ran with '-m slow' before)
> >>
> >> How are you going to make this one work? 'migration-test --level 3'
> >> vs. 'migration-test --level 3 -m slow' vs. 'migration-test -m slow'
> >>
> >> The only way I can see is to not have a level 3 at all and just use -m
> >> slow.
> >
> > I meant remove "-m" and remove QEMU_TEST_FLAKY_TESTS, instead replacing all
> > of them using --level. Then migration-test ignores '-m' in the future
> > because it's simply not enough for us.
> >
>
> Even if we remove -m, it will still be passed in when people run make
> SPEED=slow check and people who know about glib/qtests will continue to
> try to use it.
>
> Ignoring -m and FLAKY will create a diversion from the rest of qtest and
> QEMU tests in general. I think the best approach is to drop levels 3 and
> 4 from this proposal and just ignore those options altogether.
>
> If we're going to have a single binary as you suggest, then there's no
> harm still using -m slow and FLAKY as usual. We call level 1: "smoke
> tests", level 2: "full set" and we put in some code to prevent
> registering a smoke test as slow or flaky and that's it.
>
> ... and of course, if there's only two levels, then we only need a
> boolean flag: --full
>
> IOW, I still don't think slow and flaky have anything to do with what
> we're trying to do here. I do appreciate that a single binary is better
> than two.
Yes, the major goal is to keep it a single binary.
We can make it two levels. However if so I'd rather reuse -m, then we keep
"-m quick" for tcg, "-m slow" for tcg+kvm. We can move the current "slow
tests" into another env var: basically those ones (dirtylimit, auto
converge, xbzrle, iirc) and FLAKY ones won't be run by anyone but us.
>
> >>
> >> > - Level 4: flaky tests (aka, only ran when QEMU_TEST_FLAKY_TESTS set)
> >> >
> >> > Then we want to run level1 test only in tcg, and level1+2 in kvm. We can
> >> > only trigger level 1-3 or level 1-4 in manual tests.
> >> >
> >> > We used to have different way to provide the level idea, now I think we can
> >> > consider provide that level in migration-test in one shot. Obviously it's
> >> > more than quick/slow so I don't think we can reuse "-m", but we can add our
> >> >
> >> > own test level "--level" parameter, so --level N means run all tests lower
> >> > than level N, for example.
> >> >
> >>
> >> I'm not sure that works semantically for level 4. Because the reason one
> >> runs flaky tests is different from the reason one runs the other
> >> tests. So we probably don't want to run a bunch of tests just to get to
> >> the broken ones.
> >>
> >> But we don't need to spend too much time on this. I hate the idea of
> >> flaky tests anyway. Whatever we choose they'll just sit there doing
> >> nothing.
> >
> > Yes how to treat flaky tests isn't important yet. If we don't care about
> > QEMU_TEST_FLAKY_TESTS then we make it three levels. The idea is the same.
> >
> >>
> >> >>
> >> >> >
> >> >> > (2) We want to run only a small portion of tests on tcg, more tests on
> >> >> > kvm.
> >> >>
> >> >> Yes. Guests are fast with KVM and slow with TCG (generally) and the KVM
> >> >> hosts are the ones where it's actually important to ensure all migration
> >> >> features work OK. Non-KVM will only care about save/restore of
> >> >> snapshots. Therefore we don't need to have all tests running with TCG,
> >> >> only the smoke set.
> >> >>
> >> >> And "smoke set" is arbitrary, not tied to speed, but of course no slow
> >> >> tests please (which already happens because we don't pass -m slow to
> >> >> migration-test-smoke).
> >> >>
> >> >> >
> >> >> > Ideally, we don't need two separate main test files, do we?
> >> >> >
> >> >> > I mean, we can do (1) with the existing migration-test.c, with the help of
> >> >> > either gtest's "-m" or something we invent. The only unfortunate part is
> >> >> > qtest only have quick/slow, afaiu the "thorough" mode is the same as
> >> >> > "slow".. while we don't yet have real "perf" tests. It means we only have
> >> >> > two buckets if we want to reuse gtest's "-m".
> >> >> >
> >> >> > Maybe it's enough? If not, we can implement >2 categories in whatever
> >> >> > form, either custom argv/argc cmdline, or env variable.
> >> >> >
> >> >> > Then, if we always categorize one test (let me try to not reuse glib's
> >> >> > terms to be clear) into any of: FAST|NORMAL|SLOW|..., then we have a single
> >> >>
> >> >> It's either normal or slow. Because we only know a test is only after it
> >> >> bothers us.
> >> >
> >> > So I wonder if we can provide four levels, as above.. and define it for
> >> > each test in migration-test.
> >> >
> >> >>
> >> >> > migration-test that have different level of tests. We can invoke
> >> >> > "migration-test --mode FAST" if kvm is not supported, and invoke the same
> >> >> > "migration-test --mode SLOW" if kvm is supported.
> >> >>
> >> >> This is messy due to how qtest/meson.build works. Having two tests is
> >> >> the clean change. Otherwise we'll have to add "if migration-test" or
> >> >> create artificial test names to be able to restrict the arguments that
> >> >> are passed to the test per arch.
> >> >
> >> > Indeed it'll need a few extra lines in meson, but it doesn't look too bad,
> >> > but yeah if anyone is not happy with it we can rethink. I just want to
> >> > know whether it's still acceptable.
> >> >
> >> > I tried to code it up, it looks like this:
> >> >
> >> > ====8<====
> >> > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> >> > index c5a70021c5..5bec33b627 100644
> >> > --- a/tests/qtest/meson.build
> >> > +++ b/tests/qtest/meson.build
> >> > @@ -392,6 +392,12 @@ if dbus_display
> >> > qtests += {'dbus-display-test': [dbus_display1, gio]}
> >> > endif
> >> >
> >> > +if run_command('test', '-e', '/dev/kvm', check: false).returncode() == 0
> >> > + has_kvm = true
> >> > +else
> >> > + has_kvm =false
> >> > +endif
> >>
> >> This is not right. Checking /dev/kvm at configure time doesn't ensure it
> >> will be present at test runtime. It also doesn't account for builds with
> >
> > Why the test runtime would be a different host versus whoever setup the
> > meson build?
> >
>
> User permissions, containers, configuration changes in between build
> time and runtime, etc.
I was thinking there's no way in CI that it can be done separately. But
yeah I don't know well on CI.. so if it can happen easily that's a problem.
>
> Also, it's quite convenient to be able to pass any QEMU binary to any
> version of the tests, I do that with downstream builds sometimes.
IIUC we're only talking about "meson test" behavior, I assume
migration-test should be mostly unaffected on how it'll be used, except
that indeed we're changing "how to define quick/slow tests". More below.
>
> >> CONFIG_KVM=n or builds without both KVM and TCG. This needs to be done
> >> inside the test.
> >
> > This is true, but IIUC that's not a blocker, as we can use (btw, I found
> > fs.exists() a better alternative than my previous hack):
> >
> > if fs.exists('/dev/kvm') and 'CONFIG_KVM' in config_all_accel
> > has_kvm = true
> > else
> > has_kvm = false
> > endif
> >
>
> I dislike this, however I'm thinking what's the worse that could happen
> if there's a mismatch between configure and runtime? We'd just run a
> different set of tests.
>
> Can we make it:
>
> meson.build:
> # If there's KVM support, run the full set of migration tests as KVM
> # hosts tend to use more migration features than just save/restore.
> if fs.exists('/dev/kvm')
> migration_test_args = "--full"
So IMHO we could go "-m slow | -m thorough" here, as mentioned above, just
to avoid making it a matrix of (--full, -m) combinations.
Then move all old slow tests into (just to avoid using "slow" as a word):
if (getenv("QEMU_MIG_TEST_EXTRA")) ...
And rename FLAKY into:
if (getenv("QEMU_MIG_TEST_FLAKY")) ...
Then we test it with QEMU_MIG_TEST_FLAKY=1 and QEMU_MIG_TEST_EXTRA=1 if we
want all tests.
> endif
>
> cmdline invocations:
> ./migration-test # runs smoke, i.e. level 1
> ./migration-test -m slow # runs smoke only, no slow tests in the smoke set
> FLAKY=1 ./migration-test # runs smoke only, no flaky tests in the smoke set
>
> ./migration-test --full # runs full set, i.e. level 2
> ./migration-test --full -m slow # runs full set + slow tests
> FLAKY=1 ./migration-test --full # runs full set + flaky tests
>
> I made the first one like that so the compat tests in CI now run less
> tests. We don't need full set during compat because that job is about
> catching changes in device code. It would also make the argument easier
> to enable the compat job for all migration-test-supported archs.
>
> >>
> >> I think the best we can do is have a qtest_migration_level_<ARCH> and
> >> set it for every arch.
> >>
> >> Also note that we must keep plain 'migration-test' invocation working
> >> because of the compat test.
> >
> > We won't break it if we only switch to levels, right?
> >
> > Btw, I also don't know why we need to. IIRC the compat test runs the test
> > in previous release (but only feeds the new QEMU binary to the old
> > migration-test)? I think that's one reason why we decided to use the old
> > migration-test (so we won't have new tests ran on compat tests, which is a
> > loss), just to avoid any change in migration-test will break the compat
> > test.. so I assume that should be fine regardless..
>
> I meant we shouldn't break the command line invocation:
>
> ./tests/qtest/migration-test -p <test_name>
>
> As in, we cannot change the test name or add mandatory flags. Otherwise
> we have a discrepancy betweem what the CI job is calling vs. what the
> build actually provides. We run the tests from the previous build, but
> the CI job is current.
I failed to follow here. Our CI doesn't hardcode any <test_name>, right?
It should invoke "migration-test" in the old build, feeding new QEMU binary
to it, run whatever are there in the old test.
>
> Another point that is more of an annoyance is that the migration-test
> invocation not being stable affectes debug, bisect, etc. When debugging
> the recent multifd regression I already had to keep changing from
> /multifd/tcp/... to /multifd/tcp/uri/... when changing QEMU versions.
So I could have missed something above, and I can understand this adds
burden to bisections. However I do prefer we can change test path any way
we want (even if in most cases we shouldn't ever touch them, and we should
still try to not change them as frequent). IOW we also need to consider
the overhead of keeping test paths to be part of ABI as well.
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-19 17:42 ` Peter Xu
@ 2024-12-19 19:31 ` Fabiano Rosas
2024-12-20 15:18 ` Peter Xu
0 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-12-19 19:31 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
Peter Xu <peterx@redhat.com> writes:
> On Thu, Dec 19, 2024 at 12:38:05PM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>>
>> > On Wed, Dec 18, 2024 at 06:08:01PM -0300, Fabiano Rosas wrote:
>> >> Peter Xu <peterx@redhat.com> writes:
>> >>
>> >> > On Wed, Dec 18, 2024 at 03:13:08PM -0300, Fabiano Rosas wrote:
>> >> >> Peter Xu <peterx@redhat.com> writes:
>> >> >>
>> >> >> > On Wed, Nov 13, 2024 at 04:46:27PM -0300, Fabiano Rosas wrote:
>> >> >> >> diff --git a/tests/qtest/migration-test-smoke.c b/tests/qtest/migration-test-smoke.c
>> >> >> >> new file mode 100644
>> >> >> >> index 0000000000..ff2d72881f
>> >> >> >> --- /dev/null
>> >> >> >> +++ b/tests/qtest/migration-test-smoke.c
>> >> >> >> @@ -0,0 +1,39 @@
>> >> >> >> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> >> >> >> +
>> >> >> >> +#include "qemu/osdep.h"
>> >> >> >> +#include "libqtest.h"
>> >> >> >> +#include "migration/test-framework.h"
>> >> >> >> +#include "qemu/module.h"
>> >> >> >> +
>> >> >> >> +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);
>> >> >> >> +
>> >> >> >> + if (env->has_kvm) {
>> >> >> >> + g_test_message(
>> >> >> >> + "Smoke tests already run as part of the full suite on KVM hosts");
>> >> >> >> + goto out;
>> >> >> >> + }
>> >> >> >
>> >> >> > So the "smoke" here is almost "tcg".. and if i want to run a smoke test on
>> >> >> > a kvm-enabled host, it's noop.. which isn't easy to understand why.
>> >> >> >
>> >> >> > If to rethink our goal, we have two requirements:
>> >> >> >
>> >> >> > (1) We want to categorize migration tests, so some are quick, some are
>> >> >> > slow, some might be flacky. Maybe more, but it's about putting one
>> >> >> > test into only one bucket, and there're >1 buckets.
>> >> >>
>> >> >> It's true that the smoke test should never have slow or flaky tests, but
>> >> >> we can't use this categorization for anything else. IOW, what you
>> >> >> describe here is not a goal. If a test is found to be slow we put it
>> >> >> under slow and it will only run with -m slow/thorough, that's it. We can
>> >> >> just ignore this.
>> >> >
>> >> > I could have missed something, but I still think it's the same issue. In
>> >> > general, I think we want to provide different levels of tests, like:
>> >> >
>> >> > - Level 1: the minimum set of tests (aka, the "smoke" idea here)
>> >> > - Level 2: normal set of tests (aka, whatever we used to run by default)
>> >> > - Level 3: slow tests (aka, only ran with '-m slow' before)
>> >>
>> >> How are you going to make this one work? 'migration-test --level 3'
>> >> vs. 'migration-test --level 3 -m slow' vs. 'migration-test -m slow'
>> >>
>> >> The only way I can see is to not have a level 3 at all and just use -m
>> >> slow.
>> >
>> > I meant remove "-m" and remove QEMU_TEST_FLAKY_TESTS, instead replacing all
>> > of them using --level. Then migration-test ignores '-m' in the future
>> > because it's simply not enough for us.
>> >
>>
>> Even if we remove -m, it will still be passed in when people run make
>> SPEED=slow check and people who know about glib/qtests will continue to
>> try to use it.
>>
>> Ignoring -m and FLAKY will create a diversion from the rest of qtest and
>> QEMU tests in general. I think the best approach is to drop levels 3 and
>> 4 from this proposal and just ignore those options altogether.
>>
>> If we're going to have a single binary as you suggest, then there's no
>> harm still using -m slow and FLAKY as usual. We call level 1: "smoke
>> tests", level 2: "full set" and we put in some code to prevent
>> registering a smoke test as slow or flaky and that's it.
>>
>> ... and of course, if there's only two levels, then we only need a
>> boolean flag: --full
>>
>> IOW, I still don't think slow and flaky have anything to do with what
>> we're trying to do here. I do appreciate that a single binary is better
>> than two.
>
> Yes, the major goal is to keep it a single binary.
>
> We can make it two levels. However if so I'd rather reuse -m, then we keep
> "-m quick" for tcg, "-m slow" for tcg+kvm. We can move the current "slow
I don't understand why you insist in bringing slow/quick into this. '-m
quick' is for quick tests and '-m slow' is for slow tests. What we're
doing here is: "small set" vs. "large set". Except for our own sanity
we're excluding slow tests from the "small set", otherwise the
perception of smallness is counteracted by the perception of slowness.
Then, we're determining arbitrarily that the small set is to be run only
on TCG hosts and the large set is to be run in the KVM hosts. And via
cmdline we want to be able to run anything basically.
> tests" into another env var: basically those ones (dirtylimit, auto
> converge, xbzrle, iirc) and FLAKY ones won't be run by anyone but us.
>
>>
>> >>
>> >> >
>> >> > Indeed it'll need a few extra lines in meson, but it doesn't look too bad,
>> >> > but yeah if anyone is not happy with it we can rethink. I just want to
>> >> > know whether it's still acceptable.
>> >> >
>> >> > I tried to code it up, it looks like this:
>> >> >
>> >> > ====8<====
>> >> > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
>> >> > index c5a70021c5..5bec33b627 100644
>> >> > --- a/tests/qtest/meson.build
>> >> > +++ b/tests/qtest/meson.build
>> >> > @@ -392,6 +392,12 @@ if dbus_display
>> >> > qtests += {'dbus-display-test': [dbus_display1, gio]}
>> >> > endif
>> >> >
>> >> > +if run_command('test', '-e', '/dev/kvm', check: false).returncode() == 0
>> >> > + has_kvm = true
>> >> > +else
>> >> > + has_kvm =false
>> >> > +endif
>> >>
>> >> This is not right. Checking /dev/kvm at configure time doesn't ensure it
>> >> will be present at test runtime. It also doesn't account for builds with
>> >
>> > Why the test runtime would be a different host versus whoever setup the
>> > meson build?
>> >
>>
>> User permissions, containers, configuration changes in between build
>> time and runtime, etc.
>
> I was thinking there's no way in CI that it can be done separately. But
> yeah I don't know well on CI.. so if it can happen easily that's a problem.
>
I was thinking more of make check in peoples machines. Who knows what
sort of setups QEMU developers have... As for CI, I don't think it
should change and as I said, if it does then the test itself should be
able to cope.
>>
>> Also, it's quite convenient to be able to pass any QEMU binary to any
>> version of the tests, I do that with downstream builds sometimes.
>
> IIUC we're only talking about "meson test" behavior, I assume
> migration-test should be mostly unaffected on how it'll be used, except
> that indeed we're changing "how to define quick/slow tests". More below.
>
Yes, indeed.
>>
>> >> CONFIG_KVM=n or builds without both KVM and TCG. This needs to be done
>> >> inside the test.
>> >
>> > This is true, but IIUC that's not a blocker, as we can use (btw, I found
>> > fs.exists() a better alternative than my previous hack):
>> >
>> > if fs.exists('/dev/kvm') and 'CONFIG_KVM' in config_all_accel
>> > has_kvm = true
>> > else
>> > has_kvm = false
>> > endif
>> >
>>
>> I dislike this, however I'm thinking what's the worse that could happen
>> if there's a mismatch between configure and runtime? We'd just run a
>> different set of tests.
>>
>> Can we make it:
>>
>> meson.build:
>> # If there's KVM support, run the full set of migration tests as KVM
>> # hosts tend to use more migration features than just save/restore.
>> if fs.exists('/dev/kvm')
>> migration_test_args = "--full"
>
> So IMHO we could go "-m slow | -m thorough" here, as mentioned above, just
> to avoid making it a matrix of (--full, -m) combinations.
>
> Then move all old slow tests into (just to avoid using "slow" as a word):
>
> if (getenv("QEMU_MIG_TEST_EXTRA")) ...
>
> And rename FLAKY into:
>
> if (getenv("QEMU_MIG_TEST_FLAKY")) ...
>
> Then we test it with QEMU_MIG_TEST_FLAKY=1 and QEMU_MIG_TEST_EXTRA=1 if we
> want all tests.
We shouldn't change stuff that's also used by the rest of the
community. People know about QEMU_TEST_FLAKY_TESTS and -m slow. These
must continue to work the same.
We can say: "Internally we don't allow slow and flaky to be in the smoke
set".
We cannot say: "In migration-test QEMU_TEST_FLAKY_TESTS is actually
QEMU_MIG_TEST_FLAKY, -m slow is actually QEMU_MIG_TEST_EXTRA, -m slow
just implies KVM and -m quick implies TCG. Easy peasy!"
>
>> endif
>>
>> cmdline invocations:
>> ./migration-test # runs smoke, i.e. level 1
>> ./migration-test -m slow # runs smoke only, no slow tests in the smoke set
>> FLAKY=1 ./migration-test # runs smoke only, no flaky tests in the smoke set
>>
>> ./migration-test --full # runs full set, i.e. level 2
>> ./migration-test --full -m slow # runs full set + slow tests
>> FLAKY=1 ./migration-test --full # runs full set + flaky tests
Don't see this^ as a matrix of --full and -m. This is identical to what
we have today, with the addition of a flag that determines the amount of
tests run. We could call it other names if we want:
--size small/large
--testset smoke/full
>>
>> I made the first one like that so the compat tests in CI now run less
>> tests. We don't need full set during compat because that job is about
>> catching changes in device code. It would also make the argument easier
>> to enable the compat job for all migration-test-supported archs.
>>
>> >>
>> >> I think the best we can do is have a qtest_migration_level_<ARCH> and
>> >> set it for every arch.
>> >>
>> >> Also note that we must keep plain 'migration-test' invocation working
>> >> because of the compat test.
>> >
>> > We won't break it if we only switch to levels, right?
>> >
>> > Btw, I also don't know why we need to. IIRC the compat test runs the test
>> > in previous release (but only feeds the new QEMU binary to the old
>> > migration-test)? I think that's one reason why we decided to use the old
>> > migration-test (so we won't have new tests ran on compat tests, which is a
>> > loss), just to avoid any change in migration-test will break the compat
>> > test.. so I assume that should be fine regardless..
>>
>> I meant we shouldn't break the command line invocation:
>>
>> ./tests/qtest/migration-test -p <test_name>
>>
>> As in, we cannot change the test name or add mandatory flags. Otherwise
>> we have a discrepancy betweem what the CI job is calling vs. what the
>> build actually provides. We run the tests from the previous build, but
>> the CI job is current.
>
> I failed to follow here. Our CI doesn't hardcode any <test_name>, right?
> It should invoke "migration-test" in the old build, feeding new QEMU binary
> to it, run whatever are there in the old test.
Yes, but we have the words "migration-test" in the CI .yaml *today*. It
doesn't matter if it invokes the tests from the last release. If we
changed the name to "migration-foobar", then the CI job continues to
work up until 10.0 is released. It then breaks immediately in the first
commit of 10.0.50 because the previous release will now have the
"migration-foobar" name while the CI still calls for "migration-test".
Basically the point is that CI .yaml changes take effect immediately
while test cmdline changes only take effect (in CI) on the next release.
>
>>
>> Another point that is more of an annoyance is that the migration-test
>> invocation not being stable affectes debug, bisect, etc. When debugging
>> the recent multifd regression I already had to keep changing from
>> /multifd/tcp/... to /multifd/tcp/uri/... when changing QEMU versions.
>
> So I could have missed something above, and I can understand this adds
> burden to bisections. However I do prefer we can change test path any way
> we want (even if in most cases we shouldn't ever touch them, and we should
> still try to not change them as frequent). IOW we also need to consider
> the overhead of keeping test paths to be part of ABI as well.
It's annoying, that's all. Makes me 1% more grumpy.
^ permalink raw reply [flat|nested] 55+ messages in thread* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-19 19:31 ` Fabiano Rosas
@ 2024-12-20 15:18 ` Peter Xu
2024-12-20 15:34 ` Daniel P. Berrangé
2024-12-20 16:39 ` Fabiano Rosas
0 siblings, 2 replies; 55+ messages in thread
From: Peter Xu @ 2024-12-20 15:18 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
On Thu, Dec 19, 2024 at 04:31:04PM -0300, Fabiano Rosas wrote:
> We shouldn't change stuff that's also used by the rest of the
> community. People know about QEMU_TEST_FLAKY_TESTS and -m slow. These
> must continue to work the same.
I see what I overlook; it's used much more than I thought in qtest and we
also have a CI for it.. So ok, let's keep at least QEMU_TEST_FLAKY_TESTS.
But again, I don't think it matters much even if we rename it, it means the
flaky CI test won't run these two migration tests, but that's not the end
of the world either, if you see what I meant. CI relies on the normal
tests rather than flaky tests to present.
We should be able to move in / take out FLAKY tests at will, as that's not
what CI is really relying on. Here renaming the macro in migration test
almost means we take both out.
>
> We can say: "Internally we don't allow slow and flaky to be in the smoke
> set".
>
> We cannot say: "In migration-test QEMU_TEST_FLAKY_TESTS is actually
> QEMU_MIG_TEST_FLAKY, -m slow is actually QEMU_MIG_TEST_EXTRA, -m slow
> just implies KVM and -m quick implies TCG. Easy peasy!"
>
> >
> >> endif
> >>
> >> cmdline invocations:
> >> ./migration-test # runs smoke, i.e. level 1
> >> ./migration-test -m slow # runs smoke only, no slow tests in the smoke set
> >> FLAKY=1 ./migration-test # runs smoke only, no flaky tests in the smoke set
> >>
> >> ./migration-test --full # runs full set, i.e. level 2
> >> ./migration-test --full -m slow # runs full set + slow tests
> >> FLAKY=1 ./migration-test --full # runs full set + flaky tests
>
> Don't see this^ as a matrix of --full and -m. This is identical to what
> we have today, with the addition of a flag that determines the amount of
> tests run. We could call it other names if we want:
>
> --size small/large
> --testset smoke/full
>
> >>
> >> I made the first one like that so the compat tests in CI now run less
> >> tests. We don't need full set during compat because that job is about
> >> catching changes in device code. It would also make the argument easier
> >> to enable the compat job for all migration-test-supported archs.
> >>
> >> >>
> >> >> I think the best we can do is have a qtest_migration_level_<ARCH> and
> >> >> set it for every arch.
> >> >>
> >> >> Also note that we must keep plain 'migration-test' invocation working
> >> >> because of the compat test.
> >> >
> >> > We won't break it if we only switch to levels, right?
> >> >
> >> > Btw, I also don't know why we need to. IIRC the compat test runs the test
> >> > in previous release (but only feeds the new QEMU binary to the old
> >> > migration-test)? I think that's one reason why we decided to use the old
> >> > migration-test (so we won't have new tests ran on compat tests, which is a
> >> > loss), just to avoid any change in migration-test will break the compat
> >> > test.. so I assume that should be fine regardless..
> >>
> >> I meant we shouldn't break the command line invocation:
> >>
> >> ./tests/qtest/migration-test -p <test_name>
> >>
> >> As in, we cannot change the test name or add mandatory flags. Otherwise
> >> we have a discrepancy betweem what the CI job is calling vs. what the
> >> build actually provides. We run the tests from the previous build, but
> >> the CI job is current.
> >
> > I failed to follow here. Our CI doesn't hardcode any <test_name>, right?
> > It should invoke "migration-test" in the old build, feeding new QEMU binary
> > to it, run whatever are there in the old test.
>
> Yes, but we have the words "migration-test" in the CI .yaml *today*. It
> doesn't matter if it invokes the tests from the last release. If we
> changed the name to "migration-foobar", then the CI job continues to
> work up until 10.0 is released. It then breaks immediately in the first
> commit of 10.0.50 because the previous release will now have the
> "migration-foobar" name while the CI still calls for "migration-test".
Not fair at all: nobody suggested to rename the test!
>
> Basically the point is that CI .yaml changes take effect immediately
> while test cmdline changes only take effect (in CI) on the next release.
Yes, but so far the "API" is the test name only (and actually not.. more
below), and at least no path involved. That's why I want to make sure
we're on the same page. So looks like at least "what tests to run by
default", and "full path of each of the test case" can still change.
Here's the "more below" part: logically if we want we can change the name
of migration-test. We need to teach the CI on which version of QEMU to use
which program to test in the compat tests. It isn't really hard (a git-tag
-> prog-name hash), it's just unnecessary to change the test name at all.
>
> >
> >>
> >> Another point that is more of an annoyance is that the migration-test
> >> invocation not being stable affectes debug, bisect, etc. When debugging
> >> the recent multifd regression I already had to keep changing from
> >> /multifd/tcp/... to /multifd/tcp/uri/... when changing QEMU versions.
> >
> > So I could have missed something above, and I can understand this adds
> > burden to bisections. However I do prefer we can change test path any way
> > we want (even if in most cases we shouldn't ever touch them, and we should
> > still try to not change them as frequent). IOW we also need to consider
> > the overhead of keeping test paths to be part of ABI as well.
>
> It's annoying, that's all. Makes me 1% more grumpy.
I'm not going to change any.. but keeping it a protocol is another thing.
So to summarize..
My plan (after adjustment, keeping the name of QEMU_TEST_FLAKY_TESTS) is to
introduce QEMU_TEST_EXTRA_TESTS (renamed following FLAKY) and cover the
three current "slow" tests. Then we stick with -m for the new quick/slow,
which maps to tcg/kvm directly. That saves the extra --full parameter.
The diff v.s. your plan is, afaict, you prefer introducing yet another
--full parameter.
Yours is good that we stick with the whole test in compat tests with no
extra change, which is a benefit indeed.
If go with my plan, the default compat behavior will start to change after
10.0 released. We need to do one more step if we still prefer the wholeset
run in compat test, which is at the start of 10.1, we send a patch to
change compat test's parameter from none to "-m slow".
Feel free to go ahead with whatever you prefer. To me, the more important
bit is whether we both think that one program is better than two, and also
that means decouple host setup v.s. tests. I don't have a strong opinion
otherwise..
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-20 15:18 ` Peter Xu
@ 2024-12-20 15:34 ` Daniel P. Berrangé
2024-12-20 16:06 ` Peter Xu
2024-12-20 16:39 ` Fabiano Rosas
1 sibling, 1 reply; 55+ messages in thread
From: Daniel P. Berrangé @ 2024-12-20 15:34 UTC (permalink / raw)
To: Peter Xu
Cc: Fabiano Rosas, qemu-devel, Peter Maydell, Alex Bennée,
Laurent Vivier, Paolo Bonzini
On Fri, Dec 20, 2024 at 10:18:37AM -0500, Peter Xu wrote:
> On Thu, Dec 19, 2024 at 04:31:04PM -0300, Fabiano Rosas wrote:
> > We shouldn't change stuff that's also used by the rest of the
> > community. People know about QEMU_TEST_FLAKY_TESTS and -m slow. These
> > must continue to work the same.
>
> I see what I overlook; it's used much more than I thought in qtest and we
> also have a CI for it.. So ok, let's keep at least QEMU_TEST_FLAKY_TESTS.
>
> But again, I don't think it matters much even if we rename it, it means the
> flaky CI test won't run these two migration tests, but that's not the end
> of the world either, if you see what I meant. CI relies on the normal
> tests rather than flaky tests to present.
>
> We should be able to move in / take out FLAKY tests at will, as that's not
> what CI is really relying on. Here renaming the macro in migration test
> almost means we take both ou.t
Side-note - QEMU_TEST_FLAKY_TESTS is something we should apply
consistently across all types of tests - unit, qtest, functional,
and across all environments - CI and local developer execution.
In recent changes to functional testing, I've set the expectation[1]
that any use of QEMU_TEST_FLAKY_TESTS *must* be accompanied by a
link to the gitlab.com/qemu/qemu-project issue that describes
the flaky behaviour seen. We've got too many places with flaky
tests where we don't quiet remember what was flaky, so don't
know if we can remove it or not
With regards,
Daniel
[1]https://gitlab.com/qemu-project/qemu/-/blob/master/tests/functional/qemu_test/decorators.py?ref_type=heads#L42
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 55+ messages in thread
* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-20 15:34 ` Daniel P. Berrangé
@ 2024-12-20 16:06 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-12-20 16:06 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Fabiano Rosas, qemu-devel, Peter Maydell, Alex Bennée,
Laurent Vivier, Paolo Bonzini
On Fri, Dec 20, 2024 at 03:34:32PM +0000, Daniel P. Berrangé wrote:
> On Fri, Dec 20, 2024 at 10:18:37AM -0500, Peter Xu wrote:
> > On Thu, Dec 19, 2024 at 04:31:04PM -0300, Fabiano Rosas wrote:
> > > We shouldn't change stuff that's also used by the rest of the
> > > community. People know about QEMU_TEST_FLAKY_TESTS and -m slow. These
> > > must continue to work the same.
> >
> > I see what I overlook; it's used much more than I thought in qtest and we
> > also have a CI for it.. So ok, let's keep at least QEMU_TEST_FLAKY_TESTS.
> >
> > But again, I don't think it matters much even if we rename it, it means the
> > flaky CI test won't run these two migration tests, but that's not the end
> > of the world either, if you see what I meant. CI relies on the normal
> > tests rather than flaky tests to present.
> >
> > We should be able to move in / take out FLAKY tests at will, as that's not
> > what CI is really relying on. Here renaming the macro in migration test
> > almost means we take both ou.t
>
> Side-note - QEMU_TEST_FLAKY_TESTS is something we should apply
> consistently across all types of tests - unit, qtest, functional,
> and across all environments - CI and local developer execution.
>
> In recent changes to functional testing, I've set the expectation[1]
> that any use of QEMU_TEST_FLAKY_TESTS *must* be accompanied by a
> link to the gitlab.com/qemu/qemu-project issue that describes
> the flaky behaviour seen. We've got too many places with flaky
> tests where we don't quiet remember what was flaky, so don't
> know if we can remove it or not
Thanks for the heads up. Yes it makes sense to document it explicitly in
the code. For now we can still dig into git log, but not as easy as
inline.
Aside that, IIUC the major challenge will still be there though on the
justification itself. It could depend on how easily reproduceable the
issue is on the developers' hosts.. If there can be some link that not only
report where does it came from, but also to verify a fix that'll be even
nicer.
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread
* Re: [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke
2024-12-20 15:18 ` Peter Xu
2024-12-20 15:34 ` Daniel P. Berrangé
@ 2024-12-20 16:39 ` Fabiano Rosas
1 sibling, 0 replies; 55+ messages in thread
From: Fabiano Rosas @ 2024-12-20 16:39 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
Peter Xu <peterx@redhat.com> writes:
> On Thu, Dec 19, 2024 at 04:31:04PM -0300, Fabiano Rosas wrote:
>> We shouldn't change stuff that's also used by the rest of the
>> community. People know about QEMU_TEST_FLAKY_TESTS and -m slow. These
>> must continue to work the same.
>
> I see what I overlook; it's used much more than I thought in qtest and we
> also have a CI for it.. So ok, let's keep at least QEMU_TEST_FLAKY_TESTS.
>
> But again, I don't think it matters much even if we rename it, it means the
> flaky CI test won't run these two migration tests, but that's not the end
> of the world either, if you see what I meant. CI relies on the normal
> tests rather than flaky tests to present.
>
> We should be able to move in / take out FLAKY tests at will, as that's not
> what CI is really relying on. Here renaming the macro in migration test
> almost means we take both out.
>
>>
>> We can say: "Internally we don't allow slow and flaky to be in the smoke
>> set".
>>
>> We cannot say: "In migration-test QEMU_TEST_FLAKY_TESTS is actually
>> QEMU_MIG_TEST_FLAKY, -m slow is actually QEMU_MIG_TEST_EXTRA, -m slow
>> just implies KVM and -m quick implies TCG. Easy peasy!"
>>
>> >
>> >> endif
>> >>
>> >> cmdline invocations:
>> >> ./migration-test # runs smoke, i.e. level 1
>> >> ./migration-test -m slow # runs smoke only, no slow tests in the smoke set
>> >> FLAKY=1 ./migration-test # runs smoke only, no flaky tests in the smoke set
>> >>
>> >> ./migration-test --full # runs full set, i.e. level 2
>> >> ./migration-test --full -m slow # runs full set + slow tests
>> >> FLAKY=1 ./migration-test --full # runs full set + flaky tests
>>
>> Don't see this^ as a matrix of --full and -m. This is identical to what
>> we have today, with the addition of a flag that determines the amount of
>> tests run. We could call it other names if we want:
>>
>> --size small/large
>> --testset smoke/full
>>
>> >>
>> >> I made the first one like that so the compat tests in CI now run less
>> >> tests. We don't need full set during compat because that job is about
>> >> catching changes in device code. It would also make the argument easier
>> >> to enable the compat job for all migration-test-supported archs.
>> >>
>> >> >>
>> >> >> I think the best we can do is have a qtest_migration_level_<ARCH> and
>> >> >> set it for every arch.
>> >> >>
>> >> >> Also note that we must keep plain 'migration-test' invocation working
>> >> >> because of the compat test.
>> >> >
>> >> > We won't break it if we only switch to levels, right?
>> >> >
>> >> > Btw, I also don't know why we need to. IIRC the compat test runs the test
>> >> > in previous release (but only feeds the new QEMU binary to the old
>> >> > migration-test)? I think that's one reason why we decided to use the old
>> >> > migration-test (so we won't have new tests ran on compat tests, which is a
>> >> > loss), just to avoid any change in migration-test will break the compat
>> >> > test.. so I assume that should be fine regardless..
>> >>
>> >> I meant we shouldn't break the command line invocation:
>> >>
>> >> ./tests/qtest/migration-test -p <test_name>
>> >>
>> >> As in, we cannot change the test name or add mandatory flags. Otherwise
>> >> we have a discrepancy betweem what the CI job is calling vs. what the
>> >> build actually provides. We run the tests from the previous build, but
>> >> the CI job is current.
>> >
>> > I failed to follow here. Our CI doesn't hardcode any <test_name>, right?
>> > It should invoke "migration-test" in the old build, feeding new QEMU binary
>> > to it, run whatever are there in the old test.
>>
>> Yes, but we have the words "migration-test" in the CI .yaml *today*. It
>> doesn't matter if it invokes the tests from the last release. If we
>> changed the name to "migration-foobar", then the CI job continues to
>> work up until 10.0 is released. It then breaks immediately in the first
>> commit of 10.0.50 because the previous release will now have the
>> "migration-foobar" name while the CI still calls for "migration-test".
>
> Not fair at all: nobody suggested to rename the test!
>
Right, you suggested new comand line options (level) and I simply
*mentioned* that we should pay attention to not affect that CI job. The
rest of the exchange is just me trying to clarify. I was not using this
as an argument against your idea.
>>
>> Basically the point is that CI .yaml changes take effect immediately
>> while test cmdline changes only take effect (in CI) on the next release.
>
> Yes, but so far the "API" is the test name only (and actually not.. more
> below), and at least no path involved. That's why I want to make sure
> we're on the same page. So looks like at least "what tests to run by
> default", and "full path of each of the test case" can still change.
>
> Here's the "more below" part: logically if we want we can change the name
> of migration-test. We need to teach the CI on which version of QEMU to use
> which program to test in the compat tests. It isn't really hard (a git-tag
> -> prog-name hash), it's just unnecessary to change the test name at all.
>
Sure, we could add code around it if we wanted indeed.
>>
>> >
>> >>
>> >> Another point that is more of an annoyance is that the migration-test
>> >> invocation not being stable affectes debug, bisect, etc. When debugging
>> >> the recent multifd regression I already had to keep changing from
>> >> /multifd/tcp/... to /multifd/tcp/uri/... when changing QEMU versions.
>> >
>> > So I could have missed something above, and I can understand this adds
>> > burden to bisections. However I do prefer we can change test path any way
>> > we want (even if in most cases we shouldn't ever touch them, and we should
>> > still try to not change them as frequent). IOW we also need to consider
>> > the overhead of keeping test paths to be part of ABI as well.
>>
>> It's annoying, that's all. Makes me 1% more grumpy.
>
> I'm not going to change any.. but keeping it a protocol is another thing.
>
> So to summarize..
>
> My plan (after adjustment, keeping the name of QEMU_TEST_FLAKY_TESTS) is to
> introduce QEMU_TEST_EXTRA_TESTS (renamed following FLAKY) and cover the
> three current "slow" tests. Then we stick with -m for the new quick/slow,
> which maps to tcg/kvm directly. That saves the extra --full parameter.
>
> The diff v.s. your plan is, afaict, you prefer introducing yet another
> --full parameter.
>
> Yours is good that we stick with the whole test in compat tests with no
> extra change, which is a benefit indeed.
It's the other way around. If we add --full, then compat will be !full,
so it will start running less tests. This is a "breaking change" of
sorts, but since the whole point of the series is to run less stuff in
general, then I think it's aligned with the plan.
>
> If go with my plan, the default compat behavior will start to change after
> 10.0 released. We need to do one more step if we still prefer the wholeset
> run in compat test, which is at the start of 10.1, we send a patch to
> change compat test's parameter from none to "-m slow".
>
> Feel free to go ahead with whatever you prefer. To me, the more important
> bit is whether we both think that one program is better than two, and also
> that means decouple host setup v.s. tests. I don't have a strong opinion
> otherwise..
Yeah, I can't deal with subverting -m slow. My initial version had this
and it didn't felt right. Maybe in 2025 I'll be a new man and we can do
differently. Let's see =)
^ permalink raw reply [flat|nested] 55+ messages in thread
* [PATCH v2 20/22] tests/qtest/migration: Pick smoke tests
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (18 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 19/22] tests/qtest/migration: Add migration-test-smoke Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-13 19:46 ` [PATCH v2 21/22] tests/qtest: Add support for check-qtest-<subsystem> Fabiano Rosas
` (2 subsequent siblings)
22 siblings, 0 replies; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
Choose a few tests per group and move them from the full set to the
smoke set.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/migration/compression-tests.c | 7 +++----
tests/qtest/migration/cpr-tests.c | 3 ++-
tests/qtest/migration/misc-tests.c | 11 +++++------
tests/qtest/migration/postcopy-tests.c | 15 ++++++++-------
tests/qtest/migration/precopy-tests.c | 19 +++++++++----------
tests/qtest/migration/tls-tests.c | 7 ++++---
6 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c
index b48dc87239..353b7ccbdd 100644
--- a/tests/qtest/migration/compression-tests.c
+++ b/tests/qtest/migration/compression-tests.c
@@ -208,7 +208,9 @@ static void test_multifd_tcp_zlib(void)
void migration_test_add_compression_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
- /* TODO: add smoke tests */
+
+ migration_test_add("/migration/multifd/tcp/plain/zlib",
+ test_multifd_tcp_zlib);
}
void migration_test_add_compression(MigrationTestEnv *env)
@@ -239,7 +241,4 @@ void migration_test_add_compression(MigrationTestEnv *env)
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/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
index 4fe6eefe86..c5cad2a20c 100644
--- a/tests/qtest/migration/cpr-tests.c
+++ b/tests/qtest/migration/cpr-tests.c
@@ -47,7 +47,8 @@ static void test_mode_reboot(void)
void migration_test_add_cpr_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
- /* TODO: add smoke tests */
+
+ /* none for now */
}
void migration_test_add_cpr(MigrationTestEnv *env)
diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
index 480fbda1c9..ee737c3942 100644
--- a/tests/qtest/migration/misc-tests.c
+++ b/tests/qtest/migration/misc-tests.c
@@ -253,12 +253,6 @@ static void test_validate_uri_channels_none_set(void)
void migration_test_add_misc_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
- /* TODO: add smoke tests */
-}
-
-void migration_test_add_misc(MigrationTestEnv *env)
-{
- migration_test_add_misc_smoke(env);
migration_test_add("/migration/bad_dest", test_baddest);
#ifndef _WIN32
@@ -279,3 +273,8 @@ void migration_test_add_misc(MigrationTestEnv *env)
migration_test_add("/migration/validate_uri/channels/none_set",
test_validate_uri_channels_none_set);
}
+
+void migration_test_add_misc(MigrationTestEnv *env)
+{
+ migration_test_add_misc_smoke(env);
+}
diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c
index 90d2d0820c..36ef05caa0 100644
--- a/tests/qtest/migration/postcopy-tests.c
+++ b/tests/qtest/migration/postcopy-tests.c
@@ -81,19 +81,20 @@ static void test_postcopy_preempt_recovery(void)
void migration_test_add_postcopy_smoke(MigrationTestEnv *env)
{
- /* TODO: add smoke tests */
-}
-
-void migration_test_add_postcopy(MigrationTestEnv *env)
-{
- migration_test_add_postcopy_smoke(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);
+ }
+}
+
+void migration_test_add_postcopy(MigrationTestEnv *env)
+{
+ migration_test_add_postcopy_smoke(env);
+
+ if (env->has_uffd) {
migration_test_add("/migration/postcopy/preempt/recovery/plain",
test_postcopy_preempt_recovery);
diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c
index 393c7e226a..4a5e7f41a9 100644
--- a/tests/qtest/migration/precopy-tests.c
+++ b/tests/qtest/migration/precopy-tests.c
@@ -949,12 +949,6 @@ static void test_dirty_limit(void)
void migration_test_add_precopy_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
- /* TODO: add smoke tests */
-}
-
-void migration_test_add_precopy(MigrationTestEnv *env)
-{
- migration_test_add_precopy_smoke(env);
if (env->is_x86) {
migration_test_add("/migration/precopy/unix/suspend/live",
@@ -967,6 +961,15 @@ void migration_test_add_precopy(MigrationTestEnv *env)
test_precopy_unix_plain);
migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain);
+ migration_test_add("/migration/multifd/tcp/uri/plain/none",
+ test_multifd_tcp_uri_none);
+ migration_test_add("/migration/multifd/tcp/plain/cancel",
+ test_multifd_tcp_cancel);
+}
+
+void migration_test_add_precopy(MigrationTestEnv *env)
+{
+ migration_test_add_precopy_smoke(env);
migration_test_add("/migration/precopy/tcp/plain/switchover-ack",
test_precopy_tcp_switchover_ack);
@@ -990,16 +993,12 @@ void migration_test_add_precopy(MigrationTestEnv *env)
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) {
diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c
index 264b54f352..37b5b64692 100644
--- a/tests/qtest/migration/tls-tests.c
+++ b/tests/qtest/migration/tls-tests.c
@@ -725,12 +725,15 @@ static void test_multifd_tcp_tls_x509_reject_anon_client(void)
void migration_test_add_tls_smoke(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
- /* TODO: add smoke tests */
+
+ migration_test_add("/migration/precopy/tcp/tls/psk/match",
+ test_precopy_tcp_tls_psk_match);
}
void migration_test_add_tls(MigrationTestEnv *env)
{
migration_test_add_tls_smoke(env);
+
migration_test_add("/migration/precopy/unix/tls/psk",
test_precopy_unix_tls_psk);
@@ -756,8 +759,6 @@ void migration_test_add_tls(MigrationTestEnv *env)
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
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* [PATCH v2 21/22] tests/qtest: Add support for check-qtest-<subsystem>
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (19 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 20/22] tests/qtest/migration: Pick smoke tests Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-13 19:46 ` [PATCH v2 22/22] docs: Add migration tests documentation Fabiano Rosas
2024-11-25 20:58 ` [PATCH v2 00/22] tests/qtest: migration-test refactoring Peter Xu
22 siblings, 0 replies; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée, Laurent Vivier, Paolo Bonzini
Allow qtests to be ran by subsystem. Some subsystems, such as
migration, have a large number of tests and we could benefit from
being able to access them from make check without having to run the
full set of qtests.
This adds the following make check targets:
make check-qtest-migration
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
I did not include check-qtest-<arch>-<subsys> because meson generates a
long line that affects readability.
---
tests/qtest/meson.build | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 811117d264..c3fe67f78e 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -36,6 +36,11 @@ if enable_modules
qtests_generic += [ 'modules-test' ]
endif
+qtest_subsystems = {
+ 'migration-test-smoke': ['migration'],
+ 'migration-test': ['migration'],
+}
+
qtests_pci = \
(config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) + \
(config_all_devices.has_key('CONFIG_IVSHMEM_DEVICE') ? ['ivshmem-test'] : [])
@@ -434,6 +439,12 @@ foreach dir : target_dirs
test: executable(test, src, dependencies: deps)
}
endif
+
+ suites = ['qtest', 'qtest-' + target_base]
+ foreach subsys: qtest_subsystems.get(test, [])
+ suites += ['qtest-' + subsys]
+ endforeach
+
test('qtest-@0@/@1@'.format(target_base, test),
qtest_executables[test],
depends: [test_deps, qtest_emulator, emulator_modules],
@@ -442,6 +453,6 @@ foreach dir : target_dirs
protocol: 'tap',
timeout: slow_qtests.get(test, 60),
priority: slow_qtests.get(test, 60),
- suite: ['qtest', 'qtest-' + target_base])
+ suite: suites)
endforeach
endforeach
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* [PATCH v2 22/22] docs: Add migration tests documentation
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (20 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 21/22] tests/qtest: Add support for check-qtest-<subsystem> Fabiano Rosas
@ 2024-11-13 19:46 ` Fabiano Rosas
2024-11-25 20:58 ` [PATCH v2 00/22] tests/qtest: migration-test refactoring Peter Xu
22 siblings, 0 replies; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-13 19:46 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Xu, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
Add documentation about how to write, run and debug migration tests.
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
docs/devel/testing/index.rst | 1 +
docs/devel/testing/main.rst | 12 ++
docs/devel/testing/migration.rst | 261 +++++++++++++++++++++++++++++++
docs/devel/testing/qtest.rst | 1 +
4 files changed, 275 insertions(+)
create mode 100644 docs/devel/testing/migration.rst
diff --git a/docs/devel/testing/index.rst b/docs/devel/testing/index.rst
index 1171f7db8f..32948a9889 100644
--- a/docs/devel/testing/index.rst
+++ b/docs/devel/testing/index.rst
@@ -9,6 +9,7 @@ testing infrastructure.
main
qtest
+ migration
functional
avocado
acpi-bits
diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst
index 91f4dc61fb..2dde6b6518 100644
--- a/docs/devel/testing/main.rst
+++ b/docs/devel/testing/main.rst
@@ -96,6 +96,18 @@ QTest cases can be executed with
make check-qtest
+Migration
+~~~~~~~~~
+
+Migration tests are part of QTest, but are run independently. Refer
+to :doc:`migration` for more details.
+
+Migration test cases can be executed with
+
+.. code::
+
+ make check-qtest-migration
+
Writing portable test cases
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Both unit tests and qtests can run on POSIX hosts as well as Windows hosts.
diff --git a/docs/devel/testing/migration.rst b/docs/devel/testing/migration.rst
new file mode 100644
index 0000000000..aa6fd65af4
--- /dev/null
+++ b/docs/devel/testing/migration.rst
@@ -0,0 +1,261 @@
+.. _migration:
+
+Migration tests
+===============
+
+Migration tests are part of QTest, but have some particularities of
+their own, such as:
+
+- Extended test time due to the need to exercise the iterative phase
+ of migration;
+- Extra requirements on the QEMU binary being used due to
+ :ref:`cross-version migration <cross-version-tests>`;
+- The use of a custom binary for the guest code to test memory
+ integrity (see :ref:`guest-code`).
+
+Invocation
+----------
+
+Migration tests can be ran with:
+
+.. code::
+
+ make check-qtest
+ make check-qtest-migration
+
+or directly:
+
+.. code::
+
+ # all tests
+ QTEST_QEMU_BINARY=./qemu-system-x86_64 ./tests/qtest/migration-test -m thorough
+
+ # single test
+ QTEST_QEMU_BINARY=./qemu-system-x86_64 ./tests/qtest/migration-test -m thorough -p /x86_64/migration/bad_dest
+
+ # all tests under /multifd (note no trailing slash)
+ QTEST_QEMU_BINARY=./qemu-system-x86_64 ./tests/qtest/migration-test -m thorough -r /x86_64/migration/multifd
+
+for cross-version tests (see :ref:`cross-version-tests`):
+
+.. code::
+
+ # old QEMU -> new QEMU
+ QTEST_QEMU_BINARY=./qemu-system-x86_64 QTEST_QEMU_BINARY_SRC=./old/qemu-system-x86_64 -m thorough ./tests/qtest/migration-test
+ QTEST_QEMU_BINARY_DST=./qemu-system-x86_64 QTEST_QEMU_BINARY=./old/qemu-system-x86_64 -m thorough ./tests/qtest/migration-test
+
+ # new QEMU -> old QEMU (backwards migration)
+ QTEST_QEMU_BINARY_SRC=./qemu-system-x86_64 QTEST_QEMU_BINARY=./old/qemu-system-x86_64 -m thorough ./tests/qtest/migration-test
+ QTEST_QEMU_BINARY=./qemu-system-x86_64 QTEST_QEMU_BINARY_DST=./old/qemu-system-x86_64 -m thorough ./tests/qtest/migration-test
+
+ # both _SRC and _DST variants are supported for convenience
+
+.. _cross-version-tests:
+
+Cross-version tests
+~~~~~~~~~~~~~~~~~~~
+
+To detect compatibility issues between different QEMU versions, all
+migration tests can be executed with two different QEMU versions. The
+common machine type between the two versions is used.
+
+To setup cross-version tests, a previous build of QEMU must be kept,
+e.g.:
+
+.. code::
+
+ # build current code
+ mkdir build
+ cd build
+ ../configure; make
+
+ # build previous version
+ cd ../
+ mkdir build-9.1
+ git checkout v9.1.0
+ cd build
+ ../configure; make
+
+To avoid issues with newly added features and new tests, it is highly
+recommended to run the tests from the source directory of *older*
+version being tested.
+
+.. code::
+
+ ./build/qemu-system-x86_64 --version
+ QEMU emulator version 9.1.50
+
+ ./build-9.1/qemu-system-x86_64 --version
+ QEMU emulator version 9.1.0
+
+ cd build-9.1
+ QTEST_QEMU_BINARY=./qemu-system-x86_64 QTEST_QEMU_BINARY_DST=../build/qemu-system-x86_64 ./tests/qtest/migration-test -m thorough
+
+
+How to write migration tests
+----------------------------
+
+Add a test function (prefixed with ``test_``) that gets registered
+with QTest using the ``migration_test_add*()`` helpers.
+
+.. code::
+
+ migration_test_add("/migration/multifd/tcp/plain/cancel", test_multifd_tcp_cancel);
+
+There is no formal grammar for the definition of the test paths, but
+an informal rule is followed for consistency. Usually:
+
+``/migration/<multifd|precopy|postcopy>/<url type>/<test-specific>/``
+
+Bear in mind that the path string affects test execution order and
+filtering when using the ``-r`` flag.
+
+For simpler tests, the test function can setup the test arguments in
+the ``MigrateCommon`` structure and call into a common test
+routine. Currently there are two common test routines:
+
+ - test_precopy_common - for generic precopy migration
+ - test_file_common - for migration using the file: URL
+
+The general structure of a test routine is:
+
+- call ``migrate_start()`` to initialize the two QEMU
+ instances. Usually named "from", for the source machine and "to" for
+ the destination machine;
+
+- define the migration duration, (roughly speaking either quick or
+ slow) by altering the convergence parameters with
+ ``migrate_ensure[_non]_converge()``;
+
+- wait for the machines to be in the desired state with the ``wait_for_*``
+ helpers;
+
+- migrate with ``migrate_qmp()/migrate_incoming_qmp()/migrate_qmp_fail()``;
+
+- check that guest memory was not corrupted and clean up the QEMU
+ instances with ``migrate_end()``.
+
+If using the common test routines, the ``.start_hook`` and
+``.end_hook`` callbacks can be used to perform test-specific tasks.
+
+.. _guest-code:
+
+About guest code
+----------------
+
+The tests all use a custom, architecture-specific binary as the guest
+code. This code, known as a-b-kernel or a-b-bootblock, constantly
+iterates over the guest memory, writing a number to the start of each
+guest page, incrementing it as it loops around (i.e. a generation
+count). This allows the tests to catch memory corruption errors that
+occur during migration as every page's first byte must have the same
+value, except at the point where the transition happens.
+
+Whenever guest memory is migrated incorrectly, the test will output
+the address and amount of pages that present a value inconsistent with
+the generation count, e.g.:
+
+.. code::
+
+ Memory content inconsistency at d53000 first_byte = 27 last_byte = 26 current = 27 hit_edge = 1
+ Memory content inconsistency at d54000 first_byte = 27 last_byte = 26 current = 27 hit_edge = 1
+ Memory content inconsistency at d55000 first_byte = 27 last_byte = 26 current = 27 hit_edge = 1
+ and in another 4929 pages
+
+In the scenario above,
+
+``first_byte`` shows that the current generation number is 27, therefore
+all pages should have 27 as their first byte. Since ``hit_edge=1``, that
+means the transition point was found, i.e. the guest was stopped for
+migration while not all pages had yet been updated to the new
+generation count. So 26 is also a valid byte to find in some pages.
+
+The inconsistency here is that ``last_byte``, i.e. the previous
+generation count is smaller than the ``current`` byte, which should not
+be possible. This would indicate a memory layout such as:
+
+.. code::
+
+ 0xb00000 | 27 00 00 ...
+ ...
+ 0xc00000 | 27 00 00 ...
+ ...
+ 0xd00000 | 27 00 00 ...
+ 0x?????? | 26 00 00 ... <-- pages around this addr weren't migrated correctly
+ ...
+ 0xd53000 | 27 00 00 ...
+ 0xd54000 | 27 00 00 ...
+ 0xd55000 | 27 00 00 ...
+ ...
+
+The a-b code is located at ``tests/qtest/migration/<arch>``.
+
+Troubleshooting
+---------------
+
+Migration tests usually run as part of make check, which is most
+likely to not have been using the verbose flag, so the first thing to
+check is the test log from meson (``meson-logs/testlog.txt``).
+
+There, look for the last "Running" entry, which will be the current
+test. Notice whether the failing program is one of the QEMU instances
+or the migration-test-* themselves.
+
+E.g.:
+
+.. code::
+
+ # Running /s390x/migration/precopy/unix/plain
+ # Using machine type: s390-ccw-virtio-9.2
+ # starting QEMU: exec ./qemu-system-s390x -qtest ...
+ # starting QEMU: exec ./qemu-system-s390x -qtest ...
+ ----------------------------------- stderr -----------------------------------
+ migration-test: ../tests/qtest/migration-test.c:1712: test_precopy_common: Assertion `0' failed.
+
+ (test program exited with status code -6)
+
+.. code::
+
+ # Running /x86_64/migration/bad_dest
+ # Using machine type: pc-q35-9.2
+ # starting QEMU: exec ./qemu-system-x86_64 -qtest ...
+ # starting QEMU: exec ./qemu-system-x86_64 -qtest ...
+ ----------------------------------- stderr -----------------------------------
+ Broken pipe
+ ../tests/qtest/libqtest.c:205: kill_qemu() detected QEMU death from signal 6 (Aborted) (core dumped)
+
+ (test program exited with status code -6)
+
+The above is usually not enough to determine what happened, so
+re-running the test directly is helpful:
+
+.. code::
+
+ QTEST_QEMU_BINARY=./qemu-system-x86_64 ./tests/qtest/migration-test -m thorough -p /x86_64/migration/bad_dest
+
+There are also the QTEST_LOG and QTEST_TRACE variables for increased
+logging and tracing.
+
+The QTEST_QEMU_BINARY environment variable can be abused to hook GDB
+or valgrind into the invocation:
+
+.. code::
+
+ QTEST_QEMU_BINARY='gdb -q --ex "set pagination off" --ex "set print thread-events off" \
+ --ex "handle SIGUSR1 noprint" --ex "break <breakpoint>" --ex "run" --ex "quit \$_exitcode" \
+ --args ./qemu-system-x86_64' ./tests/qtest/migration-test -m thorough -p /x86_64/migration/multifd/file/mapped-ram/fdset/dio
+
+.. code::
+
+ QTEST_QEMU_BINARY='valgrind -q --leak-check=full --show-leak-kinds=definite,indirect \
+ ./qemu-system-x86_64' ./tests/qtest/migration-test -m thorough -r /x86_64/migration
+
+Whenever a test fails, it will leave behind a temporary
+directory. This is useful for file migrations to inspect the generated
+migration file:
+
+.. code::
+
+ $ file /tmp/migration-test-X496U2/migfile
+ /tmp/migration-test-X496U2/migfile: QEMU suspend to disk image
+ $ hexdump -C /tmp/migration-test-X496U2/migfile | less
diff --git a/docs/devel/testing/qtest.rst b/docs/devel/testing/qtest.rst
index c5b8546b3e..4665c160b6 100644
--- a/docs/devel/testing/qtest.rst
+++ b/docs/devel/testing/qtest.rst
@@ -5,6 +5,7 @@ QTest Device Emulation Testing Framework
.. toctree::
qgraph
+ migration
QTest is a device emulation testing framework. It can be very useful to test
device models; it could also control certain aspects of QEMU (such as virtual
--
2.35.3
^ permalink raw reply related [flat|nested] 55+ messages in thread* Re: [PATCH v2 00/22] tests/qtest: migration-test refactoring
2024-11-13 19:46 [PATCH v2 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
` (21 preceding siblings ...)
2024-11-13 19:46 ` [PATCH v2 22/22] docs: Add migration tests documentation Fabiano Rosas
@ 2024-11-25 20:58 ` Peter Xu
2024-11-25 21:18 ` Fabiano Rosas
22 siblings, 1 reply; 55+ messages in thread
From: Peter Xu @ 2024-11-25 20:58 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
On Wed, Nov 13, 2024 at 04:46:08PM -0300, Fabiano Rosas wrote:
> Fabiano Rosas (22):
> tests/qtest/migration: Fix indentations
> tests/qtest/migration: Standardize hook names
> tests/qtest/migration: Stop calling everything "test"
> tests/migration: Disambiguate guestperf vs. a-b
> tests/qtest/migration: Move bootfile code to its own file
> tests/qtest/migration: Move qmp helpers to a separate file
> tests/qtest/migration: Rename migration-helpers.c
> tests/qtest/migration: Move ufd_version_check to utils
> tests/qtest/migration: Move kvm_dirty_ring_supported to utils
> tests/qtest/migration: Isolate test initialization
> tests/qtest/migration: Move common test code
> tests/qtest/migration: Split TLS tests from migration-test.c
> tests/qtest/migration: Split compression tests from migration-test.c
> tests/qtest/migration: Split postcopy tests
> tests/qtest/migration: Split file tests
> tests/qtest/migration: Split precopy tests
> tests/qtest/migration: Split CPR tests
> tests/qtest/migration: Split validation tests + misc
I'm not sure whether the above chunk could affect people reading the last
four, which is the real meat.
One thing we could do (but you can decide which you prefer..) is you can
respin with the initial 18 patches, then we may get it in in the 1st 10.0
pull. It may conflict with some other series for sure, but it's
unavoidable with such changes one way or another.. then when it's there we
could repost the last four separately, so it can have a higher chance of
getting some comments. So I'll leave that to you to decide.
I also wonder whether we could already move migration-test*.c into
tests/qtest/migration/ too.
> tests/qtest/migration: Add migration-test-smoke
> tests/qtest/migration: Pick smoke tests
> tests/qtest: Add support for check-qtest-<subsystem>
> docs: Add migration tests documentation
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread* Re: [PATCH v2 00/22] tests/qtest: migration-test refactoring
2024-11-25 20:58 ` [PATCH v2 00/22] tests/qtest: migration-test refactoring Peter Xu
@ 2024-11-25 21:18 ` Fabiano Rosas
2024-11-25 21:23 ` Peter Xu
0 siblings, 1 reply; 55+ messages in thread
From: Fabiano Rosas @ 2024-11-25 21:18 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
Peter Xu <peterx@redhat.com> writes:
> On Wed, Nov 13, 2024 at 04:46:08PM -0300, Fabiano Rosas wrote:
>> Fabiano Rosas (22):
>> tests/qtest/migration: Fix indentations
>> tests/qtest/migration: Standardize hook names
>> tests/qtest/migration: Stop calling everything "test"
>> tests/migration: Disambiguate guestperf vs. a-b
>> tests/qtest/migration: Move bootfile code to its own file
>> tests/qtest/migration: Move qmp helpers to a separate file
>> tests/qtest/migration: Rename migration-helpers.c
>> tests/qtest/migration: Move ufd_version_check to utils
>> tests/qtest/migration: Move kvm_dirty_ring_supported to utils
>> tests/qtest/migration: Isolate test initialization
>> tests/qtest/migration: Move common test code
>> tests/qtest/migration: Split TLS tests from migration-test.c
>> tests/qtest/migration: Split compression tests from migration-test.c
>> tests/qtest/migration: Split postcopy tests
>> tests/qtest/migration: Split file tests
>> tests/qtest/migration: Split precopy tests
>> tests/qtest/migration: Split CPR tests
>> tests/qtest/migration: Split validation tests + misc
>
> I'm not sure whether the above chunk could affect people reading the last
> four, which is the real meat.
>
> One thing we could do (but you can decide which you prefer..) is you can
> respin with the initial 18 patches, then we may get it in in the 1st 10.0
> pull. It may conflict with some other series for sure, but it's
> unavoidable with such changes one way or another..
I can put them on top of migration-next, sure. As I said previously,
just point me at any conflicting series and I can provide a rebase.
> then when it's there we could repost the last four separately, so it
> can have a higher chance of getting some comments. So I'll leave that
> to you to decide.
That's fine.
>
> I also wonder whether we could already move migration-test*.c into
> tests/qtest/migration/ too.
I'll look into it.
^ permalink raw reply [flat|nested] 55+ messages in thread
* Re: [PATCH v2 00/22] tests/qtest: migration-test refactoring
2024-11-25 21:18 ` Fabiano Rosas
@ 2024-11-25 21:23 ` Peter Xu
0 siblings, 0 replies; 55+ messages in thread
From: Peter Xu @ 2024-11-25 21:23 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Peter Maydell, Daniel P . Berrangé,
Alex Bennée
On Mon, Nov 25, 2024 at 06:18:15PM -0300, Fabiano Rosas wrote:
> > One thing we could do (but you can decide which you prefer..) is you can
> > respin with the initial 18 patches, then we may get it in in the 1st 10.0
> > pull. It may conflict with some other series for sure, but it's
> > unavoidable with such changes one way or another..
>
> I can put them on top of migration-next, sure. As I said previously,
> just point me at any conflicting series and I can provide a rebase.
I didn't queue anything yet for -next. So far this series looks like the
closest to be queued first there. So I suppose if you repost you can base
onto the master branch.
--
Peter Xu
^ permalink raw reply [flat|nested] 55+ messages in thread