@samitouri / QOSamiQemu / commits / 5aa4714e9d

vmstate: Do not set size for VMS_ARRAY_OF_POINTER

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. Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io> Reviewed-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Peter Xu <peterx@redhat.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/qemu-devel/20260401202844.673494-4-peterx@redhat.com Signed-off-by: Fabiano Rosas <farosas@suse.de>

Peter Xu committed Apr 1, 2026 at 16:28 UTC 5aa4714e9d01be2d17282b223f01a492a5c9ea72
3 files changed +28 -5
include/migration/vmstate.h
+16 -4
@@ -183,11 +183,26 @@ typedef enum {
183 struct VMStateField {
184 const char *name;
185 size_t offset;
186 +
187 + /*
188 + * @size or @size_offset specifies the size of the element embeded in
189 + * the field. Only one of them should be present never both. When
190 + * @size_offset is used together with VMS_VBUFFER, it means the size is
191 + * dynamic calculated instead of a constant.
192 + *
193 + * When the field is an array of any type, this stores the size of one
194 + * element of the array.
195 + *
196 + * NOTE: even if VMS_POINTER or VMS_ARRAY_OF_POINTER may be specified,
197 + * this parameter always reflects the real size of the objects that a
198 + * pointer point to.
199 + */
200 size_t size;
201 + size_t size_offset;
202 +
203 size_t start;
204 int num;
205 size_t num_offset;
190 - size_t size_offset;
206 const VMStateInfo *info;
207 enum VMStateFlags flags;
208 const VMStateDescription *vmsd;
@@ -547,7 +562,6 @@ extern const VMStateInfo vmstate_info_qlist;
562 .version_id = (_version), \
563 .num = (_num), \
564 .info = &(_info), \
550 - .size = sizeof(_type *), \
565 .flags = VMS_ARRAY|VMS_ARRAY_OF_POINTER, \
566 .offset = vmstate_offset_array(_state, _field, _type *, _num), \
567 }
@@ -557,7 +571,6 @@ extern const VMStateInfo vmstate_info_qlist;
571 .version_id = (_v), \
572 .num = (_n), \
573 .vmsd = &(_vmsd), \
560 - .size = sizeof(_type *), \
574 .flags = VMS_ARRAY|VMS_STRUCT|VMS_ARRAY_OF_POINTER, \
575 .offset = vmstate_offset_array(_s, _f, _type*, _n), \
576 }
@@ -567,7 +580,6 @@ extern const VMStateInfo vmstate_info_qlist;
580 .version_id = (_version), \
581 .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \
582 .info = &(_info), \
570 - .size = sizeof(_type *), \
583 .flags = VMS_VARRAY_UINT32 | VMS_ARRAY_OF_POINTER | VMS_POINTER, \
584 .offset = vmstate_offset_pointer(_state, _field, _type *), \
585 }
migration/savevm.c
+3
@@ -868,6 +868,9 @@ static void vmstate_check(const VMStateDescription *vmsd)
868
869 if (field) {
870 while (field->name) {
871 + if (field->flags & VMS_ARRAY_OF_POINTER) {
872 + assert(field->size == 0);
873 + }
874 if (field->flags & (VMS_STRUCT | VMS_VSTRUCT)) {
875 /* Recurse to sub structures */
876 vmstate_check(field->vmsd);
migration/vmstate.c
+9 -1
@@ -110,13 +110,21 @@ static int vmstate_n_elems(void *opaque, const VMStateField *field)
110
111 static int vmstate_size(void *opaque, const VMStateField *field)
112 {
113 - int size = field->size;
113 + int size;
114
115 if (field->flags & VMS_VBUFFER) {
116 size = *(int32_t *)(opaque + field->size_offset);
117 if (field->flags & VMS_MULTIPLY) {
118 size *= field->size;
119 }
120 + } else if (field->flags & VMS_ARRAY_OF_POINTER) {
121 + /*
122 + * For an array of pointer, the each element is always size of a
123 + * host pointer.
124 + */
125 + size = sizeof(void *);
126 + } else {
127 + size = field->size;
128 }
129
130 return size;