replay additions
v6 cover replay additions
This set of patches includes several fixes for replay and vmstate.
This patches add rrsnapshot option for icount. rrshapshot option creates start snapshot at record and loads it at replay. It allows preserving the state of disk images used by virtual machine. This vm state can also use used to roll back the execution while replaying.
This set of patches includes fixes and additions for icount and record/replay implementation:
- VM start/stop in replay mode
- overlay creation for blkreplay filter
- rrsnapshot option for record/replay
- vmstate fix for integratorcp ARM platform
- vmstate fixes for apic and rtc
v6 changes:
- Added overlay creation for blkreplay driver
- Fixed vmstate loading for apic and rtc
- Fixed instruction counting for apic instruction patching
v5 changes:
- Recording is stopped when initial snapshot cannot be created
- Minor changes
v4 changes:
- Overlay option is removed from blkreplay driver (as suggested by Paolo Bonzini)
- Minor changes
v3 changes:
- Added rrsnapshot option for specifying the initial snapshot name (as suggested by Paolo Bonzini)
- Minor changes
---
Pavel Dovgalyuk (9):
icount: update instruction counter on apic patching
replay: improve interrupt handling
apic: save apic_delivered flag
replay: don't use rtc clock on loadvm phase
integratorcp: adding vmstate for save/restore
savevm: add public save_vmstate function
replay: save/load initial state
block: implement bdrv_snapshot_goto for blkreplay
blkreplay: create temporary overlay for underlaying devices
block/blkreplay.c | 84 +++++++++++++++++++++++++++++++++++++++ cpu-exec.c | 2 - docs/replay.txt | 16 +++++++ hw/arm/integratorcp.c | 62 +++++++++++++++++++++++++++++ hw/i386/kvmvapic.c | 6 +++ hw/intc/apic_common.c | 32 +++++++++++++++ hw/timer/mc146818rtc.c | 14 +++++-- include/hw/i386/apic_internal.h | 2 + include/sysemu/replay.h | 9 ++++ include/sysemu/sysemu.h | 1 migration/savevm.c | 33 ++++++++++----- qemu-options.hx | 8 +++- replay/replay-snapshot.c | 17 ++++++++ replay/replay.c | 5 ++ stubs/replay.c | 1 target-i386/seg_helper.c | 1 vl.c | 9 +++- 17 files changed, 282 insertions(+), 20 deletions(-)
Signature · 1 line
Pavel Dovgalyuk
PATCH v6 1/9 icount: update instruction counter on apic patching
kvmvapic patches the code when some instructions are executed. E.g. mov 0xff, 0xfffe0080 is interpreted as push 0xff/call ... This patching is also followed by some side effects (changing apic and guest memory state). Therefore deterministic execution should take this operation into account. This patch decreases icount when original mov instruction is trying to execute. Therefore patching becomes deterministic and can be replayed correctly.
--- hw/i386/kvmvapic.c | 6 ++++++ 1 file changed, 6 insertions(+)
hw/i386/kvmvapic.c +6 -0
@@ -412,6 +412,12 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)if (!kvm_enabled ()) {cpu_get_tb_cpu_state (env, ¤t_pc, ¤t_cs_base, ¤t_flags); +/* Account this instruction, because we will exit the tb. +This is the first instruction in the block. Therefore +there is no need in restoring CPU state. */ +if (use_icount) { + --cs->icount_decr.u16.low; + } }pause_all_vcpus ();
PATCH v6 2/9 replay: improve interrupt handling
In reply to Pavel DovgalyukThis patch improves interrupt handling in record/replay mode.
Now "interrupt" event is saved only when cc->cpu_exec_interrupt returns true. This patch also adds missing return to cpu_exec_interrupt function.
--- cpu-exec.c | 2 +- target-i386/seg_helper.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-)
cpu-exec.c +1 -1
@@ -508,8 +508,8 @@ static inline void cpu_handle_interrupt(CPUState *cpu,True when it is, and we should restart on a new TB, and via longjmp via cpu_loop_exit. */ else { -replay_interrupt ();if (cc->cpu_exec_interrupt (cpu, interrupt_request)) { +replay_interrupt (); *last_tb =NULL ; }/* The target hook may have updated the 'cpu->interrupt_request';
target-i386/seg_helper.c +1 -0
@@ -1331,6 +1331,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)#endif if (interrupt_request &CPU_INTERRUPT_SIPI ) {do_cpu_sipi (cpu); + ret = true; }else if (env->hflags2 &HF2_GIF_MASK ) {if ((interrupt_request &CPU_INTERRUPT_SMI ) && !(env->hflags &HF_SMM_MASK )) {
PATCH v6 3/9 apic: save apic_delivered flag
In reply to Pavel DovgalyukThis patch implements saving/restoring of static apic_delivered variable.
--- hw/intc/apic_common.c | 32 ++++++++++++++++++++++++++++++++ include/hw/i386/apic_internal.h | 2 ++ 2 files changed, 34 insertions(+)
hw/intc/apic_common.c +32 -0
@@ -384,6 +384,24 @@ static bool apic_common_sipi_needed(void *opaque)return s->wait_for_sipi !=0 ; } +static bool apic_irq_delivered_needed (void *opaque) +{ +return true; +} + +static void apic_irq_delivered_pre_save (void *opaque) +{ +APICCommonState *s =APIC_COMMON (opaque); + s->apic_irq_delivered = apic_irq_delivered; +} + +static int apic_irq_delivered_post_load (void *opaque,int version_id) +{ +APICCommonState *s =APIC_COMMON (opaque); + apic_irq_delivered = s->apic_irq_delivered; +return 0 ; +} +static const VMStateDescription vmstate_apic_common_sipi = { .name ="apic_sipi" , .version_id =1 ,
@@ -396,6 +414,19 @@ static const VMStateDescription vmstate_apic_common_sipi = { } }; +static const VMStateDescription vmstate_apic_irq_delivered = { + .name ="apic_irq_delivered" , + .version_id =1 , + .minimum_version_id =1 , + .needed = apic_irq_delivered_needed, + .pre_save = apic_irq_delivered_pre_save, + .post_load = apic_irq_delivered_post_load, + .fields = (VMStateField []) { +VMSTATE_INT32 (apic_irq_delivered, APICCommonState), +VMSTATE_END_OF_LIST () + } +}; +static const VMStateDescription vmstate_apic_common = { .name ="apic" , .version_id =3 ,
@@ -430,6 +461,7 @@ static const VMStateDescription vmstate_apic_common = { }, .subsections = (const VMStateDescription*[]) { &vmstate_apic_common_sipi, + &vmstate_apic_irq_delivered,NULL } };
include/hw/i386/apic_internal.h +2 -0
@@ -189,6 +189,8 @@ struct APICCommonState {DeviceState *vapic;hwaddr vapic_paddr;/* note: persistence via kvmvapic */ bool legacy_instance_id; + +int apic_irq_delivered;/* for saving static variable */ }; typedef struct VAPICState {
PATCH v6 4/9 replay: don't use rtc clock on loadvm phase
In reply to Pavel DovgalyukThis patch disables the update of the periodic timer of mc146818rtc in record/replay mode. State of this timer is saved and therefore does not need to be updated in record/replay mode.
Read of RTC breaks the replay because all rtc reads have to be the same as in record mode.
--- hw/timer/mc146818rtc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
hw/timer/mc146818rtc.c +10 -4
@@ -27,6 +27,7 @@#include "hw/hw.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "sysemu/replay.h" #include "hw/timer/mc146818rtc.h" #include "qapi/visitor.h" #include "qapi-event.h"
@@ -734,10 +735,15 @@ static int rtc_post_load(void *opaque, int version_id)check_update_timer (s); } -uint64_t now =qemu_clock_get_ns (rtc_clock); -if (now < s->next_periodic_time || - now > (s->next_periodic_time +get_max_clock_jump ())) { -periodic_timer_update (s,qemu_clock_get_ns (rtc_clock)); +/* Periodic timer is deterministic in record/replay mode. +No need to update it after loading the vmstate. +Reading RTC here may break the execution. */ +if (replay_mode ==REPLAY_MODE_NONE ) { +uint64_t now =qemu_clock_get_ns (rtc_clock); +if (now < s->next_periodic_time || + now > (s->next_periodic_time +get_max_clock_jump ())) { +periodic_timer_update (s,qemu_clock_get_ns (rtc_clock)); + } }#ifdef TARGET_I386
PATCH v6 5/9 integratorcp: adding vmstate for save/restore
In reply to Pavel DovgalyukFrom: Pavel Dovgalyuk <[email protected]>
VMState added by this patch preserves correct
loading of the integratorcp device state.
--- hw/arm/integratorcp.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+)
hw/arm/integratorcp.c +62 -0
@@ -53,6 +53,27 @@ static uint8_t integrator_spd[128] = {0xe ,4 ,0x1c ,1 ,2 ,0x20 ,0xc0 ,0 ,0 ,0 ,0 ,0x30 ,0x28 ,0x30 ,0x28 ,0x40 }; +static const VMStateDescription vmstate_integratorcm = { + .name ="integratorcm" , + .version_id =1 , + .minimum_version_id =1 , + .minimum_version_id_old =1 , + .fields = (VMStateField []) { +VMSTATE_UINT32 (cm_osc, IntegratorCMState), +VMSTATE_UINT32 (cm_ctrl, IntegratorCMState), +VMSTATE_UINT32 (cm_lock, IntegratorCMState), +VMSTATE_UINT32 (cm_auxosc, IntegratorCMState), +VMSTATE_UINT32 (cm_sdram, IntegratorCMState), +VMSTATE_UINT32 (cm_init, IntegratorCMState), +VMSTATE_UINT32 (cm_flags, IntegratorCMState), +VMSTATE_UINT32 (cm_nvflags, IntegratorCMState), +VMSTATE_UINT32 (int_level, IntegratorCMState), +VMSTATE_UINT32 (irq_enabled, IntegratorCMState), +VMSTATE_UINT32 (fiq_enabled, IntegratorCMState), +VMSTATE_END_OF_LIST () + } +}; +static uint64_t integratorcm_read (void *opaque,hwaddr offset,unsigned size) {
@@ -309,6 +330,19 @@ typedef struct icp_pic_state {qemu_irq parent_fiq; } icp_pic_state; +static const VMStateDescription vmstate_icp_pic = { + .name ="icp_pic" , + .version_id =1 , + .minimum_version_id =1 , + .minimum_version_id_old =1 , + .fields = (VMStateField []) { +VMSTATE_UINT32 (level, icp_pic_state), +VMSTATE_UINT32 (irq_enabled, icp_pic_state), +VMSTATE_UINT32 (fiq_enabled, icp_pic_state), +VMSTATE_END_OF_LIST () + } +}; +static void icp_pic_update (icp_pic_state *s) {uint32_t flags;
@@ -438,6 +472,17 @@ typedef struct ICPCtrlRegsState {#define ICP_INTREG_WPROT (1 << 0)#define ICP_INTREG_CARDIN (1 << 3) +static const VMStateDescription vmstate_icp_control = { + .name ="icp_control" , + .version_id =1 , + .minimum_version_id =1 , + .minimum_version_id_old =1 , + .fields = (VMStateField []) { +VMSTATE_UINT32 (intreg_state, ICPCtrlRegsState), +VMSTATE_END_OF_LIST () + } +}; +static uint64_t icp_control_read (void *opaque,hwaddr offset,unsigned size) {
@@ -640,6 +685,21 @@ static void core_class_init(ObjectClass *klass, void *data) dc->props = core_properties; dc->realize = integratorcm_realize; + dc->vmsd = &vmstate_integratorcm; +} + +static void icp_pic_class_init (ObjectClass *klass,void *data) +{ +DeviceClass *dc =DEVICE_CLASS (klass); + + dc->vmsd = &vmstate_icp_pic; +} + +static void icp_control_class_init (ObjectClass *klass,void *data) +{ +DeviceClass *dc =DEVICE_CLASS (klass); + + dc->vmsd = &vmstate_icp_control; }static const TypeInfo core_info = {
@@ -655,6 +715,7 @@ static const TypeInfo icp_pic_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(icp_pic_state), .instance_init = icp_pic_init, + .class_init = icp_pic_class_init, };static const TypeInfo icp_ctrl_regs_info = {
@@ -662,6 +723,7 @@ static const TypeInfo icp_ctrl_regs_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ICPCtrlRegsState), .instance_init = icp_control_init, + .class_init = icp_control_class_init, };static void integratorcp_register_types (void )
PATCH v6 6/9 savevm: add public save_vmstate function
In reply to Pavel DovgalyukThis patch introduces save_vmstate function to allow saving and loading vmstates from the replay module.
--- include/sysemu/sysemu.h | 1 + migration/savevm.c | 33 ++++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 11 deletions(-)
include/sysemu/sysemu.h +1 -0
@@ -75,6 +75,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify);void qemu_remove_machine_init_done_notifier (Notifier *notify);void hmp_savevm (Monitor *mon,const QDict *qdict); +int save_vmstate (Monitor *mon,const char *name);int load_vmstate (const char *name);void hmp_delvm (Monitor *mon,const QDict *qdict);void hmp_info_snapshots (Monitor *mon,const QDict *qdict);
migration/savevm.c +22 -11
@@ -2013,38 +2013,40 @@ int qemu_loadvm_state(QEMUFile *f)return ret; } -void hmp_savevm (Monitor *mon,const QDict *qdict) +int save_vmstate (Monitor *mon,const char *name) {BlockDriverState *bs, *bs1;QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; -int ret; +int ret =-1 ;QEMUFile *f;int saved_vm_running;uint64_t vm_state_size;qemu_timeval tv;struct tm tm; -const char *name =qdict_get_try_str (qdict,"name" );Error *local_err =NULL ;AioContext *aio_context;if (!bdrv_all_can_snapshot (&bs)) {monitor_printf (mon,"Device '%s' is writable but does not " "support snapshots.\n" ,bdrv_get_device_name (bs)); -return ; +return ret; }/* Delete old snapshots of the same name */ -if (name &&bdrv_all_delete_snapshot (name, &bs1, &local_err) <0 ) { -error_reportf_err (local_err, -"Error while deleting snapshot on device '%s': " , -bdrv_get_device_name (bs1)); -return ; +if (name) { + ret =bdrv_all_delete_snapshot (name, &bs1, &local_err); +if (ret <0 ) { +error_reportf_err (local_err, +"Error while deleting snapshot on device '%s': " , +bdrv_get_device_name (bs1)); +return ret; + } } bs =bdrv_all_find_vmstate_bs ();if (bs ==NULL ) {monitor_printf (mon,"No block device can accept snapshots\n" ); -return ; +return ret; } aio_context =bdrv_get_aio_context (bs);
@@ -2053,7 +2055,7 @@ void hmp_savevm(Monitor *mon, const QDict *qdict) ret =global_state_store ();if (ret) {monitor_printf (mon,"Error saving global state\n" ); -return ; +return ret; }vm_stop (RUN_STATE_SAVE_VM );
@@ -2099,13 +2101,22 @@ void hmp_savevm(Monitor *mon, const QDict *qdict)if (ret <0 ) {monitor_printf (mon,"Error while creating snapshot on '%s'\n" ,bdrv_get_device_name (bs)); + goto the_end; } + ret =0 ; + the_end:aio_context_release (aio_context);if (saved_vm_running) {vm_start (); } +return ret; +} + +void hmp_savevm (Monitor *mon,const QDict *qdict) +{ +save_vmstate (mon,qdict_get_try_str (qdict,"name" )); }void qmp_xen_save_devices_state (const char *filename,Error **errp)
PATCH v6 7/9 replay: save/load initial state
In reply to Pavel DovgalyukThis patch implements initial vmstate creation or loading at the start of record/replay. It is needed for rewinding the execution in the replay mode.
v4 changes:
- snapshots are not created by default anymore
v3 changes:
- added rrsnapshot option
--- docs/replay.txt | 16 ++++++++++++++++ include/sysemu/replay.h | 9 +++++++++ qemu-options.hx | 8 ++++++-- replay/replay-snapshot.c | 17 +++++++++++++++++ replay/replay.c | 5 +++++ vl.c | 7 ++++++- 6 files changed, 59 insertions(+), 3 deletions(-)
docs/replay.txt +16 -0
@@ -196,6 +196,22 @@ is recorded to the log. In replay phase the queue is matched with events read from the log. Therefore block devices requests are processed deterministically. +Snapshotting +------------ + +New VM snapshots may be created in replay mode. They can be used later +to recover the desired VM state. All VM states created in replay mode +are associated with the moment of time in the replay scenario. +After recovering the VM state replay will start from that position. + +Default starting snapshot name may be specified with icount field +rrsnapshot as follows: + -icount shift=7,rr=record,rrfile=replay.bin,rrsnapshot=snapshot_name + +This snapshot is created at start of recording and restored at start +of replaying. It also can be loaded while replaying to roll back +the execution. + Network devices ---------------
include/sysemu/replay.h +9 -0
@@ -43,6 +43,9 @@ typedef struct ReplayNetState ReplayNetState;extern ReplayMode replay_mode; +/* Name of the initial VM snapshot */ +extern char *replay_snapshot; +/* Replay process control functions */ /*! Enables recording or saving event log with specified parameters */
@@ -149,4 +152,10 @@ void replay_unregister_net(ReplayNetState *rns);void replay_net_packet_event (ReplayNetState *rns,unsigned flags,const struct iovec *iov,int iovcnt); +/* VM state operations */ + +/*! Called at the start of execution. +Loads or saves initial vmstate depending on execution mode. */ +void replay_vmstate_init (void ); +#endif
qemu-options.hx +6 -2
@@ -3390,12 +3390,12 @@ re-inject them. ETEXI DEF("icount", HAS_ARG, QEMU_OPTION_icount, \ - "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=<filename>]\n" \ + "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=<filename>,rrsnapshot=<snapshot>]\n" \ " enable virtual instruction counter with 2^N clock ticks per\n" \ " instruction, enable aligning the host and virtual clocks\n" \ " or disable real time cpu sleeping\n", QEMU_ARCH_ALL) STEXI -@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename}] +@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename},rrsnapshot=@var{snapshot}] @findex -icount Enable virtual instruction counter. The virtual cpu will execute one instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified
@@ -3428,6 +3428,10 @@ when the shift value is high (how high depends on the host machine). When @option{rr} option is specified deterministic record/replay is enabled. Replay log is written into @var{filename} file in record mode and read from this file in replay mode. + +Option rrsnapshot is used to create new vm snapshot named @var{snapshot} +at the start of execution recording. In replay mode this option is used +to load the initial VM state. ETEXI DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
replay/replay-snapshot.c +17 -0
@@ -59,3 +59,20 @@ void replay_vmstate_register(void) {vmstate_register (NULL ,0 , &vmstate_replay, &replay_state); } + +void replay_vmstate_init (void ) +{ +if (replay_snapshot) { +if (replay_mode ==REPLAY_MODE_RECORD ) { +if (save_vmstate (cur_mon, replay_snapshot) !=0 ) { +error_report ("Could not create snapshot for icount record" ); +exit (1 ); + } + }else if (replay_mode ==REPLAY_MODE_PLAY ) { +if (load_vmstate (replay_snapshot) !=0 ) { +error_report ("Could not load snapshot for icount replay" ); +exit (1 ); + } + } + } +}
replay/replay.c +5 -0
@@ -26,6 +26,7 @@#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))ReplayMode replay_mode =REPLAY_MODE_NONE ; +char *replay_snapshot;/* Name of replay file */ static char *replay_filename;
@@ -292,6 +293,7 @@ void replay_configure(QemuOpts *opts)exit (1 ); } + replay_snapshot =g_strdup (qemu_opt_get (opts,"rrsnapshot" ));replay_vmstate_register ();replay_enable (fname, mode);
@@ -346,6 +348,9 @@ void replay_finish(void) replay_filename =NULL ; } +g_free (replay_snapshot); + replay_snapshot =NULL ; +replay_finish_events ();replay_mutex_destroy (); }
vl.c +6 -1
@@ -462,6 +462,9 @@ static QemuOptsList qemu_icount_opts = { }, { .name = "rrfile", .type = QEMU_OPT_STRING, + }, { + .name = "rrsnapshot", + .type = QEMU_OPT_STRING, }, {/* end of list */ } },
@@ -4656,7 +4659,9 @@ int main(int argc, char **argv, char **envp)replay_checkpoint (CHECKPOINT_RESET );qemu_system_reset (VMRESET_SILENT );register_global_state (); -if (loadvm) { +if (replay_mode !=REPLAY_MODE_NONE ) { +replay_vmstate_init (); + }else if (loadvm) {if (load_vmstate (loadvm) <0 ) { autostart =0 ; }
PATCH v6 8/9 block: implement bdrv_snapshot_goto for blkreplay
In reply to Pavel DovgalyukThis patch enables making snapshots with blkreplay used in block devices.
--- block/blkreplay.c | 8 ++++++++ 1 file changed, 8 insertions(+)
block/blkreplay.c +8 -0
@@ -130,6 +130,12 @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs)return ret; } +static int blkreplay_snapshot_goto (BlockDriverState *bs, +const char *snapshot_id) +{ +return bdrv_snapshot_goto (bs->file->bs, snapshot_id); +} +static BlockDriver bdrv_blkreplay = { .format_name ="blkreplay" , .protocol_name ="blkreplay" ,
@@ -145,6 +151,8 @@ static BlockDriver bdrv_blkreplay = { .bdrv_co_pwrite_zeroes = blkreplay_co_pwrite_zeroes, .bdrv_co_pdiscard = blkreplay_co_pdiscard, .bdrv_co_flush = blkreplay_co_flush, + + .bdrv_snapshot_goto = blkreplay_snapshot_goto, };static void bdrv_blkreplay_init (void )
PATCH v6 9/9 blkreplay: create temporary overlay for underlaying devices
In reply to Pavel DovgalyukThis patch allows using '-snapshot' behavior in record/replay mode. blkreplay layer creates temporary overlays on top of underlaying disk images. It is needed, because creating an overlay over blkreplay breaks the determinism.
--- block/blkreplay.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ stubs/replay.c | 1 + vl.c | 2 + 3 files changed, 78 insertions(+), 1 deletion(-)
block/blkreplay.c +76 -0
@@ -14,12 +14,76 @@#include "block/block_int.h" #include "sysemu/replay.h" #include "qapi/error.h" +#include "qapi/qmp/qstring.h" typedef struct Request {Coroutine *co;QEMUBH *bh; }Request ; +static BlockDriverState *blkreplay_append_snapshot (BlockDriverState *bs, +Error **errp) +{ +int ret; +BlockDriverState *bs_snapshot; +int64_t total_size; +QemuOpts *opts =NULL ; +char tmp_filename[PATH_MAX +1 ]; +QDict *snapshot_options =qdict_new (); + +/* Prepare options QDict for the overlay file */ +qdict_put (snapshot_options,"file.driver" , +qstring_from_str ("file" )); +qdict_put (snapshot_options,"driver" , +qstring_from_str ("qcow2" )); + +/* Create temporary file */ + ret =get_tmp_filename (tmp_filename,PATH_MAX +1 ); +if (ret <0 ) { +error_setg_errno (errp, -ret,"Could not get temporary filename" ); + goto out; + } +qdict_put (snapshot_options,"file.filename" , +qstring_from_str (tmp_filename)); + +/* Get the required size from the image */ + total_size =bdrv_getlength (bs); +if (total_size <0 ) { +error_setg_errno (errp, -total_size,"Could not get image size" ); + goto out; + } + + opts =qemu_opts_create (bdrv_qcow2.create_opts,NULL ,0 , &error_abort); +qemu_opt_set_number (opts,BLOCK_OPT_SIZE , total_size, &error_abort); + ret =bdrv_create (&bdrv_qcow2, tmp_filename, opts, errp); +qemu_opts_del (opts); +if (ret <0 ) { +error_prepend (errp,"Could not create temporary overlay '%s': " , + tmp_filename); + goto out; + } + + bs_snapshot =bdrv_open (NULL ,NULL , snapshot_options, +BDRV_O_RDWR |BDRV_O_TEMPORARY , errp); + snapshot_options =NULL ; +if (!bs_snapshot) { + ret = -EINVAL ; + goto out; + } + +/* bdrv_append() consumes a strong reference to bs_snapshot (i.e. it will +* call bdrv_unref() on it), so in order to be able to return one, we have +* to increase bs_snapshot's refcount here */ +bdrv_ref (bs_snapshot); +bdrv_append (bs_snapshot, bs); + +return bs_snapshot; + +out: +QDECREF (snapshot_options); +return NULL ; +} +static int blkreplay_open (BlockDriverState *bs,QDict *options,int flags,Error **errp) {
@@ -35,6 +99,14 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } +/* Add temporary snapshot to preserve the image */ +if (!replay_snapshot + && !blkreplay_append_snapshot (bs->file->bs, &local_err)) { + ret = -EINVAL ; +error_propagate (errp, local_err); + goto fail; + } + ret =0 ; fail:if (ret <0 ) {
@@ -45,6 +117,10 @@ fail:static void blkreplay_close (BlockDriverState *bs) { +if (!replay_snapshot) { +/* Unref created snapshot file */ +bdrv_unref (bs->file->bs); + } }static int64_t blkreplay_getlength (BlockDriverState *bs)
stubs/replay.c +1 -0
@@ -3,6 +3,7 @@#include "sysemu/sysemu.h" ReplayMode replay_mode; +char *replay_snapshot;int64_t replay_save_clock (unsigned int kind,int64_t clock) {
vl.c +1 -1
@@ -4479,7 +4479,7 @@ int main(int argc, char **argv, char **envp) }/* open the virtual block devices */ -if (snapshot || replay_mode !=REPLAY_MODE_NONE ) { +if (snapshot) {qemu_opts_foreach (qemu_find_opts ("drive" ), drive_enable_snapshot,NULL ,NULL ); }