Migration patches for 2026-04-23
Subsystems (3)
The following changes since commit bb230769b4d01de714bed686161ad39a8f4f3fd1:
Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging (2026-04-22 14:30:04 -0400)
are available in the Git repository at:
https://gitlab.com/farosas/qemu.git tags/migration-20260423-pull-request
for you to fetch changes up to 78532838ab9edeaced231760693898614280cbb5:
migration/qemu-file: drop incorrect const from qemu_get_buffer_at buf (2026-04-23 12:14:52 -0300)
----------------------------------------------------------------
Migration pull request
- Cleanups to internal vmstate APIs
- Cleanups to handling of vmstate flags
- New vmstate VMS_ARRAY_OF_POINTER_AUTO_ALLOC
- Fixes to:
- functional exec test
- leaks in file migration
- mapped-ram migration input validation
- programming errors in qemu-file and multifd
- qtest leaks
- Additions to iochannel APIs:
qio_channel_pread{v,}_all{,_eof}
qio_channel_pwrite{v,}_all
- New migration qtest for capabilities validation
- Tweaks to documentation and checkpatch
----------------------------------------------------------------
Alexander Mikhalitsyn (1):
tests/unit/test-vmstate: add tests for VMS_ARRAY_OF_POINTER_AUTO_ALLOC
Fabiano Rosas (3):
checkpatch: Allow spaces after all coroutine annotations tests/functional: Make socat wait longer in migration exec test
vmstate: Stop checking size for nullptr compression
Junjie Cao (7):
io/channel: introduce qio_channel_pread{v, }_all{, _eof}() io/channel: introduce qio_channel_pwrite{v,}_all() migration/file: fix type mismatch and NULL deref in
multifd_file_recv_data
tests/unit: add pread/pwrite _all tests for io channel file migration/qemu-file: switch buffer_at functions to positioned I/O _all
helpers
migration/file: switch file_write_ramblock_iov to pwritev_all migration/qemu-file: drop incorrect const from qemu_get_buffer_at buf
Markus Armbruster (1):
migration: Tweak description of migration property multifd-compression
Peter Xu (9):
vmstate: Pass in struct itself for VMSTATE_ARRAY_OF_POINTER
vmstate: Pass in struct itself for VMSTATE_VARRAY_OF_POINTER_UINT32
vmstate: Do not set size for VMS_ARRAY_OF_POINTER
vmstate: Update max_elems early and check field compressable once
vmstate: Rename VMS_NULLPTR_MARKER to VMS_MARKER_PTR_NULL
vmstate: Introduce vmstate_save_field_with_vmdesc()
vmstate: Allow vmstate_info_nullptr to emit non-NULL markers
vmstate: Implement load of ptr marker in vmstate core
vmstate: Implement VMS_ARRAY_OF_POINTER_AUTO_ALLOC
Takeru Hayasaka (1):
tests/qtest/migration: Add mapped-ram/postcopy validation test
Trieu Huynh (3):
migration: fix QIOChannelFile leak on error in file_connect_outgoing
migration: validate page_size in mapped-ram header before use tests/qtest/migration: fix fd leak in ufd_version_check
Vladimir Sementsov-Ogievskiy (18):
migration: vmstate_save_state_v: fix double error_setg
migration: make vmstate_save_state_v() static
migration: make .post_save() a void function
migration: vmstate_load_state(): add some newlines
migration: vmstate_save/load_state(): refactor tracing errors
migration: factor out vmstate_pre_save() from vmstate_save_state()
migration: factor out vmstate_save_field() from vmstate_save_state()
migration: factor out vmstate_pre_load() from vmstate_load_state()
migration: factor out vmstate_load_field() from vmstate_load_state()
migration: factor out vmstate_post_load() from vmstate_load_state()
migration: convert vmstate_subsection_save/load functions to bool
migration: VMStateInfo: introduce new handlers with errp
migration: introduce vmstate_load_vmsd() and vmstate_save_vmsd() migration/cpr: move to new migration APIs migration/savevm: move to new migration APIs hw/s390x/css: drop use of .err_hint for vmstate
migration: drop VMStateField.err_hint migration/vmstate-types: move to new migration APIs
docs/devel/migration/main.rst | 2 +- hw/block/fdc.c | 2 +- hw/core/qdev-properties-system.c | 2 +- hw/display/qxl.c | 4 +- hw/display/vga.c | 2 +- hw/display/virtio-gpu.c | 2 +- hw/display/vmware_vga.c | 2 +- hw/i386/vmmouse.c | 2 +- hw/ide/ahci.c | 2 +- hw/intc/openpic.c | 2 +- hw/intc/spapr_xive.c | 2 +- hw/intc/xics.c | 2 +- hw/intc/xive.c | 2 +- hw/nvram/eeprom93xx.c | 2 +- hw/pci/pci.c | 2 +- hw/pci/pcie_aer.c | 2 +- hw/ppc/spapr_iommu.c | 2 +- hw/ppc/spapr_pci.c | 7 +- hw/ppc/spapr_vio.c | 4 +- hw/s390x/css.c | 15 +- hw/usb/hcd-uhci.c | 2 +- include/hw/intc/riscv_aclint.h | 6 +- include/io/channel.h | 133 +++++ include/migration/cpr.h | 2 +- include/migration/vmstate.h | 161 ++++-- io/channel.c | 139 ++++++ migration/cpr.c | 22 +- migration/file.c | 23 +- migration/qemu-file.c | 37 +- migration/qemu-file.h | 2 +- migration/ram.c | 6 + migration/savevm.c | 136 ++++-- migration/trace-events | 30 +- migration/vmstate-types.c | 650 ++++++++++++------------- migration/vmstate.c | 593 ++++++++++++++-------- rust/migration/src/migratable.rs | 6 +- rust/migration/src/vmstate.rs | 12 +- scripts/analyze-migration.py | 22 +- scripts/checkpatch.pl | 1 + target/arm/machine.c | 4 +- target/ppc/machine.c | 6 +- tests/functional/migration.py | 2 +- tests/qtest/migration/file-tests.c | 48 ++ tests/qtest/migration/migration-util.c | 10 +- tests/qtest/migration/misc-tests.c | 52 ++ tests/unit/test-io-channel-file.c | 207 ++++++++ tests/unit/test-vmstate.c | 90 +++- 47 files changed, 1712 insertions(+), 752 deletions(-)
Signature · 1 line
2.51.0
[PULL 01/43] checkpatch: Allow spaces after all coroutine annotations
The coroutine annotations may be used in the declaration of function pointers, which triggers checkpatch due to the space before the parentheses. E.g:
int coroutine_fn (*run)(Job *job, Error **errp);
^The coroutine_fn annotation is already included in the list of terms where spaces are allowed. Add the other coroutine annotations:coroutine_mixed_fn and no_coroutine_fn.
--- scripts/checkpatch.pl | 1 + 1 file changed, 1 insertion(+)
scripts/checkpatch.pl +1 -0
@@ -2441,6 +2441,7 @@ sub process { if ($name =~ /^(?: if|for|while|switch|return|case| volatile|__volatile__|coroutine_fn| + coroutine_mixed_fn|no_coroutine_fn| __attribute__|format|__extension__| asm|__asm__)$/x) {
Signature · 1 line
2.51.0
[PULL 04/43] migration: make vmstate_save_state_v() static
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
It's used only in vmstate.c.
--- include/migration/vmstate.h | 3 --- migration/vmstate.c | 19 ++++++++++--------- 2 files changed, 10 insertions(+), 12 deletions(-)
include/migration/vmstate.h +0 -3
@@ -1244,9 +1244,6 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id, Error **errp);int vmstate_save_state (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,JSONWriter *vmdesc,Error **errp); -int vmstate_save_state_v (QEMUFile *f,const VMStateDescription *vmsd, -void *opaque,JSONWriter *vmdesc, -int version_id,Error **errp);bool vmstate_section_needed (const VMStateDescription *vmsd,void *opaque);
migration/vmstate.c +10 -9
@@ -420,15 +420,9 @@ bool vmstate_section_needed(const VMStateDescription *vmsd, void *opaque)return true; } - -int vmstate_save_state (QEMUFile *f,const VMStateDescription *vmsd, -void *opaque,JSONWriter *vmdesc_id,Error **errp) -{ -return vmstate_save_state_v (f, vmsd, opaque, vmdesc_id, vmsd->version_id, errp); -} - -int vmstate_save_state_v (QEMUFile *f,const VMStateDescription *vmsd, -void *opaque,JSONWriter *vmdesc,int version_id,Error **errp) +static int vmstate_save_state_v (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,JSONWriter *vmdesc, +int version_id,Error **errp) {ERRP_GUARD ();int ret =0 ;
@@ -608,6 +602,13 @@ vmstate_get_subsection(const VMStateDescription * const *sub,return NULL ; } +int vmstate_save_state (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,JSONWriter *vmdesc_id,Error **errp) +{ +return vmstate_save_state_v (f, vmsd, opaque, vmdesc_id, vmsd->version_id, + errp); +} +static int vmstate_subsection_load (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,Error **errp) {
Signature · 1 line
2.51.0
[PULL 16/43] migration/cpr: move to new migration APIs
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
--- include/migration/cpr.h | 2 +- migration/cpr.c | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-)
include/migration/cpr.h +1 -1
@@ -43,7 +43,7 @@ void cpr_set_incoming_mode(MigMode mode);bool cpr_is_incoming (void );bool cpr_state_save (MigrationChannel *channel,Error **errp); -int cpr_state_load (MigrationChannel *channel,Error **errp); +bool cpr_state_load (MigrationChannel *channel,Error **errp);void cpr_state_close (void );struct QIOChannel *cpr_state_ioc (void );
migration/cpr.c +9 -13
@@ -178,7 +178,6 @@ bool cpr_is_incoming(void)bool cpr_state_save (MigrationChannel *channel,Error **errp) { -int ret;QEMUFile *f;MigMode mode =migrate_mode ();
@@ -199,8 +198,7 @@ bool cpr_state_save(MigrationChannel *channel, Error **errp)qemu_put_be32 (f,QEMU_CPR_FILE_MAGIC );qemu_put_be32 (f,QEMU_CPR_FILE_VERSION ); - ret =vmstate_save_state (f, &vmstate_cpr_state, &cpr_state,0 , errp); -if (ret) { +if (!vmstate_save_vmsd (f, &vmstate_cpr_state, &cpr_state,0 , errp)) {qemu_fclose (f);return false; }
@@ -223,9 +221,8 @@ bool cpr_state_save(MigrationChannel *channel, Error **errp)return true; } -int cpr_state_load (MigrationChannel *channel,Error **errp) +bool cpr_state_load (MigrationChannel *channel,Error **errp) { -int ret;uint32_t v;QEMUFile *f;MigMode mode =0 ;
@@ -241,10 +238,10 @@ int cpr_state_load(MigrationChannel *channel, Error **errp)cpr_set_incoming_mode (mode); f =cpr_transfer_input (channel, errp); }else { -return 0 ; +return true; }if (!f) { -return -1 ; +return false; }trace_cpr_state_load (MigMode_str (mode));
@@ -254,19 +251,18 @@ int cpr_state_load(MigrationChannel *channel, Error **errp)if (v !=QEMU_CPR_FILE_MAGIC ) {error_setg (errp,"Not a migration stream (bad magic %x)" , v);qemu_fclose (f); -return -EINVAL ; +return false; } v =qemu_get_be32 (f);if (v !=QEMU_CPR_FILE_VERSION ) {error_setg (errp,"Unsupported migration stream version %d" , v);qemu_fclose (f); -return -ENOTSUP ; +return false; } - ret =vmstate_load_state (f, &vmstate_cpr_state, &cpr_state,1 , errp); -if (ret) { +if (!vmstate_load_vmsd (f, &vmstate_cpr_state, &cpr_state,1 , errp)) {qemu_fclose (f); -return ret; +return false; }if (migrate_mode () ==MIG_MODE_CPR_EXEC ) {
@@ -280,7 +276,7 @@ int cpr_state_load(MigrationChannel *channel, Error **errp)*/ cpr_state_file = f; -return ret; +return true; }void cpr_state_close (void )
Signature · 1 line
2.51.0
[PULL 11/43] migration: factor out vmstate_load_field() from vmstate_load_state()
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
Simplify vmstate_load_state() which is rather big, and simplify further refactoring.
--- migration/vmstate.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-)
migration/vmstate.c +23 -18
@@ -161,6 +161,27 @@ static bool vmstate_pre_load(const VMStateDescription *vmsd, void *opaque,return true; } +static bool vmstate_load_field (QEMUFile *f,void *pv,size_t size, +const VMStateField *field,Error **errp) +{ +if (field->flags &VMS_STRUCT ) { +return vmstate_load_state (f, field->vmsd, pv, field->vmsd->version_id, + errp) >=0 ; + }else if (field->flags &VMS_VSTRUCT ) { +return vmstate_load_state (f, field->vmsd, pv, field->struct_version_id, + errp) >=0 ; + } + +if (field->info->get (f, pv, size, field) <0 ) { +error_setg (errp, +"Failed to load element of type %s for %s" , + field->info->name, field->name); +return false; + } + +return true; +} +int vmstate_load_state (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,int version_id,Error **errp) {
@@ -225,24 +246,8 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, inner_field = field; } -if (inner_field->flags &VMS_STRUCT ) { - ret =vmstate_load_state (f, inner_field->vmsd, curr_elem, - inner_field->vmsd->version_id, - errp); - }else if (inner_field->flags &VMS_VSTRUCT ) { - ret =vmstate_load_state (f, inner_field->vmsd, curr_elem, - inner_field->struct_version_id, - errp); - }else { - ret = inner_field->info->get (f, curr_elem, size, - inner_field); -if (ret <0 ) { -error_setg (errp, -"Failed to load element of type %s for %s: " -"%d" , inner_field->info->name, - inner_field->name, ret); - } - } + ret =vmstate_load_field (f, curr_elem, size, inner_field, + errp) ?0 : -EINVAL ;/* If we used a fake temp field.. free it now */ if (inner_field != field) {
Signature · 1 line
2.51.0
[PULL 03/43] migration: vmstate_save_state_v: fix double error_setg
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
We may call error_setg twice on same errp if inner vmstate_save_state_v() or vmstate_save_state() call fails. Next we will crash on assertion in error_setv().
--- migration/vmstate.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
migration/vmstate.c +5 -2
@@ -539,6 +539,9 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, }else { ret = inner_field->info->put (f, curr_elem, size, inner_field, vmdesc_loop); +if (ret <0 ) { +error_setg (errp,"put failed" ); + } } written_bytes =qemu_file_transferred (f) - old_offset;
@@ -551,8 +554,8 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, }if (ret) { -error_setg (errp,"Save of field %s/%s failed" , - vmsd->name, field->name); +error_prepend (errp,"Save of field %s/%s failed: " , + vmsd->name, field->name);if (vmsd->post_save) { vmsd->post_save (opaque); }
Signature · 1 line
2.51.0
[PULL 18/43] hw/s390x/css: drop use of .err_hint for vmstate
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
That's the only usage through the whole base. Doesn't worth keeping the whole complexity. And 2.7 machines were long ago.
--- hw/s390x/css.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
hw/s390x/css.c +8 -7
@@ -192,10 +192,6 @@ static const VMStateDescription vmstate_schdev_orb = {static int subch_dev_post_load (void *opaque,int version_id);static int subch_dev_pre_save (void *opaque); -const char err_hint_devno[] ="Devno mismatch, tried to load wrong section!" -" Likely reason: some sequences of plug and unplug can break" -" migration for machine versions prior to 2.7 (known design flaw)." ; -const VMStateDescription vmstate_subch_dev = { .name ="s390_subch_dev" , .version_id =1 ,
@@ -203,10 +199,15 @@ const VMStateDescription vmstate_subch_dev = { .post_load = subch_dev_post_load, .pre_save = subch_dev_pre_save, .fields = (const VMStateField []) { -VMSTATE_UINT8_EQUAL (cssid, SubchDev,"Bug!" ), -VMSTATE_UINT8_EQUAL (ssid, SubchDev,"Bug!" ), +VMSTATE_UINT8_EQUAL (cssid, SubchDev,NULL ), +VMSTATE_UINT8_EQUAL (ssid, SubchDev,NULL ),VMSTATE_UINT16 (migrated_schid, SubchDev), -VMSTATE_UINT16_EQUAL (devno, SubchDev, err_hint_devno), +/* +* If devno mismatch on target, it may be due to some +* sequences of plug and unplug breaks migration for +* machine versions prior to 2.7 (known design flaw). +*/ +VMSTATE_UINT16_EQUAL (devno, SubchDev,NULL ),VMSTATE_BOOL (thinint_active, SubchDev),VMSTATE_STRUCT (curr_status, SubchDev,0 , vmstate_schib,SCHIB ),VMSTATE_UINT8_ARRAY (sense_data, SubchDev,32 ),
Signature · 1 line
2.51.0
[PULL 09/43] migration: factor out vmstate_save_field() from vmstate_save_state()
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
Simplify vmstate_save_state() which is rather big, and simplify further refactoring.
--- migration/vmstate.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-)
migration/vmstate.c +25 -15
@@ -26,6 +26,9 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, Error **errp);static int vmstate_subsection_load (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,Error **errp); +static int vmstate_save_state_v (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,JSONWriter *vmdesc, +int version_id,Error **errp);/* Whether this field should exist for either save or load the VM? */ static bool
@@ -455,6 +458,26 @@ static bool vmstate_pre_save(const VMStateDescription *vmsd, void *opaque,return true; } +static bool vmstate_save_field (QEMUFile *f,void *pv,size_t size, +const VMStateField *field, +JSONWriter *vmdesc,Error **errp) +{ +if (field->flags &VMS_STRUCT ) { +return vmstate_save_state (f, field->vmsd, pv, vmdesc, errp) >=0 ; + }else if (field->flags &VMS_VSTRUCT ) { +return vmstate_save_state_v (f, field->vmsd, pv, vmdesc, + field->struct_version_id, + errp) >=0 ; + } + +if (field->info->put (f, pv, size, field, vmdesc) <0 ) { +error_setg (errp,"put failed" ); +return false; + } + +return true; +} +static int vmstate_save_state_v (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,JSONWriter *vmdesc,int version_id,Error **errp)
@@ -548,21 +571,8 @@ static int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,vmsd_desc_field_start (vmsd, vmdesc_loop, inner_field, i, max_elems); -if (inner_field->flags &VMS_STRUCT ) { - ret =vmstate_save_state (f, inner_field->vmsd, - curr_elem, vmdesc_loop, errp); - }else if (inner_field->flags &VMS_VSTRUCT ) { - ret =vmstate_save_state_v (f, inner_field->vmsd, - curr_elem, vmdesc_loop, - inner_field->struct_version_id, - errp); - }else { - ret = inner_field->info->put (f, curr_elem, size, - inner_field, vmdesc_loop); -if (ret <0 ) { -error_setg (errp,"put failed" ); - } - } + ret =vmstate_save_field (f, curr_elem, size, inner_field, + vmdesc_loop, errp) ?0 : -EINVAL ; written_bytes =qemu_file_transferred (f) - old_offset; vmsd_desc_field_end(vmsd, vmdesc_loop, inner_field,
Signature · 1 line
2.51.0
[PULL 07/43] migration: vmstate_save/load_state(): refactor tracing errors
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
To simplify further changes (convertion to bool+errp APIs), let's rework some error paths:
- get rid of int ret in traces, as we are moving to bool+errp APIs
- split traces to _fail / _success (seems better than add boolean result to the message).
- prefer short error paths (return immediately on error)
- around trace_vmstate_load_field_error(), do not call qemu_file_set_error(), if the erroc comes from qemu_file_get_error()
--- migration/trace-events | 6 ++++-- migration/vmstate.c | 25 +++++++++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-)
migration/trace-events +4 -2
@@ -56,13 +56,15 @@ postcopy_page_req_sync(void *host_addr) "sync page req %p" # vmstate.c vmstate_load_field_error(const char *field, int ret) "field \"%s\" load failed, ret = %d" vmstate_load_state(const char *name, int version_id) "%s v%d" -vmstate_load_state_end(const char *name, const char *reason, int val) "%s %s/%d" +vmstate_load_state_fail(const char *name, const char *reason) "%s %s" +vmstate_load_state_success(const char *name) "%s" vmstate_load_state_field(const char *name, const char *field, bool exists) "%s:%s exists=%d" vmstate_n_elems(const char *name, int n_elems) "%s: %d" vmstate_subsection_load(const char *parent) "%s" vmstate_subsection_load_bad(const char *parent, const char *sub, const char *sub2) "%s: %s/%s" vmstate_subsection_load_good(const char *parent) "%s" -vmstate_save_state_pre_save_res(const char *name, int res) "%s/%d" +vmstate_save_state_pre_save_fail(const char *name) "%s" +vmstate_save_state_pre_save_success(const char *name) "%s" vmstate_save_state_loop(const char *name, const char *field, int n_elems) "%s/%s[%d]" vmstate_save_state_top(const char *idstr) "%s" vmstate_subsection_save_loop(const char *name, const char *sub) "%s/%s"
migration/vmstate.c +15 -10
@@ -144,7 +144,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,error_setg (errp,"%s: incoming version_id %d is too new " "for local version_id %d" , vmsd->name, version_id, vmsd->version_id); -trace_vmstate_load_state_end (vmsd->name,"too new" , -EINVAL ); +trace_vmstate_load_state_fail (vmsd->name,"too new" );return -EINVAL ; }
@@ -152,7 +152,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,error_setg (errp,"%s: incoming version_id %d is too old " "for local minimum version_id %d" , vmsd->name, version_id, vmsd->minimum_version_id); -trace_vmstate_load_state_end (vmsd->name,"too old" , -EINVAL ); +trace_vmstate_load_state_fail (vmsd->name,"too old" );return -EINVAL ; }
@@ -240,10 +240,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,error_setg (errp,"Failed to load %s state: stream error: %d" , vmsd->name, ret); +trace_vmstate_load_field_error (field->name, ret); +return ret; } - } - -if (ret <0 ) { + }else {qemu_file_set_error (f, ret);trace_vmstate_load_field_error (field->name, ret);return ret;
@@ -269,7 +269,8 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,error_prepend (errp,"post load hook failed for: %s, version_id: " "%d, minimum_version: %d: " , vmsd->name, vmsd->version_id, vmsd->minimum_version_id); - ret = -EINVAL ; +trace_vmstate_load_state_fail (vmsd->name,"post-load" ); +return -EINVAL ; } }else if (vmsd->post_load) { ret = vmsd->post_load (opaque, version_id);
@@ -279,12 +280,14 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,"minimum_version: %d, ret: %d" , vmsd->name, vmsd->version_id, vmsd->minimum_version_id, ret); +trace_vmstate_load_state_fail (vmsd->name,"post-load" ); +return ret; } } -trace_vmstate_load_state_end (vmsd->name,"end" , ret); +trace_vmstate_load_state_success (vmsd->name); -return ret; +return 0 ; } static int vmfield_name_num(const VMStateField *start,
@@ -444,20 +447,22 @@ static int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,if (vmsd->pre_save_errp) { ret = vmsd->pre_save_errp (opaque, errp) ?0 : -EINVAL ; -trace_vmstate_save_state_pre_save_res (vmsd->name, ret);if (ret <0 ) {error_prepend (errp,"pre-save for %s failed: " , vmsd->name); +trace_vmstate_save_state_pre_save_fail (vmsd->name);return ret; } }else if (vmsd->pre_save) { ret = vmsd->pre_save (opaque); -trace_vmstate_save_state_pre_save_res (vmsd->name, ret);if (ret) {error_setg (errp,"pre-save failed: %s" , vmsd->name); +trace_vmstate_save_state_pre_save_fail (vmsd->name);return ret; } } +trace_vmstate_save_state_pre_save_success (vmsd->name); +if (vmdesc) {json_writer_str (vmdesc,"vmsd_name" , vmsd->name);json_writer_int64 (vmdesc,"version" , version_id);
Signature · 1 line
2.51.0
[PULL 02/43] tests/functional: Make socat wait longer in migration exec test
In reply to Fabiano RosasThe migration_with_exec test is failing sporadically for all architectures due to a race when the destination socat process takes too long to start listening while the source process is already issuing connect().
The race is inherent because the exec: migration spawns the to-be-exec'ed command asynchronously and returns from the migrate-incoming command. The localhost-only testcase is not representative of the majority of migrations. In a real scenario between two different hosts that race wouldn't happen.
Fix the testcase by configuring the source socat command to wait indefinitely while trying to connect.
--- tests/functional/migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tests/functional/migration.py +1 -1
@@ -85,5 +85,5 @@ def migration_with_exec(self): with Ports() as ports: free_port = self._get_free_port(ports) dst_uri = 'exec:socat TCP-LISTEN:%u -' % free_port - src_uri = 'exec:socat - TCP:localhost:%u' % free_port + src_uri = 'exec:socat - TCP:localhost:%u,forever' % free_port self.migrate(dst_uri, src_uri)
Signature · 1 line
2.51.0
[PULL 20/43] migration/vmstate-types: move to new migration APIs
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
--- migration/trace-events | 24 +- migration/vmstate-types.c | 627 +++++++++++++++++++------------------- 2 files changed, 327 insertions(+), 324 deletions(-)
migration/trace-events +12 -12
@@ -72,20 +72,20 @@ vmstate_subsection_save_top(const char *idstr) "%s" vmstate_field_exists(const char *vmsd, const char *name, int field_version, int version, int result) "%s:%s field_version %d version %d result %d" # vmstate-types.c -get_qtailq(const char *name, int version_id) "%s v%d" -get_qtailq_end(const char *name, const char *reason, int val) "%s %s/%d" -put_qtailq(const char *name, int version_id) "%s v%d" -put_qtailq_end(const char *name, const char *reason) "%s %s" +load_qtailq(const char *name, int version_id) "%s v%d" +load_qtailq_end(const char *name) "%s" +save_qtailq(const char *name, int version_id) "%s v%d" +save_qtailq_end(const char *name) "%s" -get_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d" -get_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d" -put_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d" -put_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d" +load_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d" +load_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name) "%s(%s/%s)" +save_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d" +save_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name) "%s(%s/%s)" -get_qlist(const char *field_name, const char *vmsd_name, int version_id) "%s(%s v%d)" -get_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" -put_qlist(const char *field_name, const char *vmsd_name, int version_id) "%s(%s v%d)" -put_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" +load_qlist(const char *field_name, const char *vmsd_name, int version_id) "%s(%s v%d)" +load_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" +save_qlist(const char *field_name, const char *vmsd_name, int version_id) "%s(%s v%d)" +save_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" # qemu-file.c qemu_file_fclose(void) ""
migration/vmstate-types.c +315 -312
1,193 lines not on this page · raw message
Signature · 1 line
2.51.0
[PULL 21/43] migration: Tweak description of migration property multifd-compression
In reply to Fabiano RosasFrom: Markus Armbruster <[email protected]>
Help for the migration pseudo-device shows property "multifd-compression" like this:
multifd-compression=<MultiFDCompression> - multifd_compression values (none/zlib/zstd/qpl/uadk/qatzip) (default: none)
Change it to
multifd-compression=<MultiFDCompression> - multifd compression method (none/zlib/zstd/qpl/uadk/qatzip) (default: none)
--- hw/core/qdev-properties-system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
hw/core/qdev-properties-system.c +1 -1
@@ -681,7 +681,7 @@ const PropertyInfo qdev_prop_fdc_drive_type = {const PropertyInfo qdev_prop_multifd_compression = { .type ="MultiFDCompression" , - .description ="multifd_compression values" + .description ="multifd compression method" " (none/zlib/zstd/qpl/uadk/qatzip)" , .enum_table = &MultiFDCompression_lookup, .get = qdev_propinfo_get_enum,
Signature · 1 line
2.51.0
[PULL 23/43] migration: fix QIOChannelFile leak on error in file_connect_outgoing
In reply to Fabiano RosasFrom: Trieu Huynh <[email protected]>
Commit 03a680c978 changed g_autoptr(QIOChannelFile) to a plain pointer but failed to restore the necessary object_unref() calls on error paths. Previously, these were handled implicitly by the g_autoptr cleanup mechanism.
Two error paths currently leak the QIOChannelFile object and its underlying file descriptor:
1. When ftruncate() fails (e.g., on character or block devices).
2. When qio_channel_io_seek() fails after the channel is created.
In environments that retry migration automatically (e.g., libvirt), these FDs accumulate until QEMU hits RLIMIT_NOFILE and fails with EMFILE (Too many open files).
Add the missing object_unref() calls to both error paths to ensure resources are properly released.
--- migration/file.c | 2 ++ tests/qtest/migration/file-tests.c | 48 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+)
migration/file.c +2 -0
@@ -112,6 +112,7 @@ QIOChannel *file_connect_outgoing(MigrationState *s,error_setg_errno (errp, errno,"failed to truncate migration file to offset %" PRIx64, offset); +object_unref (OBJECT (fioc)); goto out; }
@@ -119,6 +120,7 @@ QIOChannel *file_connect_outgoing(MigrationState *s, ioc =QIO_CHANNEL (fioc);if (offset &&qio_channel_io_seek (ioc, offset,SEEK_SET , errp) <0 ) { +object_unref (OBJECT (fioc)); ioc =NULL ; goto out; }
tests/qtest/migration/file-tests.c +48 -0
60 lines not on this page · raw message
Signature · 1 line
2.51.0
[PULL 26/43] vmstate: Do not set size for VMS_ARRAY_OF_POINTER
In reply to Fabiano RosasFrom: Peter Xu <[email protected]>
When VMS_ARRAY_OF_POINTER is specified, it means the vmstate field is an array of pointers.
The size of the element is not relevant to whatever it is stored inside: it is always the host pointer size.
Let's reserve the "size" field in this case for future use, update vmstate_size() so as to make it still work for array of pointers properly.
When at this, provide rich documentation on how size / size_offset works in vmstate.
--- include/migration/vmstate.h | 20 ++++++++++++++++---- migration/savevm.c | 3 +++ migration/vmstate.c | 10 +++++++++- 3 files changed, 28 insertions(+), 5 deletions(-)
include/migration/vmstate.h +16 -4
@@ -183,11 +183,26 @@ typedef enum {struct VMStateField {const char *name;size_t offset; + +/* +* @size or @size_offset specifies the size of the element embeded in +* the field. Only one of them should be present never both. When +* @size_offset is used together with VMS_VBUFFER, it means the size is +* dynamic calculated instead of a constant. +* +* When the field is an array of any type, this stores the size of one +* element of the array. +* +* NOTE: even if VMS_POINTER or VMS_ARRAY_OF_POINTER may be specified, +* this parameter always reflects the real size of the objects that a +* pointer point to. +*/ size_t size; +size_t size_offset; +size_t start;int num;size_t num_offset; -size_t size_offset;const VMStateInfo *info;enum VMStateFlags flags;const VMStateDescription *vmsd;
@@ -547,7 +562,6 @@ extern const VMStateInfo vmstate_info_qlist; .version_id = (_version), \ .num = (_num), \ .info = &(_info), \ - .size = sizeof(_type *), \ .flags = VMS_ARRAY|VMS_ARRAY_OF_POINTER, \ .offset = vmstate_offset_array(_state, _field, _type *, _num), \ }
@@ -557,7 +571,6 @@ extern const VMStateInfo vmstate_info_qlist; .version_id = (_v), \ .num = (_n), \ .vmsd = &(_vmsd), \ - .size = sizeof(_type *), \ .flags = VMS_ARRAY|VMS_STRUCT|VMS_ARRAY_OF_POINTER, \ .offset = vmstate_offset_array(_s, _f, _type*, _n), \ }
@@ -567,7 +580,6 @@ extern const VMStateInfo vmstate_info_qlist; .version_id = (_version), \ .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \ .info = &(_info), \ - .size = sizeof(_type *), \ .flags = VMS_VARRAY_UINT32 | VMS_ARRAY_OF_POINTER | VMS_POINTER, \ .offset = vmstate_offset_pointer(_state, _field, _type *), \ }
migration/savevm.c +3 -0
@@ -868,6 +868,9 @@ static void vmstate_check(const VMStateDescription *vmsd)if (field) {while (field->name) { +if (field->flags &VMS_ARRAY_OF_POINTER ) { +assert (field->size ==0 ); + }if (field->flags & (VMS_STRUCT |VMS_VSTRUCT )) {/* Recurse to sub structures */ vmstate_check (field->vmsd);
migration/vmstate.c +9 -1
@@ -110,13 +110,21 @@ static int vmstate_n_elems(void *opaque, const VMStateField *field)static int vmstate_size (void *opaque,const VMStateField *field) { -int size = field->size; +int size;if (field->flags &VMS_VBUFFER ) { size = *(int32_t *)(opaque + field->size_offset);if (field->flags &VMS_MULTIPLY ) { size *= field->size; } + }else if (field->flags &VMS_ARRAY_OF_POINTER ) { +/* +* For an array of pointer, the each element is always size of a +* host pointer. +*/ + size =sizeof (void *); + }else { + size = field->size; }return size;
Signature · 1 line
2.51.0
[PULL 13/43] migration: convert vmstate_subsection_save/load functions to bool
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
Convert them to bool return value, as preparation to further convertion of vmstate_save/load_state().
--- migration/vmstate.c | 57 +++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 31 deletions(-)
migration/vmstate.c +26 -31
@@ -21,11 +21,11 @@#include "qemu/error-report.h" #include "trace.h" -static int vmstate_subsection_save (QEMUFile *f,const VMStateDescription *vmsd, -void *opaque,JSONWriter *vmdesc, -Error **errp); -static int vmstate_subsection_load (QEMUFile *f,const VMStateDescription *vmsd, -void *opaque,Error **errp); +static bool vmstate_subsection_save (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,JSONWriter *vmdesc, +Error **errp); +static bool vmstate_subsection_load (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,Error **errp);static int vmstate_save_state_v (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,JSONWriter *vmdesc,int version_id,Error **errp);
@@ -305,10 +305,9 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, }assert (field->flags ==VMS_END ); - ret =vmstate_subsection_load (f, vmsd, opaque, errp); -if (ret !=0 ) { -qemu_file_set_error (f, ret); -return ret; +if (!vmstate_subsection_load (f, vmsd, opaque, errp)) { +qemu_file_set_error (f, -EINVAL ); +return -EINVAL ; }if (!vmstate_post_load (vmsd, opaque, version_id, errp)) {
@@ -637,7 +636,7 @@ static int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,json_writer_end_array (vmdesc); } - ret =vmstate_subsection_save (f, vmsd, opaque, vmdesc, errp); + ret =vmstate_subsection_save (f, vmsd, opaque, vmdesc, errp) ?0 : -EINVAL ; out:if (vmsd->post_save) {
@@ -667,15 +666,14 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, errp); } -static int vmstate_subsection_load (QEMUFile *f,const VMStateDescription *vmsd, -void *opaque,Error **errp) +static bool vmstate_subsection_load (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,Error **errp) {ERRP_GUARD ();trace_vmstate_subsection_load (vmsd->name);while (qemu_peek_byte (f,0 ) ==QEMU_VM_SUBSECTION ) {char idstr[256 ], *idstr_ret; -int ret;uint8_t version_id, len, size;const VMStateDescription *sub_vmsd;
@@ -683,12 +681,12 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,if (len <strlen (vmsd->name) +1 ) {/* subsection name has to be "section_name/a" */ trace_vmstate_subsection_load_bad (vmsd->name,"(short)" ,"" ); -return 0 ; +return true; } size =qemu_peek_buffer (f, (uint8_t **)&idstr_ret, len,2 );if (size != len) {trace_vmstate_subsection_load_bad (vmsd->name,"(peek fail)" ,"" ); -return 0 ; +return true; }memcpy (idstr, idstr_ret, size); idstr[size] =0 ;
@@ -696,41 +694,39 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,if (strncmp (vmsd->name, idstr,strlen (vmsd->name)) !=0 ) {trace_vmstate_subsection_load_bad (vmsd->name, idstr,"(prefix)" );/* it doesn't have a valid subsection name */ -return 0 ; +return true; } sub_vmsd =vmstate_get_subsection (vmsd->subsections, idstr);if (sub_vmsd ==NULL ) {trace_vmstate_subsection_load_bad (vmsd->name, idstr,"(lookup)" );error_setg (errp,"VM subsection '%s' in '%s' does not exist" , idstr, vmsd->name); -return -ENOENT ; +return false; }qemu_file_skip (f,1 );/* subsection */ qemu_file_skip (f,1 );/* len */ qemu_file_skip (f, len);/* idstr */ version_id =qemu_get_be32 (f); - ret =vmstate_load_state (f, sub_vmsd, opaque, version_id, errp); -if (ret) { +if (vmstate_load_state (f, sub_vmsd, opaque, version_id, errp) <0 ) {trace_vmstate_subsection_load_bad (vmsd->name, idstr,"(child)" );error_prepend (errp, -"Loading VM subsection '%s' in '%s' failed: %d: " , - idstr, vmsd->name, ret); -return ret; +"Loading VM subsection '%s' in '%s' failed: " , + idstr, vmsd->name); +return false; } }trace_vmstate_subsection_load_good (vmsd->name); -return 0 ; +return true; } -static int vmstate_subsection_save (QEMUFile *f,const VMStateDescription *vmsd, -void *opaque,JSONWriter *vmdesc, -Error **errp) +static bool vmstate_subsection_save (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,JSONWriter *vmdesc, +Error **errp) {const VMStateDescription *const *sub = vmsd->subsections;bool vmdesc_has_subsections = false; -int ret =0 ;trace_vmstate_subsection_save_top (vmsd->name);while (sub && *sub) {
@@ -754,9 +750,8 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,qemu_put_byte (f, len);qemu_put_buffer (f, (uint8_t *)vmsdsub->name, len);qemu_put_be32 (f, vmsdsub->version_id); - ret =vmstate_save_state (f, vmsdsub, opaque, vmdesc, errp); -if (ret) { -return ret; +if (vmstate_save_state (f, vmsdsub, opaque, vmdesc, errp) <0 ) { +return false; }if (vmdesc) {
@@ -770,5 +765,5 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,json_writer_end_array (vmdesc); } -return ret; +return true; }
Signature · 1 line
2.51.0
[PULL 37/43] io/channel: introduce qio_channel_pwrite{v,}_all()
In reply to Fabiano RosasFrom: Junjie Cao <[email protected]>
Add positioned write helpers that retry on short writes, matching the pread_all family from the previous patch.
qio_channel_pwritev_all() -- retry loop; returns 0 on success,
-1 on error.
qio_channel_pwrite_all() -- single-buffer convenience wrapper.
--- include/io/channel.h | 41 +++++++++++++++++++++++++++++++++++++ io/channel.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+)
include/io/channel.h +41 -0
@@ -598,6 +598,47 @@ ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov,ssize_t qio_channel_pwrite (QIOChannel *ioc,void *buf,size_t buflen,off_t offset,Error **errp); +/** +* qio_channel_pwritev_all: +* @ioc: the channel object +* @iov: the array of memory regions to write data from +* @niov: the length of the @iov array +* @offset: the starting offset in the channel to write to +* @errp: pointer to a NULL-initialized error object +* +* Writes @iov, possibly blocking or (if the channel is non-blocking) +* yielding from the current coroutine multiple times until the entire +* content is written. Otherwise behaves as qio_channel_pwritev(). +* +* Returns: 0 if all bytes were written, or -1 on error +*/ +int coroutine_mixed_fn qio_channel_pwritev_all(QIOChannel *ioc, +const struct iovec *iov, +size_t niov, +off_t offset, +Error **errp); + +/** +* qio_channel_pwrite_all: +* @ioc: the channel object +* @buf: the memory region to write data from +* @buflen: the number of bytes to write from @buf +* @offset: the starting offset in the channel to write to +* @errp: pointer to a NULL-initialized error object +* +* Writes @buflen bytes from @buf, possibly blocking or (if the +* channel is non-blocking) yielding from the current coroutine +* multiple times until the entire content is written. Otherwise +* behaves as qio_channel_pwrite(). +* +* Returns: 0 if all bytes were written, or -1 on error +*/ +int coroutine_mixed_fn qio_channel_pwrite_all(QIOChannel *ioc, +const void *buf, +size_t buflen, +off_t offset, +Error **errp); +/** * qio_channel_preadv * @ioc: the channel object
io/channel.c +48 -0
54 lines not on this page · raw message
Signature · 1 line
2.51.0
[PULL 08/43] migration: factor out vmstate_pre_save() from vmstate_save_state()
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
Simplify vmstate_save_state() which is rather big, and simplify further refactoring.
--- migration/vmstate.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-)
migration/vmstate.c +24 -15
@@ -435,32 +435,41 @@ bool vmstate_section_needed(const VMStateDescription *vmsd, void *opaque)return true; } -static int vmstate_save_state_v (QEMUFile *f,const VMStateDescription *vmsd, -void *opaque,JSONWriter *vmdesc, -int version_id,Error **errp) +static bool vmstate_pre_save (const VMStateDescription *vmsd,void *opaque, +Error **errp) {ERRP_GUARD (); -int ret =0 ; -const VMStateField *field = vmsd->fields; - -trace_vmstate_save_state_top (vmsd->name);if (vmsd->pre_save_errp) { - ret = vmsd->pre_save_errp (opaque, errp) ?0 : -EINVAL ; -if (ret <0 ) { +if (!vmsd->pre_save_errp (opaque, errp)) {error_prepend (errp,"pre-save for %s failed: " , vmsd->name); -trace_vmstate_save_state_pre_save_fail (vmsd->name); -return ret; +return false; } }else if (vmsd->pre_save) { - ret = vmsd->pre_save (opaque); -if (ret) { +if (vmsd->pre_save (opaque) <0 ) {error_setg (errp,"pre-save failed: %s" , vmsd->name); -trace_vmstate_save_state_pre_save_fail (vmsd->name); -return ret; +return false; } } +return true; +} + +static int vmstate_save_state_v (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,JSONWriter *vmdesc, +int version_id,Error **errp) +{ +ERRP_GUARD (); +int ret =0 ; +const VMStateField *field = vmsd->fields; + +trace_vmstate_save_state_top (vmsd->name); + +if (!vmstate_pre_save (vmsd, opaque, errp)) { +trace_vmstate_save_state_pre_save_fail (vmsd->name); +return -EINVAL ; + } +trace_vmstate_save_state_pre_save_success (vmsd->name);if (vmdesc) {
Signature · 1 line
2.51.0
[PULL 06/43] migration: vmstate_load_state(): add some newlines
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
Split logical blocks by newlines, that simplify reading the code.
--- migration/vmstate.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
migration/vmstate.c +12 -0
@@ -139,6 +139,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,int ret =0 ;trace_vmstate_load_state (vmsd->name, version_id); +if (version_id > vmsd->version_id) { error_setg(errp, "%s: incoming version_id %d is too new " "for local version_id %d",
@@ -146,6 +147,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,trace_vmstate_load_state_end (vmsd->name,"too new" , -EINVAL );return -EINVAL ; } +if (version_id < vmsd->minimum_version_id) { error_setg(errp, "%s: incoming version_id %d is too old " "for local minimum version_id %d",
@@ -153,6 +155,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,trace_vmstate_load_state_end (vmsd->name,"too old" , -EINVAL );return -EINVAL ; } +if (vmsd->pre_load_errp) {if (!vmsd->pre_load_errp (opaque, errp)) { error_prepend(errp, "pre load hook failed for: '%s', "
@@ -171,9 +174,12 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,return ret; } } +while (field->name) {bool exists =vmstate_field_exists (vmsd, field, opaque, version_id); +trace_vmstate_load_state_field (vmsd->name, field->name, exists); +if (exists) {void *first_elem = opaque + field->offset;int i, n_elems =vmstate_n_elems (opaque, field);
@@ -184,6 +190,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, first_elem = *(void **)first_elem;assert (first_elem || !n_elems || !size); } +for (i =0 ; i < n_elems; i++) {void *curr_elem = first_elem + size * i;const VMStateField *inner_field;
@@ -235,6 +242,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, vmsd->name, ret); } } +if (ret <0 ) {qemu_file_set_error (f, ret);trace_vmstate_load_field_error (field->name, ret);
@@ -249,11 +257,13 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, field++; }assert (field->flags ==VMS_END ); + ret =vmstate_subsection_load (f, vmsd, opaque, errp);if (ret !=0 ) {qemu_file_set_error (f, ret);return ret; } +if (vmsd->post_load_errp) {if (!vmsd->post_load_errp (opaque, version_id, errp)) { error_prepend(errp, "post load hook failed for: %s, version_id: "
@@ -271,7 +281,9 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, ret); } } +trace_vmstate_load_state_end (vmsd->name,"end" , ret); +return ret; }
Signature · 1 line
2.51.0
[PULL 05/43] migration: make .post_save() a void function
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
All other handlers now have _errp() variants. Should we go this way for .post_save()? Actually it's rather strange, when the vmstate do successful preparations in .pre_save(), then successfully save all sections and subsections, end then fail when all the state is successfully transferred to the target.
Happily, we have only three .post_save() realizations, all always successful. Let's make this a rule.
Also note, that we call .post_save() in two places, and handle its (theoretical) failure inconsistently. Fix that too.
--- docs/devel/migration/main.rst | 2 +- hw/ppc/spapr_pci.c | 3 +-- include/migration/vmstate.h | 8 +++++++- migration/savevm.c | 3 +-- migration/vmstate.c | 12 +++--------- rust/migration/src/migratable.rs | 6 ++---- rust/migration/src/vmstate.rs | 12 +++++++----- target/arm/machine.c | 4 +--- 8 files changed, 23 insertions(+), 27 deletions(-)
docs/devel/migration/main.rst +1 -1
@@ -439,7 +439,7 @@ The functions to do that are inside a vmstate definition, and are called: This function is called before we save the state of one device. -- ``int (*post_save)(void *opaque);`` +- ``void (*post_save)(void *opaque);`` This function is called after we save the state of one device (even upon failure, unless the call to pre_save returned an error).
hw/ppc/spapr_pci.c +1 -2
@@ -2093,14 +2093,13 @@ static int spapr_pci_pre_save(void *opaque)return 0 ; } -static int spapr_pci_post_save (void *opaque) +static void spapr_pci_post_save (void *opaque) {SpaprPhbState *sphb = opaque;g_free (sphb->msi_devs); sphb->msi_devs =NULL ; sphb->msi_devs_num =0 ; -return 0 ; }static int spapr_pci_post_load (void *opaque,int version_id)
include/migration/vmstate.h +7 -1
@@ -223,7 +223,13 @@ struct VMStateDescription {bool (*post_load_errp)(void *opaque,int version_id,Error **errp);int (*pre_save)(void *opaque);bool (*pre_save_errp)(void *opaque,Error **errp); -int (*post_save)(void *opaque); + +/* +* Unless .pre_save() fails, .post_save() is called after saving +* fields and subsections. It should not fail because at this +* point the state has potentially already been transferred. +*/ +void (*post_save)(void *opaque);bool (*needed)(void *opaque);bool (*dev_unplug_pending)(void *opaque);
migration/savevm.c +1 -2
@@ -321,14 +321,13 @@ static int configuration_pre_save(void *opaque)return 0 ; } -static int configuration_post_save (void *opaque) +static void configuration_post_save (void *opaque) {SaveState *state = opaque;g_free (state->capabilities); state->capabilities =NULL ; state->caps_count =0 ; -return 0 ; }static int configuration_pre_load (void *opaque)
migration/vmstate.c +3 -9
@@ -550,10 +550,7 @@ static int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,if (ret) {error_prepend (errp,"Save of field %s/%s failed: " , vmsd->name, field->name); -if (vmsd->post_save) { - vmsd->post_save (opaque); - } -return ret; + goto out; }/* Compressed arrays only care about the first element */
@@ -578,12 +575,9 @@ static int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, ret =vmstate_subsection_save (f, vmsd, opaque, vmdesc, errp); +out:if (vmsd->post_save) { -int ps_ret = vmsd->post_save (opaque); -if (!ret && ps_ret) { - ret = ps_ret; -error_setg (errp,"post-save failed: %s" , vmsd->name); - } + vmsd->post_save (opaque); }return ret; }
rust/migration/src/migratable.rs +2 -4
@@ -406,10 +406,8 @@ fn pre_save(&self) -> Result<(), InvalidError> {Ok (()) } -fn post_save (&self) ->Result <(),InvalidError > { -let state =unsafe {Box ::from_raw (self.migration_state.replace (ptr::null_mut ())) }; -drop (state); -Ok (()) +fn post_save (&self) { +let _ =unsafe {Box ::from_raw (self.migration_state.replace (ptr::null_mut ())) }; }fn pre_load (&self) ->Result <(),InvalidError > {
rust/migration/src/vmstate.rs +7 -5
@@ -492,6 +492,11 @@ fn from(_value: InvalidError) -> Errno {into_neg_errno (result) } +unsafe extern "C" fn vmstate_post_save_cb <T ,F :for <'a>FnCall <(&'aT ,), ()>>(opaque: *mut c_void ) { +// SAFETY: the function is used in T's implementation of VMState. +F ::call ((unsafe { &*(opaque.cast ::<T >()) },)); +} + unsafe extern "C" fn vmstate_post_load_cb< T, F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into<Errno>>>,
@@ -597,12 +602,9 @@ pub const fn pre_save<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>> }#[must_use] -pub const fn post_save <F :for <'a>FnCall <(&'aT ,),Result <(),impl Into <Errno >>>>( -mut self, - _f: &F , - ) ->Self { +pub const fn post_save <F :for <'a>FnCall <(&'aT ,), ()>>(mut self, _f: &F ) ->Self { self.0 .post_save =if F ::IS_SOME { -Some (vmstate_no_version_cb ::<T ,F >) +Some (vmstate_post_save_cb ::<T ,F >) }else {None };
target/arm/machine.c +1 -3
@@ -998,7 +998,7 @@ static int cpu_pre_save(void *opaque)return 0 ; } -static int cpu_post_save (void *opaque) +static void cpu_post_save (void *opaque) {ARMCPU *cpu = opaque;
@@ -1008,8 +1008,6 @@ static int cpu_post_save(void *opaque) cpu->cpreg_vmstate_indexes =NULL ; cpu->cpreg_vmstate_values =NULL ; - -return 0 ; }static int cpu_pre_load (void *opaque)
Signature · 1 line
2.51.0
[PULL 31/43] vmstate: Implement load of ptr marker in vmstate core
In reply to Fabiano RosasFrom: Peter Xu <[email protected]>
The loader side of ptr marker is pretty straightforward, instead of playing the inner_field trick, just do the load manually assuming the marker layout is a stable ABI (which it is true already).
This will remove some logic while loading VMSD, and hopefully it makes it slightly easier to read. Unfortunately, we still need to keep the sender side because of the JSON blob we're maintaining..
This paves way for future processing of non-NULL markers as well.
When at it, not check "size" anymore for existing NULL markers, and move it under the same VMS_ARRAY_OF_POINTER section because that's the only place that NULL marker can happen (which guarantess size==host ptr size, which is non-zero).
--- migration/vmstate-types.c | 12 ++++------ migration/vmstate.c | 46 ++++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 26 deletions(-)
migration/vmstate-types.c +4 -8
@@ -363,14 +363,10 @@ static bool load_ptr_marker(QEMUFile *f, void *pv, size_t size, const VMStateField *field, Error **errp) { - int byte = qemu_get_byte(f); - -if (byte ==VMS_MARKER_PTR_NULL || byte ==VMS_MARKER_PTR_VALID ) { -/* TODO: process PTR_VALID case */ -return true; - } - -error_setg (errp,"%s: unexpected ptr marker: %d" , __func__, byte); +/* +* Load is done in vmstate core, see vmstate_ptr_marker_load(). +*/ +g_assert_not_reached ();return false; }
migration/vmstate.c +28 -18
64 lines not on this page · raw message
Signature · 1 line
2.51.0
[PULL 19/43] migration: drop VMStateField.err_hint
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
The field is unused, all users of VMSTATE_*_EQUAL pass _err_hint=NULL.
--- hw/block/fdc.c | 2 +- hw/display/qxl.c | 4 ++-- hw/display/vga.c | 2 +- hw/display/virtio-gpu.c | 2 +- hw/display/vmware_vga.c | 2 +- hw/i386/vmmouse.c | 2 +- hw/ide/ahci.c | 2 +- hw/intc/openpic.c | 2 +- hw/intc/spapr_xive.c | 2 +- hw/intc/xics.c | 2 +- hw/intc/xive.c | 2 +- hw/nvram/eeprom93xx.c | 2 +- hw/pci/pci.c | 2 +- hw/pci/pcie_aer.c | 2 +- hw/ppc/spapr_iommu.c | 2 +- hw/ppc/spapr_pci.c | 4 ++-- hw/ppc/spapr_vio.c | 4 ++-- hw/s390x/css.c | 6 +++--- hw/usb/hcd-uhci.c | 2 +- include/migration/vmstate.h | 36 +++++++++++++++++------------------- migration/vmstate-types.c | 15 --------------- target/ppc/machine.c | 6 +++--- 22 files changed, 44 insertions(+), 61 deletions(-)
hw/block/fdc.c +1 -1
@@ -1051,7 +1051,7 @@ const VMStateDescription vmstate_fdc = {VMSTATE_UINT8 (config, FDCtrl),VMSTATE_UINT8 (lock, FDCtrl),VMSTATE_UINT8 (pwrd, FDCtrl), -VMSTATE_UINT8_EQUAL (num_floppies, FDCtrl,NULL ), +VMSTATE_UINT8_EQUAL (num_floppies, FDCtrl),VMSTATE_STRUCT_ARRAY (drives, FDCtrl,MAX_FD ,1 , vmstate_fdrive, FDrive),VMSTATE_END_OF_LIST ()
hw/display/qxl.c +2 -2
@@ -2442,12 +2442,12 @@ static const VMStateDescription qxl_vmstate = {VMSTATE_UINT32 (last_release_offset, PCIQXLDevice),VMSTATE_UINT32 (mode, PCIQXLDevice),VMSTATE_UINT32 (ssd.unique, PCIQXLDevice), -VMSTATE_INT32_EQUAL (num_memslots, PCIQXLDevice,NULL ), +VMSTATE_INT32_EQUAL (num_memslots, PCIQXLDevice),VMSTATE_STRUCT_ARRAY (guest_slots, PCIQXLDevice,NUM_MEMSLOTS ,0 , qxl_memslot, struct guest_slots),VMSTATE_STRUCT (guest_primary.surface, PCIQXLDevice,0 , qxl_surface, QXLSurfaceCreate), -VMSTATE_INT32_EQUAL (ssd.num_surfaces, PCIQXLDevice,NULL ), +VMSTATE_INT32_EQUAL (ssd.num_surfaces, PCIQXLDevice),VMSTATE_VARRAY_INT32 (guest_surfaces.cmds, PCIQXLDevice, ssd.num_surfaces,0 , vmstate_info_uint64, uint64_t),
hw/display/vga.c +1 -1
@@ -2160,7 +2160,7 @@ const VMStateDescription vmstate_vga_common = {VMSTATE_BUFFER (palette, VGACommonState),VMSTATE_INT32 (bank_offset, VGACommonState), -VMSTATE_UINT8_EQUAL (is_vbe_vmstate, VGACommonState,NULL ), +VMSTATE_UINT8_EQUAL (is_vbe_vmstate, VGACommonState),VMSTATE_UINT16 (vbe_index, VGACommonState),VMSTATE_UINT16_ARRAY (vbe_regs, VGACommonState,VBE_DISPI_INDEX_NB ), VMSTATE_UINT32(vbe_start_addr, VGACommonState),
hw/display/virtio-gpu.c +1 -1
@@ -1223,7 +1223,7 @@ static const VMStateDescription vmstate_virtio_gpu_scanouts = { .fields = (const VMStateField[]) { VMSTATE_INT32(parent_obj.enable, struct VirtIOGPU),VMSTATE_UINT32_EQUAL (parent_obj.conf.max_outputs, - struct VirtIOGPU, NULL), + struct VirtIOGPU), VMSTATE_STRUCT_VARRAY_UINT32(parent_obj.scanout, struct VirtIOGPU, parent_obj.conf.max_outputs, 1, vmstate_virtio_gpu_scanout,
hw/display/vmware_vga.c +1 -1
@@ -1209,7 +1209,7 @@ static const VMStateDescription vmstate_vmware_vga_internal = { .minimum_version_id = 0, .post_load = vmsvga_post_load, .fields = (const VMStateField []) { - VMSTATE_INT32_EQUAL(new_depth, struct vmsvga_state_s, NULL), + VMSTATE_INT32_EQUAL(new_depth, struct vmsvga_state_s), VMSTATE_INT32(enable, struct vmsvga_state_s), VMSTATE_INT32(config, struct vmsvga_state_s), VMSTATE_INT32(cursor.id, struct vmsvga_state_s),
hw/i386/vmmouse.c +1 -1
@@ -278,7 +278,7 @@ static const VMStateDescription vmstate_vmmouse = { .minimum_version_id = 0, .post_load = vmmouse_post_load, .fields = (const VMStateField []) { -VMSTATE_INT32_EQUAL (queue_size, VMMouseState,NULL ), +VMSTATE_INT32_EQUAL (queue_size, VMMouseState),VMSTATE_UINT32_ARRAY (queue, VMMouseState,VMMOUSE_QUEUE_SIZE ),VMSTATE_UINT16 (nb_queue, VMMouseState),VMSTATE_UINT16 (status, VMMouseState),
hw/ide/ahci.c +1 -1
@@ -1795,7 +1795,7 @@ const VMStateDescription vmstate_ahci = { VMSTATE_UINT32(control_regs.impl, AHCIState), VMSTATE_UINT32(control_regs.version, AHCIState), VMSTATE_UINT32(idp_index, AHCIState), - VMSTATE_UINT32_EQUAL(ports, AHCIState, NULL), + VMSTATE_UINT32_EQUAL(ports, AHCIState),VMSTATE_END_OF_LIST () }, };
hw/intc/openpic.c +1 -1
@@ -1477,7 +1477,7 @@ static const VMStateDescription vmstate_openpic = {VMSTATE_UINT32 (max_irq, OpenPICState),VMSTATE_STRUCT_VARRAY_UINT32 (src, OpenPICState, max_irq,0 , vmstate_openpic_irqsource, IRQSource), -VMSTATE_UINT32_EQUAL (nb_cpus, OpenPICState,NULL ), +VMSTATE_UINT32_EQUAL (nb_cpus, OpenPICState),VMSTATE_STRUCT_VARRAY_UINT32 (dst, OpenPICState, nb_cpus,0 , vmstate_openpic_irqdest, IRQDest), VMSTATE_STRUCT_ARRAY(timers, OpenPICState, OPENPIC_MAX_TMR, 0,
hw/intc/spapr_xive.c +1 -1
@@ -578,7 +578,7 @@ static const VMStateDescription vmstate_spapr_xive = { .pre_save = vmstate_spapr_xive_pre_save, .post_load = NULL, /* handled at the machine level */ .fields = (const VMStateField []) { -VMSTATE_UINT32_EQUAL (nr_irqs, SpaprXive,NULL ), +VMSTATE_UINT32_EQUAL (nr_irqs, SpaprXive),VMSTATE_STRUCT_VARRAY_POINTER_UINT32 (eat, SpaprXive, nr_irqs, vmstate_spapr_xive_eas, XiveEAS), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(endt, SpaprXive, nr_ends,
hw/intc/xics.c +1 -1
@@ -668,7 +668,7 @@ static const VMStateDescription vmstate_ics = { .post_load = ics_post_load, .fields = (const VMStateField []) {/* Sanity check */ -VMSTATE_UINT32_EQUAL (nr_irqs, ICSState,NULL ), +VMSTATE_UINT32_EQUAL (nr_irqs, ICSState), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, vmstate_ics_irq,
hw/intc/xive.c +1 -1
@@ -1565,7 +1565,7 @@ static const VMStateDescription vmstate_xive_source = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField []) { -VMSTATE_UINT32_EQUAL (nr_irqs, XiveSource,NULL ), +VMSTATE_UINT32_EQUAL (nr_irqs, XiveSource),VMSTATE_VBUFFER_UINT32 (status, XiveSource,1 ,NULL , nr_irqs),VMSTATE_END_OF_LIST () },
hw/nvram/eeprom93xx.c +1 -1
@@ -144,7 +144,7 @@ static const VMStateDescription vmstate_eeprom = {VMSTATE_UINT8 (addrbits, eeprom_t),VMSTATE_UINT16_HACK_TEST (size, eeprom_t, is_old_eeprom_version),VMSTATE_UNUSED_TEST (is_old_eeprom_version,1 ), -VMSTATE_UINT16_EQUAL_V (size, eeprom_t,EEPROM_VERSION ,NULL ), +VMSTATE_UINT16_EQUAL_V (size, eeprom_t,EEPROM_VERSION ),VMSTATE_UINT16 (data, eeprom_t),VMSTATE_VARRAY_UINT16_UNSAFE (contents, eeprom_t, size,0 , vmstate_info_uint16, uint16_t),
hw/pci/pci.c +1 -1
@@ -105,7 +105,7 @@ static const VMStateDescription vmstate_pcibus = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField []) { -VMSTATE_INT32_EQUAL (nirq, PCIBus,NULL ), +VMSTATE_INT32_EQUAL (nirq, PCIBus),VMSTATE_VARRAY_INT32 (irq_count, PCIBus, nirq,0 , vmstate_info_int32, int32_t),
hw/pci/pcie_aer.c +1 -1
@@ -820,7 +820,7 @@ const VMStateDescription vmstate_pcie_aer_log = { .minimum_version_id = 1, .fields = (const VMStateField []) {VMSTATE_UINT16 (log_num, PCIEAERLog), -VMSTATE_UINT16_EQUAL (log_max, PCIEAERLog,NULL ), +VMSTATE_UINT16_EQUAL (log_max, PCIEAERLog),VMSTATE_VALIDATE ("log_num <= log_max" , pcie_aer_state_log_num_valid),VMSTATE_STRUCT_VARRAY_POINTER_UINT16 (log, PCIEAERLog, log_num, vmstate_pcie_aer_err, PCIEAERErr),
hw/ppc/spapr_iommu.c +1 -1
@@ -285,7 +285,7 @@ static const VMStateDescription vmstate_spapr_tce_table = { .post_load = spapr_tce_table_post_load, .fields = (const VMStateField []) {/* Sanity check */ -VMSTATE_UINT32_EQUAL (liobn, SpaprTceTable,NULL ), +VMSTATE_UINT32_EQUAL (liobn, SpaprTceTable),/* IOMMU state */ VMSTATE_UINT32 (mig_nb_table, SpaprTceTable),
hw/ppc/spapr_pci.c +2 -2
@@ -2051,7 +2051,7 @@ static const VMStateDescription vmstate_spapr_pci_lsi = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField []) { -VMSTATE_UINT32_EQUAL (irq, SpaprPciLsi,NULL ), +VMSTATE_UINT32_EQUAL (irq, SpaprPciLsi),VMSTATE_END_OF_LIST () },
@@ -2129,7 +2129,7 @@ static const VMStateDescription vmstate_spapr_pci = { .post_save = spapr_pci_post_save, .post_load = spapr_pci_post_load, .fields = (const VMStateField []) { -VMSTATE_UINT64_EQUAL (buid, SpaprPhbState,NULL ), +VMSTATE_UINT64_EQUAL (buid, SpaprPhbState),VMSTATE_STRUCT_ARRAY (lsi_table, SpaprPhbState,PCI_NUM_PINS ,0 , vmstate_spapr_pci_lsi, SpaprPciLsi),VMSTATE_INT32 (msi_devs_num, SpaprPhbState),
hw/ppc/spapr_vio.c +2 -2
@@ -609,8 +609,8 @@ const VMStateDescription vmstate_spapr_vio = { .minimum_version_id = 1, .fields = (const VMStateField []) {/* Sanity check */ -VMSTATE_UINT32_EQUAL (reg, SpaprVioDevice,NULL ), -VMSTATE_UINT32_EQUAL (irq, SpaprVioDevice,NULL ), +VMSTATE_UINT32_EQUAL (reg, SpaprVioDevice), +VMSTATE_UINT32_EQUAL (irq, SpaprVioDevice),/* General VIO device state */ VMSTATE_UINT64 (signal_state, SpaprVioDevice),
hw/s390x/css.c +3 -3
@@ -199,15 +199,15 @@ const VMStateDescription vmstate_subch_dev = { .post_load = subch_dev_post_load, .pre_save = subch_dev_pre_save, .fields = (const VMStateField []) { -VMSTATE_UINT8_EQUAL (cssid, SubchDev,NULL ), -VMSTATE_UINT8_EQUAL (ssid, SubchDev,NULL ), +VMSTATE_UINT8_EQUAL (cssid, SubchDev), +VMSTATE_UINT8_EQUAL (ssid, SubchDev),VMSTATE_UINT16 (migrated_schid, SubchDev),/* * If devno mismatch on target, it may be due to some * sequences of plug and unplug breaks migration for * machine versions prior to 2.7 (known design flaw). */ -VMSTATE_UINT16_EQUAL (devno, SubchDev,NULL ), +VMSTATE_UINT16_EQUAL (devno, SubchDev),VMSTATE_BOOL (thinint_active, SubchDev),VMSTATE_STRUCT (curr_status, SubchDev,0 , vmstate_schib,SCHIB ),VMSTATE_UINT8_ARRAY (sense_data, SubchDev,32 ),
hw/usb/hcd-uhci.c +1 -1
@@ -364,7 +364,7 @@ static const VMStateDescription vmstate_uhci = { .post_load = uhci_post_load, .fields = (const VMStateField []) {VMSTATE_PCI_DEVICE (dev, UHCIState), -VMSTATE_UINT8_EQUAL (num_ports_vmstate, UHCIState,NULL ), +VMSTATE_UINT8_EQUAL (num_ports_vmstate, UHCIState),VMSTATE_STRUCT_ARRAY (ports, UHCIState,UHCI_PORTS ,1 , vmstate_uhci_port, UHCIPort),VMSTATE_UINT16 (cmd, UHCIState),
include/migration/vmstate.h +17 -19
@@ -182,7 +182,6 @@ typedef enum {struct VMStateField {const char *name; -const char *err_hint;size_t offset;size_t size;size_t start;
@@ -358,9 +357,8 @@ extern const VMStateInfo vmstate_info_qlist; }#define VMSTATE_SINGLE_FULL (_field, _state, _test, _version, _info, \ - _type, _err_hint) { \ + _type) { \ .name = (stringify(_field)), \ - .err_hint = (_err_hint), \ .version_id = (_version), \ .field_exists = (_test), \ .size = sizeof(_type), \
@@ -1022,35 +1020,35 @@ extern const VMStateInfo vmstate_info_qlist;#endif -#define VMSTATE_UINT8_EQUAL (_f, _s, _err_hint) \ +#define VMSTATE_UINT8_EQUAL (_f, _s) \ VMSTATE_SINGLE_FULL(_f, _s, 0, 0, \ - vmstate_info_uint8_equal, uint8_t, _err_hint) + vmstate_info_uint8_equal, uint8_t) -#define VMSTATE_UINT16_EQUAL (_f, _s, _err_hint) \ +#define VMSTATE_UINT16_EQUAL (_f, _s) \ VMSTATE_SINGLE_FULL(_f, _s, 0, 0, \ - vmstate_info_uint16_equal, uint16_t, _err_hint) + vmstate_info_uint16_equal, uint16_t) -#define VMSTATE_UINT16_EQUAL_V (_f, _s, _v, _err_hint) \ +#define VMSTATE_UINT16_EQUAL_V (_f, _s, _v) \ VMSTATE_SINGLE_FULL(_f, _s, 0, _v, \ - vmstate_info_uint16_equal, uint16_t, _err_hint) + vmstate_info_uint16_equal, uint16_t) -#define VMSTATE_INT32_EQUAL (_f, _s, _err_hint) \ +#define VMSTATE_INT32_EQUAL (_f, _s) \ VMSTATE_SINGLE_FULL(_f, _s, 0, 0, \ - vmstate_info_int32_equal, int32_t, _err_hint) + vmstate_info_int32_equal, int32_t) -#define VMSTATE_UINT32_EQUAL_V (_f, _s, _v, _err_hint) \ +#define VMSTATE_UINT32_EQUAL_V (_f, _s, _v) \ VMSTATE_SINGLE_FULL(_f, _s, 0, _v, \ - vmstate_info_uint32_equal, uint32_t, _err_hint) + vmstate_info_uint32_equal, uint32_t) -#define VMSTATE_UINT32_EQUAL (_f, _s, _err_hint) \ - VMSTATE_UINT32_EQUAL_V(_f, _s, 0, _err_hint) +#define VMSTATE_UINT32_EQUAL (_f, _s) \ + VMSTATE_UINT32_EQUAL_V(_f, _s, 0) -#define VMSTATE_UINT64_EQUAL_V (_f, _s, _v, _err_hint) \ +#define VMSTATE_UINT64_EQUAL_V (_f, _s, _v) \ VMSTATE_SINGLE_FULL(_f, _s, 0, _v, \ - vmstate_info_uint64_equal, uint64_t, _err_hint) + vmstate_info_uint64_equal, uint64_t) -#define VMSTATE_UINT64_EQUAL (_f, _s, _err_hint) \ - VMSTATE_UINT64_EQUAL_V(_f, _s, 0, _err_hint) +#define VMSTATE_UINT64_EQUAL (_f, _s) \ + VMSTATE_UINT64_EQUAL_V(_f, _s, 0)#define VMSTATE_INT32_POSITIVE_LE (_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t)
migration/vmstate-types.c +0 -15
@@ -131,9 +131,6 @@ static int get_int32_equal(QEMUFile *f, void *pv, size_t size,return 0 ; }error_report ("%" PRIx32" != %" PRIx32, *v, v2); -if (field->err_hint) { -error_printf ("%s\n" , field->err_hint); - }return -EINVAL ; }
@@ -280,9 +277,6 @@ static int get_uint32_equal(QEMUFile *f, void *pv, size_t size,return 0 ; }error_report ("%" PRIx32" != %" PRIx32, *v, v2); -if (field->err_hint) { -error_printf ("%s\n" , field->err_hint); - }return -EINVAL ; }
@@ -391,9 +385,6 @@ static int get_uint64_equal(QEMUFile *f, void *pv, size_t size,return 0 ; }error_report ("%" PRIx64" != %" PRIx64, *v, v2); -if (field->err_hint) { -error_printf ("%s\n" , field->err_hint); - }return -EINVAL ; }
@@ -417,9 +408,6 @@ static int get_uint8_equal(QEMUFile *f, void *pv, size_t size,return 0 ; }error_report ("%x != %x" , *v, v2); -if (field->err_hint) { -error_printf ("%s\n" , field->err_hint); - }return -EINVAL ; }
@@ -443,9 +431,6 @@ static int get_uint16_equal(QEMUFile *f, void *pv, size_t size,return 0 ; }error_report ("%x != %x" , *v, v2); -if (field->err_hint) { -error_printf ("%s\n" , field->err_hint); - }return -EINVAL ; }
target/ppc/machine.c +3 -3
@@ -564,7 +564,7 @@ static const VMStateDescription vmstate_tlb6xx = { .minimum_version_id = 1, .needed = tlb6xx_needed, .fields = (const VMStateField []) { -VMSTATE_INT32_EQUAL (env.nb_tlb, PowerPCCPU,NULL ), +VMSTATE_INT32_EQUAL (env.nb_tlb, PowerPCCPU), VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlb6, PowerPCCPU, env.nb_tlb, vmstate_tlb6xx_entry,
@@ -603,7 +603,7 @@ static const VMStateDescription vmstate_tlbemb = { .minimum_version_id = 1, .needed = tlbemb_needed, .fields = (const VMStateField []) { -VMSTATE_INT32_EQUAL (env.nb_tlb, PowerPCCPU,NULL ), +VMSTATE_INT32_EQUAL (env.nb_tlb, PowerPCCPU), VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbe, PowerPCCPU, env.nb_tlb, vmstate_tlbemb_entry,
@@ -639,7 +639,7 @@ static const VMStateDescription vmstate_tlbmas = { .minimum_version_id = 1, .needed = tlbmas_needed, .fields = (const VMStateField []) { -VMSTATE_INT32_EQUAL (env.nb_tlb, PowerPCCPU,NULL ), +VMSTATE_INT32_EQUAL (env.nb_tlb, PowerPCCPU), VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbm, PowerPCCPU, env.nb_tlb, vmstate_tlbmas_entry,
Signature · 1 line
2.51.0
[PULL 30/43] vmstate: Allow vmstate_info_nullptr to emit non-NULL markers
In reply to Fabiano RosasFrom: Peter Xu <[email protected]>
We used to have one vmstate called "nullptr" which is only used to generate one-byte hint to say one pointer is NULL.
Let's extend its use so that it will generate another byte to say the pointer is non-NULL.
With that, the name of the info struct (or functions) do not apply anymore. Update correspondingly.
Update analyze-migration.py to work with the new layout.
No functional change intended yet.
--- include/migration/vmstate.h | 9 +++++++-- migration/vmstate-types.c | 34 ++++++++++++++++------------------ migration/vmstate.c | 25 +++++++++++++------------ scripts/analyze-migration.py | 22 ++++++++++++---------- 4 files changed, 48 insertions(+), 42 deletions(-)
include/migration/vmstate.h +7 -2
@@ -282,9 +282,14 @@ extern const VMStateInfo vmstate_info_uint32;extern const VMStateInfo vmstate_info_uint64;extern const VMStateInfo vmstate_info_fd; -/** Put this in the stream when migrating a null pointer.*/ +/* +* Put this in the stream when migrating a pointer to reflect either a NULL +* or valid pointer. +*/ #define VMS_MARKER_PTR_NULL (0x30U)/* '0' */ -extern const VMStateInfo vmstate_info_nullptr; +#define VMS_MARKER_PTR_VALID (0x31U)/* '1' */ + +extern const VMStateInfo vmstate_info_ptr_marker;extern const VMStateInfo vmstate_info_cpudouble;
migration/vmstate-types.c +16 -18
@@ -359,36 +359,34 @@ const VMStateInfo vmstate_info_fd = { .save = save_fd, }; -static bool load_nullptr (QEMUFile *f,void *pv,size_t size, -const VMStateField *field,Error **errp) +static bool load_ptr_marker (QEMUFile *f,void *pv,size_t size, +const VMStateField *field,Error **errp) { -if (qemu_get_byte (f) ==VMS_MARKER_PTR_NULL ) { +int byte =qemu_get_byte (f); + +if (byte ==VMS_MARKER_PTR_NULL || byte ==VMS_MARKER_PTR_VALID ) { +/* TODO: process PTR_VALID case */ return true; } -error_setg (errp,"vmstate: load_nullptr expected VMS_NULLPTR_MARKER" ); +error_setg (errp,"%s: unexpected ptr marker: %d" , __func__, byte);return false; } -static bool save_nullptr (QEMUFile *f,void *pv,size_t size, -const VMStateField *field,JSONWriter *vmdesc, -Error **errp) +static bool save_ptr_marker (QEMUFile *f,void *pv,size_t size, +const VMStateField *field,JSONWriter *vmdesc, +Error **errp) { -if (pv ==NULL ) { -qemu_put_byte (f,VMS_MARKER_PTR_NULL ); -return true; - } - -error_setg (errp,"vmstate: save_nullptr must be called with pv == NULL" ); -return false; +qemu_put_byte (f, pv ?VMS_MARKER_PTR_VALID :VMS_MARKER_PTR_NULL ); +return true; } -const VMStateInfo vmstate_info_nullptr = { - .name ="nullptr" , - .load = load_nullptr, - .save = save_nullptr, +const VMStateInfo vmstate_info_ptr_marker = { + .name ="ptr-marker" , + .load = load_ptr_marker, + .save = save_ptr_marker, };/* 64 bit unsigned int. See that the received value is the same than the one
migration/vmstate.c +13 -12
@@ -55,12 +55,12 @@ vmstate_field_exists(const VMStateDescription *vmsd, const VMStateField *field, }/* -* Create a fake nullptr field when there's a NULL pointer detected in the +* Create a ptr marker field when there's a NULL pointer detected in the * array of a VMS_ARRAY_OF_POINTER VMSD field. It's needed because we * can't dereference the NULL pointer. */ static const VMStateField * -vmsd_create_fake_nullptr_field (const VMStateField *field) +vmsd_create_ptr_marker_field (const VMStateField *field) {VMStateField *fake =g_new0 (VMStateField,1 );
@@ -71,12 +71,12 @@ vmsd_create_fake_nullptr_field(const VMStateField *field) fake->name = field->name; fake->version_id = field->version_id; -/* Do not need "field_exists" check as it always exists (which is null) */ +/* Do not need "field_exists" check as it always exists */ fake->field_exists =NULL ; -/* See vmstate_info_nullptr - use 1 byte to represent nullptr */ +/* See vmstate_info_ptr_marker - use 1 byte to represent ptr status */ fake->size =1 ; - fake->info = &vmstate_info_nullptr; + fake->info = &vmstate_info_ptr_marker; fake->flags =VMS_SINGLE ;/* All the rest fields shouldn't matter.. */
@@ -278,7 +278,7 @@ bool vmstate_load_vmsd(QEMUFile *f, const VMStateDescription *vmsd,* an array of pointers), use null placeholder and do * not follow. */ - inner_field =vmsd_create_fake_nullptr_field (field); + inner_field =vmsd_create_ptr_marker_field (field); }else { inner_field = field; }
@@ -583,26 +583,27 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,for (i =0 ; i < n_elems; i++) {void *curr_elem = first_elem + size * i;const VMStateField *inner_field; -bool is_null;/* maximum number of elements to compress in the JSON blob */ int max_elems =vmsd_can_compress (field) ? (n_elems - i) :1 ; +bool use_marker_field, is_null;if (field->flags &VMS_ARRAY_OF_POINTER ) {assert (curr_elem); curr_elem = *(void **)curr_elem; } -if (!curr_elem && size) { + is_null = !curr_elem && size; + use_marker_field = is_null; + +if (use_marker_field) {/* * If null pointer found (which should only happen in * an array of pointers), use null placeholder and do * not follow. */ - inner_field =vmsd_create_fake_nullptr_field (field); - is_null = true; + inner_field =vmsd_create_ptr_marker_field (field); }else { inner_field = field; - is_null = false; }/*
@@ -638,7 +639,7 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd, i, max_elems, errp);/* If we used a fake temp field.. free it now */ -if (is_null) { +if (use_marker_field) {g_clear_pointer ((gpointer *)&inner_field, g_free); }
scripts/analyze-migration.py +12 -10
@@ -469,26 +469,26 @@ def __init__(self, desc, file): super(VMSDFieldIntLE, self).__init__(desc, file) self.dtype = '<i%d' % self.size -class VMSDFieldNull(VMSDFieldGeneric): +class VMSDFieldPtrMarker(VMSDFieldGeneric): NULL_PTR_MARKER = b'0' + VALID_PTR_MARKER = b'1' def __init__(self, desc, file): - super(VMSDFieldNull, self).__init__(desc, file) + super(VMSDFieldPtrMarker, self).__init__(desc, file) def __repr__(self): - # A NULL pointer is encoded in the stream as a '0' to - # disambiguate from a mere 0x0 value and avoid consumers - # trying to follow the NULL pointer. Displaying '0', 0x30 or - # 0x0 when analyzing the JSON debug stream could become + # A NULL / non-NULL pointer may be encoded in the stream as a + # '0'/'1' to represent the status of the pointer. Displaying '0', + # 0x30 or 0x0 when analyzing the JSON debug stream could become # confusing, so use an explicit term instead. - return "nullptr" + return "null-ptr" if self.data == self.NULL_PTR_MARKER else "valid-ptr" def __str__(self): return self.__repr__() def read(self): - super(VMSDFieldNull, self).read() - assert(self.data == self.NULL_PTR_MARKER) + super(VMSDFieldPtrMarker, self).read() + assert(self.data in [self.NULL_PTR_MARKER, self.VALID_PTR_MARKER]) return self.data class VMSDFieldBool(VMSDFieldGeneric):
@@ -642,7 +642,9 @@ def getDict(self): "bitmap" : VMSDFieldGeneric, "struct" : VMSDFieldStruct, "capability": VMSDFieldCap, - "nullptr": VMSDFieldNull, + # Keep the old nullptr for old binaries + "nullptr": VMSDFieldPtrMarker, + "ptr-marker": VMSDFieldPtrMarker, "unknown" : VMSDFieldGeneric, }
Signature · 1 line
2.51.0
[PULL 29/43] vmstate: Introduce vmstate_save_field_with_vmdesc()
In reply to Fabiano RosasFrom: Peter Xu <[email protected]>
Introduce a helper to do both the JSON blob generations and save vmstate. This further shrinks the function a bit. More importantly, we'll need to save two fields in one loop very soon in the future with the JSON blob.
--- migration/vmstate.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-)
migration/vmstate.c +32 -13
@@ -514,6 +514,35 @@ static bool vmstate_save_field(QEMUFile *f, void *pv, size_t size,return true; } +/* +* Save a whole VMSD field, including its JSON blob separately when @vmdesc +* is specified. +*/ +static inline bool +vmstate_save_field_with_vmdesc (QEMUFile *f,void *pv,size_t size, +const VMStateDescription *vmsd, +const VMStateField *field,JSONWriter *vmdesc, +int i,int max,Error **errp) +{ +uint64_t old_offset, written_bytes; +bool ok; + +vmsd_desc_field_start (vmsd, vmdesc, field, i, max); + + old_offset =qemu_file_transferred (f); + ok =vmstate_save_field (f, pv, size, field, vmdesc, errp); + written_bytes =qemu_file_transferred (f) - old_offset; + +vmsd_desc_field_end (vmsd, vmdesc, field, written_bytes); + +if (!ok) { +error_prepend (errp,"Save of field %s/%s failed: " , + vmsd->name, field->name); + } + +return ok; +} +static bool vmstate_save_vmsd_v (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,JSONWriter *vmdesc,int version_id,Error **errp)
@@ -542,7 +571,6 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,void *first_elem = opaque + field->offset;int i, n_elems =vmstate_n_elems (opaque, field);int size =vmstate_size (opaque, field); -uint64_t old_offset, written_bytes;JSONWriter *vmdesc_loop = vmdesc;bool is_prev_null = false;
@@ -559,7 +587,6 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,/* 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 ) {assert (curr_elem); curr_elem = *(void **)curr_elem;
@@ -606,15 +633,9 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd, } } -vmsd_desc_field_start (vmsd, vmdesc_loop, inner_field, - i, max_elems); - - ok =vmstate_save_field (f, curr_elem, size, inner_field, - vmdesc_loop, errp); - - written_bytes =qemu_file_transferred (f) - old_offset; -vmsd_desc_field_end (vmsd, vmdesc_loop, inner_field, - written_bytes); + ok =vmstate_save_field_with_vmdesc (f, curr_elem, size, vmsd, + inner_field, vmdesc_loop, + i, max_elems, errp);/* If we used a fake temp field.. free it now */ if (is_null) {
@@ -622,8 +643,6 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd, }if (!ok) { -error_prepend (errp,"Save of field %s/%s failed: " , - vmsd->name, field->name); goto out; }
Signature · 1 line
2.51.0
[PULL 35/43] migration: validate page_size in mapped-ram header before use
In reply to Fabiano RosasFrom: Trieu Huynh <[email protected]>
mapped_ram_read_header() reads page_size from the migration stream and stores it in MappedRamHeader, but does not validate that the value is non-zero before it is later used in parse_ramblock_mapped_ram():
num_pages = length / header.page_size;
If a corrupted or malformed migration stream provides invalid, guest resumes either with corrupted memory or crashes unexpectedly (eg. page_size = 0)
Add validation in mapped_ram_read_header() to reject invalid page_size values early and return an error instead of continuing with an invalid header.
Steps to reproduce:
Create a migration snapshot with mapped-ram enabled:
(qemu) migrate_set_capability mapped-ram on
(qemu) migrate file:/tmp/qemu-snapshots/snapshot.bin
Modify the snapshot so that MappedRamHeader.page_size becomes diff with target psize. (0/512/8192/1GB).
Restore the snapshot:
(qemu) migrate_set_capability mapped-ram on
(qemu) migrate_incoming file:/tmp/qemu-snapshots/snapshot.bin
As-is:
* [0]: Floating point exception (core dumped)
* [512/8192]: Silent corruption
* [1GB]: "post load hook failed for: kvm-tpr-opt" (EPERM) To-be:
* All: qemu-system-x86_64: Migration mapped-ram header has invalid page_size [val] (expected 4096)
--- migration/ram.c | 6 ++++++ 1 file changed, 6 insertions(+)
migration/ram.c +6 -0
@@ -3088,6 +3088,12 @@ static bool mapped_ram_read_header(QEMUFile *file, MappedRamHeader *header, } header->page_size =be64_to_cpu (header->page_size); +if (header->page_size !=TARGET_PAGE_SIZE ) { +error_setg (errp,"Migration mapped-ram header has invalid " +"page_size %" PRIu64" (expected %d)" , + header->page_size,TARGET_PAGE_SIZE ); +return false; + } header->bitmap_offset =be64_to_cpu (header->bitmap_offset); header->pages_offset =be64_to_cpu (header->pages_offset);
Signature · 1 line
2.51.0
[PULL 39/43] tests/unit: add pread/pwrite _all tests for io channel file
In reply to Fabiano RosasFrom: Junjie Cao <[email protected]>
Add unit tests for the new qio_channel_pread{v,}_all{,_eof}() and qio_channel_pwrite{v,}_all() APIs.
The basic tests write data to a file channel, then read it back at various offsets using both the single-buffer and iovec variants to make sure the round-trip produces identical content. The _eof tests verify all three return cases -- full read (1), clean EOF (0), and partial-then-EOF (-1 with error set) -- and check that the strict wrappers (preadv_all / pread_all) treat a clean EOF as an error.
All tests are guarded by CONFIG_PREADV since the underlying channel methods require preadv(2).
--- tests/unit/test-io-channel-file.c | 207 ++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+)
tests/unit/test-io-channel-file.c +207 -0
@@ -102,6 +102,203 @@ static void test_io_channel_fd(void) } +#ifdef CONFIG_PREADV +static void test_io_channel_pread_all (void ) +{ +QIOChannel *ioc; +char write_buf[] ="Hello World, pread_all" ; +char read_buf[sizeof (write_buf)] = {0 }; +int ret; + +unlink (TEST_FILE ); + ioc =QIO_CHANNEL (qio_channel_file_new_path ( +TEST_FILE , +O_RDWR |O_CREAT |O_TRUNC |O_BINARY , +TEST_MASK , + &error_abort)); + + ret =qio_channel_pwrite_all (ioc, write_buf,sizeof (write_buf), +0 , &error_abort); + g_assert_cmpint(ret, ==, 0); + +/* Read back at offset 0 */ + ret =qio_channel_pread_all (ioc, read_buf,sizeof (read_buf), +0 , &error_abort); + g_assert_cmpint(ret, ==, 0); +g_assert_cmpmem (write_buf,sizeof (write_buf), + read_buf,sizeof (read_buf)); + +/* Read at a non-zero offset */ +memset (read_buf,0 ,sizeof (read_buf)); + ret =qio_channel_pread_all (ioc, read_buf,sizeof (write_buf) -7 , +7 , &error_abort); + g_assert_cmpint(ret, ==, 0); +g_assert_cmpmem (write_buf +7 ,sizeof (write_buf) -7 , + read_buf,sizeof (write_buf) -7 ); + +unlink (TEST_FILE ); +object_unref (OBJECT (ioc)); +} + +static void test_io_channel_preadv_all (void ) +{ +QIOChannel *ioc; +char write_buf[256 ]; +char read_buf[256 ] = {0 }; +struct iovec write_iov[2 ]; +struct iovec read_iov[2 ]; +int ret; +size_t i; + +for (i =0 ; i <sizeof (write_buf); i++) { + write_buf[i] = i &0xff ; + } + +unlink (TEST_FILE ); + ioc =QIO_CHANNEL (qio_channel_file_new_path ( +TEST_FILE , +O_RDWR |O_CREAT |O_TRUNC |O_BINARY , +TEST_MASK , + &error_abort)); + +/* Write using pwritev_all with 2 iovecs */ + write_iov[0 ].iov_base = write_buf; + write_iov[0 ].iov_len =128 ; + write_iov[1 ].iov_base = write_buf +128 ; + write_iov[1 ].iov_len =128 ; + ret =qio_channel_pwritev_all (ioc, write_iov,2 ,0 , &error_abort); + g_assert_cmpint(ret, ==, 0); + +/* Read back using preadv_all with 2 iovecs */ + read_iov[0 ].iov_base = read_buf; + read_iov[0 ].iov_len =128 ; + read_iov[1 ].iov_base = read_buf +128 ; + read_iov[1 ].iov_len =128 ; + ret =qio_channel_preadv_all (ioc, read_iov,2 ,0 , &error_abort); + g_assert_cmpint(ret, ==, 0); + +g_assert_cmpmem (write_buf,sizeof (write_buf), + read_buf,sizeof (read_buf)); + +/* Read at non-zero offset with preadv_all */ +memset (read_buf,0 ,sizeof (read_buf)); + read_iov[0 ].iov_base = read_buf; + read_iov[0 ].iov_len =64 ; + read_iov[1 ].iov_base = read_buf +64 ; + read_iov[1 ].iov_len =64 ; + ret =qio_channel_preadv_all (ioc, read_iov,2 ,128 , &error_abort); + g_assert_cmpint(ret, ==, 0); + +g_assert_cmpmem (write_buf +128 ,128 , + read_buf,128 ); + +unlink (TEST_FILE ); +object_unref (OBJECT (ioc)); +} + +static void test_io_channel_preadv_all_eof (void ) +{ +QIOChannel *ioc; +char write_buf[] ="Hello World, preadv_all_eof" ; +char read_buf[sizeof (write_buf)] = {0 }; +struct iovec iov; +int ret; +Error *err =NULL ; + +unlink (TEST_FILE ); + ioc =QIO_CHANNEL (qio_channel_file_new_path ( +TEST_FILE , +O_RDWR |O_CREAT |O_TRUNC |O_BINARY , +TEST_MASK , + &error_abort)); + + ret =qio_channel_pwrite_all (ioc, write_buf,sizeof (write_buf), +0 , &error_abort); + g_assert_cmpint(ret, ==, 0); + +/* Full read succeeds: should return 1 */ + iov.iov_base = read_buf; + iov.iov_len =sizeof (read_buf); + ret =qio_channel_preadv_all_eof (ioc, &iov,1 ,0 , &error_abort); + g_assert_cmpint(ret, ==, 1); +g_assert_cmpmem (write_buf,sizeof (write_buf), + read_buf,sizeof (read_buf)); + +/* Clean EOF: offset at file end, should return 0 */ + iov.iov_base = read_buf; + iov.iov_len =1 ; + ret =qio_channel_preadv_all_eof (ioc, &iov,1 , +sizeof (write_buf), &err); + g_assert_cmpint(ret, ==, 0); +g_assert_null (err); + +/* Partial EOF: start before end, request extends past */ + iov.iov_base = read_buf; + iov.iov_len =8 ; + ret =qio_channel_preadv_all_eof (ioc, &iov,1 , +sizeof (write_buf) -4 , &err); + g_assert_cmpint(ret, ==, -1); +g_assert_nonnull (err); +error_free (err); + err =NULL ; + +/* Strict wrapper (preadv_all) treats clean EOF as error */ + iov.iov_base = read_buf; + iov.iov_len =1 ; + ret =qio_channel_preadv_all (ioc, &iov,1 , +sizeof (write_buf), &err); + g_assert_cmpint(ret, ==, -1); +g_assert_nonnull (err); +error_free (err); + +unlink (TEST_FILE ); +object_unref (OBJECT (ioc)); +} + +static void test_io_channel_pread_all_eof (void ) +{ +QIOChannel *ioc; +char write_buf[] ="Hello World, pread_all_eof" ; +char read_buf[sizeof (write_buf)] = {0 }; +int ret; +Error *err =NULL ; + +unlink (TEST_FILE ); + ioc =QIO_CHANNEL (qio_channel_file_new_path ( +TEST_FILE , +O_RDWR |O_CREAT |O_TRUNC |O_BINARY , +TEST_MASK , + &error_abort)); + + ret =qio_channel_pwrite_all (ioc, write_buf,sizeof (write_buf), +0 , &error_abort); + g_assert_cmpint(ret, ==, 0); + +/* Full read succeeds: should return 1 */ + ret =qio_channel_pread_all_eof (ioc, read_buf,sizeof (read_buf), +0 , &error_abort); + g_assert_cmpint(ret, ==, 1); +g_assert_cmpmem (write_buf,sizeof (write_buf), + read_buf,sizeof (read_buf)); + +/* Clean EOF: should return 0 */ + ret =qio_channel_pread_all_eof (ioc, read_buf,1 , +sizeof (write_buf), &err); + g_assert_cmpint(ret, ==, 0); +g_assert_null (err); + +/* Partial EOF: should return -1 */ + ret =qio_channel_pread_all_eof (ioc, read_buf,8 , +sizeof (write_buf) -4 , &err); + g_assert_cmpint(ret, ==, -1); +g_assert_nonnull (err); +error_free (err); + +unlink (TEST_FILE ); +object_unref (OBJECT (ioc)); +} +#endif /* CONFIG_PREADV */ +#ifndef _WIN32static void test_io_channel_pipe (bool async) {
@@ -147,6 +344,16 @@ int main(int argc, char **argv)g_test_add_func ("/io/channel/file" , test_io_channel_file);g_test_add_func ("/io/channel/file/rdwr" , test_io_channel_file_rdwr);g_test_add_func ("/io/channel/file/fd" , test_io_channel_fd); +#ifdef CONFIG_PREADV +g_test_add_func ("/io/channel/file/pread-all" , + test_io_channel_pread_all); +g_test_add_func ("/io/channel/file/preadv-all" , + test_io_channel_preadv_all); +g_test_add_func ("/io/channel/file/preadv-all-eof" , + test_io_channel_preadv_all_eof); +g_test_add_func ("/io/channel/file/pread-all-eof" , + test_io_channel_pread_all_eof); +#endif #ifndef _WIN32g_test_add_func ("/io/channel/pipe/sync" , test_io_channel_pipe_sync);g_test_add_func ("/io/channel/pipe/async" , test_io_channel_pipe_async);
Signature · 1 line
2.51.0
[PULL 32/43] vmstate: Implement VMS_ARRAY_OF_POINTER_AUTO_ALLOC
In reply to Fabiano RosasFrom: Peter Xu <[email protected]>
Introduce a new flag, VMS_ARRAY_OF_POINTER_AUTO_ALLOC, for VMSD field. It must be used together with VMS_ARRAY_OF_POINTER.
It can be used to allow migration of an array of pointers where the pointers may point to NULLs.
Note that we used to allow migration of a NULL pointer within an array that is being migrated. That corresponds to the code around vmstate_info_nullptr where we may get/put one byte showing that the element of an array is NULL.
That usage is fine but very limited, it's because even if it will migrate a NULL pointer with a marker, it still works in a way that both src and dest QEMUs must know exactly which elements of the array are non-NULL, so instead of dynamically loading an array (which can have NULL pointers), it actually only verifies the known NULL pointers are still NULL pointers after migration.
Also, in that case since dest QEMU knows exactly which element is NULL, which is not NULL, dest QEMU's device code will manage all allocations for the elements before invoking vmstate_load_vmsd().
That's not enough per evolving needs of new device states that may want to provide real dynamic array of pointers, like what Alexander proposed here with the NVMe device migration:
https://lore.kernel.org/r/[email protected]
This patch is an alternative approach to address the problem.
Along with the flag, introduce two new macros:
VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8|32}_ALLOC()
Which will be used very soon in the NVMe series.
--- include/migration/vmstate.h | 51 ++++++++++++- migration/savevm.c | 27 ++++++- migration/vmstate.c | 145 ++++++++++++++++++++++++++++++------ 3 files changed, 199 insertions(+), 24 deletions(-)
include/migration/vmstate.h +50 -1
@@ -161,8 +161,21 @@ enum VMStateFlags {* structure we are referencing to use. */ VMS_VSTRUCT =0x8000 , +/* +* This is a sub-flag for VMS_ARRAY_OF_POINTER. When this flag is set, +* VMS_ARRAY_OF_POINTER must also be set. When set, it means array +* elements can contain either valid or NULL pointers, vmstate core +* will be responsible for synchronizing the pointer status, providing +* proper memory allocations on the pointer when it is populated on the +* source QEMU. It also means the user of the field must make sure all +* the elements in the array are NULL pointers before loading. This +* should also work with VMS_ALLOC when the array itself also needs to +* be allocated. +*/ +VMS_ARRAY_OF_POINTER_AUTO_ALLOC =0x10000 , +/* Marker for end of list */ -VMS_END =0x10000 + VMS_END = 0x20000, }; typedef enum {
@@ -580,6 +593,42 @@ extern const VMStateInfo vmstate_info_qlist; .offset = vmstate_offset_array(_s, _f, _type*, _n), \ } +/* +* For migrating a dynamically allocated uint{8,32}-indexed array of +* pointers to structures (with NULL entries and with auto memory +* allocation). +* +* _type: type of structure pointed to +* _vmsd: VMSD for structure _type (when VMS_STRUCT is set) +* _info: VMStateInfo for _type (when VMS_STRUCT is not set) +* start: size of (_type) 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), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_POINTER | VMS_VARRAY_UINT8 | \ + VMS_ARRAY_OF_POINTER | VMS_STRUCT | \ + VMS_ARRAY_OF_POINTER_AUTO_ALLOC, \ + .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), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_POINTER | VMS_VARRAY_UINT32 | \ + VMS_ARRAY_OF_POINTER | VMS_STRUCT | \ + VMS_ARRAY_OF_POINTER_AUTO_ALLOC, \ + .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), \
migration/savevm.c +26 -1
@@ -869,8 +869,33 @@ static void vmstate_check(const VMStateDescription *vmsd)if (field) {while (field->name) {if (field->flags &VMS_ARRAY_OF_POINTER ) { -assert (field->size ==0 ); +if (field->flags &VMS_ARRAY_OF_POINTER_AUTO_ALLOC ) { +/* +* Size must be provided because dest QEMU needs that +* info to know what to allocate +*/ +assert (field->size || field->size_offset); + }else { +/* +* Otherwise size info isn't useful (because it's +* always the size of host pointer), detect accidental +* setup of sizes in this case. +*/ +assert (field->size ==0 && field->size_offset ==0 ); + } +/* +* VMS_ARRAY_OF_POINTER must be used only together with one +* of VMS_(V)ARRAY* flags. +*/ +assert (field->flags & (VMS_ARRAY |VMS_VARRAY_INT32 | +VMS_VARRAY_UINT16 |VMS_VARRAY_UINT8 | +VMS_VARRAY_UINT32 )); } + +if (field->flags &VMS_ARRAY_OF_POINTER_AUTO_ALLOC ) { +assert (field->flags &VMS_ARRAY_OF_POINTER ); + } +if (field->flags & (VMS_STRUCT |VMS_VSTRUCT )) {/* Recurse to sub structures */ vmstate_check (field->vmsd);
migration/vmstate.c +123 -22
194 lines not on this page · raw message
Signature · 1 line
2.51.0
[PULL 40/43] tests/qtest/migration: fix fd leak in ufd_version_check
In reply to Fabiano RosasFrom: Trieu Huynh <[email protected]>
ufd_version_check() opens a userfaultfd via uffd_open() but never closes it on any path where the open succeeded: the UFFDIO_API failure path, the missing-ioctls path, and the success path all returned without calling close(ufd).
Convert to a goto-out pattern consistent with uffd_open() used in util/userfaultfd.c and migration/postcopy-ram.c, ensuring the fd is always closed before returning.
--- tests/qtest/migration/migration-util.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
tests/qtest/migration/migration-util.c +7 -3
@@ -343,6 +343,7 @@ bool ufd_version_check(bool *uffd_feature_thread_id) {struct uffdio_api api_struct;uint64_t ioctl_mask; +bool ret = false;int ufd =uffd_open (O_CLOEXEC );
@@ -355,7 +356,7 @@ bool ufd_version_check(bool *uffd_feature_thread_id) api_struct.features =0 ;if (ioctl (ufd,UFFDIO_API , &api_struct)) {g_test_message ("Skipping test: UFFDIO_API failed" ); -return false; + goto release_ufd; }if (uffd_feature_thread_id) {
@@ -366,10 +367,13 @@ bool ufd_version_check(bool *uffd_feature_thread_id) 1ULL << _UFFDIO_UNREGISTER);if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) {g_test_message ("Skipping test: Missing userfault feature" ); -return false; + goto release_ufd; } - return true; + ret = true; +release_ufd: +close (ufd); + return ret; }#else bool ufd_version_check (bool *uffd_feature_thread_id)
Signature · 1 line
2.51.0
[PULL 24/43] vmstate: Pass in struct itself for VMSTATE_ARRAY_OF_POINTER
In reply to Fabiano RosasFrom: Peter Xu <[email protected]>
Passing in a pointer almost never helps. Convert it to pass in struct for further refactoring on VMS_ARRAY_OF_POINTER.
--- include/migration/vmstate.h | 6 +++--- tests/unit/test-vmstate.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-)
include/migration/vmstate.h +3 -3
@@ -547,9 +547,9 @@ extern const VMStateInfo vmstate_info_qlist; .version_id = (_version), \ .num = (_num), \ .info = &(_info), \ - .size = sizeof(_type), \ + .size = sizeof(_type *), \ .flags = VMS_ARRAY|VMS_ARRAY_OF_POINTER, \ - .offset = vmstate_offset_array(_state, _field, _type, _num), \ + .offset = vmstate_offset_array(_state, _field, _type *, _num), \ }#define VMSTATE_ARRAY_OF_POINTER_TO_STRUCT (_f, _s, _n, _v, _vmsd, _type) { \
@@ -1093,7 +1093,7 @@ extern const VMStateInfo vmstate_info_qlist; VMSTATE_TIMER_PTR_V(_f, _s, 0)#define VMSTATE_TIMER_PTR_ARRAY (_f, _s, _n) \ - VMSTATE_ARRAY_OF_POINTER(_f, _s, _n, 0, vmstate_info_timer, QEMUTimer *) + VMSTATE_ARRAY_OF_POINTER(_f, _s, _n, 0, vmstate_info_timer, QEMUTimer)#define VMSTATE_TIMER_TEST (_f, _s, _test) \ VMSTATE_SINGLE_TEST(_f, _s, _test, 0, vmstate_info_timer, QEMUTimer)
tests/unit/test-vmstate.c +1 -1
@@ -668,7 +668,7 @@ const VMStateDescription vmsd_arpp = { .minimum_version_id = 1, .fields = (const VMStateField []) {VMSTATE_ARRAY_OF_POINTER (ar, TestArrayOfPtrToInt, - AR_SIZE, 0, vmstate_info_int32, int32_t*), +AR_SIZE ,0 , vmstate_info_int32, int32_t),VMSTATE_END_OF_LIST () } };
Signature · 1 line
2.51.0
[PULL 12/43] migration: factor out vmstate_post_load() from vmstate_load_state()
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
Simplify vmstate_load_state() which is rather big, and simplify further refactoring.
--- migration/vmstate.c | 49 +++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 19 deletions(-)
migration/vmstate.c +30 -19
@@ -182,6 +182,33 @@ static bool vmstate_load_field(QEMUFile *f, void *pv, size_t size,return true; } +static bool vmstate_post_load (const VMStateDescription *vmsd, +void *opaque,int version_id,Error **errp) +{ +ERRP_GUARD (); + +if (vmsd->post_load_errp) { +if (!vmsd->post_load_errp (opaque, version_id, errp)) { +error_prepend (errp,"post load hook failed for: %s, version_id: " +"%d, minimum_version: %d: " , vmsd->name, + vmsd->version_id, vmsd->minimum_version_id); +return false; + } + }else if (vmsd->post_load) { +int ret = vmsd->post_load (opaque, version_id); +if (ret <0 ) { +error_setg (errp, +"post load hook failed for: %s, version_id: %d, " +"minimum_version: %d, ret: %d" , + vmsd->name, vmsd->version_id, vmsd->minimum_version_id, + ret); +return false; + } + } + +return true; +} +int vmstate_load_state (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,int version_id,Error **errp) {
@@ -284,25 +311,9 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,return ret; } -if (vmsd->post_load_errp) { -if (!vmsd->post_load_errp (opaque, version_id, errp)) { -error_prepend (errp,"post load hook failed for: %s, version_id: " -"%d, minimum_version: %d: " , vmsd->name, - vmsd->version_id, vmsd->minimum_version_id); -trace_vmstate_load_state_fail (vmsd->name,"post-load" ); -return -EINVAL ; - } - }else if (vmsd->post_load) { - ret = vmsd->post_load (opaque, version_id); -if (ret <0 ) { -error_setg (errp, -"post load hook failed for: %s, version_id: %d, " -"minimum_version: %d, ret: %d" , - vmsd->name, vmsd->version_id, vmsd->minimum_version_id, - ret); -trace_vmstate_load_state_fail (vmsd->name,"post-load" ); -return ret; - } +if (!vmstate_post_load (vmsd, opaque, version_id, errp)) { +trace_vmstate_load_state_fail (vmsd->name,"post-load" ); +return -EINVAL ; }trace_vmstate_load_state_success (vmsd->name);
Signature · 1 line
2.51.0
[PULL 43/43] migration/qemu-file: drop incorrect const from qemu_get_buffer_at buf
In reply to Fabiano RosasFrom: Junjie Cao <[email protected]>
qemu_get_buffer_at() reads data *into* buf -- it should not be const. Drop the qualifier and remove the now-unnecessary cast.
--- migration/qemu-file.c | 4 ++-- migration/qemu-file.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
migration/qemu-file.c +2 -2
@@ -550,7 +550,7 @@ void qemu_put_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, } -size_t qemu_get_buffer_at (QEMUFile *f,const uint8_t *buf,size_t buflen, +size_t qemu_get_buffer_at (QEMUFile *f,uint8_t *buf,size_t buflen,off_t pos) {Error *err =NULL ;
@@ -559,7 +559,7 @@ size_t qemu_get_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen,return 0 ; } -if (qio_channel_pread_all (f->ioc, (char *)buf, buflen, pos, &err) <0 ) { +if (qio_channel_pread_all (f->ioc, buf, buflen, pos, &err) <0 ) {qemu_file_set_error_obj (f, -EIO , err);return 0 ; }
migration/qemu-file.h +1 -1
@@ -76,7 +76,7 @@ void qemu_set_offset(QEMUFile *f, off_t off, int whence);off_t qemu_get_offset (QEMUFile *f);void qemu_put_buffer_at (QEMUFile *f,const uint8_t *buf,size_t buflen,off_t pos); -size_t qemu_get_buffer_at (QEMUFile *f,const uint8_t *buf,size_t buflen, +size_t qemu_get_buffer_at (QEMUFile *f,uint8_t *buf,size_t buflen,off_t pos);QIOChannel *qemu_file_get_ioc (QEMUFile *file);
Signature · 1 line
2.51.0
[PULL 34/43] tests/unit/test-vmstate: add tests for VMS_ARRAY_OF_POINTER_AUTO_ALLOC
In reply to Fabiano RosasFrom: Alexander Mikhalitsyn <[email protected]>
Add tests for VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC.
--- tests/unit/test-vmstate.c | 86 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+)
tests/unit/test-vmstate.c +86 -0
@@ -702,6 +702,88 @@ static void test_arr_ptr_prim_0_load(void) } } +static uint8_t wire_arr_ptr_with_nulls[] = { +VMS_MARKER_PTR_VALID , +0x00 ,0x00 ,0x00 ,0x00 , +VMS_MARKER_PTR_NULL , +VMS_MARKER_PTR_VALID , +0x00 ,0x00 ,0x00 ,0x02 , +VMS_MARKER_PTR_VALID , +0x00 ,0x00 ,0x00 ,0x03 , +QEMU_VM_EOF +}; + +typedef struct { +uint32_t ar_items_num; +TestStructTriv **ar; +}TestVArrayOfPtrToStuctWithNULLs ; + +const VMStateDescription vmsd_arps_with_nulls = { + .name ="test/arps_with_nulls" , + .version_id =1 , + .minimum_version_id =1 , + .fields = (const VMStateField []) { +VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC ( + ar, TestVArrayOfPtrToStuctWithNULLs, ar_items_num, +0 , vmsd_tst, TestStructTriv), +VMSTATE_END_OF_LIST () + } +}; + +static void test_arr_ptr_nulls_str_save (void ) +{ +TestStructTriv ar[AR_SIZE ] = { {.i =0 }, {.i =1 }, {.i =2 }, {.i =3 } }; +TestVArrayOfPtrToStuctWithNULLs sample = {}; +int idx; + + sample.ar_items_num =AR_SIZE ; + sample.ar = g_new0(TestStructTriv*, sample.ar_items_num); + sample.ar[0 ] =g_new0 (TestStructTriv,1 ); + *sample.ar[0 ] = ar[0 ]; +/* note, sample.ar[1] remains NULL */ + sample.ar[2 ] =g_new0 (TestStructTriv,1 ); + *sample.ar[2 ] = ar[2 ]; + sample.ar[3 ] =g_new0 (TestStructTriv,1 ); + *sample.ar[3 ] = ar[3 ]; + +save_vmstate (&vmsd_arps_with_nulls, &sample); +compare_vmstate (wire_arr_ptr_with_nulls,sizeof (wire_arr_ptr_with_nulls)); + +for (idx =0 ; idx <AR_SIZE ; ++idx) { +g_free (sample.ar[idx]); + } +g_free (sample.ar); +} + +static void test_arr_ptr_nulls_str_load (void ) +{ +TestStructTriv ar_gt[AR_SIZE ] = {{.i =0 }, {.i =0 }, {.i =2 }, {.i =3 } }; +TestVArrayOfPtrToStuctWithNULLs obj = {}; +int idx; + + obj.ar_items_num =AR_SIZE ; + obj.ar = g_new0(TestStructTriv*, obj.ar_items_num); + +save_buffer (wire_arr_ptr_with_nulls,sizeof (wire_arr_ptr_with_nulls)); +SUCCESS (load_vmstate_one ( + &vmsd_arps_with_nulls, &obj,1 , + wire_arr_ptr_with_nulls,sizeof (wire_arr_ptr_with_nulls))); + +for (idx =0 ; idx <AR_SIZE ; ++idx) { +if (idx ==1 ) { + g_assert_cmpint((uintptr_t)(obj.ar[idx]), ==, 0); + }else { +/* compare the target array ar with the ground truth array ar_gt */ + g_assert_cmpint(ar_gt[idx].i, ==, obj.ar[idx]->i); + } + } + +for (idx =0 ; idx <AR_SIZE ; ++idx) { +g_free (obj.ar[idx]); + } +g_free (obj.ar); +} +/* test QTAILQ migration */ typedef struct TestQtailqElement TestQtailqElement ;
@@ -1568,6 +1650,10 @@ int main(int argc, char **argv) test_arr_ptr_prim_0_save);g_test_add_func ("/vmstate/array/ptr/prim/0/load" , test_arr_ptr_prim_0_load); +g_test_add_func ("/vmstate/array/ptr-nulls/str/save" , + test_arr_ptr_nulls_str_save); +g_test_add_func ("/vmstate/array/ptr-nulls/str/load" , + test_arr_ptr_nulls_str_load);g_test_add_func ("/vmstate/qtailq/save/saveq" , test_save_q);g_test_add_func ("/vmstate/qtailq/load/loadq" , test_load_q);g_test_add_func ("/vmstate/gtree/save/savedomain" , test_gtree_save_domain);
Signature · 1 line
2.51.0
[PULL 17/43] migration/savevm: move to new migration APIs
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
--- migration/savevm.c | 105 ++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 50 deletions(-)
migration/savevm.c +55 -50
@@ -205,27 +205,28 @@ void timer_get(QEMUFile *f, QEMUTimer *ts)* Not in vmstate.c to not add qemu-timer.c as dependency to vmstate.c */ -static int get_timer (QEMUFile *f,void *pv,size_t size, -const VMStateField *field) +static bool load_timer (QEMUFile *f,void *pv,size_t size, +const VMStateField *field,Error **errp) {QEMUTimer *v = pv;timer_get (f, v); -return 0 ; +return true; } -static int put_timer (QEMUFile *f,void *pv,size_t size, -const VMStateField *field,JSONWriter *vmdesc) +static bool save_timer (QEMUFile *f,void *pv,size_t size, +const VMStateField *field,JSONWriter *vmdesc, +Error **errp) {QEMUTimer *v = pv;timer_put (f, v); -return 0 ; +return true; }const VMStateInfo vmstate_info_timer = { .name ="timer" , - .get = get_timer, - .put = put_timer, + .load = load_timer, + .save = save_timer, };
@@ -297,7 +298,7 @@ static uint32_t get_validatable_capabilities_count(void)return result; } -static int configuration_pre_save (void *opaque) +static bool configuration_pre_save (void *opaque,Error **errp) {SaveState *state = opaque;const char *current_name =MACHINE_GET_CLASS (current_machine)->name;
@@ -318,7 +319,7 @@ static int configuration_pre_save(void *opaque) } state->uuid = qemu_uuid; -return 0 ; +return true; }static void configuration_post_save (void *opaque)
@@ -330,7 +331,7 @@ static void configuration_post_save(void *opaque) state->caps_count =0 ; } -static int configuration_pre_load (void *opaque) +static bool configuration_pre_load (void *opaque,Error **errp) {SaveState *state = opaque;
@@ -339,7 +340,7 @@ static int configuration_pre_load(void *opaque)* minimum possible value for this CPU. */ state->target_page_bits =migration_legacy_page_bits (); -return 0 ; +return true; }static bool configuration_validate_capabilities (SaveState *state)
@@ -376,28 +377,31 @@ static bool configuration_validate_capabilities(SaveState *state)return ret; } -static int configuration_post_load (void *opaque,int version_id) +static bool configuration_post_load (void *opaque,int version_id,Error **errp) {SaveState *state = opaque;const char *current_name =MACHINE_GET_CLASS (current_machine)->name; -int ret =0 ; +bool ok = true;if (strncmp (state->name, current_name, state->len) !=0 ) { -error_report ("Machine type received is '%.*s' and local is '%s'" , - (int ) state->len, state->name, current_name); - ret = -EINVAL ; +error_setg (errp, +"Machine type received is '%.*s' and local is '%s'" , + (int ) state->len, state->name, current_name); + ok = false; goto out; }if (state->target_page_bits !=qemu_target_page_bits ()) { -error_report ("Received TARGET_PAGE_BITS is %d but local is %d" , - state->target_page_bits,qemu_target_page_bits ()); - ret = -EINVAL ; +error_setg (errp, +"Received TARGET_PAGE_BITS is %d but local is %d" , + state->target_page_bits,qemu_target_page_bits ()); + ok = false; goto out; }if (!configuration_validate_capabilities (state)) { - ret = -EINVAL ; +error_setg (errp,"Failed to validate capabilities" ); + ok = false; goto out; }
@@ -409,11 +413,12 @@ out: state->capabilities =NULL ; state->caps_count =0 ; -return ret; +return ok; } -static int get_capability (QEMUFile *f,void *pv,size_t size, -const VMStateField *field) +static bool load_capability (QEMUFile *f,void *pv,size_t size, +const VMStateField *field, +Error **errp) {MigrationCapability *capability = pv;char capability_str[UINT8_MAX +1 ];
@@ -426,15 +431,16 @@ static int get_capability(QEMUFile *f, void *pv, size_t size,for (i =0 ; i <MIGRATION_CAPABILITY__MAX ; i++) {if (!strcmp (MigrationCapability_str (i), capability_str)) { *capability = i; -return 0 ; +return true; } } -error_report ("Received unknown capability %s" , capability_str); -return -EINVAL ; +error_setg (errp,"Received unknown capability %s" , capability_str); +return false; } -static int put_capability (QEMUFile *f,void *pv,size_t size, -const VMStateField *field,JSONWriter *vmdesc) +static bool save_capability (QEMUFile *f,void *pv,size_t size, +const VMStateField *field,JSONWriter *vmdesc, +Error **errp) {MigrationCapability *capability = pv;const char *capability_str =MigrationCapability_str (*capability);
@@ -443,13 +449,13 @@ static int put_capability(QEMUFile *f, void *pv, size_t size,qemu_put_byte (f, len);qemu_put_buffer (f, (uint8_t *)capability_str, len); -return 0 ; +return true; }static const VMStateInfo vmstate_info_capability = { .name ="capability" , - .get = get_capability, - .put = put_capability, + .load = load_capability, + .save = save_capability, };/* The target-page-bits subsection is present only if the
@@ -539,9 +545,9 @@ static const VMStateDescription vmstate_uuid = {static const VMStateDescription vmstate_configuration = { .name ="configuration" , .version_id =1 , - .pre_load = configuration_pre_load, - .post_load = configuration_post_load, - .pre_save = configuration_pre_save, + .pre_load_errp = configuration_pre_load, + .post_load_errp = configuration_post_load, + .pre_save_errp = configuration_pre_save, .post_save = configuration_post_save, .fields = (const VMStateField []) {VMSTATE_UINT32 (len, SaveState),
@@ -969,8 +975,13 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se, Error **errp) }return ret; } -return vmstate_load_state (f, se->vmsd, se->opaque, se->load_version_id, - errp); + +if (!vmstate_load_vmsd (f, se->vmsd, se->opaque, se->load_version_id, + errp)) { +return -EINVAL ; + } + +return 0 ; } static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se,
@@ -1028,8 +1039,6 @@ static void save_section_footer(QEMUFile *f, SaveStateEntry *se)static int vmstate_save (QEMUFile *f,SaveStateEntry *se,JSONWriter *vmdesc,Error **errp) { -int ret; -if ((!se->ops || !se->ops->save_state) && !se->vmsd) {return 0 ; }
@@ -1050,10 +1059,8 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc,if (!se->vmsd) {vmstate_save_old_style (f, se, vmdesc); }else { - ret =vmstate_save_state (f, se->vmsd, se->opaque, vmdesc, - errp); -if (ret) { -return ret; +if (!vmstate_save_vmsd (f, se->vmsd, se->opaque, vmdesc, errp)) { +return -EINVAL ; } }
@@ -1311,8 +1318,8 @@ static void qemu_savevm_send_configuration(MigrationState *s, QEMUFile *f)json_writer_start_object (vmdesc,"configuration" ); } -vmstate_save_state (f, &vmstate_configuration, &savevm_state, - vmdesc, &local_err); +vmstate_save_vmsd (f, &vmstate_configuration, &savevm_state, + vmdesc, &local_err);if (local_err) {error_report_err (local_err); }
@@ -2721,7 +2728,6 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type, Error **errp)static int qemu_loadvm_state_header (QEMUFile *f,Error **errp) {unsigned int v; -int ret; v =qemu_get_be32 (f);if (v !=QEMU_VM_FILE_MAGIC ) {
@@ -2752,10 +2758,9 @@ static int qemu_loadvm_state_header(QEMUFile *f, Error **errp)return -EINVAL ; } - ret =vmstate_load_state (f, &vmstate_configuration, &savevm_state,0 , - errp); -if (ret) { -return ret; +if (!vmstate_load_vmsd (f, &vmstate_configuration, &savevm_state,0 , + errp)) { +return -EINVAL ; } }return 0 ;
Signature · 1 line
2.51.0
[PULL 14/43] migration: VMStateInfo: introduce new handlers with errp
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
Add new APIs with errp, to allow handlers report good error messages. We'll convert existing handlers soon.
--- include/migration/vmstate.h | 17 ++++++++++++++--- migration/vmstate.c | 4 ++++ 2 files changed, 18 insertions(+), 3 deletions(-)
include/migration/vmstate.h +14 -3
@@ -32,12 +32,16 @@typedef struct VMStateInfo VMStateInfo ;typedef struct VMStateField VMStateField ; -/* VMStateInfo allows customized migration of objects that don't fit in +/* +* VMStateInfo allows customized migration of objects that don't fit in * any category in VMStateFlags. Additional information is always passed -* into get and put in terms of field and vmdesc parameters. However +* into load and save in terms of field and vmdesc parameters. However * these two parameters should only be used in cases when customized * handling is needed, such as QTAILQ. For primitive data types such as -* integer, field and vmdesc parameters should be ignored inside get/put. +* integer, field and vmdesc parameters should be ignored inside load/save. +* +* @get and @put are deprecated copies of @load and @save. For new interfaces +* use @load and @save. */ struct VMStateInfo {const char *name;
@@ -46,6 +50,13 @@ struct VMStateInfo { int coroutine_mixed_fn (*put)(QEMUFile *f, void *pv, size_t size,const VMStateField *field,JSONWriter *vmdesc); + bool coroutine_mixed_fn (*load)(QEMUFile *f, void *pv, size_t size, +const VMStateField *field, +Error **errp); + bool coroutine_mixed_fn (*save)(QEMUFile *f, void *pv, size_t size, +const VMStateField *field, +JSONWriter *vmdesc, +Error **errp); };enum VMStateFlags {
migration/vmstate.c +4 -0
@@ -170,6 +170,8 @@ static bool vmstate_load_field(QEMUFile *f, void *pv, size_t size, }else if (field->flags &VMS_VSTRUCT ) {return vmstate_load_state (f, field->vmsd, pv, field->struct_version_id, errp) >=0 ; + }else if (field->info->load) { +return field->info->load (f, pv, size, field, errp); }if (field->info->get (f, pv, size, field) <0 ) {
@@ -495,6 +497,8 @@ static bool vmstate_save_field(QEMUFile *f, void *pv, size_t size,return vmstate_save_state_v (f, field->vmsd, pv, vmdesc, field->struct_version_id, errp) >=0 ; + }else if (field->info->save) { +return field->info->save (f, pv, size, field, vmdesc, errp); }if (field->info->put (f, pv, size, field, vmdesc) <0 ) {
Signature · 1 line
2.51.0
[PULL 33/43] vmstate: Stop checking size for nullptr compression
In reply to Fabiano RosasThe NULL pointer marker code applies only to VMS_ARRAY_OF_POINTER, where the size is never NULL. Move the setting of is_null under VMS_ARRAY_OF_POINTER, so we can stop checking the size.
--- migration/vmstate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
migration/vmstate.c +3 -3
@@ -682,14 +682,14 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,const VMStateField *inner_field;/* maximum number of elements to compress in the JSON blob */ int max_elems =vmsd_can_compress (field) ? (n_elems - i) :1 ; -bool use_marker_field, is_null; +bool use_marker_field, is_null = false;if (field->flags &VMS_ARRAY_OF_POINTER ) {assert (curr_elem); curr_elem = *(void **)curr_elem; + is_null = !curr_elem; } - is_null = !curr_elem && size; use_marker_field = use_dynamic_array || is_null;if (use_marker_field) {
@@ -717,7 +717,7 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,for (int j = i +1 ; j < n_elems; j++) {void *elem = *(void **)(first_elem + size * j); -bool elem_is_null = !elem && size; +bool elem_is_null = !elem;if (is_null != elem_is_null) { max_elems = j - i;
Signature · 1 line
2.51.0
[PULL 10/43] migration: factor out vmstate_pre_load() from vmstate_load_state()
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
Simplify vmstate_load_state() which is rather big, and simplify further refactoring.
--- migration/vmstate.c | 46 ++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-)
migration/vmstate.c +29 -17
@@ -134,6 +134,33 @@ static void vmstate_handle_alloc(void *ptr, const VMStateField *field, } } +static bool vmstate_pre_load (const VMStateDescription *vmsd,void *opaque, +Error **errp) +{ +ERRP_GUARD (); + +if (vmsd->pre_load_errp) { +if (!vmsd->pre_load_errp (opaque, errp)) { +error_prepend (errp,"pre load hook failed for: '%s', " +"version_id: %d, minimum version_id: %d: " , + vmsd->name, vmsd->version_id, + vmsd->minimum_version_id); +return false; + } + }else if (vmsd->pre_load) { +int ret = vmsd->pre_load (opaque); +if (ret) { +error_setg (errp,"pre load hook failed for: '%s', " +"version_id: %d, minimum version_id: %d, ret: %d" , + vmsd->name, vmsd->version_id, vmsd->minimum_version_id, + ret); +return false; + } + } + +return true; +} +int vmstate_load_state (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,int version_id,Error **errp) {
@@ -159,23 +186,8 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,return -EINVAL ; } -if (vmsd->pre_load_errp) { -if (!vmsd->pre_load_errp (opaque, errp)) { -error_prepend (errp,"pre load hook failed for: '%s', " -"version_id: %d, minimum version_id: %d: " , - vmsd->name, vmsd->version_id, - vmsd->minimum_version_id); -return -EINVAL ; - } - }else if (vmsd->pre_load) { - ret = vmsd->pre_load (opaque); -if (ret) { -error_setg (errp,"pre load hook failed for: '%s', " -"version_id: %d, minimum version_id: %d, ret: %d" , - vmsd->name, vmsd->version_id, vmsd->minimum_version_id, - ret); -return ret; - } +if (!vmstate_pre_load (vmsd, opaque, errp)) { +return -EINVAL ; }while (field->name) {
Signature · 1 line
2.51.0
[PULL 15/43] migration: introduce vmstate_load_vmsd() and vmstate_save_vmsd()
In reply to Fabiano RosasFrom: Vladimir Sementsov-Ogievskiy <[email protected]>
Introduce new APIs, returning bool.
The analysis
https://lore.kernel.org/qemu-devel/[email protected]/
shows, that vmstate_load_state() return value actually only used to check for success, specific errno values doesn't make sense.
With this commit we introduce new functions with modern bool interface, and in following commits we'll update the code base to use them, starting from migration/ code, and finally we will remove old vmstate_load_state() and vmstate_save_state().
This patch reworks existing functions to new one, so that old interfaces are simple wrappers, which will be easy to remove later.
--- include/migration/vmstate.h | 9 ++++ migration/vmstate.c | 89 ++++++++++++++++++++----------------- 2 files changed, 58 insertions(+), 40 deletions(-)
include/migration/vmstate.h +9 -0
@@ -1257,10 +1257,19 @@ extern const VMStateInfo vmstate_info_qlist; .flags = VMS_END, \ } +/* +* vmstate_load_state() and vmstate_save_state() are +* depreacated, use vmstate_load_vmsd() and vmstate_save_vmsd() +* instead. +*/ int vmstate_load_state (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,int version_id,Error **errp);int vmstate_save_state (QEMUFile *f,const VMStateDescription *vmsd,void *opaque,JSONWriter *vmdesc,Error **errp); +bool vmstate_load_vmsd (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,int version_id,Error **errp); +bool vmstate_save_vmsd (QEMUFile *f,const VMStateDescription *vmsd, +void *opaque,JSONWriter *vmdesc,Error **errp);bool vmstate_section_needed (const VMStateDescription *vmsd,void *opaque);
migration/vmstate.c +49 -40
239 lines not on this page · raw message
Signature · 1 line
2.51.0
[PULL 28/43] vmstate: Rename VMS_NULLPTR_MARKER to VMS_MARKER_PTR_NULL
In reply to Fabiano RosasFrom: Peter Xu <[email protected]>
Prepare for a new MARKER for non-NULL pointer.
--- include/migration/vmstate.h | 2 +- migration/vmstate-types.c | 4 ++-- tests/unit/test-vmstate.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-)
include/migration/vmstate.h +1 -1
@@ -283,7 +283,7 @@ extern const VMStateInfo vmstate_info_uint64;extern const VMStateInfo vmstate_info_fd;/** Put this in the stream when migrating a null pointer.*/ -#define VMS_NULLPTR_MARKER (0x30U)/* '0' */ +#define VMS_MARKER_PTR_NULL (0x30U)/* '0' */ extern const VMStateInfo vmstate_info_nullptr;extern const VMStateInfo vmstate_info_cpudouble;
migration/vmstate-types.c +2 -2
@@ -363,7 +363,7 @@ static bool load_nullptr(QEMUFile *f, void *pv, size_t size, const VMStateField *field, Error **errp) { - if (qemu_get_byte(f) == VMS_NULLPTR_MARKER) { + if (qemu_get_byte(f) == VMS_MARKER_PTR_NULL) {return true; }
@@ -377,7 +377,7 @@ static bool save_nullptr(QEMUFile *f, void *pv, size_t size, {if (pv ==NULL ) { -qemu_put_byte (f,VMS_NULLPTR_MARKER ); +qemu_put_byte (f,VMS_MARKER_PTR_NULL );return true; }
tests/unit/test-vmstate.c +1 -1
@@ -620,7 +620,7 @@ static void test_arr_ptr_str_no0_load(void)static uint8_t wire_arr_ptr_0[] = {0x00 ,0x00 ,0x00 ,0x00 , -VMS_NULLPTR_MARKER , +VMS_MARKER_PTR_NULL ,0x00 ,0x00 ,0x00 ,0x02 ,0x00 ,0x00 ,0x00 ,0x03 ,QEMU_VM_EOF
Signature · 1 line
2.51.0
[PULL 27/43] vmstate: Update max_elems early and check field compressable once
In reply to Fabiano RosasFrom: Peter Xu <[email protected]>
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.
--- migration/vmstate.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
migration/vmstate.c +5 -3
@@ -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 ; } }
Signature · 1 line
2.51.0
[PULL 38/43] migration/file: fix type mismatch and NULL deref in multifd_file_recv_data
In reply to Fabiano RosasFrom: Junjie Cao <[email protected]>
multifd_file_recv_data() stores the return value of qio_channel_pread() (ssize_t) in a size_t variable. On I/O error the -1 return value wraps to SIZE_MAX, producing a nonsensical read size in the error message.
More critically, a short read (0 <= ret < data->size) is possible when the migration file is truncated. In that case qio_channel_pread() returns a non-negative value without setting *errp. The function then calls error_prepend(errp, ...) which dereferences *errp -- a NULL pointer -- crashing QEMU.
Fix both issues by switching to qio_channel_pread_all() introduced in a previous patch, which retries on short reads and treats end-of-file as an error, so the caller no longer needs to check the byte count manually. Add ERRP_GUARD() so that error_prepend() works correctly even when errp is &error_fatal or NULL.
--- migration/file.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
migration/file.c +7 -6
@@ -256,15 +256,16 @@ int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov,int multifd_file_recv_data (MultiFDRecvParams *p,Error **errp) { +ERRP_GUARD ();MultiFDRecvData *data = p->data; -size_t ret; +int ret; - ret =qio_channel_pread (p->c, (char *) data->opaque, - data->size, data->file_offset, errp); -if (ret != data->size) { + ret =qio_channel_pread_all (p->c, (char *) data->opaque, + data->size, data->file_offset, errp); +if (ret !=0 ) {error_prepend (errp, -"multifd recv (%u): read 0x%zx, expected 0x%zx" , - p->id, ret, data->size); +"multifd recv (%u): " , + p->id);return -1 ; }
Signature · 1 line
2.51.0
[PULL 42/43] migration/file: switch file_write_ramblock_iov to pwritev_all
In reply to Fabiano RosasFrom: Junjie Cao <[email protected]>
file_write_ramblock_iov() uses single-shot qio_channel_pwritev() and only checks for ret < 0. A short write (0 <= ret < requested) would be treated as success.
Switch to qio_channel_pwritev_all() which retries until all bytes are written or an error occurs.
--- migration/file.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
migration/file.c +4 -4
@@ -202,7 +202,7 @@ void file_connect_incoming(FileMigrationArgs *file_args, Error **errp)int file_write_ramblock_iov (QIOChannel *ioc,const struct iovec *iov,int niov,MultiFDPages_t *pages,Error **errp) { -ssize_t ret =0 ; +int ret =0 ;int i, slice_idx, slice_num;uintptr_t base, next, offset;size_t len;
@@ -241,8 +241,8 @@ int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov,break ; } - ret =qio_channel_pwritev (ioc, &iov[slice_idx], slice_num, - block->pages_offset + offset, errp); + ret =qio_channel_pwritev_all (ioc, &iov[slice_idx], slice_num, + block->pages_offset + offset, errp);if (ret <0 ) {break ; }
@@ -251,7 +251,7 @@ int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, slice_num =0 ; } -return (ret <0 ) ? ret :0 ; +return ret; }int multifd_file_recv_data (MultiFDRecvParams *p,Error **errp)
Signature · 1 line
2.51.0
[PULL 22/43] tests/qtest/migration: Add mapped-ram/postcopy validation test
In reply to Fabiano RosasFrom: Takeru Hayasaka <[email protected]>
The migration capability checks reject enabling postcopy-ram together with mapped-ram, but there is no qtest covering this incompatibility.
Add a validation test that verifies QMP rejects the combination in both capability ordering cases and returns the expected error.
This keeps the existing capability boundary covered without changing migration behavior.
--- tests/qtest/migration/misc-tests.c | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+)
tests/qtest/migration/misc-tests.c +52 -0
@@ -215,6 +215,55 @@ static void do_test_validate_uri_channel(MigrateCommon *args)migrate_end (from, to, false); } +static void validate_caps_pair (QTestState *from, +const char *first_capability, +const char *second_capability, +const char *expected_error) +{ +QDict *rsp; +const char *error_desc; + +migrate_set_capability (from, first_capability, true); + + rsp =qtest_qmp_assert_failure_ref ( + from, +"{ 'execute': 'migrate-set-capabilities'," +" 'arguments': { 'capabilities': [ { " +" 'capability': %s, 'state': true } ] } }" , + second_capability); + + error_desc =qdict_get_str (rsp,"desc" ); + g_assert_cmpstr(error_desc, ==, expected_error); +qobject_unref (rsp); + +migrate_set_capability (from, first_capability, false); +} + +static void test_validate_caps_pair (char *test_path,MigrateCommon *args) +{ + g_autofree char *serial_path = g_strconcat(tmpfs, "/src_serial", NULL); + g_autofree char *cap_pair = g_path_get_basename(test_path); +QTestState *from, *to; + + args->start.hide_stderr = true; + args->start.only_source = true; + +if (migrate_start (&from, &to,"defer" , &args->start)) { +return ; + } + +if (g_str_equal (cap_pair,"mapped_ram_postcopy" )) { +const char *error = +"Mapped-ram migration is incompatible with postcopy" ; + +validate_caps_pair (from,"mapped-ram" ,"postcopy-ram" , error); +validate_caps_pair (from,"postcopy-ram" ,"mapped-ram" , error); + } + +qtest_quit (from); +unlink (serial_path); +} +static void test_validate_uri_channels_both_set (char *name,MigrateCommon *args) { args->listen_uri = "defer",
@@ -276,4 +325,7 @@ void migration_test_add_misc(MigrationTestEnv *env) test_validate_uri_channels_both_set);migration_test_add ("/migration/validate_uri/channels/none_set" , test_validate_uri_channels_none_set); +migration_test_add_suffix ("/migration/validate_caps/" , +"mapped_ram_postcopy" , + test_validate_caps_pair); }
Signature · 1 line
2.51.0
[PULL 25/43] vmstate: Pass in struct itself for VMSTATE_VARRAY_OF_POINTER_UINT32
In reply to Fabiano RosasFrom: Peter Xu <[email protected]>
Passing in a pointer almost never helps. Convert it to pass in struct for further refactoring on VMS_ARRAY_OF_POINTER.
--- include/hw/intc/riscv_aclint.h | 6 +++--- include/migration/vmstate.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-)
include/hw/intc/riscv_aclint.h +3 -3
@@ -80,8 +80,8 @@ enum {RISCV_ACLINT_SWI_SIZE =0x4000 }; -#define VMSTATE_TIMER_PTR_VARRAY (_f, _s, _f_n) \ -VMSTATE_VARRAY_OF_POINTER_UINT32(_f, _s, _f_n, 0, vmstate_info_timer, \ - QEMUTimer *) +#define VMSTATE_TIMER_PTR_VARRAY (_f, _s, _f_n) \ + VMSTATE_VARRAY_OF_POINTER_UINT32(_f, _s, _f_n, 0, vmstate_info_timer, \ + QEMUTimer)#endif
include/migration/vmstate.h +2 -2
@@ -567,9 +567,9 @@ extern const VMStateInfo vmstate_info_qlist; .version_id = (_version), \ .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \ .info = &(_info), \ - .size = sizeof(_type), \ + .size = sizeof(_type *), \ .flags = VMS_VARRAY_UINT32 | VMS_ARRAY_OF_POINTER | VMS_POINTER, \ - .offset = vmstate_offset_pointer(_state, _field, _type), \ + .offset = vmstate_offset_pointer(_state, _field, _type *), \ }#define VMSTATE_STRUCT_SUB_ARRAY (_field, _state, _start, _num, _version, _vmsd, _type) { \
Signature · 1 line
2.51.0
[PULL 36/43] io/channel: introduce qio_channel_pread{v, }_all{, _eof}()
In reply to Fabiano RosasFrom: Junjie Cao <[email protected]>
qio_channel_pread() and qio_channel_preadv() perform a single positioned read and may return a short result. Callers that need all bytes currently have to open-code a retry loop or simply treat a short read as an error.
Introduce four new helpers following the existing read_all / readv_all pattern:
qio_channel_preadv_all_eof() -- retry loop; returns 1 on success,
0 on clean EOF, -1 on error.
qio_channel_preadv_all() -- wraps _eof; treats early EOF as
error; returns 0 / -1.
qio_channel_pread_all_eof() -- single-buffer convenience wrapper
around preadv_all_eof().
qio_channel_pread_all() -- single-buffer convenience wrapper
around preadv_all().
These advance the file offset internally after each partial read. All four are marked coroutine_mixed_fn, consistent with the existing _all helpers.
--- include/io/channel.h | 92 ++++++++++++++++++++++++++++++++++++++++++++ io/channel.c | 91 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+)
include/io/channel.h +92 -0
@@ -634,6 +634,98 @@ ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov,ssize_t qio_channel_pread (QIOChannel *ioc,void *buf,size_t buflen,off_t offset,Error **errp); +/** +* qio_channel_preadv_all_eof: +* @ioc: the channel object +* @iov: the array of memory regions to read data into +* @niov: the length of the @iov array +* @offset: the starting offset in the channel to read from +* @errp: pointer to a NULL-initialized error object +* +* Reads @iov, possibly blocking or (if the channel is non-blocking) +* yielding from the current coroutine multiple times until the entire +* content is read. If end-of-file occurs immediately it is not an +* error, but if it occurs after data has been read it will return +* an error rather than a short-read. Otherwise behaves as +* qio_channel_preadv(). +* +* Returns: 1 if all bytes were read, 0 if end-of-file occurs +* without data, or -1 on error +*/ +int coroutine_mixed_fn qio_channel_preadv_all_eof(QIOChannel *ioc, +const struct iovec *iov, +size_t niov, +off_t offset, +Error **errp); + +/** +* qio_channel_preadv_all: +* @ioc: the channel object +* @iov: the array of memory regions to read data into +* @niov: the length of the @iov array +* @offset: the starting offset in the channel to read from +* @errp: pointer to a NULL-initialized error object +* +* Reads @iov, possibly blocking or (if the channel is non-blocking) +* yielding from the current coroutine multiple times until the entire +* content is read. If end-of-file occurs before all requested data +* has been read, an error will be reported. Otherwise behaves as +* qio_channel_preadv(). +* +* Returns: 0 if all bytes were read, or -1 on error +*/ +int coroutine_mixed_fn qio_channel_preadv_all(QIOChannel *ioc, +const struct iovec *iov, +size_t niov, +off_t offset, +Error **errp); + +/** +* qio_channel_pread_all_eof: +* @ioc: the channel object +* @buf: the memory region to read data into +* @buflen: the number of bytes to read into @buf +* @offset: the starting offset in the channel to read from +* @errp: pointer to a NULL-initialized error object +* +* Reads @buflen bytes, possibly blocking or (if the channel is +* non-blocking) yielding from the current coroutine multiple times +* until the entire content is read. If end-of-file occurs +* immediately it is not an error, but if it occurs after data has +* been read it will return an error rather than a short-read. +* Otherwise behaves as qio_channel_pread(). +* +* Returns: 1 if all bytes were read, 0 if end-of-file occurs +* without data, or -1 on error +*/ +int coroutine_mixed_fn qio_channel_pread_all_eof(QIOChannel *ioc, +void *buf, +size_t buflen, +off_t offset, +Error **errp); + +/** +* qio_channel_pread_all: +* @ioc: the channel object +* @buf: the memory region to read data into +* @buflen: the number of bytes to read into @buf +* @offset: the starting offset in the channel to read from +* @errp: pointer to a NULL-initialized error object +* +* Reads @buflen bytes, possibly blocking or (if the channel is +* non-blocking) yielding from the current coroutine multiple times +* until the entire content is read. If end-of-file occurs before +* all requested data has been read, an error will be reported. +* Otherwise behaves as qio_channel_pread(). +* +* Returns: 0 if all bytes were read, or -1 on error +*/ +int coroutine_mixed_fn qio_channel_pread_all(QIOChannel *ioc, +void *buf, +size_t buflen, +off_t offset, +Error **errp); +/** * qio_channel_shutdown: * @ioc: the channel object
io/channel.c +91 -0
97 lines not on this page · raw message
Signature · 1 line
2.51.0
[PULL 41/43] migration/qemu-file: switch buffer_at functions to positioned I/O _all helpers
In reply to Fabiano RosasFrom: Junjie Cao <[email protected]>
qemu_put_buffer_at() and qemu_get_buffer_at() have the same pattern as the bug fixed in multifd_file_recv_data(): the ssize_t return value from the channel layer is stored in a size_t variable, and a short transfer would be mishandled rather than retried.
Switch to qio_channel_pwrite_all() / qio_channel_pread_all() which handle short transfers internally and make the code more robust and consistent with the rest of the positioned I/O call sites.
--- migration/qemu-file.c | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-)
migration/qemu-file.c +3 -32
@@ -535,28 +535,13 @@ void qemu_put_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, off_t pos) {Error *err =NULL ; -size_t ret;if (f->last_error) {return ; }qemu_fflush (f); - ret =qio_channel_pwrite (f->ioc, (char *)buf, buflen, pos, &err); - -if (err) { -qemu_file_set_error_obj (f, -EIO , err); -return ; - } - -if ((ssize_t )ret ==QIO_CHANNEL_ERR_BLOCK ) { -qemu_file_set_error_obj (f, -EAGAIN ,NULL ); -return ; - } - -if (ret != buflen) { -error_setg (&err,"Partial write of size %zu, expected %zu" , ret, - buflen); +if (qio_channel_pwrite_all (f->ioc, buf, buflen, pos, &err) <0 ) {qemu_file_set_error_obj (f, -EIO , err);return ; }
@@ -569,31 +554,17 @@ size_t qemu_get_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, off_t pos) {Error *err =NULL ; -size_t ret;if (f->last_error) {return 0 ; } - ret =qio_channel_pread (f->ioc, (char *)buf, buflen, pos, &err); - -if ((ssize_t )ret ==-1 || err) { -qemu_file_set_error_obj (f, -EIO , err); -return 0 ; - } - -if ((ssize_t )ret ==QIO_CHANNEL_ERR_BLOCK ) { -qemu_file_set_error_obj (f, -EAGAIN ,NULL ); -return 0 ; - } - -if (ret != buflen) { -error_setg (&err,"Partial read of size %zu, expected %zu" , ret, buflen); +if (qio_channel_pread_all (f->ioc, (char *)buf, buflen, pos, &err) <0 ) {qemu_file_set_error_obj (f, -EIO , err);return 0 ; } -return ret; +return buflen; }void qemu_set_offset (QEMUFile *f,off_t off,int whence)
Signature · 1 line
2.51.0
Stefan Hajnoczi
AppliedApplied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.