On Tue, Dec 24, 2024 at 08:17:07AM -0800, Steve Sistare wrote: > Add a migration test for cpr-transfer mode. Defer the connection to the > target monitor, else the test hangs because in cpr-transfer mode QEMU does > not listen for monitor connections until we send the migrate command to > source QEMU. > > To test -incoming defer, send a migrate incoming command to the target, > after sending the migrate command to the source, as required by > cpr-transfer mode. > > Signed-off-by: Steve Sistare <steven.sist...@oracle.com> > --- > tests/qtest/migration/cpr-tests.c | 60 > +++++++++++++++++++++++++++++++++++++++ > tests/qtest/migration/framework.c | 19 +++++++++++++ > tests/qtest/migration/framework.h | 3 ++ > 3 files changed, 82 insertions(+) > > diff --git a/tests/qtest/migration/cpr-tests.c > b/tests/qtest/migration/cpr-tests.c > index 44ce89a..b221980 100644 > --- a/tests/qtest/migration/cpr-tests.c > +++ b/tests/qtest/migration/cpr-tests.c > @@ -44,6 +44,62 @@ static void test_mode_reboot(void) > test_file_common(&args, true); > } > > +static void *test_mode_transfer_start(QTestState *from, QTestState *to) > +{ > + migrate_set_parameter_str(from, "mode", "cpr-transfer"); > + return NULL; > +} > + > +/* > + * cpr-transfer mode cannot use the target monitor prior to starting the > + * migration, and cannot connect synchronously to the monitor, so defer > + * the target connection. > + */ > +static void test_mode_transfer_common(bool incoming_defer) > +{ > + g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs); > + g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs); > + g_autofree char *uri = g_strdup_printf("unix:%s", mig_path); > + > + const char *opts = "-machine aux-ram-share=on -nodefaults"; > + g_autofree const char *cpr_channel = g_strdup_printf( > + "cpr,addr.transport=socket,addr.type=unix,addr.path=%s", > + cpr_path); > + g_autofree char *opts_target = g_strdup_printf("-incoming %s %s", > + cpr_channel, opts); > + > + g_autofree char *connect_channels = g_strdup_printf( > + "[ { 'channel-type': 'main'," > + " 'addr': { 'transport': 'socket'," > + " 'type': 'unix'," > + " 'path': '%s' } } ]", > + mig_path);
So this test already uses json-format channels, IMHO we probably don't need MigrateCommon.cpr_channel anymore? We could put them all here. Then.. > + > + MigrateCommon args = { > + .start.opts_source = opts, > + .start.opts_target = opts_target, > + .start.defer_target_connect = true, > + .start.memory_backend = "-object > memory-backend-memfd,id=pc.ram,size=%s" > + " -machine memory-backend=pc.ram", > + .listen_uri = incoming_defer ? "defer" : uri, > + .connect_channels = connect_channels, > + .cpr_channel = cpr_channel, > + .start_hook = test_mode_transfer_start, > + }; > + > + test_precopy_common(&args); > +} > + > +static void test_mode_transfer(void) > +{ > + test_mode_transfer_common(NULL); > +} > + > +static void test_mode_transfer_defer(void) > +{ > + test_mode_transfer_common(true); > +} > + > void migration_test_add_cpr(MigrationTestEnv *env) > { > tmpfs = env->tmpfs; > @@ -55,4 +111,8 @@ void migration_test_add_cpr(MigrationTestEnv *env) > if (getenv("QEMU_TEST_FLAKY_TESTS")) { > migration_test_add("/migration/mode/reboot", test_mode_reboot); > } > + > + migration_test_add("/migration/mode/transfer", test_mode_transfer); > + migration_test_add("/migration/mode/transfer/defer", > + test_mode_transfer_defer); > } > diff --git a/tests/qtest/migration/framework.c > b/tests/qtest/migration/framework.c > index c6ea3e1..f6175de 100644 > --- a/tests/qtest/migration/framework.c > +++ b/tests/qtest/migration/framework.c > @@ -411,6 +411,7 @@ void migrate_end(QTestState *from, QTestState *to, bool > test_dest) > qtest_quit(to); > > cleanup("migsocket"); > + cleanup("cpr.sock"); > cleanup("src_serial"); > cleanup("dest_serial"); > cleanup(FILE_TEST_FILENAME); > @@ -692,8 +693,11 @@ void test_precopy_common(MigrateCommon *args) > { > QTestState *from, *to; > void *data_hook = NULL; > + QObject *in_channels = NULL; > QObject *out_channels = NULL; > > + g_assert(!args->cpr_channel || args->connect_channels); > + > if (migrate_start(&from, &to, args->listen_uri, &args->start)) { > return; > } > @@ -725,8 +729,20 @@ void test_precopy_common(MigrateCommon *args) > } > } > > + /* > + * The cpr channel must be included in outgoing channels, but not in > + * migrate-incoming channels. > + */ > if (args->connect_channels) { > + in_channels = qobject_from_json(args->connect_channels, > &error_abort); > out_channels = qobject_from_json(args->connect_channels, > &error_abort); > + > + if (args->cpr_channel) { > + QList *channels_list = qobject_to(QList, out_channels); > + QObject *obj = migrate_str_to_channel(args->cpr_channel); > + > + qlist_append(channels_list, obj); > + } ... here IIUC we don't need this trick to manipulate the qlist anymore? Looks like if with that (convert the current cpr_connect string to JSON format, attach it with connect_channels), then we also don't need the pre-requisite patch to make connect_channels manipulate-able. Not sure. > } > > if (args->result == MIG_TEST_QMP_ERROR) { > @@ -739,6 +755,9 @@ void test_precopy_common(MigrateCommon *args) > if (args->start.defer_target_connect) { > qtest_connect(to); > qtest_qmp_handshake(to); > + if (!strcmp(args->listen_uri, "defer")) { > + migrate_incoming_qmp(to, args->connect_uri, in_channels, "{}"); > + } > } > > if (args->result != MIG_TEST_SUCCEED) { > diff --git a/tests/qtest/migration/framework.h > b/tests/qtest/migration/framework.h > index 1341368..4678e2a 100644 > --- a/tests/qtest/migration/framework.h > +++ b/tests/qtest/migration/framework.h > @@ -152,6 +152,9 @@ typedef struct { > */ > const char *connect_channels; > > + /* Optional: the cpr migration channel, in JSON or dotted keys format */ > + const char *cpr_channel; > + > /* Optional: callback to run at start to set migration parameters */ > TestMigrateStartHook start_hook; > /* Optional: callback to run at finish to cleanup */ > -- > 1.8.3.1 > -- Peter Xu