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


Reply via email to