@@ -16,10 +16,11 @@ use std::num::Wrapping;
1616use std:: os:: unix:: fs:: OpenOptionsExt ;
1717use std:: os:: unix:: io:: { AsRawFd , FromRawFd } ;
1818use std:: path:: { Path , PathBuf } ;
19- use std:: result;
2019use std:: sync:: { Arc , Mutex } ;
20+ use std:: time:: Duration ;
2121#[ cfg( not( target_arch = "riscv64" ) ) ]
2222use std:: time:: Instant ;
23+ use std:: { result, thread} ;
2324
2425use acpi_tables:: sdt:: GenericAddress ;
2526use acpi_tables:: { Aml , aml} ;
@@ -90,8 +91,8 @@ use vfio_ioctls::{VfioContainer, VfioDevice, VfioDeviceFd};
9091use virtio_devices:: transport:: { VirtioPciDevice , VirtioPciDeviceActivator , VirtioTransport } ;
9192use virtio_devices:: vhost_user:: VhostUserConfig ;
9293use virtio_devices:: {
93- AccessPlatformMapping , ActivateError , Block , Endpoint , IommuMapping , VdpaDmaMapping ,
94- VirtioMemMappingSource ,
94+ AccessPlatformMapping , ActivateError , Block , Endpoint , IommuMapping , PostMigrationAnnouncer ,
95+ VdpaDmaMapping , VirtioMemMappingSource ,
9596} ;
9697use vm_allocator:: { AddressAllocator , SystemAllocator } ;
9798use vm_device:: dma_mapping:: ExternalDmaMapping ;
@@ -5063,6 +5064,48 @@ impl DeviceManager {
50635064 self . vfio_container = None ;
50645065 }
50655066 }
5067+
5068+ // Calls the PostMigrationAnnouncers of each device that has one, and schedules
5069+ // periodic announcements. Currently only network devices use this to announce
5070+ // the new location of the VM to the network.
5071+ pub fn post_migration_announce ( & self ) {
5072+ let mut announcers: Vec < Box < dyn PostMigrationAnnouncer > > = self
5073+ . virtio_devices
5074+ . iter ( )
5075+ . filter_map ( |dev| dev. virtio_device . lock ( ) . unwrap ( ) . post_migration_announcer ( ) )
5076+ . collect ( ) ;
5077+
5078+ announcers. iter_mut ( ) . for_each ( |a| a. announce_once ( ) ) ;
5079+ schedule_post_migration_announces ( announcers, 4 , 50 , 100 , 450 ) ;
5080+ }
5081+ }
5082+
5083+ // We could make this announcer configurable.
5084+ fn schedule_post_migration_announces (
5085+ mut announcers : Vec < Box < dyn PostMigrationAnnouncer > > ,
5086+ rounds : u32 ,
5087+ initial_ms : u64 ,
5088+ step_ms : u64 ,
5089+ max_ms : u64 ,
5090+ ) {
5091+ if announcers. is_empty ( ) || rounds == 0 {
5092+ return ;
5093+ }
5094+
5095+ let _ = thread:: Builder :: new ( )
5096+ . name ( "post-migration-announcers" . to_string ( ) )
5097+ . spawn ( move || {
5098+ for round in 0 ..rounds {
5099+ // The first announce is done synchronous, thus we sleep at the
5100+ // start of the loop.
5101+
5102+ let delay = ( initial_ms + ( round as u64 ) * step_ms) . min ( max_ms) ;
5103+ let delay = Duration :: from_millis ( delay) ;
5104+ thread:: sleep ( delay) ;
5105+
5106+ announcers. iter_mut ( ) . for_each ( |a| a. announce_once ( ) ) ;
5107+ }
5108+ } ) ;
50665109}
50675110
50685111#[ cfg( feature = "ivshmem" ) ]
@@ -5406,6 +5449,10 @@ impl Pausable for DeviceManager {
54065449 }
54075450
54085451 fn resume ( & mut self ) -> result:: Result < ( ) , MigratableError > {
5452+ // Before resuming the devices, we active the post migration announcers
5453+ // of devices that have one.
5454+ self . post_migration_announce ( ) ;
5455+
54095456 for ( _, device_node) in self . device_tree . lock ( ) . unwrap ( ) . iter ( ) {
54105457 if let Some ( migratable) = & device_node. migratable {
54115458 migratable. lock ( ) . unwrap ( ) . resume ( ) ?;
0 commit comments