rust: vmstate: implement VMState for non-leaf types

Arrays, pointers and cells use a VMStateField that is based on that
for the inner type.  The implementation therefore delegates to the
VMState implementation of the inner type.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2024-12-29 12:29:45 +01:00
parent 0d43ddae35
commit 80aa3045bd

View file

@ -19,8 +19,9 @@
//! `include/migration/vmstate.h`. These are not type-safe and should not be
//! used if the equivalent functionality is available with `vmstate_of!`.
use core::marker::PhantomData;
use core::{marker::PhantomData, mem, ptr::NonNull};
use crate::bindings::VMStateFlags;
pub use crate::bindings::{VMStateDescription, VMStateField};
/// This macro is used to call a function with a generic argument bound
@ -102,6 +103,15 @@ macro_rules! vmstate_of {
};
}
impl VMStateFlags {
const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags(
VMStateFlags::VMS_VARRAY_INT32.0
| VMStateFlags::VMS_VARRAY_UINT8.0
| VMStateFlags::VMS_VARRAY_UINT16.0
| VMStateFlags::VMS_VARRAY_UINT32.0,
);
}
// Add a couple builder-style methods to VMStateField, allowing
// easy derivation of VMStateField constants from other types.
impl VMStateField {
@ -111,6 +121,73 @@ impl VMStateField {
self.version_id = version_id;
self
}
#[must_use]
pub const fn with_array_flag(mut self, num: usize) -> Self {
assert!(num <= 0x7FFF_FFFFusize);
assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0);
assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0);
if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 {
self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0);
self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0);
}
self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0);
self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0);
self.num = num as i32;
self
}
#[must_use]
pub const fn with_pointer_flag(mut self) -> Self {
assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0);
self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0);
self
}
}
// Transparent wrappers: just use the internal type
macro_rules! impl_vmstate_transparent {
($type:ty where $base:tt: VMState $($where:tt)*) => {
unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
const BASE: VMStateField = VMStateField {
size: mem::size_of::<$type>(),
..<$base as VMState>::BASE
};
}
};
}
impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
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);
// Pointer types using the underlying type's VMState plus VMS_POINTER
// Note that references are not supported, though references to cells
// could be allowed.
macro_rules! impl_vmstate_pointer {
($type:ty where $base:tt: VMState $($where:tt)*) => {
unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag();
}
};
}
impl_vmstate_pointer!(*const T where T: VMState);
impl_vmstate_pointer!(*mut T where T: VMState);
impl_vmstate_pointer!(NonNull<T> where T: VMState);
// Unlike C pointers, Box is always non-null therefore there is no need
// to specify VMS_ALLOC.
impl_vmstate_pointer!(Box<T> where T: VMState);
// Arrays using the underlying type's VMState plus
// VMS_ARRAY/VMS_ARRAY_OF_POINTER
unsafe impl<T: VMState, const N: usize> VMState for [T; N] {
const BASE: VMStateField = <T as VMState>::BASE.with_array_flag(N);
}
#[doc(alias = "VMSTATE_UNUSED_BUFFER")]