Message ID | 1596122076-341293-6-git-send-email-steven.sistare@oracle.com |
---|---|
State | New |
Headers | show |
Series | [V1,01/32] savevm: add vmstate handler iterators | expand |
* Steven Sistare (steven.sistare@oracle.com) wrote: > On 7/30/2020 12:14 PM, Eric Blake wrote: > > On 7/30/20 10:14 AM, Steve Sistare wrote: > >> Provide the cprload QMP command. The VM is created from the file produced > >> by the cprsave command. Guest RAM is restored in-place from the shared > >> memory backend file, and guest block devices are used as is. The contents > >> of such devices must not be modified between the cprsave and cprload > >> operations. If the VM was running at cprsave time, then VM execution > >> resumes. > > > > Is it always wise to unconditionally resume, or might this command need an additional optional knob that says what state (paused or running) to move into? > > This can already be done. Issue a stop command before cprsave, then cprload will finish in a > paused state. > > Also, cprsave re-execs and leaves the guest in a paused state. One can > > send device add commands, then send cprload which continues > . You're suffering here because you're reinventing stuff rather than reusing existing migration paths. With the existing migration code we require the qemu to be started with -incoming ... so we know it's in a clean state ready for being loaded, and we've already got the -S mechanism that dictates whether or not the VM autostarts (regardless of the saved state in the image). The management layers find this pretty useful if they need to wire some networking or storage up at the point they know they've got a VM image that's loaded OK. Dave > > >> Syntax: > >>   {'command':'cprload', 'data':{'file':'str'}} > >> > >> Signed-off-by: Steve Sistare <steven.sistare@oracle.com> > >> Signed-off-by: Maran Wilson <maran.wilson@oracle.com> > >> --- > > > >> +++ b/qapi/migration.json > >> @@ -1635,3 +1635,14 @@ > >>  ## > >>  { 'command': 'cprsave', 'data': { 'file': 'str', 'mode': 'str' } } > >>  +## > >> +# @cprload: > >> +# > >> +# Start virtual machine from checkpoint file that was created earlier using > >> +# the cprsave command. > >> +# > >> +# @file: name of checkpoint file > >> +# > >> +# Since 5.0 > > > > another 5.2 instance. I'll quit pointing it out for the rest of the series. > > Will find and fix all, thanks. > > - Steve >
On 9/11/2020 1:18 PM, Dr. David Alan Gilbert wrote: > * Steven Sistare (steven.sistare@oracle.com) wrote: >> On 7/30/2020 12:14 PM, Eric Blake wrote: >>> On 7/30/20 10:14 AM, Steve Sistare wrote: >>>> Provide the cprload QMP command. The VM is created from the file produced >>>> by the cprsave command. Guest RAM is restored in-place from the shared >>>> memory backend file, and guest block devices are used as is. The contents >>>> of such devices must not be modified between the cprsave and cprload >>>> operations. If the VM was running at cprsave time, then VM execution >>>> resumes. >>> >>> Is it always wise to unconditionally resume, or might this command need an additional optional knob that says what state (paused or running) to move into? >> >> This can already be done. Issue a stop command before cprsave, then cprload will finish in a >> paused state. >> >> Also, cprsave re-execs and leaves the guest in a paused state. One can >> >> send device add commands, then send cprload which continues >> . > > You're suffering here because you're reinventing stuff rather than > reusing existing migration paths. > With the existing migration code we require the qemu > to be started with -incoming ... so we know it's in a clean > state ready for being loaded, and we've already got the -S > mechanism that dictates whether or not the VM autostarts > (regardless of the saved state in the image). The management > layers find this pretty useful if they need to wire some networking > or storage up at the point they know they've got a VM image that's > loaded OK. I am not seeing the issue here. The manager can hot plug with cprsave as easily as with migration, at the same transition points. I don't see what migration paths should be reused here. CPR Migration - cprsave restarts qemu with the env - qemu -S -incoming defer var QEMU_START_FREEZE set, which clears autostart just like -S. (see patch 15) - command-line devices created - command-line devices created - vmstate is prelaunch - vmstate is inmigrate - manager sends hotplug commands - manager sends hotplug commands - manager sends cprload - manager sends migrate_incoming It would perhaps be more correct for cprsave to leave the vm in the preconfig state. I don't feel like I'm suffering. At least not yet :) - Steve >>>> Syntax: >>>>   {'command':'cprload', 'data':{'file':'str'}} >>>> >>>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com> >>>> Signed-off-by: Maran Wilson <maran.wilson@oracle.com> >>>> --- >>> >>>> +++ b/qapi/migration.json >>>> @@ -1635,3 +1635,14 @@ >>>>  ## >>>>  { 'command': 'cprsave', 'data': { 'file': 'str', 'mode': 'str' } } >>>>  +## >>>> +# @cprload: >>>> +# >>>> +# Start virtual machine from checkpoint file that was created earlier using >>>> +# the cprsave command. >>>> +# >>>> +# @file: name of checkpoint file >>>> +# >>>> +# Since 5.0 >>> >>> another 5.2 instance. I'll quit pointing it out for the rest of the series. >> >> Will find and fix all, thanks. >> >> - Steve >>
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 6fe86e6..5360da5 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -25,6 +25,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify); void qemu_remove_machine_init_done_notifier(Notifier *notify); void save_cpr_snapshot(const char *file, const char *mode, Error **errp); +void load_cpr_snapshot(const char *file, Error **errp); extern int autostart; @@ -53,6 +54,7 @@ extern uint8_t *boot_splash_filedata; extern bool enable_mlock; extern bool enable_cpu_pm; extern QEMUClockType rtc_clock; +extern int start_on_wake; #define MAX_OPTION_ROMS 16 typedef struct QEMUOptionRom { diff --git a/migration/savevm.c b/migration/savevm.c index ff1a46e..1509173 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2948,6 +2948,40 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) migration_incoming_state_destroy(); } +void load_cpr_snapshot(const char *file, Error **errp) +{ + QEMUFile *f; + int ret; + RunState state; + + if (runstate_is_running()) { + error_setg(errp, "cprload called for a running VM"); + return; + } + + f = qf_file_open(file, O_RDONLY, 0, errp); + if (!f) { + return; + } + + ret = qemu_loadvm_state(f, VMS_REBOOT); + qemu_fclose(f); + if (ret < 0) { + error_setg(errp, "Error %d while loading VM state", ret); + return; + } + + state = global_state_get_runstate(); + if (state == RUN_STATE_RUNNING) { + vm_start(); + } else { + runstate_set(state); + if (runstate_check(RUN_STATE_SUSPENDED)) { + start_on_wake = 1; + } + } +} + int load_snapshot(const char *name, Error **errp) { BlockDriverState *bs, *bs_vm_state; diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 9ec7b88..81e6feb 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -167,6 +167,11 @@ void qmp_cprsave(const char *file, const char *mode, Error **errp) save_cpr_snapshot(file, mode, errp); } +void qmp_cprload(const char *file, Error **errp) +{ + load_cpr_snapshot(file, errp); +} + void qmp_system_wakeup(Error **errp) { if (!qemu_wakeup_suspend_enabled()) { diff --git a/qapi/migration.json b/qapi/migration.json index b61df1d..ce4d32b 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1635,3 +1635,14 @@ ## { 'command': 'cprsave', 'data': { 'file': 'str', 'mode': 'str' } } +## +# @cprload: +# +# Start virtual machine from checkpoint file that was created earlier using +# the cprsave command. +# +# @file: name of checkpoint file +# +# Since 5.0 +## +{ 'command': 'cprload', 'data': { 'file': 'str' } } diff --git a/softmmu/vl.c b/softmmu/vl.c index 660537a..8478778 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -137,6 +137,7 @@ static time_t rtc_ref_start_datetime; static int rtc_realtime_clock_offset; /* used only with QEMU_CLOCK_REALTIME */ static int rtc_host_datetime_offset = -1; /* valid & used only with RTC_BASE_DATETIME */ +int start_on_wake; QEMUClockType rtc_clock; int vga_interface_type = VGA_NONE; static DisplayOptions dpy; @@ -602,6 +603,8 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_PRELAUNCH, RUN_STATE_RUNNING }, { RUN_STATE_PRELAUNCH, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_PRELAUNCH, RUN_STATE_INMIGRATE }, + { RUN_STATE_PRELAUNCH, RUN_STATE_SUSPENDED }, + { RUN_STATE_PRELAUNCH, RUN_STATE_PAUSED }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PAUSED }, @@ -1519,7 +1522,17 @@ void qemu_system_wakeup_request(WakeupReason reason, Error **errp) if (!(wakeup_reason_mask & (1 << reason))) { return; } - runstate_set(RUN_STATE_RUNNING); + + /* + * Must call vm_start if it has never been called, to invoke the state + * change callbacks for the first time. + */ + if (start_on_wake) { + start_on_wake = 0; + vm_start(); + } else { + runstate_set(RUN_STATE_RUNNING); + } wakeup_reason = reason; qemu_notify_event(); }