There are a few special cases of vmstate usage:

The vmstate_msix and vmstate_scsi_device have fields that contain no
data, only a vmstate_info structure.

The VMSTATE_VALIDATE macro serves only to invoke the .field_exists
routine for validation.

Regardless whether these scenarios are valid, add a separate flag to
identify them so we can enforce common constraints for the normal
vmstates such as having a size greater than zero.

Note that n_elems is hardcoded to 1 for all vmstates, except
VMS_[V]ARRAY, so VMSTATE_VALIDATE needed to set VMS_ARRAY to be able
to force n_elems to 0. This patch now checks the flag at
vmstate_n_elems().

Acked-by: Peter Xu <[email protected]>
Signed-off-by: Fabiano Rosas <[email protected]>
---
 hw/pci/msix.c                     | 6 +-----
 hw/scsi/scsi-bus.c                | 6 +-----
 include/migration/vmstate.h       | 9 +++++++--
 migration/vmstate.c               | 6 +++++-
 rust/migration/src/vmstate.rs     | 2 +-
 rust/tests/tests/vmstate_tests.rs | 2 +-
 6 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/hw/pci/msix.c b/hw/pci/msix.c
index 1b23eaf1007..adf76b5bccc 100644
--- a/hw/pci/msix.c
+++ b/hw/pci/msix.c
@@ -711,12 +711,8 @@ const VMStateDescription vmstate_msix = {
     .fields = (const VMStateField[]) {
         {
             .name         = "msix",
-            .version_id   = 0,
-            .field_exists = NULL,
-            .size         = 0,   /* ouch */
             .info         = &vmstate_info_msix,
-            .flags        = VMS_SINGLE,
-            .offset       = 0,
+            .flags        = VMS_SINGLE | VMS_NO_STATE,
         },
         VMSTATE_END_OF_LIST()
     }
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index deb43d5560e..aa02ff631b7 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -1980,12 +1980,8 @@ const VMStateDescription vmstate_scsi_device = {
         VMSTATE_UINT32(sense_len, SCSIDevice),
         {
             .name         = "requests",
-            .version_id   = 0,
-            .field_exists = NULL,
-            .size         = 0,   /* ouch */
             .info         = &vmstate_info_scsi_requests,
-            .flags        = VMS_SINGLE,
-            .offset       = 0,
+            .flags        = VMS_SINGLE | VMS_NO_STATE,
         },
         VMSTATE_END_OF_LIST()
     },
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 4b6d52955ff..e72c3fae9a6 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -109,6 +109,12 @@ enum VMStateFlags {
      */
     VMS_ARRAY_OF_POINTER = 0x040,
 
+    /*
+     * The field contains no data. Used for special cases such as
+     * invoking a custom VMStateInfo.
+     */
+    VMS_NO_STATE = 0x080,
+
     /* The size of the individual entries (a single array entry if
      * VMS_ARRAY or VMS_VARRAY are set, or the field itself if
      * neither is set) is variable (i.e. not known at compile-time),
@@ -454,8 +460,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
 #define VMSTATE_VALIDATE(_name, _test) { \
     .name         = (_name),                                         \
     .field_exists = (_test),                                         \
-    .flags        = VMS_ARRAY | VMS_MUST_EXIST,                      \
-    .num          = 0, /* 0 elements: no data, only run _test */     \
+    .flags        = VMS_MUST_EXIST | VMS_NO_STATE,                   \
 }
 
 #define VMSTATE_POINTER(_field, _state, _version, _info, _type) {    \
diff --git a/migration/vmstate.c b/migration/vmstate.c
index bc5285bcea2..51d02b87e7e 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -97,12 +97,16 @@ static uint64_t vmstate_read_from_offset(const 
VMStateStructMember *member,
 
 static uint64_t vmstate_n_elems(void *opaque, const VMStateField *field)
 {
-    uint64_t n_elems = 1;
+    uint64_t n_elems;
 
     if (field->flags & VMS_ARRAY) {
         n_elems = field->num;
     } else if (field->flags & VMS_VARRAY) {
         n_elems = vmstate_read_from_offset(&field->num_indirect, opaque);
+    } else if (field->flags & VMS_MUST_EXIST && field->flags & VMS_NO_STATE) {
+        n_elems = 0;
+    } else {
+        n_elems = 1;
     }
 
     trace_vmstate_n_elems(field->name, n_elems);
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index 45c0b52738b..5ed061f2abb 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -381,7 +381,7 @@ macro_rules! vmstate_validate {
             field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),
             flags: $crate::bindings::VMStateFlags(
                 $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0
-                    | $crate::bindings::VMStateFlags::VMS_ARRAY.0,
+                    | $crate::bindings::VMStateFlags::VMS_NO_STATE.0,
             ),
             num: 0, // 0 elements: no data, only run test_fn callback
             ..::common::zeroable::Zeroable::ZERO
diff --git a/rust/tests/tests/vmstate_tests.rs 
b/rust/tests/tests/vmstate_tests.rs
index 5f6dd8ae7f1..6578d54b37d 100644
--- a/rust/tests/tests/vmstate_tests.rs
+++ b/rust/tests/tests/vmstate_tests.rs
@@ -461,7 +461,7 @@ fn test_vmstate_validate() {
     assert_eq!(foo_fields[0].num, 0);
     assert_eq!(
         foo_fields[0].flags.0,
-        VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0
+        VMStateFlags::VMS_NO_STATE.0 | VMStateFlags::VMS_MUST_EXIST.0
     );
     assert!(foo_fields[0].vmsd.is_null());
     assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) });
-- 
2.53.0


Reply via email to