[libvirt] [PATCH v3] qemu: wait for SPICE to migrate
Daniel P. Berrange
berrange at redhat.com
Tue Sep 25 17:57:29 UTC 2012
On Thu, Sep 20, 2012 at 01:29:21PM +0200, Michal Privoznik wrote:
> Recently, there have been some improvements made to qemu so it
> supports seamless migration or something very close to it.
> However, it requires libvirt interaction. Once qemu is migrated,
> the SPICE server needs to send its internal state to the destination.
> Once it's done, it fires SPICE_MIGRATE_COMPLETED event and this
> fact is advertised in 'query-spice' output as well.
> We must not kill qemu until SPICE server finishes the transfer.
> ---
> src/qemu/qemu_capabilities.c | 4 +++
> src/qemu/qemu_capabilities.h | 1 +
> src/qemu/qemu_command.c | 8 ++++++
> src/qemu/qemu_migration.c | 33 ++++++++++++++++++++++++--
> src/qemu/qemu_monitor.c | 24 +++++++++++++++++++
> src/qemu/qemu_monitor.h | 2 +
> src/qemu/qemu_monitor_json.c | 52 ++++++++++++++++++++++++++++++++++++++++++
> src/qemu/qemu_monitor_json.h | 3 ++
> 8 files changed, 124 insertions(+), 3 deletions(-)
>
> diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
> index 278b550..26364ec 100644
> --- a/src/qemu/qemu_capabilities.c
> +++ b/src/qemu/qemu_capabilities.c
> @@ -180,6 +180,8 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
> "ide-drive.wwn",
> "scsi-disk.wwn",
> "seccomp-sandbox",
> +
> + "seamless-migration", /* 110 */
> );
>
> struct _qemuCaps {
> @@ -1146,6 +1148,8 @@ qemuCapsComputeCmdFlags(const char *help,
> }
> if (strstr(help, "-spice"))
> qemuCapsSet(caps, QEMU_CAPS_SPICE);
> + if (strstr(help, "seamless-migration="))
> + qemuCapsSet(caps, QEMU_CAPS_SEAMLESS_MIGRATION);
> if (strstr(help, "boot=on"))
> qemuCapsSet(caps, QEMU_CAPS_DRIVE_BOOT);
> if (strstr(help, "serial=s"))
> diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
> index 4da2a29..2567fd7 100644
> --- a/src/qemu/qemu_capabilities.h
> +++ b/src/qemu/qemu_capabilities.h
> @@ -145,6 +145,7 @@ enum qemuCapsFlags {
> QEMU_CAPS_IDE_DRIVE_WWN = 107, /* Is ide-drive.wwn available? */
> QEMU_CAPS_SCSI_DISK_WWN = 108, /* Is scsi-disk.wwn available? */
> QEMU_CAPS_SECCOMP_SANDBOX = 109, /* -sandbox */
> + QEMU_CAPS_SEAMLESS_MIGRATION = 110, /* seamless-migration for SPICE */
>
> QEMU_CAPS_LAST, /* this must always be the last item */
> };
> diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
> index cbf4aee..33f7e55 100644
> --- a/src/qemu/qemu_command.c
> +++ b/src/qemu/qemu_command.c
> @@ -6081,6 +6081,14 @@ qemuBuildCommandLine(virConnectPtr conn,
> if (def->graphics[0]->data.spice.copypaste == VIR_DOMAIN_GRAPHICS_SPICE_CLIPBOARD_COPYPASTE_NO)
> virBufferAddLit(&opt, ",disable-copy-paste");
>
> + if (qemuCapsGet(caps, QEMU_CAPS_SEAMLESS_MIGRATION)) {
> + /* If qemu supports seamless migration turn it
> + * unconditionally on. If migration destination
> + * doesn't support it, it fallbacks to previous
> + * migration algorithm silently. */
> + virBufferAddLit(&opt, ",seamless-migration=on");
> + }
> +
> virCommandAddArg(cmd, "-spice");
> virCommandAddArgBuffer(cmd, &opt);
> if (def->graphics[0]->data.spice.keymap)
> diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
> index 99fc8ce..114d04a 100644
> --- a/src/qemu/qemu_migration.c
> +++ b/src/qemu/qemu_migration.c
> @@ -891,11 +891,13 @@ static int
> qemuMigrationUpdateJobStatus(struct qemud_driver *driver,
> virDomainObjPtr vm,
> const char *job,
> - enum qemuDomainAsyncJob asyncJob)
> + enum qemuDomainAsyncJob asyncJob,
> + bool wait_for_spice)
> {
> qemuDomainObjPrivatePtr priv = vm->privateData;
> int ret;
> int status;
> + bool spice_migrated = true;
> unsigned long long memProcessed;
> unsigned long long memRemaining;
> unsigned long long memTotal;
> @@ -910,6 +912,13 @@ qemuMigrationUpdateJobStatus(struct qemud_driver *driver,
> &memProcessed,
> &memRemaining,
> &memTotal);
> +
> + /* If qemu says migrated, check spice */
> + if (wait_for_spice && (ret == 0) &&
> + (status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED))
> + ret = qemuMonitorGetSpiceMigrationStatus(priv->mon,
> + &spice_migrated);
> +
> qemuDomainObjExitMonitorWithDriver(driver, vm);
>
> if (ret < 0 || virTimeMillisNow(&priv->job.info.timeElapsed) < 0) {
> @@ -940,6 +949,8 @@ qemuMigrationUpdateJobStatus(struct qemud_driver *driver,
>
> case QEMU_MONITOR_MIGRATION_STATUS_COMPLETED:
> priv->job.info.type = VIR_DOMAIN_JOB_COMPLETED;
> + if (spice_migrated)
> + priv->job.info.type = VIR_DOMAIN_JOB_COMPLETED;
This addition doesn't do anything, since the previous line
has already set the same value.
> ret = 0;
> break;
>
> @@ -967,6 +978,13 @@ qemuMigrationWaitForCompletion(struct qemud_driver *driver, virDomainObjPtr vm,
> {
> qemuDomainObjPrivatePtr priv = vm->privateData;
> const char *job;
> + bool wait_for_spice;
> +
> + /* If guest uses SPICE and supports seamles_migration we have to hold up
> + * migration finish until SPICE server transfers its data */
> + wait_for_spice = (vm->def->ngraphics == 1) &&
> + (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) &&
> + qemuCapsGet(priv->caps, QEMU_CAPS_SEAMLESS_MIGRATION);
I don't find this very readable - it is better to do it as a
regular if() IMHO. eg
bool wait_for_spice = false;
if (...)
wait_for_spice = true;
>
> switch (priv->job.asyncJob) {
> case QEMU_ASYNC_JOB_MIGRATION_OUT:
> @@ -988,7 +1006,8 @@ qemuMigrationWaitForCompletion(struct qemud_driver *driver, virDomainObjPtr vm,
> /* Poll every 50ms for progress & to allow cancellation */
> struct timespec ts = { .tv_sec = 0, .tv_nsec = 50 * 1000 * 1000ull };
>
> - if (qemuMigrationUpdateJobStatus(driver, vm, job, asyncJob) < 0)
> + if (qemuMigrationUpdateJobStatus(driver, vm, job,
> + asyncJob, wait_for_spice) < 0)
> goto cleanup;
>
> if (dconn && virConnectIsAlive(dconn) <= 0) {
> @@ -1840,6 +1859,7 @@ qemuMigrationRun(struct qemud_driver *driver,
> int fd = -1;
> unsigned long migrate_speed = resource ? resource : priv->migMaxBandwidth;
> virErrorPtr orig_err = NULL;
> + bool wait_for_spice;
>
> VIR_DEBUG("driver=%p, vm=%p, cookiein=%s, cookieinlen=%d, "
> "cookieout=%p, cookieoutlen=%p, flags=%lx, resource=%lu, "
> @@ -1848,6 +1868,12 @@ qemuMigrationRun(struct qemud_driver *driver,
> cookieout, cookieoutlen, flags, resource,
> spec, spec->destType, spec->fwdType);
>
> + /* If guest uses SPICE and supports seamless migration we have to hold up
> + * migration finish until SPICE server transfers its data */
> + wait_for_spice = (vm->def->ngraphics == 1) &&
> + (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) &&
> + qemuCapsGet(priv->caps, QEMU_CAPS_SEAMLESS_MIGRATION);
Same comment as before.
Since both callers of qemuMigrateUpdateJobStatus() have to repeat
this logic, why not push this down into that method itself and
make 'wait_for_spice' be a local variable there instead of a param.
> +
> if (virLockManagerPluginUsesState(driver->lockManager) &&
> !cookieout) {
> virReportError(VIR_ERR_INTERNAL_ERROR,
> @@ -1946,7 +1972,8 @@ qemuMigrationRun(struct qemud_driver *driver,
> * connection from qemu which may never be initiated.
> */
> if (qemuMigrationUpdateJobStatus(driver, vm, _("migration job"),
> - QEMU_ASYNC_JOB_MIGRATION_OUT) < 0)
> + QEMU_ASYNC_JOB_MIGRATION_OUT,
> + wait_for_spice) < 0)
> goto cancel;
>
> while ((fd = accept(spec->dest.unix_socket.sock, NULL, NULL)) < 0) {
> diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
> index f36c8a8..7695a81 100644
> --- a/src/qemu/qemu_monitor.c
> +++ b/src/qemu/qemu_monitor.c
> @@ -1827,6 +1827,30 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon,
> }
>
>
> +int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon,
> + bool *spice_migrated)
> +{
> + int ret;
> + VIR_DEBUG("mon=%p", mon);
> +
> + if (!mon) {
> + virReportError(VIR_ERR_INVALID_ARG, "%s",
> + _("monitor must not be NULL"));
> + return -1;
> + }
> +
> + if (mon->json) {
> + ret = qemuMonitorJSONGetSpiceMigrationStatus(mon, spice_migrated);
> + } else {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("JSON monitor is required"));
This error code (any in fact all other similar uses in that file)
should be VIR_ERR_OPERATION_UNSUPPORTED, since this isn't an
end user configuration issue.
REgards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
More information about the libvir-list
mailing list