Oh, and here is a possible way that ToMigrationState could be implemented automatically:
#[derive(ToMigrationState)] struct DeviceRegisters { #[migration_state(omit)] runtime_field: u32, #[migration_state(clone)] shared_data: String, #[migration_state(into(Cow<'static, str>), clone)] converted_field: String, #[migration_state(try_into(i8))] fallible_field: u32, // Default: use ToMigrationState trait recursively nested_field: NestedStruct, // Primitive types have a default implementation of ToMigrationState simple_field: u32, } Paolo On Sat, Sep 6, 2025 at 8:45 AM Paolo Bonzini <pbonz...@redhat.com> wrote: > > Hi, > > based on the low-level sketch in Zhao and my presentation, > I would like to propose this more high-level implementation > of pre/post migration callbacks. > > Instead of dealing with pre/post callbacks, devices implement a > snapshot/restore mechanism; this way, C code sees a simplified > picture and does not have to deal with Rust concepts such as > Mutex<>. > > Using it is very easy, you can just declare your state like: > > regs: Migratable<Mutex<MyDeviceRegisters>> > > If a pure snapshot is possible, implementing the new trait > is also simple: > > impl_vmstate_struct!(MyDeviceRegisters, ...); > > impl ToMigrationState for MyDeviceRegisters { > type Migrated = Self; > fn to_migration_state(&self) -> > Result<Box<Self>, ...> { > Ok(Box::new(self.clone())) > } > > fn restore_migrated_state_mut(&mut self, source: &Self, > _version_id: u8) -> Result<(), migration::InvalidError> { > *self = source; > Ok(()) > } > } > > I'm really bad at writing Rust code with the correct syntax > from the get-go, but I'll try anyway. > > new traits: > > /// Enables QEMU migration support for types that may be wrapped in > /// synchronization primitives (like `Mutex`) that the C migration > /// code cannot directly handle. The trait provides methods to > /// extract essential state for migration and restore it after > /// migration completes. > /// > /// On top of extracting data from synchronization wrappers during save > /// and restoring it during load, it's also possible to convert > /// runtime representations to migration-safe formats. > trait ToMigrationState { > type Migrated: Default + VMState; > fn to_migration_state(&self) -> > Result<Box<Self::Migrated>, migration::InvalidError>; > fn restore_migrated_state_mut(&mut self, source: &Self::Migrated, > version_id: u8) -> Result<(), migration::InvalidError>; > } > > /// Extension trait for types that support migration state restoration > /// through interior mutability. > /// > /// This trait extends `ToMigrationState` for types that can restore > /// their state without requiring mutable access. While user structs > /// will generally use `ToMigrationState`, the device will have multiple > /// references and therefore the device struct has to employ an interior > /// mutability wrapper like `Mutex`, `RefCell`, or `BqlRefCell`. In > /// turn, wrappers implementing this trait can be used within `Migratable<T>`, > /// which makes no assumptions on how to achieve mutable access to the > /// run-time state. > trait ToMigrationStateShared: ToMigrationState { > fn restore_migrated_state(&self, source: &Self::Migrated) -> > Result<(), migration::InvalidError>; > } > > > with implementations for wrapper types like: > > impl<T> ToMigrationState for Mutex<T: ToMigrationState> { > type Migrated = T::Migrated; > fn to_migration_state(&self) -> > Result<Box<Self::Migrated>, migration::InvalidError> { > self.lock().to_migration_state() > } > ... > } > > impl<T> ToMigrationStateShared for Mutex<T: ToMigrationState> { > fn restore_migrated_state(&self, source: &Self::Migrated, > version_id: u8) -> Result<(), migration::InvalidError>{ > self.lock().restore_migrated_state_mut(source, version_id) > } > } > > impl<T> ToMigrationState for BqlRefCell<T: ToMigrationState> { > type Migrated = T::Migrated; > fn to_migration_state(&self) -> > Result<Box<Self::Migrated>, migration::InvalidError> { > self.borrow().to_migration_state() > } > ... > } > > impl<T> ToMigrationStateShared for BqlRefCell<T: ToMigrationState> { > fn restore_migrated_state(&self, source: &Self::Migrated, > version_id: u8) ->Result<(), migration::InvalidError> { > self.borrow_mut().restore_migrated_state_mut(source, version_id) > } > } > > new struct maps the above trait to the C-style callbacks: > > /// A wrapper that bridges Rust types with QEMU's C-based migration system. > /// > /// `Migratable<T>` enables QEMU migration support for Rust types that > implement > /// `ToMigrationState`, as long as they are wrapped with an interior > mutability > /// like `Mutex` or `BqlRefCell`. It provides translation functionality as > well > /// as access to synchronization primitives that the C code cannot directly > handle. > /// > /// This wrapper acts as a transparent proxy during normal operation > /// (via `Deref`/`DerefMut`), while handling state extraction and restoration > /// around migration. > pub struct<T: ToMigrationStateShared> Migratable { > runtime_state: T, > // C vmstate does not support NULL pointers, so no Option<Box<>> > // Actually a BqlCell<*mut T::Migrated>, but keeping it simple > // for now. > migration_state: *mut T::Migrated > }; > > unsafe impl<T> Send for Migratable<T: Send> {} > unsafe impl<T> Sync for Migratable<T: Sync> {} > > // just return runtime_state > impl<T> Deref for Migratable<T: ToMigrationStateShared> { > type Migrated = T; > ... > } > impl<T> DerefMut for Migratable<T: ToMigrationStateShared> { > ... > } > > impl Migratable { > fn pre_save(...) -> ... { > self.migration_state = Box::into_raw(self.0.to_migration_state()?); > } > > fn post_save(...) -> ... { > drop(Box::from_raw(self.migration_state.replace(ptr::null_mut())); > } > > fn pre_load(...) -> ... { > self.migration_state = Box::into_raw(Box::default()); > } > > fn post_load(...) -> ... { > let state = > Box::from_raw(self.migration_state.replace(ptr::null_mut()); > self.0.restore_migrated_state(state, version_id) > } > } > > unsafe impl VMState for Migratable<T: ToMigrationStateShared> { > const BASE: bindings::VMStateField = { > static VMSD: &$crate::bindings::VMStateDescription = > VMStateDescriptionBuilder::<Self>::new() > .version_id(T::VMSD.version_id) > .minimum_version_id(T::VMSD.minimum_version_id) > .priority(T::VMSD.priority) > .pre_load(Self::pre_load) > .post_load(Self::post_load) > .pre_save(Self::pre_save) > .post_save(Self::post_save) > .fields(vmstate_fields! { > vmstate_of!(Migratable<T>, migration_state) > } > .build(); > > bindings::VMStateField { > vmsd: addr_of!(*VMSD), > size: size_of::<Migratable<T>>(), > flags: bindings::VMStateFlags::VMS_STRUCT, > ..common::Zeroable::ZERO > } > }; > } > > This is just a sketch but should give the idea. > > Paolo