> It seems the generated migration_state_struct needs a complete VMSD:
> collect all its fields and build a VMSD.
>
> And then apply impl_vmstate_struct! to this migration_state_struct.
>
> If we zero the BASE, then `vmstate_of!(Self, migration_state)` seems
> can't migrate its fields.
I think the following generated vmstate implementation should be what we
need:
diff --git a/rust/qemu-macros/src/migration_state.rs
b/rust/qemu-macros/src/migration_state.rs
index 5edf0efe687f..cb047638e3da 100644
--- a/rust/qemu-macros/src/migration_state.rs
+++ b/rust/qemu-macros/src/migration_state.rs
@@ -1,6 +1,6 @@
use std::borrow::Cow;
-use proc_macro2::TokenStream;
+use proc_macro2::{Literal, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{spanned::Spanned, DeriveInput, Error, Field, Ident, Result, Type};
@@ -236,6 +236,43 @@ pub struct #name {
}
}
+ fn generate_vmstate_impl(&self) -> TokenStream {
+ let name = self.migration_state_name();
+ let name_str = name.to_string();
+ // Most of the time, const variables use uppercase snake case style,
+ // while VMStateDescription.name uses lowercase snake case. But we're
+ // not strictly following the usual snake case conventions here because
+ // what we have is good enough for the generated code right now.
+ //
+ // QEMU struct names are typically in camel case, but there are
exceptions
+ // - like non-standard camel case such as HPETState. So converting from
+ // camel to snake case would need an extra helper.
+ let vmsd_name = format_ident!("VMSTATE_{}", name_str);
+ // C-string literal "c" can't be used in quote! directly.
+ let vmsd_c_name =
Literal::c_string(&std::ffi::CString::new(name_str).unwrap());
+
+ let vmstate_fields = self.fields.iter().map(|field| {
+ let field_name = &field.name;
+ quote! {
+ vmstate_of!(#name, #field_name),
+ }
+ });
+
+ quote! {
+ const #vmsd_name: VMStateDescription<#name> =
+ VMStateDescriptionBuilder::<#name>::new()
+ .name(#vmsd_c_name)
+ .version_id(0)
+ .minimum_version_id(0)
+ .fields(vmstate_fields! {
+ #(#vmstate_fields)*
+ })
+ .build();
+
+ impl_vmstate_struct!(#name, #vmsd_name);
+ }
+ }
+
fn generate_snapshot_migration_state(&self) -> TokenStream {
let fields = self
.fields
@@ -275,12 +312,15 @@ fn generate(&self) -> TokenStream {
let (impl_generics, ty_generics, where_clause) =
generics.split_for_impl();
let name = self.migration_state_name();
let migration_state_struct = self.generate_migration_state_struct();
+ let vmstate_impl = self.generate_vmstate_impl();
let snapshot_impl = self.generate_snapshot_migration_state();
let restore_impl = self.generate_restore_migrated_state();
quote! {
#migration_state_struct
+ #vmstate_impl
+
impl #impl_generics ToMigrationState for #struct_name #ty_generics
#where_clause {
type Migrated = #name;
diff --git a/rust/qemu-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs
index 65691412ff57..d70ebf23c273 100644
--- a/rust/qemu-macros/src/tests.rs
+++ b/rust/qemu-macros/src/tests.rs
@@ -408,6 +408,20 @@ pub struct CustomMigration {
pub nested_field: <NestedStruct as ToMigrationState>::Migrated,
pub simple_field: <u32 as ToMigrationState>::Migrated,
}
+ const VMSTATE_CustomMigration: VMStateDescription
<CustomMigration> =
+ VMStateDescriptionBuilder::<CustomMigration>::new()
+ .name(c"CustomMigration")
+ .version_id(0)
+ .minimum_version_id(0)
+ .fields(vmstate_fields! {
+ vmstate_of!(CustomMigration, shared_data),
+ vmstate_of!(CustomMigration, converted_field),
+ vmstate_of!(CustomMigration, fallible_field),
+ vmstate_of!(CustomMigration, nested_field),
+ vmstate_of!(CustomMigration, simple_field),
+ })
+ .build();
+ impl_vmstate_struct!(CustomMigration, VMSTATE_CustomMigration);
impl ToMigrationState for MyStruct {
type Migrated = CustomMigration;
fn snapshot_migration_state(