From: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Add VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8, 32}_ALLOC, which
helps to save/restore a dynamic array of pointers to
structures.
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
v2:
- added VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT8_ALLOC
---
include/migration/vmstate.h | 33 ++++++++++++++
migration/vmstate-types.c | 88 +++++++++++++++++++++++++++++++++++++
2 files changed, 121 insertions(+)
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 89f9f49d20a..0bbe3317740 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -265,6 +265,7 @@ extern const VMStateInfo vmstate_info_bitmap;
extern const VMStateInfo vmstate_info_qtailq;
extern const VMStateInfo vmstate_info_gtree;
extern const VMStateInfo vmstate_info_qlist;
+extern const VMStateInfo vmstate_info_ptrs_array_entry;
#define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
/*
@@ -537,6 +538,38 @@ extern const VMStateInfo vmstate_info_qlist;
.offset = vmstate_offset_array(_s, _f, _type*, _n), \
}
+/*
+ * For migrating a dynamically allocated uint32-indexed array
+ * of pointers to structures (with NULL entries and with auto memory allocation).
+ *
+ * _type: type of structure pointed to
+ * _vmsd: VMSD for structure
+ * start: size of structure pointed to (for auto memory allocation)
+ */
+#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT8_ALLOC(_field, _state, _field_num, _version, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \
+ .info = &vmstate_info_ptrs_array_entry, \
+ .vmsd = &(_vmsd), \
+ .start = sizeof(_type), \
+ .size = sizeof(_type *), \
+ .flags = VMS_VARRAY_UINT8|VMS_POINTER, \
+ .offset = vmstate_offset_pointer(_state, _field, _type *), \
+}
+
+#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC(_field, _state, _field_num, _version, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \
+ .info = &vmstate_info_ptrs_array_entry, \
+ .vmsd = &(_vmsd), \
+ .start = sizeof(_type), \
+ .size = sizeof(_type *), \
+ .flags = VMS_VARRAY_UINT32|VMS_POINTER, \
+ .offset = vmstate_offset_pointer(_state, _field, _type *), \
+}
+
#define VMSTATE_VARRAY_OF_POINTER_UINT32(_field, _state, _field_num, _version, _info, _type) { \
.name = (stringify(_field)), \
.version_id = (_version), \
diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
index 89cb2114721..3335377cd07 100644
--- a/migration/vmstate-types.c
+++ b/migration/vmstate-types.c
@@ -942,3 +942,91 @@ const VMStateInfo vmstate_info_qlist = {
.get = get_qlist,
.put = put_qlist,
};
+
+static int put_ptrs_array_entry(QEMUFile *f, void *ppv, size_t unused_size,
+ const VMStateField *field, JSONWriter *vmdesc)
+{
+ const VMStateDescription *vmsd = field->vmsd;
+ int ret;
+ Error *local_err = NULL;
+ void *pv;
+
+ /*
+ * (ppv) is an address of an i-th element of a dynamic array.
+ *
+ * (ppv) can not be NULL unless we have some regression/bug in
+ * vmstate_save_state_v(), because it is result of pointer arithemic like:
+ * first_elem + size * i.
+ */
+ if (ppv == NULL) {
+ error_report("vmstate: put_ptrs_array_entry must be called with ppv != NULL");
+ return -EINVAL;
+ }
+
+ /* get a pointer to a structure */
+ pv = *(void **)ppv;
+
+ if (pv == NULL) {
+ /* write a mark telling that there was a NULL pointer */
+ qemu_put_byte(f, false);
+ return 0;
+ }
+
+ /* if pointer is not NULL, dump the structure contents with help of vmsd */
+ qemu_put_byte(f, true);
+ ret = vmstate_save_state(f, vmsd, pv, vmdesc, &local_err);
+ if (ret) {
+ error_report_err(local_err);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int get_ptrs_array_entry(QEMUFile *f, void *ppv, size_t unused_size,
+ const VMStateField *field)
+{
+ int ret = 0;
+ Error *local_err = NULL;
+ const VMStateDescription *vmsd = field->vmsd;
+ /* size of structure pointed to by elements of array */
+ size_t size = field->start;
+
+ if (ppv == NULL) {
+ error_report("vmstate: get_ptrs_array_entry must be called with ppv != NULL");
+ return -EINVAL;
+ }
+
+ /*
+ * We start from a clean array, all elements must be NULL, unless
+ * something we haven't prepared for has changed in vmstate_save_state_v().
+ * Let's check for this just in case.
+ */
+ if (*(void **)ppv != NULL) {
+ error_report("vmstate: get_ptrs_array_entry must be called with *ppv == NULL");
+ return -EINVAL;
+ }
+
+ if (qemu_get_byte(f)) {
+ void *pv;
+
+ /* allocate memory for structure */
+ pv = g_malloc0(size);
+ ret = vmstate_load_state(f, vmsd, pv, vmsd->version_id, &local_err);
+ if (ret) {
+ error_report_err(local_err);
+ g_free(pv);
+ return ret;
+ }
+
+ *(void **)ppv = pv;
+ }
+
+ return ret;
+}
+
+const VMStateInfo vmstate_info_ptrs_array_entry = {
+ .name = "ptrs_array_entry",
+ .get = get_ptrs_array_entry,
+ .put = put_ptrs_array_entry,
+};
--
2.47.3
On Fri, Mar 06, 2026 at 04:03:37PM +0100, Alexander Mikhalitsyn wrote:
> +#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT8_ALLOC(_field, _state, _field_num, _version, _vmsd, _type) { \
> + .name = (stringify(_field)), \
> + .version_id = (_version), \
> + .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \
> + .info = &vmstate_info_ptrs_array_entry, \
> + .vmsd = &(_vmsd), \
> + .start = sizeof(_type), \
> + .size = sizeof(_type *), \
> + .flags = VMS_VARRAY_UINT8|VMS_POINTER, \
> + .offset = vmstate_offset_pointer(_state, _field, _type *), \
> +}
> +
> +#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC(_field, _state, _field_num, _version, _vmsd, _type) { \
> + .name = (stringify(_field)), \
> + .version_id = (_version), \
> + .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \
> + .info = &vmstate_info_ptrs_array_entry, \
> + .vmsd = &(_vmsd), \
> + .start = sizeof(_type), \
> + .size = sizeof(_type *), \
> + .flags = VMS_VARRAY_UINT32|VMS_POINTER, \
> + .offset = vmstate_offset_pointer(_state, _field, _type *), \
> +}
Hi, Alex,
Per previous discussion, I thought we plan to add a new VMS_* flag, am I
right?
https://lore.kernel.org/r/CAJqdLrrKK-pTzk+Qm_45p9A6DRgj4VbFzSb6vFtJTkpN3UaFpg@mail.gmail.com
--
Peter Xu
Am Fr., 6. März 2026 um 16:55 Uhr schrieb Peter Xu <peterx@redhat.com>:
>
> On Fri, Mar 06, 2026 at 04:03:37PM +0100, Alexander Mikhalitsyn wrote:
> > +#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT8_ALLOC(_field, _state, _field_num, _version, _vmsd, _type) { \
> > + .name = (stringify(_field)), \
> > + .version_id = (_version), \
> > + .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \
> > + .info = &vmstate_info_ptrs_array_entry, \
> > + .vmsd = &(_vmsd), \
> > + .start = sizeof(_type), \
> > + .size = sizeof(_type *), \
> > + .flags = VMS_VARRAY_UINT8|VMS_POINTER, \
> > + .offset = vmstate_offset_pointer(_state, _field, _type *), \
> > +}
> > +
> > +#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC(_field, _state, _field_num, _version, _vmsd, _type) { \
> > + .name = (stringify(_field)), \
> > + .version_id = (_version), \
> > + .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \
> > + .info = &vmstate_info_ptrs_array_entry, \
> > + .vmsd = &(_vmsd), \
> > + .start = sizeof(_type), \
> > + .size = sizeof(_type *), \
> > + .flags = VMS_VARRAY_UINT32|VMS_POINTER, \
> > + .offset = vmstate_offset_pointer(_state, _field, _type *), \
> > +}
>
> Hi, Alex,
Hi Peter,
>
> Per previous discussion, I thought we plan to add a new VMS_* flag, am I
> right?
yes, yes, I'll do this in -v4, sorry for this. I was busy adding a
test for NVMe migration
and decided to send -v3 with this new test.
Sorry about confusing you.
Kind regards,
Alex
>
> https://lore.kernel.org/r/CAJqdLrrKK-pTzk+Qm_45p9A6DRgj4VbFzSb6vFtJTkpN3UaFpg@mail.gmail.com
>
> --
> Peter Xu
>
On Fri, Mar 06, 2026 at 04:57:56PM +0100, Alexander Mikhalitsyn wrote: > yes, yes, I'll do this in -v4, sorry for this. I was busy adding a > test for NVMe migration > and decided to send -v3 with this new test. > > Sorry about confusing you. Ahhh, no worries. Just to say, if you send a patchset without RFC or any mark, it by default says: "please consider merging it". Good to know we're still on the same page. -- Peter Xu
Am Fr., 6. März 2026 um 17:34 Uhr schrieb Peter Xu <peterx@redhat.com>: > > On Fri, Mar 06, 2026 at 04:57:56PM +0100, Alexander Mikhalitsyn wrote: > > yes, yes, I'll do this in -v4, sorry for this. I was busy adding a > > test for NVMe migration > > and decided to send -v3 with this new test. > > > > Sorry about confusing you. Hi Peter, > > Ahhh, no worries. Just to say, if you send a patchset without RFC or any > mark, it by default says: "please consider merging it". yeah, sorry about this again. > > Good to know we're still on the same page. yep, I've just sent a version 4 and it is based on your idea with new VMS_ARRAY_OF_POINTER_ALLOW_NULL flag. Hope that I get everything right. Also, it includes tests and many new asserts here and there :) Kind regards, Alex > > -- > Peter Xu >
© 2016 - 2026 Red Hat, Inc.