When VMS_ARRAY_OF_POINTER is specified, it means the vmstate field is an
array of pointers.
The size of the element is not relevant to whatever it is stored inside: it
is always the host pointer size.
Let's reserve the "size" field in this case for future use, update
vmstate_size() so as to make it still work for array of pointers properly.
When at this, provide rich documentation on how size / size_offset works in
vmstate.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
include/migration/vmstate.h | 20 ++++++++++++++++----
migration/savevm.c | 3 +++
migration/vmstate.c | 10 +++++++++-
3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 68fd954411..b4bc69486d 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -183,11 +183,26 @@ typedef enum {
struct VMStateField {
const char *name;
size_t offset;
+
+ /*
+ * @size or @size_offset specifies the size of the element embeded in
+ * the field. Only one of them should be present never both. When
+ * @size_offset is used together with VMS_VBUFFER, it means the size is
+ * dynamic calculated instead of a constant.
+ *
+ * When the field is an array of any type, this stores the size of one
+ * element of the array.
+ *
+ * NOTE: even if VMS_POINTER or VMS_ARRAY_OF_POINTER may be specified,
+ * this parameter always reflects the real size of the objects that a
+ * pointer point to.
+ */
size_t size;
+ size_t size_offset;
+
size_t start;
int num;
size_t num_offset;
- size_t size_offset;
const VMStateInfo *info;
enum VMStateFlags flags;
const VMStateDescription *vmsd;
@@ -547,7 +562,6 @@ extern const VMStateInfo vmstate_info_qlist;
.version_id = (_version), \
.num = (_num), \
.info = &(_info), \
- .size = sizeof(_type *), \
.flags = VMS_ARRAY|VMS_ARRAY_OF_POINTER, \
.offset = vmstate_offset_array(_state, _field, _type*, _num), \
}
@@ -557,7 +571,6 @@ extern const VMStateInfo vmstate_info_qlist;
.version_id = (_v), \
.num = (_n), \
.vmsd = &(_vmsd), \
- .size = sizeof(_type *), \
.flags = VMS_ARRAY|VMS_STRUCT|VMS_ARRAY_OF_POINTER, \
.offset = vmstate_offset_array(_s, _f, _type*, _n), \
}
@@ -567,7 +580,6 @@ extern const VMStateInfo vmstate_info_qlist;
.version_id = (_version), \
.num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \
.info = &(_info), \
- .size = sizeof(_type *), \
.flags = VMS_VARRAY_UINT32 | VMS_ARRAY_OF_POINTER | VMS_POINTER, \
.offset = vmstate_offset_pointer(_state, _field, _type *), \
}
diff --git a/migration/savevm.c b/migration/savevm.c
index 8115203b51..f5a6fd0c66 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -868,6 +868,9 @@ static void vmstate_check(const VMStateDescription *vmsd)
if (field) {
while (field->name) {
+ if (field->flags & VMS_ARRAY_OF_POINTER) {
+ assert(field->size == 0);
+ }
if (field->flags & (VMS_STRUCT | VMS_VSTRUCT)) {
/* Recurse to sub structures */
vmstate_check(field->vmsd);
diff --git a/migration/vmstate.c b/migration/vmstate.c
index e98b5f5346..e29a8c3f49 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -110,13 +110,21 @@ static int vmstate_n_elems(void *opaque, const VMStateField *field)
static int vmstate_size(void *opaque, const VMStateField *field)
{
- int size = field->size;
+ int size;
if (field->flags & VMS_VBUFFER) {
size = *(int32_t *)(opaque + field->size_offset);
if (field->flags & VMS_MULTIPLY) {
size *= field->size;
}
+ } else if (field->flags & VMS_ARRAY_OF_POINTER) {
+ /*
+ * For an array of pointer, the each element is always size of a
+ * host pointer.
+ */
+ size = sizeof(void *);
+ } else {
+ size = field->size;
}
return size;
--
2.50.1
Am Mi., 18. März 2026 um 00:23 Uhr schrieb Peter Xu <peterx@redhat.com>:
>
> When VMS_ARRAY_OF_POINTER is specified, it means the vmstate field is an
> array of pointers.
>
> The size of the element is not relevant to whatever it is stored inside: it
> is always the host pointer size.
>
> Let's reserve the "size" field in this case for future use, update
> vmstate_size() so as to make it still work for array of pointers properly.
>
> When at this, provide rich documentation on how size / size_offset works in
> vmstate.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
> ---
> include/migration/vmstate.h | 20 ++++++++++++++++----
> migration/savevm.c | 3 +++
> migration/vmstate.c | 10 +++++++++-
> 3 files changed, 28 insertions(+), 5 deletions(-)
>
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index 68fd954411..b4bc69486d 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -183,11 +183,26 @@ typedef enum {
> struct VMStateField {
> const char *name;
> size_t offset;
> +
> + /*
> + * @size or @size_offset specifies the size of the element embeded in
> + * the field. Only one of them should be present never both. When
> + * @size_offset is used together with VMS_VBUFFER, it means the size is
> + * dynamic calculated instead of a constant.
> + *
> + * When the field is an array of any type, this stores the size of one
> + * element of the array.
> + *
> + * NOTE: even if VMS_POINTER or VMS_ARRAY_OF_POINTER may be specified,
> + * this parameter always reflects the real size of the objects that a
> + * pointer point to.
> + */
> size_t size;
> + size_t size_offset;
> +
> size_t start;
> int num;
> size_t num_offset;
> - size_t size_offset;
> const VMStateInfo *info;
> enum VMStateFlags flags;
> const VMStateDescription *vmsd;
> @@ -547,7 +562,6 @@ extern const VMStateInfo vmstate_info_qlist;
> .version_id = (_version), \
> .num = (_num), \
> .info = &(_info), \
> - .size = sizeof(_type *), \
> .flags = VMS_ARRAY|VMS_ARRAY_OF_POINTER, \
> .offset = vmstate_offset_array(_state, _field, _type*, _num), \
> }
> @@ -557,7 +571,6 @@ extern const VMStateInfo vmstate_info_qlist;
> .version_id = (_v), \
> .num = (_n), \
> .vmsd = &(_vmsd), \
> - .size = sizeof(_type *), \
> .flags = VMS_ARRAY|VMS_STRUCT|VMS_ARRAY_OF_POINTER, \
> .offset = vmstate_offset_array(_s, _f, _type*, _n), \
> }
> @@ -567,7 +580,6 @@ extern const VMStateInfo vmstate_info_qlist;
> .version_id = (_version), \
> .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \
> .info = &(_info), \
> - .size = sizeof(_type *), \
> .flags = VMS_VARRAY_UINT32 | VMS_ARRAY_OF_POINTER | VMS_POINTER, \
> .offset = vmstate_offset_pointer(_state, _field, _type *), \
> }
> diff --git a/migration/savevm.c b/migration/savevm.c
> index 8115203b51..f5a6fd0c66 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -868,6 +868,9 @@ static void vmstate_check(const VMStateDescription *vmsd)
>
> if (field) {
> while (field->name) {
> + if (field->flags & VMS_ARRAY_OF_POINTER) {
> + assert(field->size == 0);
> + }
> if (field->flags & (VMS_STRUCT | VMS_VSTRUCT)) {
> /* Recurse to sub structures */
> vmstate_check(field->vmsd);
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index e98b5f5346..e29a8c3f49 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -110,13 +110,21 @@ static int vmstate_n_elems(void *opaque, const VMStateField *field)
>
> static int vmstate_size(void *opaque, const VMStateField *field)
> {
> - int size = field->size;
> + int size;
>
> if (field->flags & VMS_VBUFFER) {
> size = *(int32_t *)(opaque + field->size_offset);
> if (field->flags & VMS_MULTIPLY) {
> size *= field->size;
> }
> + } else if (field->flags & VMS_ARRAY_OF_POINTER) {
> + /*
> + * For an array of pointer, the each element is always size of a
> + * host pointer.
> + */
> + size = sizeof(void *);
> + } else {
> + size = field->size;
> }
>
> return size;
> --
> 2.50.1
>
© 2016 - 2026 Red Hat, Inc.