| // Copyright 2025 Red Hat, Inc. |
| // Author(s): Paolo Bonzini <pbonzini@redhat.com> |
| // SPDX-License-Identifier: GPL-2.0-or-later |
| |
| use std::{ |
| fmt, |
| mem::size_of, |
| ptr::{self, addr_of, NonNull}, |
| sync::{Arc, Mutex}, |
| }; |
| |
| use bql::{BqlCell, BqlRefCell}; |
| use common::Zeroable; |
| |
| use crate::{ |
| bindings, vmstate_fields_ref, vmstate_of, InvalidError, VMState, VMStateDescriptionBuilder, |
| }; |
| |
| /// Enables QEMU migration support even when a type is wrapped with |
| /// 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 use `ToMigrationState` |
| /// to convert runtime representations to migration-safe formats. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use bql::BqlCell; |
| /// use migration::{InvalidError, ToMigrationState, VMState}; |
| /// # use migration::VMStateField; |
| /// |
| /// # #[derive(Debug, PartialEq, Eq)] |
| /// struct DeviceState { |
| /// counter: BqlCell<u32>, |
| /// enabled: bool, |
| /// } |
| /// |
| /// # #[derive(Debug)] |
| /// #[derive(Default)] |
| /// struct DeviceMigrationState { |
| /// counter: u32, |
| /// enabled: bool, |
| /// } |
| /// |
| /// # unsafe impl VMState for DeviceMigrationState { |
| /// # const BASE: VMStateField = ::common::Zeroable::ZERO; |
| /// # } |
| /// impl ToMigrationState for DeviceState { |
| /// type Migrated = DeviceMigrationState; |
| /// |
| /// fn snapshot_migration_state( |
| /// &self, |
| /// target: &mut Self::Migrated, |
| /// ) -> Result<(), InvalidError> { |
| /// target.counter = self.counter.get(); |
| /// target.enabled = self.enabled; |
| /// Ok(()) |
| /// } |
| /// |
| /// fn restore_migrated_state_mut( |
| /// &mut self, |
| /// source: Self::Migrated, |
| /// _version_id: u8, |
| /// ) -> Result<(), InvalidError> { |
| /// self.counter.set(source.counter); |
| /// self.enabled = source.enabled; |
| /// Ok(()) |
| /// } |
| /// } |
| /// # bql::start_test(); |
| /// # let dev = DeviceState { counter: 10.into(), enabled: true }; |
| /// # let mig = dev.to_migration_state().unwrap(); |
| /// # assert!(matches!(*mig, DeviceMigrationState { counter: 10, enabled: true })); |
| /// # let mut dev2 = DeviceState { counter: 42.into(), enabled: false }; |
| /// # dev2.restore_migrated_state_mut(*mig, 1).unwrap(); |
| /// # assert_eq!(dev2, dev); |
| /// ``` |
| /// |
| /// More commonly, the trait is derived through the |
| /// [`derive(ToMigrationState)`](qemu_macros::ToMigrationState) procedural |
| /// macro. |
| pub trait ToMigrationState { |
| /// The type used to represent the migrated state. |
| type Migrated: Default + VMState; |
| |
| /// Capture the current state into a migration-safe format, failing |
| /// if the state cannot be migrated. |
| fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError>; |
| |
| /// Restores state from a migrated representation, failing if the |
| /// state cannot be restored. |
| fn restore_migrated_state_mut( |
| &mut self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError>; |
| |
| /// Convenience method to combine allocation and state capture |
| /// into a single operation. |
| fn to_migration_state(&self) -> Result<Box<Self::Migrated>, InvalidError> { |
| let mut migrated = Box::<Self::Migrated>::default(); |
| self.snapshot_migration_state(&mut migrated)?; |
| Ok(migrated) |
| } |
| } |
| |
| // Implementations for primitive types. Do not use a blanket implementation |
| // for all Copy types, because [T; N] is Copy if T is Copy; that would conflict |
| // with the below implementation for arrays. |
| macro_rules! impl_for_primitive { |
| ($($t:ty),*) => { |
| $( |
| impl ToMigrationState for $t { |
| type Migrated = Self; |
| |
| fn snapshot_migration_state( |
| &self, |
| target: &mut Self::Migrated, |
| ) -> Result<(), InvalidError> { |
| *target = *self; |
| Ok(()) |
| } |
| |
| fn restore_migrated_state_mut( |
| &mut self, |
| source: Self::Migrated, |
| _version_id: u8, |
| ) -> Result<(), InvalidError> { |
| *self = source; |
| Ok(()) |
| } |
| } |
| )* |
| }; |
| } |
| |
| impl_for_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, bool); |
| |
| impl<T: ToMigrationState, const N: usize> ToMigrationState for [T; N] |
| where |
| [T::Migrated; N]: Default, |
| { |
| type Migrated = [T::Migrated; N]; |
| |
| fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> { |
| for (item, target_item) in self.iter().zip(target.iter_mut()) { |
| item.snapshot_migration_state(target_item)?; |
| } |
| Ok(()) |
| } |
| |
| fn restore_migrated_state_mut( |
| &mut self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError> { |
| for (item, source_item) in self.iter_mut().zip(source) { |
| item.restore_migrated_state_mut(source_item, version_id)?; |
| } |
| Ok(()) |
| } |
| } |
| |
| impl<T: ToMigrationState> ToMigrationState for Mutex<T> { |
| type Migrated = T::Migrated; |
| |
| fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> { |
| self.lock().unwrap().snapshot_migration_state(target) |
| } |
| |
| fn restore_migrated_state_mut( |
| &mut self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError> { |
| self.get_mut() |
| .unwrap() |
| .restore_migrated_state_mut(source, version_id) |
| } |
| } |
| |
| impl<T: ToMigrationState> ToMigrationState for BqlRefCell<T> { |
| type Migrated = T::Migrated; |
| |
| fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> { |
| self.borrow().snapshot_migration_state(target) |
| } |
| |
| fn restore_migrated_state_mut( |
| &mut self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError> { |
| self.get_mut() |
| .restore_migrated_state_mut(source, version_id) |
| } |
| } |
| |
| /// 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`] or [`BqlRefCell`]. |
| /// |
| /// Anything that implements this trait can in turn be used within |
| /// [`Migratable<T>`], which makes no assumptions on how to achieve mutable |
| /// access to the runtime state. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use std::sync::Mutex; |
| /// |
| /// use migration::ToMigrationStateShared; |
| /// |
| /// let device_state = Mutex::new(42); |
| /// // Can restore without &mut access |
| /// device_state.restore_migrated_state(100, 1).unwrap(); |
| /// assert_eq!(*device_state.lock().unwrap(), 100); |
| /// ``` |
| pub trait ToMigrationStateShared: ToMigrationState { |
| /// Restores state from a migrated representation to an interior-mutable |
| /// object. Similar to `restore_migrated_state_mut`, but requires a |
| /// shared reference; therefore it can be used to restore a device's |
| /// state even though devices have multiple references to them. |
| fn restore_migrated_state( |
| &self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError>; |
| } |
| |
| impl<T: ToMigrationStateShared, const N: usize> ToMigrationStateShared for [T; N] |
| where |
| [T::Migrated; N]: Default, |
| { |
| fn restore_migrated_state( |
| &self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError> { |
| for (item, source_item) in self.iter().zip(source) { |
| item.restore_migrated_state(source_item, version_id)?; |
| } |
| Ok(()) |
| } |
| } |
| |
| // Arc requires the contained object to be interior-mutable |
| impl<T: ToMigrationStateShared> ToMigrationState for Arc<T> { |
| type Migrated = T::Migrated; |
| |
| fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> { |
| (**self).snapshot_migration_state(target) |
| } |
| |
| fn restore_migrated_state_mut( |
| &mut self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError> { |
| (**self).restore_migrated_state(source, version_id) |
| } |
| } |
| |
| impl<T: ToMigrationStateShared> ToMigrationStateShared for Arc<T> { |
| fn restore_migrated_state( |
| &self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError> { |
| (**self).restore_migrated_state(source, version_id) |
| } |
| } |
| |
| // Interior-mutable types. Note how they only require ToMigrationState for |
| // the inner type! |
| |
| impl<T: ToMigrationState> ToMigrationStateShared for Mutex<T> { |
| fn restore_migrated_state( |
| &self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError> { |
| self.lock() |
| .unwrap() |
| .restore_migrated_state_mut(source, version_id) |
| } |
| } |
| |
| impl<T: ToMigrationState> ToMigrationStateShared for BqlRefCell<T> { |
| fn restore_migrated_state( |
| &self, |
| source: Self::Migrated, |
| version_id: u8, |
| ) -> Result<(), InvalidError> { |
| self.borrow_mut() |
| .restore_migrated_state_mut(source, version_id) |
| } |
| } |
| |
| /// A wrapper that enables QEMU migration for types with shared state. |
| /// |
| /// `Migratable<T>` provides a bridge between Rust types that use interior |
| /// mutability (like `Mutex<T>`) and QEMU's C-based migration infrastructure. |
| /// It manages the lifecycle of migration state and provides automatic |
| /// conversion between runtime and migration representations. |
| /// |
| /// ``` |
| /// # use std::sync::Mutex; |
| /// # use migration::{Migratable, ToMigrationState, VMState, VMStateField}; |
| /// |
| /// #[derive(ToMigrationState)] |
| /// pub struct DeviceRegs { |
| /// status: u32, |
| /// } |
| /// # unsafe impl VMState for DeviceRegsMigration { |
| /// # const BASE: VMStateField = ::common::Zeroable::ZERO; |
| /// # } |
| /// |
| /// pub struct SomeDevice { |
| /// // ... |
| /// registers: Migratable<Mutex<DeviceRegs>>, |
| /// } |
| /// ``` |
| #[repr(C)] |
| pub struct Migratable<T: ToMigrationStateShared> { |
| /// Pointer to migration state, valid only during migration operations. |
| /// C vmstate does not support NULL pointers, so no `Option<Box<>>`. |
| migration_state: BqlCell<*mut T::Migrated>, |
| |
| /// The runtime state that can be accessed during normal operation |
| runtime_state: T, |
| } |
| |
| impl<T: ToMigrationStateShared> std::ops::Deref for Migratable<T> { |
| type Target = T; |
| |
| fn deref(&self) -> &Self::Target { |
| &self.runtime_state |
| } |
| } |
| |
| impl<T: ToMigrationStateShared> std::ops::DerefMut for Migratable<T> { |
| fn deref_mut(&mut self) -> &mut Self::Target { |
| &mut self.runtime_state |
| } |
| } |
| |
| impl<T: ToMigrationStateShared> Migratable<T> { |
| /// Creates a new `Migratable` wrapper around the given runtime state. |
| /// |
| /// # Returns |
| /// A new `Migratable` instance ready for use and migration |
| pub fn new(runtime_state: T) -> Self { |
| Self { |
| migration_state: BqlCell::new(ptr::null_mut()), |
| runtime_state, |
| } |
| } |
| |
| fn pre_save(&self) -> Result<(), InvalidError> { |
| let state = self.runtime_state.to_migration_state()?; |
| self.migration_state.set(Box::into_raw(state)); |
| Ok(()) |
| } |
| |
| fn post_save(&self) -> Result<(), InvalidError> { |
| let state = unsafe { Box::from_raw(self.migration_state.replace(ptr::null_mut())) }; |
| drop(state); |
| Ok(()) |
| } |
| |
| fn pre_load(&self) -> Result<(), InvalidError> { |
| self.migration_state |
| .set(Box::into_raw(Box::<T::Migrated>::default())); |
| Ok(()) |
| } |
| |
| fn post_load(&self, version_id: u8) -> Result<(), InvalidError> { |
| let state = unsafe { Box::from_raw(self.migration_state.replace(ptr::null_mut())) }; |
| self.runtime_state |
| .restore_migrated_state(*state, version_id) |
| } |
| } |
| |
| impl<T: ToMigrationStateShared + fmt::Debug> fmt::Debug for Migratable<T> |
| where |
| T::Migrated: fmt::Debug, |
| { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| let mut struct_f = f.debug_struct("Migratable"); |
| struct_f.field("runtime_state", &self.runtime_state); |
| |
| let state = NonNull::new(self.migration_state.get()).map(|x| unsafe { x.as_ref() }); |
| struct_f.field("migration_state", &state); |
| struct_f.finish() |
| } |
| } |
| |
| impl<T: ToMigrationStateShared + Default> Default for Migratable<T> { |
| fn default() -> Self { |
| Self::new(T::default()) |
| } |
| } |
| |
| impl<T: 'static + ToMigrationStateShared> Migratable<T> { |
| const FIELD: bindings::VMStateField = vmstate_of!(Self, migration_state); |
| |
| const FIELDS: &[bindings::VMStateField] = vmstate_fields_ref! { |
| Migratable::<T>::FIELD |
| }; |
| |
| const VMSD: &'static bindings::VMStateDescription = VMStateDescriptionBuilder::<Self>::new() |
| .version_id(1) |
| .minimum_version_id(1) |
| .pre_save(&Self::pre_save) |
| .pre_load(&Self::pre_load) |
| .post_save(&Self::post_save) |
| .post_load(&Self::post_load) |
| .fields(Self::FIELDS) |
| .build() |
| .as_ref(); |
| } |
| |
| unsafe impl<T: 'static + ToMigrationStateShared> VMState for Migratable<T> { |
| const BASE: bindings::VMStateField = { |
| bindings::VMStateField { |
| vmsd: addr_of!(*Self::VMSD), |
| size: size_of::<Self>(), |
| flags: bindings::VMStateFlags::VMS_STRUCT, |
| ..Zeroable::ZERO |
| } |
| }; |
| } |