On Wed, Oct 01, 2025 at 09:52:09AM +0200, Paolo Bonzini wrote:
> Date: Wed, 1 Oct 2025 09:52:09 +0200
> From: Paolo Bonzini <[email protected]>
> Subject: [PATCH 10/11] rust: migration: implement ToMigrationState for Timer
> X-Mailer: git-send-email 2.51.0
>
> Timer is a complex struct, allow adding it to a struct that
> uses #[derive(ToMigrationState)]; similar to vmstate_timer, only
> the expiration time has to be preserved.
>
> In fact, because it is thread-safe, ToMigrationStateShared can
> also be implemented without needing a cell or mutex that wraps
> the timer.
>
> Signed-off-by: Paolo Bonzini <[email protected]>
> ---
> rust/migration/src/migratable.rs | 31 +++++++++++++++++++++++++++++++
> rust/util/src/timer.rs | 10 +++++++++-
> 2 files changed, 40 insertions(+), 1 deletion(-)
>
> diff --git a/rust/migration/src/migratable.rs
> b/rust/migration/src/migratable.rs
> index ded6fe8f4a6..e85ef810efc 100644
> --- a/rust/migration/src/migratable.rs
> +++ b/rust/migration/src/migratable.rs
> @@ -140,6 +140,26 @@ fn restore_migrated_state_mut(
>
> impl_for_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, bool);
>
> +impl ToMigrationState for util::timer::Timer {
> + type Migrated = i64;
> +
> + fn snapshot_migration_state(&self, target: &mut i64) -> Result<(),
> InvalidError> {
> + // SAFETY: as_ptr() is unsafe to ensure that the caller reasons about
> + // the pinning of the data inside the Opaque<>. Here all we do is
> + // access a field.
> + *target = self.expire_time_ns().unwrap_or(-1);
> + Ok(())
> + }
> +
> + fn restore_migrated_state_mut(
> + &mut self,
> + source: Self::Migrated,
> + version_id: u8,
> + ) -> Result<(), InvalidError> {
> + self.restore_migrated_state(source, version_id)
> + }
> +}
> +
> impl<T: ToMigrationState, const N: usize> ToMigrationState for [T; N]
> where
> [T::Migrated; N]: Default,
> @@ -237,6 +257,17 @@ fn restore_migrated_state(
> ) -> Result<(), InvalidError>;
> }
>
> +impl ToMigrationStateShared for util::timer::Timer {
> + fn restore_migrated_state(&self, source: i64, _version_id: u8) ->
> Result<(), InvalidError> {
> + if source >= 0 {
> + self.modify(source as u64);
Timer::modify() is the wrapper of timer_mod(), and timer_mod() will
re-compute the expire time with scale factor.
So here we should use a binding of timer_mod_ns() - Timer::modify_ns()
instead of Timer::modify(), just like C side did.
Moreover, for Timer::modify_ns() & Timer::modify(), the simplest
way is to build them based on timer_mod_ns() & timer_mod() (but this
sounds like shifting the responsibility for type checking and overflow
checking to the C side :-( ).
Alternative, we can make Timer::modify() based on Timer::modify_ns(),
but then it involves many numeric type conversions: scale is i32 but
expire_time is u64, and Timer::modify_ns() may accept a u64 argument.
Fortunately,, we can make Timer's modify() accept i64 to reduce type
conversions.
> + } else {
> + self.delete();
> + }
> + Ok(())
> + }
> +}
> +
> impl<T: ToMigrationStateShared, const N: usize> ToMigrationStateShared for
> [T; N]
> where
> [T::Migrated; N]: Default,
> diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs
> index c6b3e4088ec..a99ff5e7ef7 100644
> --- a/rust/util/src/timer.rs
> +++ b/rust/util/src/timer.rs
> @@ -10,7 +10,8 @@
> use common::{callbacks::FnCall, Opaque};
>
> use crate::bindings::{
> - self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod,
> QEMUClockType,
> + self, qemu_clock_get_ns, timer_del, timer_expire_time_ns,
> timer_init_full, timer_mod,
> + QEMUClockType,
> };
>
> /// A safe wrapper around [`bindings::QEMUTimer`].
> @@ -86,6 +87,13 @@ pub fn init_full<'timer, 'opaque: 'timer, T, F>(
> }
> }
>
> + pub fn expire_time_ns(&self) -> Option<i64> {
> + // SAFETY: the only way to obtain a Timer safely is via methods that
> + // take a Pin<&mut Self>, therefore the timer is pinned
> + let ret = unsafe { timer_expire_time_ns(self.as_ptr()) };
> + i64::try_from(ret).ok()
Good! This detects all negative values.
> + }
> +
> pub fn modify(&self, expire_time: u64) {
> // SAFETY: the only way to obtain a Timer safely is via methods that
> // take a Pin<&mut Self>, therefore the timer is pinned
> --
> 2.51.0
>