@@ -54,7 +54,9 @@ use vm_memory::{
5454 GuestAddress , GuestAddressSpace , GuestMemory , GuestMemoryAtomic , ReadVolatile ,
5555 VolatileMemoryError , VolatileSlice , WriteVolatile ,
5656} ;
57- use vm_migration:: progress:: MigrationProgressAndStatus ;
57+ use vm_migration:: progress:: {
58+ MemoryTransmissionInfo , MigrationPhase , MigrationProgressAndStatus , TransportationMode ,
59+ } ;
5860use vm_migration:: protocol:: * ;
5961use vm_migration:: tls:: { TlsConnectionWrapper , TlsStream , TlsStreamWrapper } ;
6062use vm_migration:: {
@@ -278,6 +280,9 @@ impl From<u64> for EpollDispatch {
278280 }
279281}
280282
283+ // TODO make this a member of Vmm
284+ static MIGRATION_PROGRESS_SNAPSHOT : Mutex < Option < MigrationProgressAndStatus > > = Mutex :: new ( None ) ;
285+
281286enum SocketStream {
282287 Unix ( UnixStream ) ,
283288 Tcp ( TcpStream ) ,
@@ -2039,6 +2044,12 @@ impl Vmm {
20392044 ) -> result:: Result < MemoryRangeTable , MigratableError > {
20402045 let mut bandwidth = 0.0 ;
20412046 let mut iteration_table;
2047+ let total_memory_size_bytes = vm
2048+ . memory_range_table ( ) ?
2049+ . regions ( )
2050+ . iter ( )
2051+ . map ( |range| range. length )
2052+ . sum :: < u64 > ( ) ;
20422053
20432054 loop {
20442055 // todo: check if auto-converge is enabled at all?
@@ -2095,6 +2106,30 @@ impl Vmm {
20952106 s. current_dirty_pages = s. pending_size . div_ceil ( PAGE_SIZE as u64 ) ;
20962107 s. total_transferred_dirty_pages += s. current_dirty_pages ;
20972108
2109+ // Update migration progress snapshot
2110+ // TODO the first version alters this at the beginning of each iteration.
2111+ // We should do this continuously instead.
2112+ {
2113+ let mut lock = MIGRATION_PROGRESS_SNAPSHOT . lock ( ) . unwrap ( ) ;
2114+ lock. as_mut ( )
2115+ . expect ( "live migration should be ongoing" )
2116+ . update_ongoing_migration_state (
2117+ MigrationPhase :: MemoryPrecopy ,
2118+ Some ( MemoryTransmissionInfo {
2119+ memory_iteration : s. iteration ,
2120+ memory_transmission_bps : s. mb_per_sec as u64 * 1024 * 1024 ,
2121+ memory_bytes_total : total_memory_size_bytes,
2122+ memory_bytes_transmitted : s. total_transferred_bytes ,
2123+ memory_pages_4k_transmitted : s. total_transferred_dirty_pages ,
2124+ memory_pages_4k_remaining_iteration : s. current_dirty_pages ,
2125+ memory_bytes_remaining_iteration : s. pending_size ,
2126+ memory_dirty_rate_pps : 0 , /* TODO */
2127+ memory_pages_constant_count : 0 ,
2128+ } ) ,
2129+ Some ( vm. throttle_percent ( ) ) ,
2130+ ) ;
2131+ }
2132+
20982133 // Send the current dirty pages
20992134 let transfer_start = Instant :: now ( ) ;
21002135 mem_send. send_memory ( & iteration_table, socket) ?;
@@ -2207,6 +2242,25 @@ impl Vmm {
22072242 hypervisor : & dyn hypervisor:: Hypervisor ,
22082243 send_data_migration : & VmSendMigrationData ,
22092244 ) -> result:: Result < ( ) , MigratableError > {
2245+ // Update migration progress snapshot
2246+ {
2247+ let mut lock = MIGRATION_PROGRESS_SNAPSHOT . lock ( ) . unwrap ( ) ;
2248+ // if this fails, we made a programming error in our state handling
2249+ assert ! ( lock. is_none( ) ) ;
2250+ let transportation_mode = if send_data_migration. local {
2251+ TransportationMode :: Local
2252+ } else {
2253+ TransportationMode :: Tcp {
2254+ connections : send_data_migration. connections ,
2255+ tls : send_data_migration. tls_dir . is_some ( ) ,
2256+ }
2257+ } ;
2258+ lock. replace ( MigrationProgressAndStatus :: new (
2259+ transportation_mode,
2260+ Duration :: from_millis ( send_data_migration. downtime ) ,
2261+ ) ) ;
2262+ }
2263+
22102264 let mut s = MigrationState :: new ( ) ;
22112265
22122266 // Set up the socket connection
@@ -2251,6 +2305,11 @@ impl Vmm {
22512305 if send_data_migration. local {
22522306 match & mut socket {
22532307 SocketStream :: Unix ( unix_socket) => {
2308+ let mut lock = MIGRATION_PROGRESS_SNAPSHOT . lock ( ) . unwrap ( ) ;
2309+ lock. as_mut ( )
2310+ . expect ( "live migration should be ongoing" )
2311+ . update_ongoing_migration_state ( MigrationPhase :: MemoryFds , None , None ) ;
2312+
22542313 // Proceed with sending memory file descriptors over UNIX socket
22552314 vm. send_memory_fds ( unix_socket) ?;
22562315 }
@@ -2291,6 +2350,14 @@ impl Vmm {
22912350 vm. pause ( ) ?;
22922351 } else {
22932352 Self :: do_memory_migration ( vm, & mut socket, & mut s, send_data_migration) ?;
2353+
2354+ // Update migration progress snapshot
2355+ {
2356+ let mut lock = MIGRATION_PROGRESS_SNAPSHOT . lock ( ) . unwrap ( ) ;
2357+ lock. as_mut ( )
2358+ . expect ( "live migration should be ongoing" )
2359+ . update_ongoing_migration_state ( MigrationPhase :: Completing , None , None ) ;
2360+ }
22942361 }
22952362
22962363 // We release the locks early to enable locking them on the destination host.
@@ -2336,10 +2403,26 @@ impl Vmm {
23362403 // Record total migration time
23372404 s. total_time = s. start_time . elapsed ( ) ;
23382405
2339- info ! ( "Migration complete" ) ;
2406+ info ! ( "Migration complete: {}s" , s. total_time. as_secs_f32( ) ) ;
2407+
2408+ // Update migration progress snapshot
2409+ {
2410+ let mut lock = MIGRATION_PROGRESS_SNAPSHOT . lock ( ) . unwrap ( ) ;
2411+ lock. as_mut ( )
2412+ . expect ( "live migration should be ongoing" )
2413+ . mark_as_finished ( ) ;
2414+ }
23402415
23412416 // Let every Migratable object know about the migration being complete
2342- vm. complete_migration ( )
2417+ vm. complete_migration ( ) ?;
2418+
2419+ // Give management software a chance to fetch the migration state.
2420+ info ! ( "Sleeping five seconds before shutting off." ) ;
2421+ // TODO right now, the http-server is single-threaded and the blocking
2422+ // start-migration API call will block other requests here.
2423+ thread:: sleep ( Duration :: from_secs ( 5 ) ) ;
2424+
2425+ Ok ( ( ) )
23432426 }
23442427
23452428 #[ cfg( all( feature = "kvm" , target_arch = "x86_64" ) ) ]
@@ -2488,6 +2571,14 @@ impl Vmm {
24882571 }
24892572 }
24902573 Err ( e) => {
2574+ // Update migration progress snapshot
2575+ {
2576+ let mut lock = MIGRATION_PROGRESS_SNAPSHOT . lock ( ) . unwrap ( ) ;
2577+ lock. as_mut ( )
2578+ . expect ( "live migration should be ongoing" )
2579+ . mark_as_failed ( & e) ;
2580+ }
2581+
24912582 error ! ( "Migration failed: {e}" ) ;
24922583 {
24932584 info ! ( "Sending Receiver in HTTP thread that migration failed" ) ;
0 commit comments