[PATCH v3 1/6] migration: add VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8, 32}_ALLOC

Alexander Mikhalitsyn posted 6 patches 1 month, 1 week ago
Maintainers: Keith Busch <kbusch@kernel.org>, Klaus Jensen <its@irrelevant.dk>, Jesper Devantier <foss@defmacro.it>, Peter Xu <peterx@redhat.com>, Fabiano Rosas <farosas@suse.de>, Paolo Bonzini <pbonzini@redhat.com>, Zhao Liu <zhao1.liu@intel.com>
There is a newer version of this series
[PATCH v3 1/6] migration: add VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8, 32}_ALLOC
Posted by Alexander Mikhalitsyn 1 month, 1 week ago
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
Re: [PATCH v3 1/6] migration: add VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8, 32}_ALLOC
Posted by Peter Xu 1 month, 1 week ago
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
Re: [PATCH v3 1/6] migration: add VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8, 32}_ALLOC
Posted by Alexander Mikhalitsyn 1 month, 1 week ago
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
>
Re: [PATCH v3 1/6] migration: add VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8, 32}_ALLOC
Posted by Peter Xu 1 month, 1 week ago
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
Re: [PATCH v3 1/6] migration: add VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8, 32}_ALLOC
Posted by Alexander Mikhalitsyn 1 month ago
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
>