@ -20,6 +20,7 @@
# include "migration/migration-util.h"
# include "ppc-util.h"
# include "qobject/qlist.h"
# include "qapi-types-migration.h"
# include "qemu/module.h"
# include "qemu/option.h"
# include "qemu/range.h"
@ -536,6 +537,161 @@ static void test_multifd_tcp_cancel(void)
migrate_end ( from , to2 , true ) ;
}
static void test_cancel_src_after_failed ( QTestState * from , QTestState * to ,
const char * uri , const char * phase )
{
/*
* No migrate_incoming_qmp ( ) at the start to force source into
* failed state during migrate_qmp ( ) .
*/
wait_for_serial ( " src_serial " ) ;
migrate_ensure_converge ( from ) ;
migrate_qmp ( from , to , uri , NULL , " {} " ) ;
migration_event_wait ( from , phase ) ;
migrate_cancel ( from ) ;
/* cancelling will not move the migration out of 'failed' */
wait_for_migration_status ( from , " failed " ,
( const char * [ ] ) { " completed " , NULL } ) ;
/*
* Not waiting for the destination because it never started
* migration .
*/
}
static void test_cancel_src_after_cancelled ( QTestState * from , QTestState * to ,
const char * uri , const char * phase )
{
migrate_incoming_qmp ( to , uri , NULL , " { 'exit-on-error': false } " ) ;
wait_for_serial ( " src_serial " ) ;
migrate_ensure_converge ( from ) ;
migrate_qmp ( from , to , uri , NULL , " {} " ) ;
/* To move to cancelled/cancelling */
migrate_cancel ( from ) ;
migration_event_wait ( from , phase ) ;
/* The migrate_cancel under test */
migrate_cancel ( from ) ;
wait_for_migration_status ( from , " cancelled " ,
( const char * [ ] ) { " completed " , NULL } ) ;
wait_for_migration_status ( to , " failed " ,
( const char * [ ] ) { " completed " , NULL } ) ;
}
static void test_cancel_src_after_complete ( QTestState * from , QTestState * to ,
const char * uri , const char * phase )
{
migrate_incoming_qmp ( to , uri , NULL , " { 'exit-on-error': false } " ) ;
wait_for_serial ( " src_serial " ) ;
migrate_ensure_converge ( from ) ;
migrate_qmp ( from , to , uri , NULL , " {} " ) ;
migration_event_wait ( from , phase ) ;
migrate_cancel ( from ) ;
/*
* qmp_migrate_cancel ( ) exits early if migration is not running
* anymore , the status will not change to cancelled .
*/
wait_for_migration_complete ( from ) ;
wait_for_migration_complete ( to ) ;
}
static void test_cancel_src_after_none ( QTestState * from , QTestState * to ,
const char * uri , const char * phase )
{
/*
* Test that cancelling without a migration happening does not
* affect subsequent migrations
*/
migrate_cancel ( to ) ;
wait_for_serial ( " src_serial " ) ;
migrate_cancel ( from ) ;
migrate_incoming_qmp ( to , uri , NULL , " { 'exit-on-error': false } " ) ;
migrate_ensure_converge ( from ) ;
migrate_qmp ( from , to , uri , NULL , " {} " ) ;
wait_for_migration_complete ( from ) ;
wait_for_migration_complete ( to ) ;
}
static void test_cancel_src_pre_switchover ( QTestState * from , QTestState * to ,
const char * uri , const char * phase )
{
migrate_set_capability ( from , " pause-before-switchover " , true ) ;
migrate_set_capability ( to , " pause-before-switchover " , true ) ;
migrate_set_capability ( from , " multifd " , true ) ;
migrate_set_capability ( to , " multifd " , true ) ;
migrate_incoming_qmp ( to , uri , NULL , " { 'exit-on-error': false } " ) ;
wait_for_serial ( " src_serial " ) ;
migrate_ensure_converge ( from ) ;
migrate_qmp ( from , to , uri , NULL , " {} " ) ;
migration_event_wait ( from , phase ) ;
migrate_cancel ( from ) ;
migration_event_wait ( from , " cancelling " ) ;
wait_for_migration_status ( from , " cancelled " ,
( const char * [ ] ) { " completed " , NULL } ) ;
wait_for_migration_status ( to , " failed " ,
( const char * [ ] ) { " completed " , NULL } ) ;
}
static void test_cancel_src_after_status ( void * opaque )
{
const char * test_path = opaque ;
g_autofree char * phase = g_path_get_basename ( test_path ) ;
g_autofree char * uri = g_strdup_printf ( " unix:%s/migsocket " , tmpfs ) ;
QTestState * from , * to ;
MigrateStart args = {
. hide_stderr = true ,
} ;
if ( migrate_start ( & from , & to , " defer " , & args ) ) {
return ;
}
if ( g_str_equal ( phase , " cancelling " ) | |
g_str_equal ( phase , " cancelled " ) ) {
test_cancel_src_after_cancelled ( from , to , uri , phase ) ;
} else if ( g_str_equal ( phase , " completed " ) ) {
test_cancel_src_after_complete ( from , to , uri , phase ) ;
} else if ( g_str_equal ( phase , " failed " ) ) {
test_cancel_src_after_failed ( from , to , uri , phase ) ;
} else if ( g_str_equal ( phase , " none " ) ) {
test_cancel_src_after_none ( from , to , uri , phase ) ;
} else {
/* any state that comes before pre-switchover */
test_cancel_src_pre_switchover ( from , to , uri , phase ) ;
}
migrate_end ( from , to , false ) ;
}
static void calc_dirty_rate ( QTestState * who , uint64_t calc_time )
{
qtest_qmp_assert_success ( who ,
@ -1018,4 +1174,24 @@ void migration_test_add_precopy(MigrationTestEnv *env)
test_vcpu_dirty_limit ) ;
}
}
/* ensure new status don't go unnoticed */
assert ( MIGRATION_STATUS__MAX = = 15 ) ;
for ( int i = MIGRATION_STATUS_NONE ; i < MIGRATION_STATUS__MAX ; i + + ) {
switch ( i ) {
case MIGRATION_STATUS_DEVICE : /* happens too fast */
case MIGRATION_STATUS_WAIT_UNPLUG : /* no support in tests */
case MIGRATION_STATUS_COLO : /* no support in tests */
case MIGRATION_STATUS_POSTCOPY_ACTIVE : /* postcopy can't be cancelled */
case MIGRATION_STATUS_POSTCOPY_PAUSED :
case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP :
case MIGRATION_STATUS_POSTCOPY_RECOVER :
continue ;
default :
migration_test_add_suffix ( " /migration/cancel/src/after/ " ,
MigrationStatus_str ( i ) ,
test_cancel_src_after_status ) ;
}
}
}