migration/vmstate: avoid per-element heap churn in vmsd ptr marker field
For every NULL slot in a VMS_ARRAY_OF_POINTER (or every entry of a dynamic array), the saver allocates a 1-element fake VMStateField via g_new0 and frees it again right after the save. For arrays of thousands of entries this is thousands of malloc/free pairs on the hot save path. Replace the heap-allocated marker with a stack-resident field populated by an init helper. The caller passes a pointer to a local VMStateField, the helper fills it in (still asserting the precondition), and no g_free is needed. Signed-off-by: Bin Guo <guobin@linux.alibaba.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20260518110112.21395-4-guobin@linux.alibaba.com Signed-off-by: Peter Xu <peterx@redhat.com>
Bin Guo committed
May 18, 2026 at 19:01 UTC
36dec7c619dc54019bb7fb7dec224fa23570edee
1 file changed
+16
-25
migration/vmstate.c
+16
-25
@@ -59,29 +59,23 @@ vmstate_field_exists(const VMStateDescription *vmsd, const VMStateField *field,
59
* array of a VMS_ARRAY_OF_POINTER VMSD field. It's needed because we
60
* can't dereference the NULL pointer.
61
*/
62
-static const VMStateField *
63
-vmsd_create_ptr_marker_field(const VMStateField *field)
62
+static void
63
+vmsd_init_ptr_marker_field(VMStateField *fake, const VMStateField *field)
64
{
65
- VMStateField *fake = g_new0(VMStateField, 1);
66
-
65
/* It can only happen on an array of pointers! */
66
assert(field->flags & VMS_ARRAY_OF_POINTER);
67
70
- /* Some of fake's properties should match the original's */
71
- fake->name = field->name;
72
- fake->version_id = field->version_id;
73
-
74
- /* Do not need "field_exists" check as it always exists */
75
- fake->field_exists = NULL;
76
-
77
- /* See vmstate_info_ptr_marker - use 1 byte to represent ptr status */
78
- fake->size = 1;
79
- fake->info = &vmstate_info_ptr_marker;
80
- fake->flags = VMS_SINGLE;
81
-
82
- /* All the rest fields shouldn't matter.. */
83
-
84
- return (const VMStateField *)fake;
68
+ /* See vmstate_info_ptr_marker - 1 byte represents ptr status */
69
+ *fake = (VMStateField) {
70
+ .name = field->name,
71
+ .version_id = field->version_id,
72
+ /* Marker always exists, no field_exists callback needed */
73
+ .field_exists = NULL,
74
+ .size = 1,
75
+ .info = &vmstate_info_ptr_marker,
76
+ .flags = VMS_SINGLE,
77
+ /* All other fields stay zero-initialised */
78
+ };
79
}
80
81
static int vmstate_n_elems(void *opaque, const VMStateField *field)
@@ -676,6 +670,7 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
670
for (i = 0; i < n_elems; i++) {
671
void *curr_elem = first_elem + size * i;
672
const VMStateField *inner_field;
673
+ VMStateField marker_field;
674
/* maximum number of elements to compress in the JSON blob */
675
int max_elems = vmsd_can_compress(field) ? (n_elems - i) : 1;
676
bool use_marker_field, is_null = false;
@@ -689,7 +684,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
684
use_marker_field = use_dynamic_array || is_null;
685
686
if (use_marker_field) {
692
- inner_field = vmsd_create_ptr_marker_field(field);
687
+ vmsd_init_ptr_marker_field(&marker_field, field);
688
+ inner_field = &marker_field;
689
} else {
690
inner_field = field;
691
}
@@ -726,11 +722,6 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
722
inner_field, vmdesc_loop,
723
i, max_elems, errp);
724
729
- /* If we used a fake temp field.. free it now */
730
- if (use_marker_field) {
731
- g_clear_pointer((gpointer *)&inner_field, g_free);
732
- }
733
-
725
if (!ok) {
726
goto out;
727
}