Scalar types are those that have their own VMStateInfo. This poses
a problem in that references to VMStateInfo can only be included in
associated consts starting with Rust 1.83.0, when the const_refs_static
was stabilized. Removing the requirement is done by placing a limited
list of VMStateInfos in an enum, and going from enum to &VMStateInfo
only when building the VMStateField.
The same thing cannot be done with VMS_STRUCT because the set of
VMStateDescriptions extends to structs defined by the devices.
Therefore, structs and cells cannot yet use vmstate_of!.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/src/vmstate.rs | 128 ++++++++++++++++++++++++++++++++++-
1 file changed, 126 insertions(+), 2 deletions(-)
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 22a5ed50d4a..b0a2ce105cc 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -21,8 +21,11 @@
use core::{marker::PhantomData, mem, ptr::NonNull};
-pub use crate::bindings::{VMStateDescription, VMStateField};
use crate::bindings::VMStateFlags;
+pub use crate::{
+ bindings::{self, VMStateDescription, VMStateField},
+ zeroable::Zeroable,
+};
/// This macro is used to call a function with a generic argument bound
/// to the type of a field. The function must take a
@@ -61,6 +64,70 @@ const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker:
};
}
+/// Workaround for lack of `const_refs_static`: references to global variables
+/// can be included in a `static`, but not in a `const`; unfortunately, this
+/// is exactly what would go in the `VMStateField`'s `info` member.
+///
+/// This enum contains the contents of the `VMStateField`'s `info` member,
+/// but as an `enum` instead of a pointer.
+#[allow(non_camel_case_types)]
+pub enum VMStateFieldType {
+ null,
+ vmstate_info_bool,
+ vmstate_info_int8,
+ vmstate_info_int16,
+ vmstate_info_int32,
+ vmstate_info_int64,
+ vmstate_info_uint8,
+ vmstate_info_uint16,
+ vmstate_info_uint32,
+ vmstate_info_uint64,
+ vmstate_info_timer,
+}
+
+/// Workaround for lack of `const_refs_static`. Converts a `VMStateFieldType`
+/// to a `*const VMStateInfo`, for inclusion in a `VMStateField`.
+#[macro_export]
+macro_rules! info_enum_to_ref {
+ ($e:expr) => {
+ unsafe {
+ match $e {
+ $crate::vmstate::VMStateFieldType::null => ::core::ptr::null(),
+ $crate::vmstate::VMStateFieldType::vmstate_info_bool => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_bool)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_int8 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_int8)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_int16 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_int16)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_int32 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_int64 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_int64)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_uint8 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_uint16 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_uint32 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_uint64 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_timer => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_timer)
+ }
+ }
+ }
+ };
+}
+
/// A trait for types that can be included in a device's migration stream. It
/// provides the base contents of a `VMStateField` (minus the name and offset).
///
@@ -69,6 +136,12 @@ const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker:
/// The contents of this trait go straight into structs that are parsed by C
/// code and used to introspect into other structs. Be careful.
pub unsafe trait VMState {
+ /// The `info` member of a `VMStateField` is a pointer and as such cannot
+ /// yet be included in the [`BASE`](VMState::BASE) associated constant;
+ /// this is only allowed by Rust 1.83.0 and newer. For now, include the
+ /// member as an enum which is stored in a separate constant.
+ const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null;
+
/// The base contents of a `VMStateField` (minus the name and offset) for
/// the type that is implementing the trait.
const BASE: VMStateField;
@@ -83,6 +156,12 @@ pub unsafe trait VMState {
};
}
+/// Internal utility function to retrieve a type's `VMStateFieldType`;
+/// used by [`vmstate_of!`](crate::vmstate_of).
+pub const fn vmstate_scalar_type<T: VMState>(_: PhantomData<T>) -> VMStateFieldType {
+ T::SCALAR_TYPE
+}
+
/// Internal utility function to retrieve a type's `VMStateField`;
/// used by [`vmstate_of!`](crate::vmstate_of).
pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField {
@@ -99,6 +178,15 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateField
/// Return the `VMStateField` for a field of a struct. The field must be
/// visible in the current scope.
///
+/// Only a limited set of types is supported out of the box:
+/// * scalar types (integer and `bool`)
+/// * the C struct `QEMUTimer`
+/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`,
+/// [`BqlCell`](crate::cell::BqlCell), [`BqlRefCell`](crate::cell::BqlRefCell)
+/// * a raw pointer to any of the above
+/// * a `NonNull` pointer to any of the above, possibly wrapped with `Option`
+/// * an array of any of the above
+///
/// In order to support other types, the trait `VMState` must be implemented
/// for them.
#[macro_export]
@@ -109,8 +197,14 @@ macro_rules! vmstate_of {
.as_bytes()
.as_ptr() as *const ::std::os::raw::c_char,
offset: $crate::offset_of!($struct_name, $field_name),
- // Compute most of the VMStateField from the type of the field.
$(.num_offset: $crate::offset_of!($struct_name, $num),)?
+ // The calls to `call_func_with_field!` are the magic that
+ // computes most of the VMStateField from the type of the field.
+ info: $crate::info_enum_to_ref!($crate::call_func_with_field!(
+ $crate::vmstate::vmstate_scalar_type,
+ $struct_name,
+ $field_name
+ )),
..$crate::call_func_with_field!(
$crate::vmstate::vmstate_base,
$struct_name,
@@ -187,6 +281,7 @@ pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField {
macro_rules! impl_vmstate_transparent {
($type:ty where $base:tt: VMState $($where:tt)*) => {
unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
+ const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE;
const BASE: VMStateField = VMStateField {
size: mem::size_of::<$type>(),
..<$base as VMState>::BASE
@@ -201,6 +296,33 @@ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState);
impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
+// Scalar types using predefined VMStateInfos
+
+macro_rules! impl_vmstate_scalar {
+ ($info:ident, $type:ty$(, $varray_flag:ident)?) => {
+ unsafe impl VMState for $type {
+ const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info;
+ const BASE: VMStateField = VMStateField {
+ size: mem::size_of::<$type>(),
+ flags: VMStateFlags::VMS_SINGLE,
+ ..Zeroable::ZERO
+ };
+ $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)?
+ }
+ };
+}
+
+impl_vmstate_scalar!(vmstate_info_bool, bool);
+impl_vmstate_scalar!(vmstate_info_int8, i8);
+impl_vmstate_scalar!(vmstate_info_int16, i16);
+impl_vmstate_scalar!(vmstate_info_int32, i32);
+impl_vmstate_scalar!(vmstate_info_int64, i64);
+impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8);
+impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16);
+impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32);
+impl_vmstate_scalar!(vmstate_info_uint64, u64);
+impl_vmstate_scalar!(vmstate_info_timer, bindings::QEMUTimer);
+
// Pointer types using the underlying type's VMState plus VMS_POINTER
// Note that references are not supported, though references to cells
// could be allowed.
@@ -208,6 +330,7 @@ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
macro_rules! impl_vmstate_pointer {
($type:ty where $base:tt: VMState $($where:tt)*) => {
unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
+ const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag();
}
};
@@ -225,6 +348,7 @@ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
// VMS_ARRAY/VMS_ARRAY_OF_POINTER
unsafe impl<T: VMState, const N: usize> VMState for [T; N] {
+ const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
const BASE: VMStateField = <T as VMState>::BASE.with_array_flag(N);
}
--
2.47.1
On Fri, Jan 17, 2025 at 10:00:41AM +0100, Paolo Bonzini wrote: > Date: Fri, 17 Jan 2025 10:00:41 +0100 > From: Paolo Bonzini <pbonzini@redhat.com> > Subject: [PATCH 05/10] rust: vmstate: implement VMState for scalar types > X-Mailer: git-send-email 2.47.1 > > Scalar types are those that have their own VMStateInfo. This poses > a problem in that references to VMStateInfo can only be included in > associated consts starting with Rust 1.83.0, when the const_refs_static > was stabilized. Removing the requirement is done by placing a limited > list of VMStateInfos in an enum, and going from enum to &VMStateInfo > only when building the VMStateField. > > The same thing cannot be done with VMS_STRUCT because the set of > VMStateDescriptions extends to structs defined by the devices. > Therefore, structs and cells cannot yet use vmstate_of!. > > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> > --- > rust/qemu-api/src/vmstate.rs | 128 ++++++++++++++++++++++++++++++++++- > 1 file changed, 126 insertions(+), 2 deletions(-) > /// Internal utility function to retrieve a type's `VMStateField`; > /// used by [`vmstate_of!`](crate::vmstate_of). > pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField { > @@ -99,6 +178,15 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateField > /// Return the `VMStateField` for a field of a struct. The field must be > /// visible in the current scope. > /// > +/// Only a limited set of types is supported out of the box: > +/// * scalar types (integer and `bool`) > +/// * the C struct `QEMUTimer` > +/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, > +/// [`BqlCell`](crate::cell::BqlCell), [`BqlRefCell`](crate::cell::BqlRefCell) > +/// * a raw pointer to any of the above > +/// * a `NonNull` pointer to any of the above, possibly wrapped with `Option` I just found your rust-next has already updated and removed `Option` :-) > +/// * an array of any of the above > +/// > /// In order to support other types, the trait `VMState` must be implemented > /// for them. > #[macro_export] > @@ -109,8 +197,14 @@ macro_rules! vmstate_of { > .as_bytes() > .as_ptr() as *const ::std::os::raw::c_char, > offset: $crate::offset_of!($struct_name, $field_name), > - // Compute most of the VMStateField from the type of the field. Rebase mistake? This comment seems no need to be deleted. > $(.num_offset: $crate::offset_of!($struct_name, $num),)? > + // The calls to `call_func_with_field!` are the magic that > + // computes most of the VMStateField from the type of the field. > + info: $crate::info_enum_to_ref!($crate::call_func_with_field!( > + $crate::vmstate::vmstate_scalar_type, > + $struct_name, > + $field_name > + )), > Only a nit above, Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
On 1/22/25 13:33, Zhao Liu wrote: > On Fri, Jan 17, 2025 at 10:00:41AM +0100, Paolo Bonzini wrote: >> Date: Fri, 17 Jan 2025 10:00:41 +0100 >> From: Paolo Bonzini <pbonzini@redhat.com> >> Subject: [PATCH 05/10] rust: vmstate: implement VMState for scalar types >> X-Mailer: git-send-email 2.47.1 >> >> Scalar types are those that have their own VMStateInfo. This poses >> a problem in that references to VMStateInfo can only be included in >> associated consts starting with Rust 1.83.0, when the const_refs_static >> was stabilized. Removing the requirement is done by placing a limited >> list of VMStateInfos in an enum, and going from enum to &VMStateInfo >> only when building the VMStateField. >> >> The same thing cannot be done with VMS_STRUCT because the set of >> VMStateDescriptions extends to structs defined by the devices. >> Therefore, structs and cells cannot yet use vmstate_of!. >> >> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> >> --- >> rust/qemu-api/src/vmstate.rs | 128 ++++++++++++++++++++++++++++++++++- >> 1 file changed, 126 insertions(+), 2 deletions(-) > > >> /// Internal utility function to retrieve a type's `VMStateField`; >> /// used by [`vmstate_of!`](crate::vmstate_of). >> pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField { >> @@ -99,6 +178,15 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateField >> /// Return the `VMStateField` for a field of a struct. The field must be >> /// visible in the current scope. >> /// >> +/// Only a limited set of types is supported out of the box: >> +/// * scalar types (integer and `bool`) >> +/// * the C struct `QEMUTimer` >> +/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, >> +/// [`BqlCell`](crate::cell::BqlCell), [`BqlRefCell`](crate::cell::BqlRefCell) >> +/// * a raw pointer to any of the above >> +/// * a `NonNull` pointer to any of the above, possibly wrapped with `Option` > > I just found your rust-next has already updated and removed `Option` :-) > >> +/// * an array of any of the above >> +/// >> /// In order to support other types, the trait `VMState` must be implemented >> /// for them. >> #[macro_export] >> @@ -109,8 +197,14 @@ macro_rules! vmstate_of { >> .as_bytes() >> .as_ptr() as *const ::std::os::raw::c_char, >> offset: $crate::offset_of!($struct_name, $field_name), >> - // Compute most of the VMStateField from the type of the field. > > Rebase mistake? This comment seems no need to be deleted. It's moved below because there's now more than one call_func_with_field call. I can make it so that the phrasing and placement remains the same throughout the series. Paolo >> $(.num_offset: $crate::offset_of!($struct_name, $num),)? >> + // The calls to `call_func_with_field!` are the magic that >> + // computes most of the VMStateField from the type of the field. >> + info: $crate::info_enum_to_ref!($crate::call_func_with_field!( >> + $crate::vmstate::vmstate_scalar_type, >> + $struct_name, >> + $field_name >> + )), >> > > Only a nit above, > > Reviewed-by: Zhao Liu <zhao1.liu@intel.com> > > >
© 2016 - 2025 Red Hat, Inc.