@samitouri / QOSamiQemu / commits / 36dec7c619

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 }