This patch would fix the case where paused/running state is changed
during live migration. In order to do so, libvirt attaches an
event handler that tracks the paused/running state on the source side.
Then, based on the state _just before_ the end of the migration, it can
stick a VIR_MIGRATE_PAUSED flag into the flags passed to MigrateFinish
or MigrateFinish2. It is not a problem for the QEMU driver if the flag
is not passed in the Perform callback.
This would have the advantage of working even with asynchronous changes
to the state that can happen during the migration itself. However, since
the driver lock is held during migration, this is basically impossible to
test until asynchronous notifications are in place. Also, it is somewhat
inherently racy: maybe when notifications are added to QEMU, it should
also have a "sync" monitor command to flush all notifications.
Hence, this is just an RFC.
* src/libvirt.c (struct _migrateEventState, migrateEventState,
virMigrateEventCallback): New.
(virDomainMigrateVersion1, virDomainMigrateVersion2): Use them.
---
src/libvirt.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 60 insertions(+), 8 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c
index 9d03e13..12e90cd 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -2951,6 +2951,42 @@ error:
return NULL;
}
+typedef struct _migrateEventData migrateEventData;
+
+struct _migrateEventData {
+ virDomainPtr domain;
+ virDomainInfo info;
+};
+
+static int
+virMigrateEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virDomainPtr dom,
+ int event,
+ int detail,
+ void *opaque)
+{
+ migrateEventData *data = opaque;
+
+ if (dom != data->domain)
+ return 0;
+
+ switch (event)
+ {
+ case VIR_DOMAIN_EVENT_SUSPENDED:
+ if (detail != VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED)
+ data->info.state = VIR_DOMAIN_PAUSED;
+ break;
+ case VIR_DOMAIN_EVENT_STOPPED:
+ if (detail == VIR_DOMAIN_EVENT_STOPPED_CRASHED)
+ data->info.state = VIR_DOMAIN_CRASHED;
+ else if (detail != VIR_DOMAIN_EVENT_STOPPED_MIGRATED)
+ data->info.state = VIR_DOMAIN_SHUTOFF;
+ break;
+ }
+
+ return 0;
+}
+
static virDomainPtr
virDomainMigrateVersion1 (virDomainPtr domain,
@@ -2964,12 +3000,14 @@ virDomainMigrateVersion1 (virDomainPtr domain,
char *uri_out = NULL;
char *cookie = NULL;
int cookielen = 0;
- virDomainInfo info;
+ migrateEventData data;
- virDomainGetInfo (domain, &info);
- if (info.state == VIR_DOMAIN_PAUSED) {
- flags |= VIR_MIGRATE_PAUSED;
+ data.domain = domain;
+ if (virConnectDomainEventRegister (domain->conn, virMigrateEventCallback,
+ &data, NULL) == -1) {
+ return NULL;
}
+ virDomainGetInfo (domain, &data.info);
/* Prepare the migration.
*
@@ -3003,6 +3041,11 @@ virDomainMigrateVersion1 (virDomainPtr domain,
(domain, cookie, cookielen, uri, flags, dname, bandwidth) == -1)
goto done;
+ /* TODO: what if the host has crashed during live migration? */
+ if (data.info.state == VIR_DOMAIN_PAUSED) {
+ flags |= VIR_MIGRATE_PAUSED;
+ }
+
/* Get the destination domain and return it or error.
* 'domain' no longer actually exists at this point
* (or so we hope), but we still use the object in memory
@@ -3016,6 +3059,7 @@ virDomainMigrateVersion1 (virDomainPtr domain,
ddomain = virDomainLookupByName (dconn, dname);
done:
+ virConnectDomainEventDeregister (domain->conn, virMigrateEventCallback);
VIR_FREE (uri_out);
VIR_FREE (cookie);
return ddomain;
@@ -3034,7 +3078,7 @@ virDomainMigrateVersion2 (virDomainPtr domain,
char *cookie = NULL;
char *dom_xml = NULL;
int cookielen = 0, ret;
- virDomainInfo info;
+ migrateEventData data;
/* Prepare the migration.
*
@@ -3061,10 +3105,12 @@ virDomainMigrateVersion2 (virDomainPtr domain,
if (!dom_xml)
return NULL;
- virDomainGetInfo (domain, &info);
- if (info.state == VIR_DOMAIN_PAUSED) {
- flags |= VIR_MIGRATE_PAUSED;
+ data.domain = domain;
+ if (virConnectDomainEventRegister (domain->conn, virMigrateEventCallback,
+ &data, NULL) == -1) {
+ return NULL;
}
+ virDomainGetInfo (domain, &data.info);
ret = dconn->driver->domainMigratePrepare2
(dconn, &cookie, &cookielen, uri, &uri_out, flags, dname,
@@ -3088,6 +3134,11 @@ virDomainMigrateVersion2 (virDomainPtr domain,
ret = domain->conn->driver->domainMigratePerform
(domain, cookie, cookielen, uri, flags, dname, bandwidth);
+ /* TODO: what if the host has crashed during live migration? */
+ if (data.info.state == VIR_DOMAIN_PAUSED) {
+ flags |= VIR_MIGRATE_PAUSED;
+ }
+
/* In version 2 of the migration protocol, we pass the
* status code from the sender to the destination host,
* so it can do any cleanup if the migration failed.
@@ -3097,6 +3148,7 @@ virDomainMigrateVersion2 (virDomainPtr domain,
(dconn, dname, cookie, cookielen, uri, flags, ret);
done:
+ virConnectDomainEventDeregister (domain->conn, virMigrateEventCallback);
VIR_FREE (uri_out);
VIR_FREE (cookie);
return ddomain;
--
1.6.5.2