We have a trick in vmstate_save_vmsd_v() where we will try to compress
multiple JSON vmstate field dumps into one line with a count to avoid
duplicated entries.
That only applies to the cases where vmsd_can_compress() should return
true. For example, vmsd_desc_field_start() later (who will take the
updated max_elems as the last parameter) will ignore the value passed in
when vmsd_can_compress() returns false.
Add that check to the trick too, it will be used later to bypass this logic
to some special new VMSD fields.
No functional change intended in this patch alone.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
migration/vmstate.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/migration/vmstate.c b/migration/vmstate.c
index e29a8c3f49..caa7b50bce 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -578,7 +578,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
}
/*
- * This logic only matters when dumping VM Desc.
+ * This logic only matters when dumping VM Desc, and only
+ * when the VMSD field can be compressed.
*
* Due to the fake nullptr handling above, if there's mixed
* null/non-null data, it doesn't make sense to emit a
@@ -587,7 +588,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
* vs. nullptr). Search ahead for the next null/non-null element
* and start a new compressed array if found.
*/
- if (vmdesc && (field->flags & VMS_ARRAY_OF_POINTER) &&
+ if (vmdesc && vmsd_can_compress(field) &&
+ (field->flags & VMS_ARRAY_OF_POINTER) &&
is_null != is_prev_null) {
is_prev_null = is_null;
--
2.50.1
On Tue, Mar 17, 2026 at 07:23:26PM -0400, Peter Xu wrote:
> We have a trick in vmstate_save_vmsd_v() where we will try to compress
> multiple JSON vmstate field dumps into one line with a count to avoid
> duplicated entries.
>
> That only applies to the cases where vmsd_can_compress() should return
> true. For example, vmsd_desc_field_start() later (who will take the
> updated max_elems as the last parameter) will ignore the value passed in
> when vmsd_can_compress() returns false.
>
> Add that check to the trick too, it will be used later to bypass this logic
> to some special new VMSD fields.
>
> No functional change intended in this patch alone.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> migration/vmstate.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index e29a8c3f49..caa7b50bce 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -578,7 +578,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
> }
>
> /*
> - * This logic only matters when dumping VM Desc.
> + * This logic only matters when dumping VM Desc, and only
> + * when the VMSD field can be compressed.
> *
> * Due to the fake nullptr handling above, if there's mixed
> * null/non-null data, it doesn't make sense to emit a
> @@ -587,7 +588,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
> * vs. nullptr). Search ahead for the next null/non-null element
> * and start a new compressed array if found.
> */
> - if (vmdesc && (field->flags & VMS_ARRAY_OF_POINTER) &&
> + if (vmdesc && vmsd_can_compress(field) &&
> + (field->flags & VMS_ARRAY_OF_POINTER) &&
> is_null != is_prev_null) {
>
> is_prev_null = is_null;
> --
> 2.50.1
>
This patch unfortunately stops working when I start to test
analyze-migration.py. I plan to use another similar patch below to replace
this one:
From e72df6470e64715caaf3857a899fe63086bf4628 Mon Sep 17 00:00:00 2001
From: Peter Xu <peterx@redhat.com>
Date: Tue, 17 Mar 2026 19:23:26 -0400
Subject: [PATCH] vmstate: Update max_elems early and check field compressable
once
QEMU has a trick in vmstate_save_vmsd_v(), where it will try to compress
multiple JSON entries into one with a count to avoid duplicated entries.
That only applies to the cases where vmsd_can_compress() should return
true. For example, vmsd_desc_field_start() later (who will take the
updated max_elems as the last parameter) will ignore the value passed in
when vmsd_can_compress() returns false.
Do that check once at the start of loop, and use it to update max_elems, so
that max_elems keeps 1 for uncompressable VMSD fields, which is more
straightforward.
This also paves way to make this counter work for ptr marker VMSD fields
too.
No functional change intended in this patch alone.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
migration/vmstate.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/migration/vmstate.c b/migration/vmstate.c
index e29a8c3f49..05badef42f 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -556,7 +556,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
void *curr_elem = first_elem + size * i;
const VMStateField *inner_field;
bool is_null;
- int max_elems = n_elems - i;
+ /* maximum number of elements to compress in the JSON blob */
+ int max_elems = vmsd_can_compress(field) ? (n_elems - i) : 1;
old_offset = qemu_file_transferred(f);
if (field->flags & VMS_ARRAY_OF_POINTER) {
@@ -587,7 +588,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
* vs. nullptr). Search ahead for the next null/non-null element
* and start a new compressed array if found.
*/
- if (vmdesc && (field->flags & VMS_ARRAY_OF_POINTER) &&
+ if (vmdesc && max_elems > 1 &&
+ (field->flags & VMS_ARRAY_OF_POINTER) &&
is_null != is_prev_null) {
is_prev_null = is_null;
@@ -626,7 +628,7 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
}
/* Compressed arrays only care about the first element */
- if (vmdesc_loop && vmsd_can_compress(field)) {
+ if (vmdesc_loop && max_elems > 1) {
vmdesc_loop = NULL;
}
}
--
2.50.1
--
Peter Xu
Am Mi., 18. März 2026 um 00:23 Uhr schrieb Peter Xu <peterx@redhat.com>:
>
> We have a trick in vmstate_save_vmsd_v() where we will try to compress
> multiple JSON vmstate field dumps into one line with a count to avoid
> duplicated entries.
>
> That only applies to the cases where vmsd_can_compress() should return
> true. For example, vmsd_desc_field_start() later (who will take the
> updated max_elems as the last parameter) will ignore the value passed in
> when vmsd_can_compress() returns false.
>
> Add that check to the trick too, it will be used later to bypass this logic
> to some special new VMSD fields.
>
> No functional change intended in this patch alone.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
> ---
> migration/vmstate.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index e29a8c3f49..caa7b50bce 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -578,7 +578,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
> }
>
> /*
> - * This logic only matters when dumping VM Desc.
> + * This logic only matters when dumping VM Desc, and only
> + * when the VMSD field can be compressed.
> *
> * Due to the fake nullptr handling above, if there's mixed
> * null/non-null data, it doesn't make sense to emit a
> @@ -587,7 +588,8 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
> * vs. nullptr). Search ahead for the next null/non-null element
> * and start a new compressed array if found.
> */
> - if (vmdesc && (field->flags & VMS_ARRAY_OF_POINTER) &&
> + if (vmdesc && vmsd_can_compress(field) &&
> + (field->flags & VMS_ARRAY_OF_POINTER) &&
> is_null != is_prev_null) {
>
> is_prev_null = is_null;
> --
> 2.50.1
>
© 2016 - 2026 Red Hat, Inc.