qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Zhao Liu <zhao1.liu@intel.com>
To: Paolo Bonzini <pbonzini@redhat.com>
Cc: qemu-rust@nondevel.org, qemu-devel <qemu-devel@nongnu.org>,
	Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Subject: Re: Rust high-level pre/post migration callbacks
Date: Wed, 10 Sep 2025 16:19:59 +0800	[thread overview]
Message-ID: <aME0r+dDsdmGCbxA@intel.com> (raw)
In-Reply-To: <8076a298-1cd9-4c90-a64c-f65004753975@redhat.com>

> 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>>

I realized there's no need for QemuMutex binding, since we can use Rust
native Mutex directly, except some case which will share the Mutex with
C side.

> 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) ->

Just about the name:

`to_migration_state` and `restore_migrated_state*` sound not a proper pair.
What about `snapshoot_migration_state` and `restore_migration_state`?

>         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) ->

I think maybe here it's also necessary to accept a `&mut self` since
device would make some changes in pre_save.

Then this trate can provide mutable methods and ToMigrationStateShare
provides immuatable ones.

BTW, if possible, maybe we can rename the traits like:
* ToMigrationState -> ToMigrationStateMut
* ToMigrationStateShared -> ToMigrationState

>         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()

I'm considerring maybe we could use get_mut() (and check bql by
assert!(bql_locked())) instead of locking this Mutex.

In this context, C side should hold the BQL lock so that this is
already a stronger protection.

>     }
>     ...

This omits the restore_migrated_state_mut, I guess it should be
filled with `unimplemented!()`.

> }
> 
> 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)

And here, maybe `get_mut()` + `assert!(bql_locked())`?

>     }
> }

...

> 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)
>     }

Yes, this is a nice way to handle Rust wrappers.

> }
> 
> 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)

Maybe performance is a thing, and it might be worth comparing the
impact of these additional callbacks.

>                 .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
>         }
>     };
> }

Overall, the design looks great!

Thanks,
Zhao



  parent reply	other threads:[~2025-09-10  7:59 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-06  6:45 Rust high-level pre/post migration callbacks Paolo Bonzini
2025-09-07 14:35 ` Paolo Bonzini
2025-09-08 10:02 ` Manos Pitsidianakis
2025-09-08 10:19   ` Paolo Bonzini
2025-09-08 10:25     ` Manos Pitsidianakis
2025-09-10  8:19 ` Zhao Liu [this message]
2025-09-10 11:33   ` Paolo Bonzini
2025-09-10 16:24     ` Zhao Liu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aME0r+dDsdmGCbxA@intel.com \
    --to=zhao1.liu@intel.com \
    --cc=manos.pitsidianakis@linaro.org \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-rust@nondevel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).