mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:10 +00:00
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>
This commit is contained in:
@@ -59,29 +59,23 @@ vmstate_field_exists(const VMStateDescription *vmsd, const VMStateField *field,
|
|||||||
* array of a VMS_ARRAY_OF_POINTER VMSD field. It's needed because we
|
* array of a VMS_ARRAY_OF_POINTER VMSD field. It's needed because we
|
||||||
* can't dereference the NULL pointer.
|
* can't dereference the NULL pointer.
|
||||||
*/
|
*/
|
||||||
static const VMStateField *
|
static void
|
||||||
vmsd_create_ptr_marker_field(const VMStateField *field)
|
vmsd_init_ptr_marker_field(VMStateField *fake, const VMStateField *field)
|
||||||
{
|
{
|
||||||
VMStateField *fake = g_new0(VMStateField, 1);
|
|
||||||
|
|
||||||
/* It can only happen on an array of pointers! */
|
/* It can only happen on an array of pointers! */
|
||||||
assert(field->flags & VMS_ARRAY_OF_POINTER);
|
assert(field->flags & VMS_ARRAY_OF_POINTER);
|
||||||
|
|
||||||
/* Some of fake's properties should match the original's */
|
/* See vmstate_info_ptr_marker - 1 byte represents ptr status */
|
||||||
fake->name = field->name;
|
*fake = (VMStateField) {
|
||||||
fake->version_id = field->version_id;
|
.name = field->name,
|
||||||
|
.version_id = field->version_id,
|
||||||
/* Do not need "field_exists" check as it always exists */
|
/* Marker always exists, no field_exists callback needed */
|
||||||
fake->field_exists = NULL;
|
.field_exists = NULL,
|
||||||
|
.size = 1,
|
||||||
/* See vmstate_info_ptr_marker - use 1 byte to represent ptr status */
|
.info = &vmstate_info_ptr_marker,
|
||||||
fake->size = 1;
|
.flags = VMS_SINGLE,
|
||||||
fake->info = &vmstate_info_ptr_marker;
|
/* All other fields stay zero-initialised */
|
||||||
fake->flags = VMS_SINGLE;
|
};
|
||||||
|
|
||||||
/* All the rest fields shouldn't matter.. */
|
|
||||||
|
|
||||||
return (const VMStateField *)fake;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vmstate_n_elems(void *opaque, const VMStateField *field)
|
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,
|
|||||||
for (i = 0; i < n_elems; i++) {
|
for (i = 0; i < n_elems; i++) {
|
||||||
void *curr_elem = first_elem + size * i;
|
void *curr_elem = first_elem + size * i;
|
||||||
const VMStateField *inner_field;
|
const VMStateField *inner_field;
|
||||||
|
VMStateField marker_field;
|
||||||
/* maximum number of elements to compress in the JSON blob */
|
/* maximum number of elements to compress in the JSON blob */
|
||||||
int max_elems = vmsd_can_compress(field) ? (n_elems - i) : 1;
|
int max_elems = vmsd_can_compress(field) ? (n_elems - i) : 1;
|
||||||
bool use_marker_field, is_null = false;
|
bool use_marker_field, is_null = false;
|
||||||
@@ -689,7 +684,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
|
|||||||
use_marker_field = use_dynamic_array || is_null;
|
use_marker_field = use_dynamic_array || is_null;
|
||||||
|
|
||||||
if (use_marker_field) {
|
if (use_marker_field) {
|
||||||
inner_field = vmsd_create_ptr_marker_field(field);
|
vmsd_init_ptr_marker_field(&marker_field, field);
|
||||||
|
inner_field = &marker_field;
|
||||||
} else {
|
} else {
|
||||||
inner_field = field;
|
inner_field = field;
|
||||||
}
|
}
|
||||||
@@ -726,11 +722,6 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
|
|||||||
inner_field, vmdesc_loop,
|
inner_field, vmdesc_loop,
|
||||||
i, max_elems, errp);
|
i, max_elems, errp);
|
||||||
|
|
||||||
/* If we used a fake temp field.. free it now */
|
|
||||||
if (use_marker_field) {
|
|
||||||
g_clear_pointer((gpointer *)&inner_field, g_free);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user