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
© 2016 - 2026 Red Hat, Inc.