rust: vmstate: implement VMState for scalar types

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>
This commit is contained in:
Paolo Bonzini 2024-12-29 11:59:34 +01:00
parent 2537f83098
commit f2cb78bdbe

View file

@ -22,7 +22,10 @@
use core::{marker::PhantomData, mem, ptr::NonNull};
pub use crate::bindings::{VMStateDescription, VMStateField};
use crate::bindings::VMStateFlags;
use crate::{
bindings::{self, VMStateFlags},
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 @@ macro_rules! call_func_with_field {
};
}
/// 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 @@ macro_rules! call_func_with_field {
/// 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>) -> VMStateFlags
/// 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 or a `Box` for any of the above
/// * 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 @@ impl 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 @@ impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
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 @@ impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
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 @@ impl_vmstate_pointer!(Box<T> where T: VMState);
// 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);
}