[libvirt] [PATCH 00/14] Add support for XBZRLE migration compression

QEMU supports XBZRLE compression of repeatedly transferred pages during live migration and this series makes it usable through libvirt. The first two patches add support for VIR_MIGRATE_COMPRESSED flag that enables XBZRLE compression. Patches 3--9 add an enhanced and extensible version of virDomainGetJobInfo and make various migration statistics available through it. The rest of the patches (10--14) add APIs for querying and changing XBZRLE compression cache size. Jiri Denemark (14): Introduce VIR_MIGRATE_COMPRESSED flag qemu: Add support for compressed migration Introduce virDomainGetJobStats API python: Implement virDomainGetJobStats wrapper remote: Auto-allocate params in remoteDeserializeTypedParameters remote: Implement virDomainGetJobStats virsh: Use virDomainGetJobStats in domjobinfo if available qemu: Parse more fields from query-migrate QMP command qemu: Implement virDomainGetJobStats Introduce virDomainMigrate*CompressionCache APIs python: Implement virDomainMigrateGetCompressionCache wrapper remote: Implement virDomainMigrate*CompressionCache virsh: Add migrate-compcache command qemu: Implement virDomainMigrate*CompressionCache daemon/remote.c | 44 ++++++ include/libvirt/libvirt.h.in | 213 ++++++++++++++++++++++++++ python/generator.py | 2 + python/libvirt-override-api.xml | 13 ++ python/libvirt-override.c | 70 +++++++++ src/driver.h | 18 +++ src/libvirt.c | 158 +++++++++++++++++++ src/libvirt_public.syms | 3 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 3 +- src/qemu/qemu_driver.c | 235 ++++++++++++++++++++++++++++ src/qemu/qemu_migration.c | 103 ++++++++++--- src/qemu/qemu_migration.h | 3 +- src/qemu/qemu_monitor.c | 101 ++++++++++-- src/qemu/qemu_monitor.h | 52 ++++++- src/qemu/qemu_monitor_json.c | 331 ++++++++++++++++++++++++++++++++++------ src/qemu/qemu_monitor_json.h | 15 +- src/qemu/qemu_monitor_text.c | 47 +++--- src/qemu/qemu_monitor_text.h | 5 +- src/remote/remote_driver.c | 138 ++++++++++++----- src/remote/remote_protocol.x | 31 +++- src/remote_protocol-structs | 26 ++++ src/rpc/gendispatch.pl | 2 +- tools/virsh-domain.c | 291 +++++++++++++++++++++++++++++------ tools/virsh.pod | 18 ++- 25 files changed, 1718 insertions(+), 205 deletions(-) -- 1.8.1.2

This flag may be used with migration APIs to request compression of migration data. --- include/libvirt/libvirt.h.in | 1 + tools/virsh-domain.c | 8 ++++++++ tools/virsh.pod | 6 ++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ad30cd0..eda9e12 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1187,6 +1187,7 @@ typedef enum { * when supported */ VIR_MIGRATE_UNSAFE = (1 << 9), /* force migration even if it is considered unsafe */ VIR_MIGRATE_OFFLINE = (1 << 10), /* offline migrate */ + VIR_MIGRATE_COMPRESSED = (1 << 11), /* compress data during migration */ } virDomainMigrateFlags; /* Domain migration. */ diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 3e4be89..f8b0cec 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8340,6 +8340,11 @@ static const vshCmdOptDef opts_migrate[] = { .flags = 0, .help = N_("display the progress of migration") }, + {.name = "compressed", + .type = VSH_OT_BOOL, + .flags = 0, + .help = N_("compress repeated pages during live migration") + }, {.name = "domain", .type = VSH_OT_DATA, .flags = VSH_OFLAG_REQ, @@ -8430,6 +8435,9 @@ doMigrate(void *opaque) if (vshCommandOptBool(cmd, "unsafe")) flags |= VIR_MIGRATE_UNSAFE; + if (vshCommandOptBool(cmd, "compressed")) + flags |= VIR_MIGRATE_COMPRESSED; + if (vshCommandOptBool(cmd, "offline")) { flags |= VIR_MIGRATE_OFFLINE; } diff --git a/tools/virsh.pod b/tools/virsh.pod index 96666c4..e675850 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1043,7 +1043,7 @@ stats. =item B<migrate> [I<--live>] [I<--offline>] [I<--direct>] [I<--p2p> [I<--tunnelled>]] [I<--persistent>] [I<--undefinesource>] [I<--suspend>] [I<--copy-storage-all>] [I<--copy-storage-inc>] [I<--change-protection>] [I<--unsafe>] [I<--verbose>] -I<domain> I<desturi> [I<migrateuri>] [I<dname>] +[I<--compressed>] I<domain> I<desturi> [I<migrateuri>] [I<dname>] [I<--timeout> B<seconds>] [I<--xml> B<file>] Migrate domain to another host. Add I<--live> for live migration; <--p2p> @@ -1064,7 +1064,9 @@ host. I<--change-protection> enforces that no incompatible configuration changes will be made to the domain while the migration is underway; this flag is implicitly enabled when supported by the hypervisor, but can be explicitly used to reject the migration if the hypervisor lacks change protection -support. I<--verbose> displays the progress of migration. +support. I<--verbose> displays the progress of migration. I<--compressed> +activates compression of memory pages that have to be transferred repeatedly +during live migration. B<Note>: Individual hypervisors usually do not support all possible types of migration. For example, QEMU does not support direct migration. -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
This flag may be used with migration APIs to request compression of migration data. --- include/libvirt/libvirt.h.in | 1 + tools/virsh-domain.c | 8 ++++++++ tools/virsh.pod | 6 ++++-- 3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ad30cd0..eda9e12 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1187,6 +1187,7 @@ typedef enum { * when supported */ VIR_MIGRATE_UNSAFE = (1 << 9), /* force migration even if it is considered unsafe */ VIR_MIGRATE_OFFLINE = (1 << 10), /* offline migrate */ + VIR_MIGRATE_COMPRESSED = (1 << 11), /* compress data during migration */
I'm just wondering if this is enough future proof if somebody invents other migration compression methods. Well but that's something our future selves will need to worry about. ACK (unless somebody else speaks until I'm finished) Peter

On 02/21/2013 02:04 PM, Peter Krempa wrote:
On 02/19/13 13:35, Jiri Denemark wrote:
This flag may be used with migration APIs to request compression of migration data. --- include/libvirt/libvirt.h.in | 1 + tools/virsh-domain.c | 8 ++++++++ tools/virsh.pod | 6 ++++-- 3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ad30cd0..eda9e12 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1187,6 +1187,7 @@ typedef enum { * when supported */ VIR_MIGRATE_UNSAFE = (1 << 9), /* force migration even if it is considered unsafe */ VIR_MIGRATE_OFFLINE = (1 << 10), /* offline migrate */ + VIR_MIGRATE_COMPRESSED = (1 << 11), /* compress data during migration */
I'm just wondering if this is enough future proof if somebody invents other migration compression methods. Well but that's something our future selves will need to worry about.
ACK (unless somebody else speaks until I'm finished)
I guess there are use cases where compression helps, and use cases where it costs more memory and actually slows things down to turn on, so requiring the user to pass the new option in order to use compression makes sense. However, I'm wondering if we need further control, such as the ability to control the side of the source's xbzrle cache. I haven't checked if later in the series you add a counterpart to migrate-setspeed that allows modifying compression cache size. At any rate, I'm okay with this patch as-is. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 02/22/13 01:45, Eric Blake wrote:
On 02/21/2013 02:04 PM, Peter Krempa wrote:
On 02/19/13 13:35, Jiri Denemark wrote:
This flag may be used with migration APIs to request compression of migration data. --- include/libvirt/libvirt.h.in | 1 + tools/virsh-domain.c | 8 ++++++++ tools/virsh.pod | 6 ++++-- 3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ad30cd0..eda9e12 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1187,6 +1187,7 @@ typedef enum { * when supported */ VIR_MIGRATE_UNSAFE = (1 << 9), /* force migration even if it is considered unsafe */ VIR_MIGRATE_OFFLINE = (1 << 10), /* offline migrate */ + VIR_MIGRATE_COMPRESSED = (1 << 11), /* compress data during migration */
I'm just wondering if this is enough future proof if somebody invents other migration compression methods. Well but that's something our future selves will need to worry about.
ACK (unless somebody else speaks until I'm finished)
I guess there are use cases where compression helps, and use cases where it costs more memory and actually slows things down to turn on, so requiring the user to pass the new option in order to use compression makes sense. However, I'm wondering if we need further control, such as the ability to control the side of the source's xbzrle cache. I haven't checked if later in the series you add a counterpart to migrate-setspeed that allows modifying compression cache size.
Patch 10/14 adds APIs that allow to control the xbzrle cache. If the cache is small enough, the xbzrle compression is effectively disabled. As the compression is applied only on pages that became dirty during live migration and it doesn't use any CPU heavy algorithm, this shouldn't slow things down. Peter

On Thu, Feb 21, 2013 at 10:04:07PM +0100, Peter Krempa wrote:
On 02/19/13 13:35, Jiri Denemark wrote:
This flag may be used with migration APIs to request compression of migration data. --- include/libvirt/libvirt.h.in | 1 + tools/virsh-domain.c | 8 ++++++++ tools/virsh.pod | 6 ++++-- 3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ad30cd0..eda9e12 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1187,6 +1187,7 @@ typedef enum { * when supported */ VIR_MIGRATE_UNSAFE = (1 << 9), /* force migration even if it is considered unsafe */ VIR_MIGRATE_OFFLINE = (1 << 10), /* offline migrate */ + VIR_MIGRATE_COMPRESSED = (1 << 11), /* compress data during migration */
I'm just wondering if this is enough future proof if somebody invents other migration compression methods. Well but that's something our future selves will need to worry about.
I think this is ok as it is.
ACK (unless somebody else speaks until I'm finished)
ACK 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 :|

--- src/qemu/qemu_migration.c | 59 ++++++++++++++++++++-- src/qemu/qemu_migration.h | 3 +- src/qemu/qemu_monitor.c | 43 ++++++++++++++++ src/qemu/qemu_monitor.h | 13 +++++ src/qemu/qemu_monitor_json.c | 118 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 5 ++ 6 files changed, 237 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 11d7d6c..de8dfec 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1150,6 +1150,47 @@ qemuMigrationSetOffline(virQEMUDriverPtr driver, static int +qemuMigrationSetCompression(virQEMUDriverPtr driver, + virDomainObjPtr vm, + enum qemuDomainAsyncJob job) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + int ret; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, job) < 0) + return -1; + + ret = qemuMonitorGetMigrationCapability( + priv->mon, + QEMU_MONITOR_MIGRATION_CAPS_XBZRLE); + + if (ret < 0) { + goto cleanup; + } else if (ret == 0) { + if (job == QEMU_ASYNC_JOB_MIGRATION_IN) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Compressed migration is not supported by " + "target QEMU binary")); + } else { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Compressed migration is not supported by " + "source QEMU binary")); + } + ret = -1; + goto cleanup; + } + + ret = qemuMonitorSetMigrationCapability( + priv->mon, + QEMU_MONITOR_MIGRATION_CAPS_XBZRLE); + +cleanup: + qemuDomainObjExitMonitor(driver, vm); + return ret; +} + + +static int qemuMigrationUpdateJobStatus(virQEMUDriverPtr driver, virDomainObjPtr vm, const char *job, @@ -1704,13 +1745,16 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, if (virFDStreamOpen(st, dataFD[1]) < 0) { virReportSystemError(errno, "%s", _("cannot pass pipe for tunnelled migration")); - virDomainAuditStart(vm, "migrated", false); - qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED, 0); - goto endjob; + goto stop; } dataFD[1] = -1; /* 'st' owns the FD now & will close it */ } + if (flags & VIR_MIGRATE_COMPRESSED && + qemuMigrationSetCompression(driver, vm, + QEMU_ASYNC_JOB_MIGRATION_IN) < 0) + goto stop; + if (mig->lockState) { VIR_DEBUG("Received lockstate %s", mig->lockState); VIR_FREE(priv->lockState); @@ -1776,6 +1820,10 @@ cleanup: virObjectUnref(caps); return ret; +stop: + virDomainAuditStart(vm, "migrated", false); + qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED, 0); + endjob: if (!qemuMigrationJobFinish(driver, vm)) { vm = NULL; @@ -2255,6 +2303,11 @@ qemuMigrationRun(virQEMUDriverPtr driver, goto cleanup; } + if (flags & VIR_MIGRATE_COMPRESSED && + qemuMigrationSetCompression(driver, vm, + QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) + goto cleanup; + if (qemuDomainObjEnterMonitorAsync(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) goto cleanup; diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index 1729d73..505e911 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -37,7 +37,8 @@ VIR_MIGRATE_NON_SHARED_INC | \ VIR_MIGRATE_CHANGE_PROTECTION | \ VIR_MIGRATE_UNSAFE | \ - VIR_MIGRATE_OFFLINE) + VIR_MIGRATE_OFFLINE | \ + VIR_MIGRATE_COMPRESSED) enum qemuMigrationJobPhase { QEMU_MIGRATION_PHASE_NONE = 0, diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 7af571d..631ff92 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -101,6 +101,10 @@ VIR_ENUM_IMPL(qemuMonitorMigrationStatus, QEMU_MONITOR_MIGRATION_STATUS_LAST, "inactive", "active", "completed", "failed", "cancelled") +VIR_ENUM_IMPL(qemuMonitorMigrationCaps, + QEMU_MONITOR_MIGRATION_CAPS_LAST, + "xbzrle") + VIR_ENUM_IMPL(qemuMonitorVMStatus, QEMU_MONITOR_VM_STATUS_LAST, "debug", "inmigrate", "internal-error", "io-error", "paused", @@ -3383,3 +3387,42 @@ char *qemuMonitorGetTargetArch(qemuMonitorPtr mon) return qemuMonitorJSONGetTargetArch(mon); } + + +int qemuMonitorGetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability) +{ + VIR_DEBUG("mon=%p capability=%d", mon, capability); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + /* No capability is supported without JSON monitor */ + if (!mon->json) + return 0; + + return qemuMonitorJSONGetMigrationCapability(mon, capability); +} + +int qemuMonitorSetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability) +{ + VIR_DEBUG("mon=%p capability=%d", mon, capability); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONSetMigrationCapability(mon, capability); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index ac77158..e3a4568 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -347,6 +347,19 @@ int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon, bool *spice_migrated); typedef enum { + QEMU_MONITOR_MIGRATION_CAPS_XBZRLE, + + QEMU_MONITOR_MIGRATION_CAPS_LAST +} qemuMonitorMigrationCaps; + +VIR_ENUM_DECL(qemuMonitorMigrationCaps); + +int qemuMonitorGetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability); +int qemuMonitorSetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability); + +typedef enum { QEMU_MONITOR_MIGRATE_BACKGROUND = 1 << 0, QEMU_MONITOR_MIGRATE_NON_SHARED_DISK = 1 << 1, /* migration with non-shared storage with full disk copy */ QEMU_MONITOR_MIGRATE_NON_SHARED_INC = 1 << 2, /* migration with non-shared storage with incremental copy */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a86d90c..545d4d4 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4359,3 +4359,121 @@ cleanup: virJSONValueFree(reply); return ret; } + + +int +qemuMonitorJSONGetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability) +{ + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + virJSONValuePtr caps; + int i; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-migrate-capabilities", + NULL))) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) { + if (qemuMonitorJSONHasError(reply, "CommandNotFound")) + goto cleanup; + ret = qemuMonitorJSONCheckError(cmd, reply); + } + + if (ret < 0) + goto cleanup; + + ret = -1; + + caps = virJSONValueObjectGet(reply, "return"); + if (!caps || caps->type != VIR_JSON_TYPE_ARRAY) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing migration capabilities")); + goto cleanup; + } + + for (i = 0; i < virJSONValueArraySize(caps); i++) { + virJSONValuePtr cap = virJSONValueArrayGet(caps, i); + const char *name; + + if (!cap || cap->type != VIR_JSON_TYPE_OBJECT) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing entry in migration capabilities list")); + goto cleanup; + } + + if (!(name = virJSONValueObjectGetString(cap, "capability"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing migration capability name")); + goto cleanup; + } + + if (qemuMonitorMigrationCapsTypeFromString(name) == capability) { + ret = 1; + goto cleanup; + } + } + + ret = 0; + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int +qemuMonitorJSONSetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability) +{ + int ret = -1; + + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + virJSONValuePtr cap = NULL; + virJSONValuePtr caps; + + if (!(caps = virJSONValueNewArray())) + goto cleanup; + + if (!(cap = virJSONValueNewObject())) + goto no_memory; + + if (virJSONValueObjectAppendString( + cap, "capability", + qemuMonitorMigrationCapsTypeToString(capability)) < 0) + goto no_memory; + + if (virJSONValueObjectAppendBoolean(cap, "state", 1) < 0) + goto no_memory; + + if (virJSONValueArrayAppend(caps, cap) < 0) + goto no_memory; + + cap = NULL; + + cmd = qemuMonitorJSONMakeCommand("migrate-set-capabilities", + "a:capabilities", caps, + NULL); + if (!cmd) + goto cleanup; + + if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0) + goto cleanup; + + ret = qemuMonitorJSONCheckError(cmd, reply); + +cleanup: + virJSONValueFree(cap); + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; + +no_memory: + virReportOOMError(); + goto cleanup; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 925d937..356c10a 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -126,6 +126,11 @@ int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, unsigned long long *remaining, unsigned long long *total); +int qemuMonitorJSONGetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability); +int qemuMonitorJSONSetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability); + int qemuMonitorJSONMigrate(qemuMonitorPtr mon, unsigned int flags, const char *uri); -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
--- src/qemu/qemu_migration.c | 59 ++++++++++++++++++++-- src/qemu/qemu_migration.h | 3 +- src/qemu/qemu_monitor.c | 43 ++++++++++++++++ src/qemu/qemu_monitor.h | 13 +++++ src/qemu/qemu_monitor_json.c | 118 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 5 ++ 6 files changed, 237 insertions(+), 4 deletions(-) ...
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 7af571d..631ff92 100644 ... @@ -3383,3 +3387,42 @@ char *qemuMonitorGetTargetArch(qemuMonitorPtr mon)
return qemuMonitorJSONGetTargetArch(mon); } + +
I think it would be worth briefly mentioning the meaning of return valiues in a comment.
+int qemuMonitorGetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability) +{ + VIR_DEBUG("mon=%p capability=%d", mon, capability); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + /* No capability is supported without JSON monitor */ + if (!mon->json) + return 0; + + return qemuMonitorJSONGetMigrationCapability(mon, capability); +} + ... diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a86d90c..545d4d4 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c
...
+ +int +qemuMonitorJSONSetMigrationCapability(qemuMonitorPtr mon, + qemuMonitorMigrationCaps capability) +{ + int ret = -1; + + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + virJSONValuePtr cap = NULL; + virJSONValuePtr caps; + + if (!(caps = virJSONValueNewArray())) + goto cleanup; + + if (!(cap = virJSONValueNewObject())) + goto no_memory; + + if (virJSONValueObjectAppendString( + cap, "capability", + qemuMonitorMigrationCapsTypeToString(capability)) < 0) + goto no_memory; + + if (virJSONValueObjectAppendBoolean(cap, "state", 1) < 0) + goto no_memory; + + if (virJSONValueArrayAppend(caps, cap) < 0) + goto no_memory; + + cap = NULL; + + cmd = qemuMonitorJSONMakeCommand("migrate-set-capabilities", + "a:capabilities", caps, + NULL); + if (!cmd) + goto cleanup; + + if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0) + goto cleanup; + + ret = qemuMonitorJSONCheckError(cmd, reply); + +cleanup: + virJSONValueFree(cap); + virJSONValueFree(cmd); + virJSONValueFree(reply);
You leak "caps" here, at least when hitting out of memory error.
+ return ret; + +no_memory: + virReportOOMError(); + goto cleanup; +}
ACK with comment added and memleak addressed. Peter

This is an extensible version of virDomainGetJobInfo. --- include/libvirt/libvirt.h.in | 205 +++++++++++++++++++++++++++++++++++++++++++ python/generator.py | 1 + src/driver.h | 7 ++ src/libvirt.c | 58 ++++++++++++ src/libvirt_public.syms | 1 + 5 files changed, 272 insertions(+) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index eda9e12..9d1c6ea 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3945,9 +3945,214 @@ struct _virDomainJobInfo { int virDomainGetJobInfo(virDomainPtr dom, virDomainJobInfoPtr info); +int virDomainGetJobStats(virDomainPtr domain, + int *type, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); int virDomainAbortJob(virDomainPtr dom); /** + * VIR_DOMAIN_JOB_TIME_ELAPSED: + * + * virDomainGetJobStats field: time (ms) since the beginning of the + * job, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to timeElapsed field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_TIME_ELAPSED "time_elapsed" + +/** + * VIR_DOMAIN_JOB_TIME_REMAINING: + * + * virDomainGetJobStats field: remaining time (ms) for VIR_DOMAIN_JOB_BOUNDED + * jobs, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to timeRemaining field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_TIME_REMAINING "time_remaining" + +/** + * VIR_DOMAIN_JOB_DOWNTIME: + * + * virDomainGetJobStats field: downtime (ms) that is expected to happen + * during migration, as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_DOWNTIME "downtime" + +/** + * VIR_DOMAIN_JOB_DATA_TOTAL: + * + * virDomainGetJobStats field: total number of bytes supposed to be + * transferred, as VIR_TYPED_PARAM_ULLONG. For VIR_DOMAIN_JOB_UNBOUNDED + * jobs, this may be less than the sum of VIR_DOMAIN_JOB_DATA_PROCESSED and + * VIR_DOMAIN_JOB_DATA_REMAINING in the event that the hypervisor has to + * repeat some data, e.g., due to dirtied pages during migration. For + * VIR_DOMAIN_JOB_BOUNDED jobs, VIR_DOMAIN_JOB_DATA_TOTAL shall always equal + * VIR_DOMAIN_JOB_DATA_PROCESSED + VIR_DOMAIN_JOB_DATA_REMAINING. + * + * This field corresponds to dataTotal field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_DATA_TOTAL "data_total" + +/** + * VIR_DOMAIN_JOB_DATA_PROCESSED: + * + * virDomainGetJobStats field: number of bytes transferred from the + * beginning of the job, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to dataProcessed field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_DATA_PROCESSED "data_processed" + +/** + * VIR_DOMAIN_JOB_DATA_REMAINING: + * + * virDomainGetJobStats field: number of bytes that still need to be + * transferred, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to dataRemaining field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_DATA_REMAINING "data_remaining" + +/** + * VIR_DOMAIN_JOB_MEMORY_TOTAL: + * + * virDomainGetJobStats field: as VIR_DOMAIN_JOB_DATA_TOTAL but only + * tracking guest memory progress, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to memTotal field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_MEMORY_TOTAL "memory_total" + +/** + * VIR_DOMAIN_JOB_MEMORY_PROCESSED: + * + * virDomainGetJobStats field: as VIR_DOMAIN_JOB_DATA_PROCESSED but only + * tracking guest memory progress, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to memProcessed field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_MEMORY_PROCESSED "memory_processed" + +/** + * VIR_DOMAIN_JOB_MEMORY_REMAINING: + * + * virDomainGetJobStats field: as VIR_DOMAIN_JOB_DATA_REMAINING but only + * tracking guest memory progress, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to memRemaining field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_MEMORY_REMAINING "memory_remaining" + +/** + * VIR_DOMAIN_JOB_MEMORY_CONSTANT: + * + * virDomainGetJobStats field: number of pages filled with a constant + * byte (all bytes in a single page are identical) transferred since the + * beginning of the migration job, as VIR_TYPED_PARAM_ULLONG. + * + * The most common example of such pages are zero pages, i.e., pages filled + * with zero bytes. + */ +#define VIR_DOMAIN_JOB_MEMORY_CONSTANT "memory_constant" + +/** + * VIR_DOMAIN_JOB_MEMORY_NORMAL: + * + * virDomainGetJobStats field: number of pages that were transferred without + * any kind of compression (i.e., pages which were not filled with a constant + * byte and which could not be compressed) transferred since the beginning + * of the migration job, as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_MEMORY_NORMAL "memory_normal" + +/** + * VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES: + * + * virDomainGetJobStats field: number of bytes transferred as normal pages, + * as VIR_TYPED_PARAM_ULLONG. + * + * See VIR_DOMAIN_JOB_MEMORY_NORMAL for more details. + */ +#define VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES "memory_normal_bytes" + +/** + * VIR_DOMAIN_JOB_DISK_TOTAL: + * + * virDomainGetJobStats field: as VIR_DOMAIN_JOB_DATA_TOTAL but only + * tracking guest disk progress, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to fileTotal field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_DISK_TOTAL "disk_total" + +/** + * VIR_DOMAIN_JOB_DISK_PROCESSED: + * + * virDomainGetJobStats field: as VIR_DOMAIN_JOB_DATA_PROCESSED but only + * tracking guest memory progress, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to fileProcessed field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_DISK_PROCESSED "disk_processed" + +/** + * VIR_DOMAIN_JOB_DISK_REMAINING: + * + * virDomainGetJobStats field: as VIR_DOMAIN_JOB_DATA_REMAINING but only + * tracking guest memory progress, as VIR_TYPED_PARAM_ULLONG. + * + * This field corresponds to fileRemaining field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_DISK_REMAINING "disk_remaining" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_CACHE: + * + * virDomainGetJobStats field: size of the cache (in bytes) used for + * compressing repeatedly transferred memory pages during live migration, + * as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_COMPRESSION_CACHE "compression_cache" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_BYTES: + * + * virDomainGetJobStats field: number of compressed bytes transferred + * since the beginning of migration, as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_COMPRESSION_BYTES "compression_bytes" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_PAGES: + * + * virDomainGetJobStats field: number of compressed pages transferred + * since the beginning of migration, as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_COMPRESSION_PAGES "compression_pages" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES: + * + * virDomainGetJobStats field: number of repeatedly changing pages that + * were not found in compression cache and thus could not be compressed, + * as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES "compression_cache_misses" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW: + * + * virDomainGetJobStats field: number of repeatedly changing pages that + * were found in compression cache but were sent uncompressed because + * the result of compression was larger than the original page as a whole, + * as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW "compression_overflow" + + +/** * virDomainSnapshot: * * a virDomainSnapshot is a private structure representing a snapshot of diff --git a/python/generator.py b/python/generator.py index 02209f9..92a7f58 100755 --- a/python/generator.py +++ b/python/generator.py @@ -389,6 +389,7 @@ skip_impl = ( 'virDomainGetControlInfo', 'virDomainGetBlockInfo', 'virDomainGetJobInfo', + 'virDomainGetJobStats', 'virNodeGetInfo', 'virDomainGetUUID', 'virDomainGetUUIDString', diff --git a/src/driver.h b/src/driver.h index 8d0f0a5..71b71f6 100644 --- a/src/driver.h +++ b/src/driver.h @@ -590,6 +590,12 @@ typedef char * typedef int (*virDrvDomainGetJobInfo)(virDomainPtr domain, virDomainJobInfoPtr info); +typedef int + (*virDrvDomainGetJobStats)(virDomainPtr domain, + int *type, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); typedef int (*virDrvDomainAbortJob)(virDomainPtr domain); @@ -1060,6 +1066,7 @@ struct _virDriver { virDrvCompareCPU cpuCompare; virDrvBaselineCPU cpuBaseline; virDrvDomainGetJobInfo domainGetJobInfo; + virDrvDomainGetJobStats domainGetJobStats; virDrvDomainAbortJob domainAbortJob; virDrvDomainMigrateSetMaxDowntime domainMigrateSetMaxDowntime; virDrvDomainMigrateGetMaxSpeed domainMigrateGetMaxSpeed; diff --git a/src/libvirt.c b/src/libvirt.c index 1e78500..3611839 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17399,6 +17399,64 @@ error: /** + * virDomainGetJobStats: + * @domain: a domain object + * @type: where to store the job type (one of virDomainJobType) + * @params: where to store job statistics + * @nparams: number of items in @params + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Extract information about progress of a background job on a domain. + * Will return an error if the domain is not active. The function returns + * a superset of progress information provided by virDomainGetJobInfo. + * Possible fields returned in @params are defined by VIR_DOMAIN_JOB_* + * macros. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virDomainGetJobStats(virDomainPtr domain, + int *type, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "type=%p, params=%p, nparams=%p, flags=%x", + type, params, nparams, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + virCheckNonNullArgGoto(type, error); + virCheckNonNullArgGoto(params, error); + virCheckNonNullArgGoto(nparams, error); + + conn = domain->conn; + + if (conn->driver->domainGetJobStats) { + int ret; + ret = conn->driver->domainGetJobStats(domain, type, params, + nparams, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + + +/** * virDomainAbortJob: * @domain: a domain object * diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 3bdfd57..361408f 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -605,6 +605,7 @@ LIBVIRT_1.0.2 { LIBVIRT_1.0.3 { global: + virDomainGetJobStats; virNodeDeviceLookupSCSIHostByWWN; } LIBVIRT_1.0.2; -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
This is an extensible version of virDomainGetJobInfo. --- include/libvirt/libvirt.h.in | 205 +++++++++++++++++++++++++++++++++++++++++++ python/generator.py | 1 + src/driver.h | 7 ++ src/libvirt.c | 58 ++++++++++++ src/libvirt_public.syms | 1 + 5 files changed, 272 insertions(+)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index eda9e12..9d1c6ea 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3945,9 +3945,214 @@ struct _virDomainJobInfo {
...
+/** + * VIR_DOMAIN_JOB_DISK_PROCESSED: + * + * virDomainGetJobStats field: as VIR_DOMAIN_JOB_DATA_PROCESSED but only + * tracking guest memory progress, as VIR_TYPED_PARAM_ULLONG.
s/memory/disk/
+ * + * This field corresponds to fileProcessed field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_DISK_PROCESSED "disk_processed" + +/** + * VIR_DOMAIN_JOB_DISK_REMAINING: + * + * virDomainGetJobStats field: as VIR_DOMAIN_JOB_DATA_REMAINING but only + * tracking guest memory progress, as VIR_TYPED_PARAM_ULLONG.
s/memory/disk/
+ * + * This field corresponds to fileRemaining field in virDomainJobInfo. + */ +#define VIR_DOMAIN_JOB_DISK_REMAINING "disk_remaining" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_CACHE: + * + * virDomainGetJobStats field: size of the cache (in bytes) used for + * compressing repeatedly transferred memory pages during live migration, + * as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_COMPRESSION_CACHE "compression_cache" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_BYTES: + * + * virDomainGetJobStats field: number of compressed bytes transferred + * since the beginning of migration, as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_COMPRESSION_BYTES "compression_bytes" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_PAGES: + * + * virDomainGetJobStats field: number of compressed pages transferred + * since the beginning of migration, as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_COMPRESSION_PAGES "compression_pages" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES: + * + * virDomainGetJobStats field: number of repeatedly changing pages that + * were not found in compression cache and thus could not be compressed, + * as VIR_TYPED_PARAM_ULLONG. + */
This field and those above are a bit too XBZRLE specific although try to be general. But it doesn't bother me more than writing this.
+#define VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES "compression_cache_misses" + +/** + * VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW: + * + * virDomainGetJobStats field: number of repeatedly changing pages that + * were found in compression cache but were sent uncompressed because + * the result of compression was larger than the original page as a whole, + * as VIR_TYPED_PARAM_ULLONG. + */ +#define VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW "compression_overflow" + + +/** * virDomainSnapshot: * * a virDomainSnapshot is a private structure representing a snapshot of
...
diff --git a/src/libvirt.c b/src/libvirt.c index 1e78500..3611839 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17399,6 +17399,64 @@ error:
/** + * virDomainGetJobStats: + * @domain: a domain object + * @type: where to store the job type (one of virDomainJobType) + * @params: where to store job statistics + * @nparams: number of items in @params + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Extract information about progress of a background job on a domain. + * Will return an error if the domain is not active. The function returns + * a superset of progress information provided by virDomainGetJobInfo. + * Possible fields returned in @params are defined by VIR_DOMAIN_JOB_* + * macros.
Possibly worth mentioning that newer daemon when used with older client may return some fields unknown by the client.
+ * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virDomainGetJobStats(virDomainPtr domain, + int *type, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags)
...
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 3bdfd57..361408f 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -605,6 +605,7 @@ LIBVIRT_1.0.2 {
LIBVIRT_1.0.3 {
Hopefully you won't need to change this.
global: + virDomainGetJobStats; virNodeDeviceLookupSCSIHostByWWN; } LIBVIRT_1.0.2;
ACK with the comments fixed. Peter

--- python/libvirt-override-api.xml | 6 ++++++ python/libvirt-override.c | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index a0e0496..446cdbd 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -84,6 +84,12 @@ <return type='int *' info='the list of information or None in case of error'/> <arg name='domain' type='virDomainPtr' info='a domain object'/> </function> + <function name='virDomainGetJobStats' file='python'> + <info>Extract information about an active job being processed for a domain.</info> + <return type='virDomainJobStats' info='dictionary mapping field names to values or None in case of error'/> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + <arg name='flags' type='unsigned int' info='flags, currently unused, pass 0.'/> + </function> <function name='virNodeGetInfo' file='python'> <info>Extract hardware information about the Node.</info> <return type='int *' info='the list of information or None in case of error'/> diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 8154024..ebd6878 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -4183,6 +4183,47 @@ libvirt_virDomainGetJobInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { } static PyObject * +libvirt_virDomainGetJobStats(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) +{ + PyObject *pyobj_domain; + virDomainPtr domain; + unsigned int flags; + virTypedParameterPtr params = NULL; + int nparams = 0; + int type; + PyObject *dict = NULL; + int rc; + + if (!PyArg_ParseTuple(args, (char *) "Oi:virDomainGetJobStats", + &pyobj_domain, &flags)) + goto cleanup; + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + LIBVIRT_BEGIN_ALLOW_THREADS; + rc = virDomainGetJobStats(domain, &type, ¶ms, &nparams, flags); + LIBVIRT_END_ALLOW_THREADS; + if (rc < 0) + goto cleanup; + + if (!(dict = getPyVirTypedParameter(params, nparams))) + goto cleanup; + + if (PyDict_SetItem(dict, libvirt_constcharPtrWrap("type"), + libvirt_intWrap(type)) < 0) { + Py_DECREF(dict); + dict = NULL; + goto cleanup; + } + +cleanup: + virTypedParamsFree(params, nparams); + if (dict) + return dict; + else + return VIR_PY_NONE; +} + +static PyObject * libvirt_virDomainGetBlockJobInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { @@ -6673,6 +6714,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virConnectListAllInterfaces", libvirt_virConnectListAllInterfaces, METH_VARARGS, NULL}, {(char *) "virConnectBaselineCPU", libvirt_virConnectBaselineCPU, METH_VARARGS, NULL}, {(char *) "virDomainGetJobInfo", libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, + {(char *) "virDomainGetJobStats", libvirt_virDomainGetJobStats, METH_VARARGS, NULL}, {(char *) "virDomainSnapshotListNames", libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, {(char *) "virDomainListAllSnapshots", libvirt_virDomainListAllSnapshots, METH_VARARGS, NULL}, {(char *) "virDomainSnapshotListChildrenNames", libvirt_virDomainSnapshotListChildrenNames, METH_VARARGS, NULL}, -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
--- python/libvirt-override-api.xml | 6 ++++++ python/libvirt-override.c | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+)
ACK Peter

remoteDeserializeTypedParameters can now be called with either preallocated params array (size of which is announced by nparams) or it can allocate params array according to the number of parameters received from the server. --- src/remote/remote_driver.c | 95 +++++++++++++++++++++++++++------------------- src/rpc/gendispatch.pl | 2 +- 2 files changed, 57 insertions(+), 40 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index feb372e..5a4b3c7 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1543,67 +1543,78 @@ static int remoteDeserializeTypedParameters(remote_typed_param *ret_params_val, u_int ret_params_len, int limit, - virTypedParameterPtr params, + virTypedParameterPtr *params, int *nparams) { int i = 0; int rv = -1; + bool userAllocated = *params != NULL; - /* Check the length of the returned list carefully. */ - if (ret_params_len > limit || ret_params_len > *nparams) { - virReportError(VIR_ERR_RPC, "%s", - _("returned number of parameters exceeds limit")); - goto cleanup; + if (userAllocated) { + /* Check the length of the returned list carefully. */ + if (ret_params_len > limit || ret_params_len > *nparams) { + virReportError(VIR_ERR_RPC, "%s", + _("returned number of parameters exceeds limit")); + goto cleanup; + } + } else { + if (VIR_ALLOC_N(*params, ret_params_len) < 0) { + virReportOOMError(); + goto cleanup; + } } - *nparams = ret_params_len; /* Deserialise the result. */ for (i = 0; i < ret_params_len; ++i) { - if (virStrcpyStatic(params[i].field, - ret_params_val[i].field) == NULL) { + virTypedParameterPtr param = *params + i; + remote_typed_param *ret_param = ret_params_val + i; + + if (virStrcpyStatic(param->field, + ret_param->field) == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Parameter %s too big for destination"), - ret_params_val[i].field); + ret_param->field); goto cleanup; } - params[i].type = ret_params_val[i].value.type; - switch (params[i].type) { + + param->type = ret_param->value.type; + switch (param->type) { case VIR_TYPED_PARAM_INT: - params[i].value.i = - ret_params_val[i].value.remote_typed_param_value_u.i; + param->value.i = + ret_param->value.remote_typed_param_value_u.i; break; case VIR_TYPED_PARAM_UINT: - params[i].value.ui = - ret_params_val[i].value.remote_typed_param_value_u.ui; + param->value.ui = + ret_param->value.remote_typed_param_value_u.ui; break; case VIR_TYPED_PARAM_LLONG: - params[i].value.l = - ret_params_val[i].value.remote_typed_param_value_u.l; + param->value.l = + ret_param->value.remote_typed_param_value_u.l; break; case VIR_TYPED_PARAM_ULLONG: - params[i].value.ul = - ret_params_val[i].value.remote_typed_param_value_u.ul; + param->value.ul = + ret_param->value.remote_typed_param_value_u.ul; break; case VIR_TYPED_PARAM_DOUBLE: - params[i].value.d = - ret_params_val[i].value.remote_typed_param_value_u.d; + param->value.d = + ret_param->value.remote_typed_param_value_u.d; break; case VIR_TYPED_PARAM_BOOLEAN: - params[i].value.b = - ret_params_val[i].value.remote_typed_param_value_u.b; + param->value.b = + ret_param->value.remote_typed_param_value_u.b; break; case VIR_TYPED_PARAM_STRING: - params[i].value.s = - strdup(ret_params_val[i].value.remote_typed_param_value_u.s); - if (params[i].value.s == NULL) { + param->value.s = + strdup(ret_param->value.remote_typed_param_value_u.s); + if (!param->value.s) { virReportOOMError(); goto cleanup; } break; default: virReportError(VIR_ERR_RPC, _("unknown parameter type: %d"), - params[i].type); + param->type); goto cleanup; } } @@ -1611,8 +1622,14 @@ remoteDeserializeTypedParameters(remote_typed_param *ret_params_val, rv = 0; cleanup: - if (rv < 0) - virTypedParamsClear(params, i); + if (rv < 0) { + if (userAllocated) { + virTypedParamsClear(*params, i); + } else { + virTypedParamsFree(*params, i); + *params = NULL; + } + } return rv; } @@ -1698,7 +1715,7 @@ remoteDomainBlockStatsFlags(virDomainPtr domain, if (remoteDeserializeTypedParameters(ret.params.params_val, ret.params.params_len, REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX, - params, + ¶ms, nparams) < 0) goto cleanup; @@ -1746,7 +1763,7 @@ remoteDomainGetMemoryParameters(virDomainPtr domain, if (remoteDeserializeTypedParameters(ret.params.params_val, ret.params.params_len, REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX, - params, + ¶ms, nparams) < 0) goto cleanup; @@ -1794,7 +1811,7 @@ remoteDomainGetNumaParameters(virDomainPtr domain, if (remoteDeserializeTypedParameters(ret.params.params_val, ret.params.params_len, REMOTE_DOMAIN_NUMA_PARAMETERS_MAX, - params, + ¶ms, nparams) < 0) goto cleanup; @@ -1842,7 +1859,7 @@ remoteDomainGetBlkioParameters(virDomainPtr domain, if (remoteDeserializeTypedParameters(ret.params.params_val, ret.params.params_len, REMOTE_DOMAIN_BLKIO_PARAMETERS_MAX, - params, + ¶ms, nparams) < 0) goto cleanup; @@ -2658,7 +2675,7 @@ static int remoteDomainGetBlockIoTune(virDomainPtr domain, if (remoteDeserializeTypedParameters(ret.params.params_val, ret.params.params_len, REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX, - params, + ¶ms, nparams) < 0) goto cleanup; @@ -2742,12 +2759,12 @@ static int remoteDomainGetCPUStats(virDomainPtr domain, ncpus = ret.params.params_len / ret.nparams; for (cpu = 0; cpu < ncpus; cpu++) { int tmp = nparams; + virTypedParameterPtr cpu_params = ¶ms[cpu * nparams]; remote_typed_param *stride = &ret.params.params_val[cpu * ret.nparams]; if (remoteDeserializeTypedParameters(stride, ret.nparams, REMOTE_NODE_CPU_STATS_MAX, - ¶ms[cpu * nparams], - &tmp) < 0) + &cpu_params, &tmp) < 0) goto cleanup; } @@ -5648,7 +5665,7 @@ remoteDomainGetInterfaceParameters(virDomainPtr domain, if (remoteDeserializeTypedParameters(ret.params.params_val, ret.params.params_len, REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX, - params, + ¶ms, nparams) < 0) goto cleanup; @@ -5822,7 +5839,7 @@ remoteNodeGetMemoryParameters(virConnectPtr conn, if (remoteDeserializeTypedParameters(ret.params.params_val, ret.params.params_len, REMOTE_NODE_MEMORY_PARAMETERS_MAX, - params, + ¶ms, nparams) < 0) goto cleanup; diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index 1da28a4..ef387e0 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -1343,7 +1343,7 @@ elsif ($opt_k) { push(@ret_list2, "if (remoteDeserializeTypedParameters(ret.$1.$1_val,\n" . " ret.$1.$1_len,\n" . " $2,\n" . - " $1,\n" . + " &$1,\n" . " n$1) < 0)\n" . " goto cleanup;\n"); $single_ret_cleanup = 1; -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
remoteDeserializeTypedParameters can now be called with either preallocated params array (size of which is announced by nparams) or it can allocate params array according to the number of parameters received from the server. --- src/remote/remote_driver.c | 95 +++++++++++++++++++++++++++------------------- src/rpc/gendispatch.pl | 2 +- 2 files changed, 57 insertions(+), 40 deletions(-)
ACK. Peter

--- daemon/remote.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 14 +++++++++++++- src/remote_protocol-structs | 12 ++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 51dbd01..c92223e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4662,6 +4662,50 @@ cleanup: return rv; } +static int +remoteDispatchDomainGetJobStats(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_job_stats_args *args, + remote_domain_get_job_stats_ret *ret) +{ + virDomainPtr dom = NULL; + virTypedParameterPtr params = NULL; + int nparams = 0; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if (virDomainGetJobStats(dom, &ret->type, ¶ms, + &nparams, args->flags) < 0) + goto cleanup; + + if (remoteSerializeTypedParameters(params, nparams, + &ret->params.params_val, + &ret->params.params_len, + 0) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virTypedParamsFree(params, nparams); + if (dom) + virDomainFree(dom); + return rv; +} + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 5a4b3c7..f35c44f 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -5934,6 +5934,46 @@ done: return rv; } +static int +remoteDomainGetJobStats(virDomainPtr domain, + int *type, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int rv = -1; + remote_domain_get_job_stats_args args; + remote_domain_get_job_stats_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, domain); + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_JOB_STATS, + (xdrproc_t) xdr_remote_domain_get_job_stats_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_get_job_stats_ret, (char *) &ret) == -1) + goto done; + + *type = ret.type; + + if (remoteDeserializeTypedParameters(ret.params.params_val, + ret.params.params_len, + 0, params, nparams) < 0) + goto cleanup; + + rv = 0; + +cleanup: + xdr_free((xdrproc_t) xdr_remote_domain_get_job_stats_ret, + (char *) &ret); +done: + remoteDriverUnlock(priv); + return rv; +} + static void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event) @@ -6193,6 +6233,7 @@ static virDriver remote_driver = { .cpuCompare = remoteCPUCompare, /* 0.7.5 */ .cpuBaseline = remoteCPUBaseline, /* 0.7.7 */ .domainGetJobInfo = remoteDomainGetJobInfo, /* 0.7.7 */ + .domainGetJobStats = remoteDomainGetJobStats, /* 1.0.3 */ .domainAbortJob = remoteDomainAbortJob, /* 0.7.7 */ .domainMigrateSetMaxDowntime = remoteDomainMigrateSetMaxDowntime, /* 0.8.0 */ .domainMigrateSetMaxSpeed = remoteDomainMigrateSetMaxSpeed, /* 0.9.0 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 5ace31f..306d6b2 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2156,6 +2156,17 @@ struct remote_domain_get_job_info_ret { /* insert@1 */ }; +struct remote_domain_get_job_stats_args { + remote_nonnull_domain dom; + unsigned int flags; +}; + +struct remote_domain_get_job_stats_ret { + int type; + remote_typed_param params<>; +}; + + struct remote_domain_abort_job_args { remote_nonnull_domain dom; }; @@ -3060,7 +3071,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_FSTRIM = 294, /* autogen autogen */ REMOTE_PROC_DOMAIN_SEND_PROCESS_SIGNAL = 295, /* autogen autogen */ REMOTE_PROC_DOMAIN_OPEN_CHANNEL = 296, /* autogen autogen | readstream@2 */ - REMOTE_PROC_NODE_DEVICE_LOOKUP_SCSI_HOST_BY_WWN = 297 /* autogen autogen priority:high */ + REMOTE_PROC_NODE_DEVICE_LOOKUP_SCSI_HOST_BY_WWN = 297, /* autogen autogen priority:high */ + REMOTE_PROC_DOMAIN_GET_JOB_STATS = 298 /* skipgen skipgen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 7f5ff7a..54b6009 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1634,6 +1634,17 @@ struct remote_domain_get_job_info_ret { uint64_t fileProcessed; uint64_t fileRemaining; }; +struct remote_domain_get_job_stats_args { + remote_nonnull_domain dom; + u_int flags; +}; +struct remote_domain_get_job_stats_ret { + int type; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; +}; struct remote_domain_abort_job_args { remote_nonnull_domain dom; }; @@ -2462,4 +2473,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SEND_PROCESS_SIGNAL = 295, REMOTE_PROC_DOMAIN_OPEN_CHANNEL = 296, REMOTE_PROC_NODE_DEVICE_LOOKUP_SCSI_HOST_BY_WWN = 297, + REMOTE_PROC_DOMAIN_GET_JOB_STATS = 298, }; -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
--- daemon/remote.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 14 +++++++++++++- src/remote_protocol-structs | 12 ++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-)
ACK. Peter

--- tools/virsh-domain.c | 215 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 169 insertions(+), 46 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f8b0cec..ba05fa7 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -4914,64 +4914,187 @@ cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd) { virDomainJobInfo info; virDomainPtr dom; - bool ret = true; + bool ret = false; + const char *unit; + double val; + virTypedParameterPtr params = NULL; + int nparams = 0; + unsigned long long value; + int rc; if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) return false; - if (virDomainGetJobInfo(dom, &info) == 0) { - const char *unit; - double val; + memset(&info, 0, sizeof(info)); - vshPrint(ctl, "%-17s ", _("Job type:")); - switch (info.type) { - case VIR_DOMAIN_JOB_BOUNDED: - vshPrint(ctl, "%-12s\n", _("Bounded")); - break; + rc = virDomainGetJobStats(dom, &info.type, ¶ms, &nparams, 0); + if (rc == 0) { + if (virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_TIME_ELAPSED, + &info.timeElapsed) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_TIME_REMAINING, + &info.timeRemaining) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DATA_TOTAL, + &info.dataTotal) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DATA_PROCESSED, + &info.dataProcessed) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DATA_REMAINING, + &info.dataRemaining) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_TOTAL, + &info.memTotal) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_PROCESSED, + &info.memProcessed) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_REMAINING, + &info.memRemaining) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DISK_TOTAL, + &info.fileTotal) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DISK_PROCESSED, + &info.fileProcessed) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DISK_REMAINING, + &info.fileRemaining) < 0) + goto save_error; + } else if (last_error->code == VIR_ERR_NO_SUPPORT) { + vshDebug(ctl, VSH_ERR_DEBUG, "detailed statistics not supported\n"); + vshResetLibvirtError(); + rc = virDomainGetJobInfo(dom, &info); + } + if (rc < 0) + goto cleanup; - case VIR_DOMAIN_JOB_UNBOUNDED: - vshPrint(ctl, "%-12s\n", _("Unbounded")); - break; + vshPrint(ctl, "%-17s ", _("Job type:")); + switch (info.type) { + case VIR_DOMAIN_JOB_BOUNDED: + vshPrint(ctl, "%-12s\n", _("Bounded")); + break; - case VIR_DOMAIN_JOB_NONE: - default: - vshPrint(ctl, "%-12s\n", _("None")); - goto cleanup; - } + case VIR_DOMAIN_JOB_UNBOUNDED: + vshPrint(ctl, "%-12s\n", _("Unbounded")); + break; - vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed); - if (info.type == VIR_DOMAIN_JOB_BOUNDED) - vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining); - if (info.dataTotal || info.dataRemaining || info.dataProcessed) { - val = vshPrettyCapacity(info.dataProcessed, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit); - val = vshPrettyCapacity(info.dataRemaining, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit); - val = vshPrettyCapacity(info.dataTotal, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit); - } - if (info.memTotal || info.memRemaining || info.memProcessed) { - val = vshPrettyCapacity(info.memProcessed, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit); - val = vshPrettyCapacity(info.memRemaining, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit); - val = vshPrettyCapacity(info.memTotal, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit); - } - if (info.fileTotal || info.fileRemaining || info.fileProcessed) { - val = vshPrettyCapacity(info.fileProcessed, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit); - val = vshPrettyCapacity(info.fileRemaining, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit); - val = vshPrettyCapacity(info.fileTotal, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit); - } - } else { - ret = false; + case VIR_DOMAIN_JOB_NONE: + default: + vshPrint(ctl, "%-12s\n", _("None")); + goto cleanup; + } + + vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed); + if (info.type == VIR_DOMAIN_JOB_BOUNDED) + vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining); + if (info.dataTotal || info.dataRemaining || info.dataProcessed) { + val = vshPrettyCapacity(info.dataProcessed, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit); + val = vshPrettyCapacity(info.dataRemaining, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit); + val = vshPrettyCapacity(info.dataTotal, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit); + } + + if (info.memTotal || info.memRemaining || info.memProcessed) { + val = vshPrettyCapacity(info.memProcessed, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit); + val = vshPrettyCapacity(info.memRemaining, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit); + val = vshPrettyCapacity(info.memTotal, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit); + } + if (info.fileTotal || info.fileRemaining || info.fileProcessed) { + val = vshPrettyCapacity(info.fileProcessed, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit); + val = vshPrettyCapacity(info.fileRemaining, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit); + val = vshPrettyCapacity(info.fileTotal, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit); + } + + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_CONSTANT, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-12llu\n", _("Constant pages:"), value); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_NORMAL, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-12llu\n", _("Normal pages:"), value); } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES, + &value)) < 0) { + goto save_error; + } else if (rc) { + val = vshPrettyCapacity(value, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Normal data:"), val, unit); + } + + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DOWNTIME, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-12llu ms\n", _("Expected downtime:"), value); + } + + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_CACHE, + &value)) < 0) { + goto save_error; + } else if (rc) { + val = vshPrettyCapacity(value, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Compression cache:"), val, unit); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_BYTES, + &value)) < 0) { + goto save_error; + } else if (rc) { + val = vshPrettyCapacity(value, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Compressed data:"), val, unit); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_PAGES, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-13llu\n", _("Compressed pages:"), value); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-13llu\n", _("Compression cache misses:"), value); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-13llu\n", _("Compression overflows:"), value); + } + + ret = true; + cleanup: virDomainFree(dom); + virTypedParamsFree(params, nparams); return ret; + +save_error: + vshSaveLibvirtError(); + goto cleanup; } /* -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
--- tools/virsh-domain.c | 215 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 169 insertions(+), 46 deletions(-)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f8b0cec..ba05fa7 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -4914,64 +4914,187 @@ cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd) { virDomainJobInfo info; virDomainPtr dom; - bool ret = true; + bool ret = false; + const char *unit; + double val; + virTypedParameterPtr params = NULL; + int nparams = 0; + unsigned long long value; + int rc;
if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) return false;
- if (virDomainGetJobInfo(dom, &info) == 0) { - const char *unit; - double val; + memset(&info, 0, sizeof(info));
- vshPrint(ctl, "%-17s ", _("Job type:")); - switch (info.type) { - case VIR_DOMAIN_JOB_BOUNDED: - vshPrint(ctl, "%-12s\n", _("Bounded")); - break; + rc = virDomainGetJobStats(dom, &info.type, ¶ms, &nparams, 0); + if (rc == 0) { + if (virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_TIME_ELAPSED, + &info.timeElapsed) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_TIME_REMAINING, + &info.timeRemaining) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DATA_TOTAL, + &info.dataTotal) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DATA_PROCESSED, + &info.dataProcessed) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DATA_REMAINING, + &info.dataRemaining) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_TOTAL, + &info.memTotal) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_PROCESSED, + &info.memProcessed) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_REMAINING, + &info.memRemaining) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DISK_TOTAL, + &info.fileTotal) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DISK_PROCESSED, + &info.fileProcessed) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DISK_REMAINING, + &info.fileRemaining) < 0) + goto save_error; + } else if (last_error->code == VIR_ERR_NO_SUPPORT) { + vshDebug(ctl, VSH_ERR_DEBUG, "detailed statistics not supported\n"); + vshResetLibvirtError(); + rc = virDomainGetJobInfo(dom, &info); + } + if (rc < 0) + goto cleanup;
- case VIR_DOMAIN_JOB_UNBOUNDED: - vshPrint(ctl, "%-12s\n", _("Unbounded")); - break; + vshPrint(ctl, "%-17s ", _("Job type:")); + switch (info.type) { + case VIR_DOMAIN_JOB_BOUNDED: + vshPrint(ctl, "%-12s\n", _("Bounded")); + break;
- case VIR_DOMAIN_JOB_NONE: - default: - vshPrint(ctl, "%-12s\n", _("None")); - goto cleanup; - } + case VIR_DOMAIN_JOB_UNBOUNDED: + vshPrint(ctl, "%-12s\n", _("Unbounded")); + break;
- vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed); - if (info.type == VIR_DOMAIN_JOB_BOUNDED) - vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining); - if (info.dataTotal || info.dataRemaining || info.dataProcessed) { - val = vshPrettyCapacity(info.dataProcessed, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit); - val = vshPrettyCapacity(info.dataRemaining, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit); - val = vshPrettyCapacity(info.dataTotal, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit); - } - if (info.memTotal || info.memRemaining || info.memProcessed) { - val = vshPrettyCapacity(info.memProcessed, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit); - val = vshPrettyCapacity(info.memRemaining, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit); - val = vshPrettyCapacity(info.memTotal, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit); - } - if (info.fileTotal || info.fileRemaining || info.fileProcessed) { - val = vshPrettyCapacity(info.fileProcessed, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit); - val = vshPrettyCapacity(info.fileRemaining, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit); - val = vshPrettyCapacity(info.fileTotal, &unit); - vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit); - } - } else { - ret = false; + case VIR_DOMAIN_JOB_NONE: + default: + vshPrint(ctl, "%-12s\n", _("None")); + goto cleanup; + } + + vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed); + if (info.type == VIR_DOMAIN_JOB_BOUNDED) + vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining); + if (info.dataTotal || info.dataRemaining || info.dataProcessed) { + val = vshPrettyCapacity(info.dataProcessed, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit); + val = vshPrettyCapacity(info.dataRemaining, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit); + val = vshPrettyCapacity(info.dataTotal, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit); + }
Inconsistent block spacing.
+ + if (info.memTotal || info.memRemaining || info.memProcessed) { + val = vshPrettyCapacity(info.memProcessed, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit); + val = vshPrettyCapacity(info.memRemaining, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit); + val = vshPrettyCapacity(info.memTotal, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit); + } + if (info.fileTotal || info.fileRemaining || info.fileProcessed) { + val = vshPrettyCapacity(info.fileProcessed, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit); + val = vshPrettyCapacity(info.fileRemaining, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit); + val = vshPrettyCapacity(info.fileTotal, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit); + } + + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_CONSTANT, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-12llu\n", _("Constant pages:"), value); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_NORMAL, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-12llu\n", _("Normal pages:"), value); } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES, + &value)) < 0) { + goto save_error; + } else if (rc) { + val = vshPrettyCapacity(value, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Normal data:"), val, unit); + }
Again, inconsistent empty line.
+ + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_DOWNTIME, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-12llu ms\n", _("Expected downtime:"), value); + }
And here too.
+ + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_CACHE, + &value)) < 0) { + goto save_error; + } else if (rc) { + val = vshPrettyCapacity(value, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Compression cache:"), val, unit); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_BYTES, + &value)) < 0) { + goto save_error; + } else if (rc) { + val = vshPrettyCapacity(value, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Compressed data:"), val, unit); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_PAGES, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-13llu\n", _("Compressed pages:"), value); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-13llu\n", _("Compression cache misses:"), value); + } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-13llu\n", _("Compression overflows:"), value); + } + + ret = true; + cleanup: virDomainFree(dom); + virTypedParamsFree(params, nparams); return ret; + +save_error: + vshSaveLibvirtError(); + goto cleanup; }
/*
ACK. Peter

On Fri, Feb 22, 2013 at 08:18:56 +0100, Peter Krempa wrote:
On 02/19/13 13:35, Jiri Denemark wrote:
--- tools/virsh-domain.c | 215 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 169 insertions(+), 46 deletions(-)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f8b0cec..ba05fa7 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c ... + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_NORMAL, + &value)) < 0) { + goto save_error; + } else if (rc) { + vshPrint(ctl, "%-17s %-12llu\n", _("Normal pages:"), value); } + if ((rc = virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES, + &value)) < 0) { + goto save_error; + } else if (rc) { + val = vshPrettyCapacity(value, &unit); + vshPrint(ctl, "%-17s %-.3lf %s\n", _("Normal data:"), val, unit); + }
Again, inconsistent empty line.
This is actually intentional. I wanted to group the code by fields describing the same thing. So memory fields are grouped, time fields are group, compression fields are grouped. And the groups are separated with an empty line. But you're right I got it wrong in few places :-) Jirka

As a side effect, this also fixes reporting disk migration process. It was added to memory migration progress, which was wrong. Disk progress has dedicated fields in virDomainJobInfo structure. --- src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 3 +- src/qemu/qemu_migration.c | 44 +++++++------ src/qemu/qemu_monitor.c | 15 +---- src/qemu/qemu_monitor.h | 34 ++++++++-- src/qemu/qemu_monitor_json.c | 150 ++++++++++++++++++++++++++++++------------- src/qemu/qemu_monitor_json.h | 5 +- src/qemu/qemu_monitor_text.c | 47 +++++++------- src/qemu/qemu_monitor_text.h | 5 +- 9 files changed, 192 insertions(+), 112 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 482f64a..eca85fc 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -160,6 +160,7 @@ qemuDomainObjResetAsyncJob(qemuDomainObjPrivatePtr priv) job->start = 0; job->dump_memory_only = false; job->asyncAbort = false; + memset(&job->status, 0, sizeof(job->status)); memset(&job->info, 0, sizeof(job->info)); } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index c9d5f8b..e4fb2f6 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -110,7 +110,8 @@ struct qemuDomainJobObj { unsigned long long mask; /* Jobs allowed during async job */ unsigned long long start; /* When the async job started */ bool dump_memory_only; /* use dump-guest-memory to do dump */ - virDomainJobInfo info; /* Async job progress data */ + qemuMonitorMigrationStatus status; /* Raw async job progress data */ + virDomainJobInfo info; /* Processed async job progress data */ bool asyncAbort; /* abort of async job requested */ }; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index de8dfec..4142872 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1198,12 +1198,11 @@ qemuMigrationUpdateJobStatus(virQEMUDriverPtr driver, { qemuDomainObjPrivatePtr priv = vm->privateData; int ret; - int status; bool wait_for_spice = false; bool spice_migrated = false; - unsigned long long memProcessed; - unsigned long long memRemaining; - unsigned long long memTotal; + qemuMonitorMigrationStatus status; + + memset(&status, 0, sizeof(status)); /* If guest uses SPICE and supports seamles_migration we have to hold up * migration finish until SPICE server transfers its data */ @@ -1217,20 +1216,19 @@ qemuMigrationUpdateJobStatus(virQEMUDriverPtr driver, /* Guest already exited; nothing further to update. */ return -1; } - ret = qemuMonitorGetMigrationStatus(priv->mon, - &status, - &memProcessed, - &memRemaining, - &memTotal); + ret = qemuMonitorGetMigrationStatus(priv->mon, &status); /* If qemu says migrated, check spice */ - if (wait_for_spice && (ret == 0) && - (status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED)) + if (wait_for_spice && + ret == 0 && + status.status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) ret = qemuMonitorGetSpiceMigrationStatus(priv->mon, &spice_migrated); qemuDomainObjExitMonitor(driver, vm); + priv->job.status = status; + if (ret < 0 || virTimeMillisNow(&priv->job.info.timeElapsed) < 0) { priv->job.info.type = VIR_DOMAIN_JOB_FAILED; return -1; @@ -1238,7 +1236,7 @@ qemuMigrationUpdateJobStatus(virQEMUDriverPtr driver, priv->job.info.timeElapsed -= priv->job.start; ret = -1; - switch (status) { + switch (priv->job.status.status) { case QEMU_MONITOR_MIGRATION_STATUS_INACTIVE: priv->job.info.type = VIR_DOMAIN_JOB_NONE; virReportError(VIR_ERR_OPERATION_FAILED, @@ -1246,13 +1244,21 @@ qemuMigrationUpdateJobStatus(virQEMUDriverPtr driver, break; case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE: - priv->job.info.dataTotal = memTotal; - priv->job.info.dataRemaining = memRemaining; - priv->job.info.dataProcessed = memProcessed; - - priv->job.info.memTotal = memTotal; - priv->job.info.memRemaining = memRemaining; - priv->job.info.memProcessed = memProcessed; + priv->job.info.fileTotal = priv->job.status.disk_total; + priv->job.info.fileRemaining = priv->job.status.disk_remaining; + priv->job.info.fileProcessed = priv->job.status.disk_transferred; + + priv->job.info.memTotal = priv->job.status.ram_total; + priv->job.info.memRemaining = priv->job.status.ram_remaining; + priv->job.info.memProcessed = priv->job.status.ram_transferred; + + priv->job.info.dataTotal = + priv->job.status.ram_total + priv->job.status.disk_total; + priv->job.info.dataRemaining = + priv->job.status.ram_remaining + priv->job.status.disk_remaining; + priv->job.info.dataProcessed = + priv->job.status.ram_transferred + + priv->job.status.disk_transferred; ret = 0; break; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 631ff92..21489fb 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1805,10 +1805,7 @@ int qemuMonitorSetMigrationDowntime(qemuMonitorPtr mon, int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, - int *status, - unsigned long long *transferred, - unsigned long long *remaining, - unsigned long long *total) + qemuMonitorMigrationStatusPtr status) { int ret; VIR_DEBUG("mon=%p", mon); @@ -1820,15 +1817,9 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, } if (mon->json) - ret = qemuMonitorJSONGetMigrationStatus(mon, status, - transferred, - remaining, - total); + ret = qemuMonitorJSONGetMigrationStatus(mon, status); else - ret = qemuMonitorTextGetMigrationStatus(mon, status, - transferred, - remaining, - total); + ret = qemuMonitorTextGetMigrationStatus(mon, status); return ret; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index e3a4568..40e635d 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -338,11 +338,37 @@ enum { VIR_ENUM_DECL(qemuMonitorMigrationStatus) +typedef struct _qemuMonitorMigrationStatus qemuMonitorMigrationStatus; +typedef qemuMonitorMigrationStatus *qemuMonitorMigrationStatusPtr; +struct _qemuMonitorMigrationStatus { + int status; + unsigned long long total_time; + /* total or expected depending on status */ + bool downtime_set; + unsigned long long downtime; + + unsigned long long ram_transferred; + unsigned long long ram_remaining; + unsigned long long ram_total; + bool ram_duplicate_set; + unsigned long long ram_duplicate; + unsigned long long ram_normal; + unsigned long long ram_normal_bytes; + + unsigned long long disk_transferred; + unsigned long long disk_remaining; + unsigned long long disk_total; + + bool xbzrle_set; + unsigned long long xbzrle_cache_size; + unsigned long long xbzrle_bytes; + unsigned long long xbzrle_pages; + unsigned long long xbzrle_cache_miss; + unsigned long long xbzrle_overflow; +}; + int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, - int *status, - unsigned long long *transferred, - unsigned long long *remaining, - unsigned long long *total); + qemuMonitorMigrationStatusPtr status); int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon, bool *spice_migrated); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 545d4d4..e8c1f81 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2258,14 +2258,11 @@ int qemuMonitorJSONSetMigrationDowntime(qemuMonitorPtr mon, static int qemuMonitorJSONGetMigrationStatusReply(virJSONValuePtr reply, - int *status, - unsigned long long *transferred, - unsigned long long *remaining, - unsigned long long *total) + qemuMonitorMigrationStatusPtr status) { virJSONValuePtr ret; const char *statusstr; - unsigned long long t; + int rc; if (!(ret = virJSONValueObjectGet(reply, "return"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -2279,13 +2276,25 @@ qemuMonitorJSONGetMigrationStatusReply(virJSONValuePtr reply, return -1; } - if ((*status = qemuMonitorMigrationStatusTypeFromString(statusstr)) < 0) { + status->status = qemuMonitorMigrationStatusTypeFromString(statusstr); + if (status->status < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected migration status in %s"), statusstr); return -1; } - if (*status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) { + virJSONValueObjectGetNumberUlong(ret, "total-time", &status->total_time); + if (status->status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) { + rc = virJSONValueObjectGetNumberUlong(ret, "downtime", + &status->downtime); + } else { + rc = virJSONValueObjectGetNumberUlong(ret, "expected-downtime", + &status->downtime); + } + if (rc == 0) + status->downtime_set = true; + + if (status->status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) { virJSONValuePtr ram = virJSONValueObjectGet(ret, "ram"); if (!ram) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -2294,51 +2303,112 @@ qemuMonitorJSONGetMigrationStatusReply(virJSONValuePtr reply, } if (virJSONValueObjectGetNumberUlong(ram, "transferred", - transferred) < 0) { + &status->ram_transferred) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("migration was active, but RAM 'transferred' " "data was missing")); return -1; } - if (virJSONValueObjectGetNumberUlong(ram, "remaining", remaining) < 0) { + if (virJSONValueObjectGetNumberUlong(ram, "remaining", + &status->ram_remaining) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("migration was active, but RAM 'remaining' " "data was missing")); return -1; } - if (virJSONValueObjectGetNumberUlong(ram, "total", total) < 0) { + if (virJSONValueObjectGetNumberUlong(ram, "total", + &status->ram_total) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("migration was active, but RAM 'total' " "data was missing")); return -1; } + if (virJSONValueObjectGetNumberUlong(ram, "duplicate", + &status->ram_duplicate) == 0) + status->ram_duplicate_set = true; + virJSONValueObjectGetNumberUlong(ram, "normal", &status->ram_normal); + virJSONValueObjectGetNumberUlong(ram, "normal-bytes", + &status->ram_normal_bytes); + virJSONValuePtr disk = virJSONValueObjectGet(ret, "disk"); - if (!disk) { - return 0; - } + if (disk) { + rc = virJSONValueObjectGetNumberUlong(disk, "transferred", + &status->disk_transferred); + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("disk migration was active, but " + "'transferred' data was missing")); + return -1; + } - if (virJSONValueObjectGetNumberUlong(disk, "transferred", &t) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("disk migration was active, but 'transferred' " - "data was missing")); - return -1; - } - *transferred += t; - if (virJSONValueObjectGetNumberUlong(disk, "remaining", &t) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("disk migration was active, but 'remaining' " - "data was missing")); - return -1; + rc = virJSONValueObjectGetNumberUlong(disk, "remaining", + &status->disk_remaining); + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("disk migration was active, but 'remaining' " + "data was missing")); + return -1; + } + + rc = virJSONValueObjectGetNumberUlong(disk, "total", + &status->disk_total); + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("disk migration was active, but 'total' " + "data was missing")); + return -1; + } } - *remaining += t; - if (virJSONValueObjectGetNumberUlong(disk, "total", &t) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("disk migration was active, but 'total' " - "data was missing")); - return -1; + + virJSONValuePtr comp = virJSONValueObjectGet(ret, "xbzrle-cache"); + if (comp) { + status->xbzrle_set = true; + rc = virJSONValueObjectGetNumberUlong(comp, "cache-size", + &status->xbzrle_cache_size); + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("XBZRLE is active, but 'cache-size' data " + "was missing")); + return -1; + } + + rc = virJSONValueObjectGetNumberUlong(comp, "bytes", + &status->xbzrle_bytes); + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("XBZRLE is active, but 'bytes' data " + "was missing")); + return -1; + } + + rc = virJSONValueObjectGetNumberUlong(comp, "pages", + &status->xbzrle_pages); + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("XBZRLE is active, but 'pages' data " + "was missing")); + return -1; + } + + rc = virJSONValueObjectGetNumberUlong(comp, "cache-miss", + &status->xbzrle_cache_miss); + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("XBZRLE is active, but 'cache-miss' data " + "was missing")); + return -1; + } + + rc = virJSONValueObjectGetNumberUlong(comp, "overflow", + &status->xbzrle_overflow); + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("XBZRLE is active, but 'overflow' data " + "was missing")); + return -1; + } } - *total += t; } return 0; @@ -2346,18 +2416,14 @@ qemuMonitorJSONGetMigrationStatusReply(virJSONValuePtr reply, int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, - int *status, - unsigned long long *transferred, - unsigned long long *remaining, - unsigned long long *total) + qemuMonitorMigrationStatusPtr status) { int ret; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-migrate", NULL); virJSONValuePtr reply = NULL; - *status = 0; - *transferred = *remaining = *total = 0; + memset(status, 0, sizeof(*status)); if (!cmd) return -1; @@ -2368,13 +2434,11 @@ int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, ret = qemuMonitorJSONCheckError(cmd, reply); if (ret == 0 && - qemuMonitorJSONGetMigrationStatusReply(reply, - status, - transferred, - remaining, - total) < 0) + qemuMonitorJSONGetMigrationStatusReply(reply, status) < 0) ret = -1; + if (ret < 0) + memset(status, 0, sizeof(*status)); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 356c10a..66635fd 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -121,10 +121,7 @@ int qemuMonitorJSONSetMigrationDowntime(qemuMonitorPtr mon, unsigned long long downtime); int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, - int *status, - unsigned long long *transferred, - unsigned long long *remaining, - unsigned long long *total); + qemuMonitorMigrationStatusPtr status); int qemuMonitorJSONGetMigrationCapability(qemuMonitorPtr mon, qemuMonitorMigrationCaps capability); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index bc0a11d..58f6323 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -1474,22 +1474,14 @@ cleanup: #define MIGRATION_DISK_TOTAL_PREFIX "total disk: " int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, - int *status, - unsigned long long *transferred, - unsigned long long *remaining, - unsigned long long *total) { + qemuMonitorMigrationStatusPtr status) +{ char *reply; char *tmp; char *end; - unsigned long long disk_transferred = 0; - unsigned long long disk_remaining = 0; - unsigned long long disk_total = 0; int ret = -1; - *status = QEMU_MONITOR_MIGRATION_STATUS_INACTIVE; - *transferred = 0; - *remaining = 0; - *total = 0; + memset(status, 0, sizeof(*status)); if (qemuMonitorHMPCommand(mon, "info migrate", &reply) < 0) return -1; @@ -1504,52 +1496,54 @@ int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, } *end = '\0'; - if ((*status = qemuMonitorMigrationStatusTypeFromString(tmp)) < 0) { + status->status = qemuMonitorMigrationStatusTypeFromString(tmp); + if (status->status < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected migration status in %s"), reply); goto cleanup; } - if (*status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) { + if (status->status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) { tmp = end + 1; if (!(tmp = strstr(tmp, MIGRATION_TRANSFER_PREFIX))) goto done; tmp += strlen(MIGRATION_TRANSFER_PREFIX); - if (virStrToLong_ull(tmp, &end, 10, transferred) < 0) { + if (virStrToLong_ull(tmp, &end, 10, + &status->ram_transferred) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse migration data transferred " "statistic %s"), tmp); goto cleanup; } - *transferred *= 1024; + status->ram_transferred *= 1024; tmp = end; if (!(tmp = strstr(tmp, MIGRATION_REMAINING_PREFIX))) goto done; tmp += strlen(MIGRATION_REMAINING_PREFIX); - if (virStrToLong_ull(tmp, &end, 10, remaining) < 0) { + if (virStrToLong_ull(tmp, &end, 10, &status->ram_remaining) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse migration data remaining " "statistic %s"), tmp); goto cleanup; } - *remaining *= 1024; + status->ram_remaining *= 1024; tmp = end; if (!(tmp = strstr(tmp, MIGRATION_TOTAL_PREFIX))) goto done; tmp += strlen(MIGRATION_TOTAL_PREFIX); - if (virStrToLong_ull(tmp, &end, 10, total) < 0) { + if (virStrToLong_ull(tmp, &end, 10, &status->ram_total) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse migration data total " "statistic %s"), tmp); goto cleanup; } - *total *= 1024; + status->ram_total *= 1024; tmp = end; /* @@ -1559,39 +1553,40 @@ int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, goto done; tmp += strlen(MIGRATION_DISK_TRANSFER_PREFIX); - if (virStrToLong_ull(tmp, &end, 10, &disk_transferred) < 0) { + if (virStrToLong_ull(tmp, &end, 10, + &status->disk_transferred) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse disk migration data " "transferred statistic %s"), tmp); goto cleanup; } - *transferred += disk_transferred * 1024; + status->disk_transferred *= 1024; tmp = end; if (!(tmp = strstr(tmp, MIGRATION_DISK_REMAINING_PREFIX))) goto done; tmp += strlen(MIGRATION_DISK_REMAINING_PREFIX); - if (virStrToLong_ull(tmp, &end, 10, &disk_remaining) < 0) { + if (virStrToLong_ull(tmp, &end, 10, &status->disk_remaining) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse disk migration data remaining " "statistic %s"), tmp); goto cleanup; } - *remaining += disk_remaining * 1024; + status->disk_remaining *= 1024; tmp = end; if (!(tmp = strstr(tmp, MIGRATION_DISK_TOTAL_PREFIX))) goto done; tmp += strlen(MIGRATION_DISK_TOTAL_PREFIX); - if (virStrToLong_ull(tmp, &end, 10, &disk_total) < 0) { + if (virStrToLong_ull(tmp, &end, 10, &status->disk_total) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse disk migration data total " "statistic %s"), tmp); goto cleanup; } - *total += disk_total * 1024; + status->disk_total *= 1024; } } @@ -1600,6 +1595,8 @@ done: cleanup: VIR_FREE(reply); + if (ret < 0) + memset(status, 0, sizeof(*status)); return ret; } diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 97abad6..fb8e904 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -117,10 +117,7 @@ int qemuMonitorTextSetMigrationDowntime(qemuMonitorPtr mon, unsigned long long downtime); int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, - int *status, - unsigned long long *transferred, - unsigned long long *remaining, - unsigned long long *total); + qemuMonitorMigrationStatusPtr status); int qemuMonitorTextMigrate(qemuMonitorPtr mon, unsigned int flags, -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
As a side effect, this also fixes reporting disk migration process. It was added to memory migration progress, which was wrong. Disk progress has dedicated fields in virDomainJobInfo structure. --- src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 3 +- src/qemu/qemu_migration.c | 44 +++++++------ src/qemu/qemu_monitor.c | 15 +---- src/qemu/qemu_monitor.h | 34 ++++++++-- src/qemu/qemu_monitor_json.c | 150 ++++++++++++++++++++++++++++++------------- src/qemu/qemu_monitor_json.h | 5 +- src/qemu/qemu_monitor_text.c | 47 +++++++------- src/qemu/qemu_monitor_text.h | 5 +- 9 files changed, 192 insertions(+), 112 deletions(-)
ACK. Peter

--- src/qemu/qemu_driver.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 6d73fea..07f7061 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10123,6 +10123,146 @@ cleanup: } +static int +qemuDomainGetJobStats(virDomainPtr dom, + int *type, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virDomainObjPtr vm; + qemuDomainObjPrivatePtr priv; + virTypedParameterPtr par = NULL; + int maxpar = 0; + int npar = 0; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + priv = vm->privateData; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + if (!priv->job.asyncJob || priv->job.dump_memory_only) { + *type = VIR_DOMAIN_JOB_NONE; + *params = NULL; + *nparams = 0; + ret = 0; + goto cleanup; + } + + /* Refresh elapsed time again just to ensure it + * is fully updated. This is primarily for benefit + * of incoming migration which we don't currently + * monitor actively in the background thread + */ + if (virTimeMillisNow(&priv->job.info.timeElapsed) < 0) + goto cleanup; + priv->job.info.timeElapsed -= priv->job.start; + + if (virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_TIME_ELAPSED, + priv->job.info.timeElapsed) < 0) + goto cleanup; + + if (priv->job.info.type == VIR_DOMAIN_JOB_BOUNDED && + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_TIME_REMAINING, + priv->job.info.timeRemaining) < 0) + goto cleanup; + + if (priv->job.status.downtime_set && + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_DOWNTIME, + priv->job.status.downtime) < 0) + goto cleanup; + + if (virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_DATA_TOTAL, + priv->job.info.dataTotal) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_DATA_PROCESSED, + priv->job.info.dataProcessed) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_DATA_REMAINING, + priv->job.info.dataRemaining) < 0) + goto cleanup; + + if (virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_MEMORY_TOTAL, + priv->job.info.memTotal) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_MEMORY_PROCESSED, + priv->job.info.memProcessed) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_MEMORY_REMAINING, + priv->job.info.memRemaining) < 0) + goto cleanup; + + if (priv->job.status.ram_duplicate_set) { + if (virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_MEMORY_CONSTANT, + priv->job.status.ram_duplicate) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_MEMORY_NORMAL, + priv->job.status.ram_normal) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES, + priv->job.status.ram_normal_bytes) < 0) + goto cleanup; + } + + if (virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_DISK_TOTAL, + priv->job.info.fileTotal) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_DISK_PROCESSED, + priv->job.info.fileProcessed) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_DISK_REMAINING, + priv->job.info.fileRemaining) < 0) + goto cleanup; + + if (priv->job.status.xbzrle_set) { + if (virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_COMPRESSION_CACHE, + priv->job.status.xbzrle_cache_size) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_COMPRESSION_BYTES, + priv->job.status.xbzrle_bytes) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_COMPRESSION_PAGES, + priv->job.status.xbzrle_pages) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES, + priv->job.status.xbzrle_cache_miss) < 0 || + virTypedParamsAddULLong(&par, &npar, &maxpar, + VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW, + priv->job.status.xbzrle_overflow) < 0) + goto cleanup; + } + + *type = priv->job.info.type; + *params = par; + *nparams = npar; + ret = 0; + +cleanup: + if (vm) + virObjectUnlock(vm); + if (ret < 0) + virTypedParamsFree(par, npar); + return ret; +} + + static int qemuDomainAbortJob(virDomainPtr dom) { virQEMUDriverPtr driver = dom->conn->privateData; virDomainObjPtr vm; @@ -14776,6 +14916,7 @@ static virDriver qemuDriver = { .cpuCompare = qemuCPUCompare, /* 0.7.5 */ .cpuBaseline = qemuCPUBaseline, /* 0.7.7 */ .domainGetJobInfo = qemuDomainGetJobInfo, /* 0.7.7 */ + .domainGetJobStats = qemuDomainGetJobStats, /* 1.0.3 */ .domainAbortJob = qemuDomainAbortJob, /* 0.7.7 */ .domainMigrateSetMaxDowntime = qemuDomainMigrateSetMaxDowntime, /* 0.8.0 */ .domainMigrateSetMaxSpeed = qemuDomainMigrateSetMaxSpeed, /* 0.9.0 */ -- 1.8.1.2

Introduce virDomainMigrateGetCompressionCache and virDomainMigrateSetCompressionCache APIs. --- include/libvirt/libvirt.h.in | 7 +++ python/generator.py | 1 + src/driver.h | 11 +++++ src/libvirt.c | 100 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + 5 files changed, 121 insertions(+) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 9d1c6ea..7e89e2e 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1215,6 +1215,13 @@ int virDomainMigrateSetMaxDowntime (virDomainPtr domain, unsigned long long downtime, unsigned int flags); +int virDomainMigrateGetCompressionCache(virDomainPtr domain, + unsigned long long *cacheSize, + unsigned int flags); +int virDomainMigrateSetCompressionCache(virDomainPtr domain, + unsigned long long cacheSize, + unsigned int flags); + int virDomainMigrateSetMaxSpeed(virDomainPtr domain, unsigned long bandwidth, unsigned int flags); diff --git a/python/generator.py b/python/generator.py index 92a7f58..e4c9579 100755 --- a/python/generator.py +++ b/python/generator.py @@ -444,6 +444,7 @@ skip_impl = ( 'virNodeGetCPUStats', 'virNodeGetMemoryStats', 'virDomainGetBlockJobInfo', + 'virDomainMigrateGetCompressionCache', 'virDomainMigrateGetMaxSpeed', 'virDomainBlockStatsFlags', 'virDomainSetBlockIoTune', diff --git a/src/driver.h b/src/driver.h index 71b71f6..f60bf93 100644 --- a/src/driver.h +++ b/src/driver.h @@ -605,6 +605,15 @@ typedef int unsigned long long downtime, unsigned int flags); typedef int + (*virDrvDomainMigrateGetCompressionCache)(virDomainPtr domain, + unsigned long long *cacheSize, + unsigned int flags); +typedef int + (*virDrvDomainMigrateSetCompressionCache)(virDomainPtr domain, + unsigned long long cacheSize, + unsigned int flags); + +typedef int (*virDrvDomainMigrateSetMaxSpeed)(virDomainPtr domain, unsigned long bandwidth, unsigned int flags); @@ -1069,6 +1078,8 @@ struct _virDriver { virDrvDomainGetJobStats domainGetJobStats; virDrvDomainAbortJob domainAbortJob; virDrvDomainMigrateSetMaxDowntime domainMigrateSetMaxDowntime; + virDrvDomainMigrateGetCompressionCache domainMigrateGetCompressionCache; + virDrvDomainMigrateSetCompressionCache domainMigrateSetCompressionCache; virDrvDomainMigrateGetMaxSpeed domainMigrateGetMaxSpeed; virDrvDomainMigrateSetMaxSpeed domainMigrateSetMaxSpeed; virDrvDomainEventRegisterAny domainEventRegisterAny; diff --git a/src/libvirt.c b/src/libvirt.c index 3611839..d1dca40 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17550,6 +17550,106 @@ error: } /** + * virDomainMigrateGetCompressionCache: + * @domain: a domain object + * @cacheSize: return value of current size of the cache (in bytes) + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Gets current size of the cache (in bytes) used for compressing repeatedly + * transferred memory pages during live migration. + * + * Returns 0 in case of success, -1 otherwise. + */ +int +virDomainMigrateGetCompressionCache(virDomainPtr domain, + unsigned long long *cacheSize, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "cacheSize=%p, flags=%x", cacheSize, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + + virCheckNonNullArgGoto(cacheSize, error); + + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainMigrateGetCompressionCache) { + if (conn->driver->domainMigrateGetCompressionCache(domain, cacheSize, + flags) < 0) + goto error; + return 0; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainMigrateSetCompressionCache: + * @domain: a domain object + * @cacheSize: size of the cache (in bytes) used for compression + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Sets size of the cache (in bytes) used for compressing repeatedly + * transferred memory pages during live migration. It's supposed to be called + * while the domain is being live-migrated as a reaction to migration progress + * and increasing number of compression cache misses obtained from + * virDomainGetJobStats. + * + * Returns 0 in case of success, -1 otherwise. + */ +int +virDomainMigrateSetCompressionCache(virDomainPtr domain, + unsigned long long cacheSize, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "cacheSize=%llu, flags=%x", cacheSize, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainMigrateSetCompressionCache) { + if (conn->driver->domainMigrateSetCompressionCache(domain, cacheSize, + flags) < 0) + goto error; + return 0; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** * virDomainMigrateSetMaxSpeed: * @domain: a domain object * @bandwidth: migration bandwidth limit in Mbps diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 361408f..ab993ed 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -606,6 +606,8 @@ LIBVIRT_1.0.2 { LIBVIRT_1.0.3 { global: virDomainGetJobStats; + virDomainMigrateGetCompressionCache; + virDomainMigrateSetCompressionCache; virNodeDeviceLookupSCSIHostByWWN; } LIBVIRT_1.0.2; -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
Introduce virDomainMigrateGetCompressionCache and virDomainMigrateSetCompressionCache APIs. --- include/libvirt/libvirt.h.in | 7 +++ python/generator.py | 1 + src/driver.h | 11 +++++ src/libvirt.c | 100 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + 5 files changed, 121 insertions(+)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 9d1c6ea..7e89e2e 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1215,6 +1215,13 @@ int virDomainMigrateSetMaxDowntime (virDomainPtr domain, unsigned long long downtime, unsigned int flags);
+int virDomainMigrateGetCompressionCache(virDomainPtr domain, + unsigned long long *cacheSize, + unsigned int flags); +int virDomainMigrateSetCompressionCache(virDomainPtr domain, + unsigned long long cacheSize, + unsigned int flags);
Hm, again those seem to me a bit too qemucentric. As we now have APIs to set migration speed and this would introduce another set for cache size, I'm wondering if we couldn't go for something more universal. For example the flag would control which parameter of migration gets set. As this would introduce increased complexity, I'm not insisting on this. A second opinion is welcome.
+ int virDomainMigrateSetMaxSpeed(virDomainPtr domain, unsigned long bandwidth, unsigned int flags);
...
diff --git a/src/libvirt.c b/src/libvirt.c index 3611839..d1dca40 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17550,6 +17550,106 @@ error: }
/** + * virDomainMigrateGetCompressionCache: + * @domain: a domain object + * @cacheSize: return value of current size of the cache (in bytes) + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Gets current size of the cache (in bytes) used for compressing repeatedly + * transferred memory pages during live migration. + * + * Returns 0 in case of success, -1 otherwise. + */ +int +virDomainMigrateGetCompressionCache(virDomainPtr domain, + unsigned long long *cacheSize, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "cacheSize=%p, flags=%x", cacheSize, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + + virCheckNonNullArgGoto(cacheSize, error); + + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__);
IMHO the *get* function could be allowed to work on RO connections.
+ goto error; + } + + if (conn->driver->domainMigrateGetCompressionCache) { + if (conn->driver->domainMigrateGetCompressionCache(domain, cacheSize, + flags) < 0) + goto error; + return 0; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} +
The code looks fine, so ACK with the check removed. We might want to wait for a second opinion on the design though. Peter

On Fri, Feb 22, 2013 at 09:06:42AM +0100, Peter Krempa wrote:
On 02/19/13 13:35, Jiri Denemark wrote:
Introduce virDomainMigrateGetCompressionCache and virDomainMigrateSetCompressionCache APIs. --- include/libvirt/libvirt.h.in | 7 +++ python/generator.py | 1 + src/driver.h | 11 +++++ src/libvirt.c | 100 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + 5 files changed, 121 insertions(+)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 9d1c6ea..7e89e2e 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1215,6 +1215,13 @@ int virDomainMigrateSetMaxDowntime (virDomainPtr domain, unsigned long long downtime, unsigned int flags);
+int virDomainMigrateGetCompressionCache(virDomainPtr domain, + unsigned long long *cacheSize, + unsigned int flags); +int virDomainMigrateSetCompressionCache(virDomainPtr domain, + unsigned long long cacheSize, + unsigned int flags);
Hm, again those seem to me a bit too qemucentric. As we now have APIs to set migration speed and this would introduce another set for cache size, I'm wondering if we couldn't go for something more universal. For example the flag would control which parameter of migration gets set.
As this would introduce increased complexity, I'm not insisting on this. A second opinion is welcome.
I both agree & disagree. Given our existing approach to migration, I'm fine with this patch as is. Long term I do think we need todo something different. Our approach to migration has clearly failed, as witnessed by the many many versions of APIs we've created. I think we need to create a standalone virDomainMigrateParams object, and have methods to read/write params on that. Then we just need virDomainMigrateWithParmas() and virDomainMigrateUpdateParams() to change them on the fly. This will avoid us having to crate new v2/v3/v4 migration APIs longer term. 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 :|

On Fri, Feb 22, 2013 at 09:06:42 +0100, Peter Krempa wrote:
On 02/19/13 13:35, Jiri Denemark wrote:
Introduce virDomainMigrateGetCompressionCache and virDomainMigrateSetCompressionCache APIs. --- +virDomainMigrateGetCompressionCache(virDomainPtr domain, + unsigned long long *cacheSize, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "cacheSize=%p, flags=%x", cacheSize, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + + virCheckNonNullArgGoto(cacheSize, error); + + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__);
IMHO the *get* function could be allowed to work on RO connections.
Yeah, I actually copied this from virDomainMigrateGetMaxSpeed, which means it could be fixed too. Jirka

--- python/libvirt-override-api.xml | 7 +++++++ python/libvirt-override.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 446cdbd..5976fb2 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -491,6 +491,13 @@ <arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/> <return type='virDomainBlockJobInfo' info='A dictionary containing job information.' /> </function> + <function name='virDomainMigrateGetCompressionCache' file='python'> + <info>Get current size of the cache (in bytes) used for compressing + repeatedly transferred memory pages during live migration.</info> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + <arg name='flags' type='unsigned int' info='flags, currently unused, pass 0.'/> + <return type='unsigned long long' info='current cache size, or None in case of error'/> + </function> <function name='virDomainMigrateGetMaxSpeed' file='python'> <info>Get currently configured maximum migration speed for a domain</info> <arg name='domain' type='virDomainPtr' info='a domain object'/> diff --git a/python/libvirt-override.c b/python/libvirt-override.c index ebd6878..9637598 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -6322,6 +6322,33 @@ libvirt_virDomainSendKey(PyObject *self ATTRIBUTE_UNUSED, } static PyObject * +libvirt_virDomainMigrateGetCompressionCache(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_domain; + virDomainPtr domain; + unsigned int flags; + unsigned long long cacheSize; + int rc; + + if (!PyArg_ParseTuple(args, + (char *) "Oi:virDomainMigrateGetCompressionCache", + &pyobj_domain, &flags)) + return VIR_PY_NONE; + + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + LIBVIRT_BEGIN_ALLOW_THREADS; + rc = virDomainMigrateGetCompressionCache(domain, &cacheSize, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (rc < 0) + return VIR_PY_NONE; + + return libvirt_ulonglongWrap(cacheSize); +} + +static PyObject * libvirt_virDomainMigrateGetMaxSpeed(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { PyObject *py_retval; int c_retval; @@ -6724,6 +6751,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainSetBlockIoTune", libvirt_virDomainSetBlockIoTune, METH_VARARGS, NULL}, {(char *) "virDomainGetBlockIoTune", libvirt_virDomainGetBlockIoTune, METH_VARARGS, NULL}, {(char *) "virDomainSendKey", libvirt_virDomainSendKey, METH_VARARGS, NULL}, + {(char *) "virDomainMigrateGetCompressionCache", libvirt_virDomainMigrateGetCompressionCache, METH_VARARGS, NULL}, {(char *) "virDomainMigrateGetMaxSpeed", libvirt_virDomainMigrateGetMaxSpeed, METH_VARARGS, NULL}, {(char *) "virDomainBlockPeek", libvirt_virDomainBlockPeek, METH_VARARGS, NULL}, {(char *) "virDomainMemoryPeek", libvirt_virDomainMemoryPeek, METH_VARARGS, NULL}, -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
--- python/libvirt-override-api.xml | 7 +++++++ python/libvirt-override.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+)
diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 446cdbd..5976fb2 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -491,6 +491,13 @@ <arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/> <return type='virDomainBlockJobInfo' info='A dictionary containing job information.' /> </function> + <function name='virDomainMigrateGetCompressionCache' file='python'> + <info>Get current size of the cache (in bytes) used for compressing + repeatedly transferred memory pages during live migration.</info> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + <arg name='flags' type='unsigned int' info='flags, currently unused, pass 0.'/> + <return type='unsigned long long' info='current cache size, or None in case of error'/> + </function>
Um, no need to document the Set function too?
<function name='virDomainMigrateGetMaxSpeed' file='python'> <info>Get currently configured maximum migration speed for a domain</info> <arg name='domain' type='virDomainPtr' info='a domain object'/>
ACK. Peter

On Fri, Feb 22, 2013 at 09:07:59 +0100, Peter Krempa wrote:
On 02/19/13 13:35, Jiri Denemark wrote:
--- python/libvirt-override-api.xml | 7 +++++++ python/libvirt-override.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+)
diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 446cdbd..5976fb2 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -491,6 +491,13 @@ <arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/> <return type='virDomainBlockJobInfo' info='A dictionary containing job information.' /> </function> + <function name='virDomainMigrateGetCompressionCache' file='python'> + <info>Get current size of the cache (in bytes) used for compressing + repeatedly transferred memory pages during live migration.</info> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + <arg name='flags' type='unsigned int' info='flags, currently unused, pass 0.'/> + <return type='unsigned long long' info='current cache size, or None in case of error'/> + </function>
Um, no need to document the Set function too?
No :-) Our python generator works just fine for the Set function and thus it does not need to be overridden this way. Jirka

--- src/remote/remote_driver.c | 2 ++ src/remote/remote_protocol.x | 19 ++++++++++++++++++- src/remote_protocol-structs | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index f35c44f..3721af9 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6236,6 +6236,8 @@ static virDriver remote_driver = { .domainGetJobStats = remoteDomainGetJobStats, /* 1.0.3 */ .domainAbortJob = remoteDomainAbortJob, /* 0.7.7 */ .domainMigrateSetMaxDowntime = remoteDomainMigrateSetMaxDowntime, /* 0.8.0 */ + .domainMigrateGetCompressionCache = remoteDomainMigrateGetCompressionCache, /* 1.0.3 */ + .domainMigrateSetCompressionCache = remoteDomainMigrateSetCompressionCache, /* 1.0.3 */ .domainMigrateSetMaxSpeed = remoteDomainMigrateSetMaxSpeed, /* 0.9.0 */ .domainMigrateGetMaxSpeed = remoteDomainMigrateGetMaxSpeed, /* 0.9.5 */ .domainEventRegisterAny = remoteDomainEventRegisterAny, /* 0.8.0 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 306d6b2..b957b8e 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2178,6 +2178,21 @@ struct remote_domain_migrate_set_max_downtime_args { unsigned int flags; }; +struct remote_domain_migrate_get_compression_cache_args { + remote_nonnull_domain dom; + unsigned int flags; +}; + +struct remote_domain_migrate_get_compression_cache_ret { + unsigned hyper cacheSize; /* insert@1 */ +}; + +struct remote_domain_migrate_set_compression_cache_args { + remote_nonnull_domain dom; + unsigned hyper cacheSize; + unsigned int flags; +}; + struct remote_domain_migrate_set_max_speed_args { remote_nonnull_domain dom; unsigned hyper bandwidth; @@ -3072,7 +3087,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SEND_PROCESS_SIGNAL = 295, /* autogen autogen */ REMOTE_PROC_DOMAIN_OPEN_CHANNEL = 296, /* autogen autogen | readstream@2 */ REMOTE_PROC_NODE_DEVICE_LOOKUP_SCSI_HOST_BY_WWN = 297, /* autogen autogen priority:high */ - REMOTE_PROC_DOMAIN_GET_JOB_STATS = 298 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_GET_JOB_STATS = 298, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_MIGRATE_GET_COMPRESSION_CACHE = 299, /* autogen autogen */ + REMOTE_PROC_DOMAIN_MIGRATE_SET_COMPRESSION_CACHE = 300 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 54b6009..c85defd 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1653,6 +1653,18 @@ struct remote_domain_migrate_set_max_downtime_args { uint64_t downtime; u_int flags; }; +struct remote_domain_migrate_get_compression_cache_args { + remote_nonnull_domain dom; + u_int flags; +}; +struct remote_domain_migrate_get_compression_cache_ret { + uint64_t cacheSize; +}; +struct remote_domain_migrate_set_compression_cache_args { + remote_nonnull_domain dom; + uint64_t cacheSize; + u_int flags; +}; struct remote_domain_migrate_set_max_speed_args { remote_nonnull_domain dom; uint64_t bandwidth; @@ -2474,4 +2486,6 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_OPEN_CHANNEL = 296, REMOTE_PROC_NODE_DEVICE_LOOKUP_SCSI_HOST_BY_WWN = 297, REMOTE_PROC_DOMAIN_GET_JOB_STATS = 298, + REMOTE_PROC_DOMAIN_MIGRATE_GET_COMPRESSION_CACHE = 299, + REMOTE_PROC_DOMAIN_MIGRATE_SET_COMPRESSION_CACHE = 300, }; -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
--- src/remote/remote_driver.c | 2 ++ src/remote/remote_protocol.x | 19 ++++++++++++++++++- src/remote_protocol-structs | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-)
Straightforward. ACK. Peter

This is a command wrapping virDomainMigrateGetCompressionCache and virDomainMigrateSetCompressionCache. --- tools/virsh-domain.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 12 ++++++++++ 2 files changed, 80 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index ba05fa7..83885df 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8734,6 +8734,68 @@ done: } /* + * "migrate-compcache" command + */ +static const vshCmdInfo info_migrate_compcache[] = { + {.name = "help", + .data = N_("get/set compression cache size") + }, + {.name = "desc", + .data = N_("Get/set size of the cache (in bytes) used for compressing " + "repeatedly transferred memory pages during live migration.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_migrate_compcache[] = { + {.name = "domain", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("domain name, id or uuid") + }, + {.name = "size", + .type = VSH_OT_INT, + .flags = VSH_OFLAG_REQ_OPT, + .help = N_("requested size of the cache (in bytes) used for compression") + }, + {.name = NULL} +}; + +static bool +cmdMigrateCompCache(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + unsigned long long size = 0; + bool ret = false; + const char *unit; + double value; + int rc; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + rc = vshCommandOptULongLong(cmd, "size", &size); + if (rc < 0) { + vshError(ctl, "%s", _("Unable to parse size parameter")); + goto cleanup; + } else if (rc != 0) { + if (virDomainMigrateSetCompressionCache(dom, size, 0) < 0) + goto cleanup; + } + + if (virDomainMigrateGetCompressionCache(dom, &size, 0) < 0) + goto cleanup; + + value = vshPrettyCapacity(size, &unit); + vshPrint(ctl, _("Compression cache: %.3lf %s"), value, unit); + + ret = true; +cleanup: + virDomainFree(dom); + return ret; +} + +/* * "migrate-setspeed" command */ static const vshCmdInfo info_migrate_setspeed[] = { @@ -10677,6 +10739,12 @@ const vshCmdDef domManagementCmds[] = { .info = info_migrate_setmaxdowntime, .flags = 0 }, + {.name = "migrate-compcache", + .handler = cmdMigrateCompCache, + .opts = opts_migrate_compcache, + .info = info_migrate_compcache, + .flags = 0 + }, {.name = "migrate-setspeed", .handler = cmdMigrateSetMaxSpeed, .opts = opts_migrate_setspeed, diff --git a/tools/virsh.pod b/tools/virsh.pod index e675850..8f62378 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1114,6 +1114,18 @@ Set maximum tolerable downtime for a domain which is being live-migrated to another host. The I<downtime> is a number of milliseconds the guest is allowed to be down at the end of live migration. +=item B<migrate-compcache> I<domain> [I<--size> B<bytes>] + +Sets and/or gets size of the cache (in bytes) used for compressing repeatedly +transferred memory pages during live migration. When called without I<size>, +the command just prints current size of the compression cache. When I<size> +is specified, the hypervisor is asked to change compression cache to I<size> +bytes and then the current size is printed (the result may differ from the +requested size due to rounding done by the hypervisor). With I<size> this +command is supposed to be called while the domain is being live-migrated as +a reaction to migration progress and increasing number of compression cache +misses obtained from domjobinfo. + =item B<migrate-setspeed> I<domain> I<bandwidth> Set the maximum migration bandwidth (in Mbps) for a domain which is being -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
This is a command wrapping virDomainMigrateGetCompressionCache and virDomainMigrateSetCompressionCache. --- tools/virsh-domain.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 12 ++++++++++ 2 files changed, 80 insertions(+)
...
diff --git a/tools/virsh.pod b/tools/virsh.pod index e675850..8f62378 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1114,6 +1114,18 @@ Set maximum tolerable downtime for a domain which is being live-migrated to another host. The I<downtime> is a number of milliseconds the guest is allowed to be down at the end of live migration.
+=item B<migrate-compcache> I<domain> [I<--size> B<bytes>] + +Sets and/or gets size of the cache (in bytes) used for compressing repeatedly +transferred memory pages during live migration. When called without I<size>, +the command just prints current size of the compression cache. When I<size> +is specified, the hypervisor is asked to change compression cache to I<size> +bytes and then the current size is printed (the result may differ from the +requested size due to rounding done by the hypervisor). With I<size> this
I'd rephrase as: This command is supposed to be called with size while ...
+command is supposed to be called while the domain is being live-migrated as +a reaction to migration progress and increasing number of compression cache +misses obtained from domjobinfo. + =item B<migrate-setspeed> I<domain> I<bandwidth>
Set the maximum migration bandwidth (in Mbps) for a domain which is being
ACK. Peter

--- src/qemu/qemu_driver.c | 94 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 43 ++++++++++++++++++++ src/qemu/qemu_monitor.h | 5 +++ src/qemu/qemu_monitor_json.c | 63 +++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 5 +++ 5 files changed, 210 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 07f7061..5aef22d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10359,6 +10359,98 @@ cleanup: } static int +qemuDomainMigrateGetCompressionCache(virDomainPtr dom, + unsigned long long *cacheSize, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm; + qemuDomainObjPrivatePtr priv; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + priv = vm->privateData; + + qemuDomainObjEnterMonitor(driver, vm); + + ret = qemuMonitorGetMigrationCapability( + priv->mon, + QEMU_MONITOR_MIGRATION_CAPS_XBZRLE); + if (ret == 0) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("Compressed migration is not supported by " + "QEMU binary")); + } else if (ret > 0) { + ret = qemuMonitorGetMigrationCacheSize(priv->mon, cacheSize); + } + + qemuDomainObjExitMonitor(driver, vm); + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) + vm = NULL; + +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static int +qemuDomainMigrateSetCompressionCache(virDomainPtr dom, + unsigned long long cacheSize, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm; + qemuDomainObjPrivatePtr priv; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MIGRATION_OP) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + priv = vm->privateData; + + VIR_DEBUG("Setting compression cache to %llu B", cacheSize); + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorSetMigrationCacheSize(priv->mon, cacheSize); + qemuDomainObjExitMonitor(driver, vm); + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) + vm = NULL; + +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static int qemuDomainMigrateSetMaxSpeed(virDomainPtr dom, unsigned long bandwidth, unsigned int flags) @@ -14919,6 +15011,8 @@ static virDriver qemuDriver = { .domainGetJobStats = qemuDomainGetJobStats, /* 1.0.3 */ .domainAbortJob = qemuDomainAbortJob, /* 0.7.7 */ .domainMigrateSetMaxDowntime = qemuDomainMigrateSetMaxDowntime, /* 0.8.0 */ + .domainMigrateGetCompressionCache = qemuDomainMigrateGetCompressionCache, /* 1.0.3 */ + .domainMigrateSetCompressionCache = qemuDomainMigrateSetCompressionCache, /* 1.0.3 */ .domainMigrateSetMaxSpeed = qemuDomainMigrateSetMaxSpeed, /* 0.9.0 */ .domainMigrateGetMaxSpeed = qemuDomainMigrateGetMaxSpeed, /* 0.9.5 */ .domainEventRegisterAny = qemuDomainEventRegisterAny, /* 0.8.0 */ diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 21489fb..e14fad5 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1804,6 +1804,49 @@ int qemuMonitorSetMigrationDowntime(qemuMonitorPtr mon, } +int +qemuMonitorGetMigrationCacheSize(qemuMonitorPtr mon, + unsigned long long *cacheSize) +{ + VIR_DEBUG("mon=%p cacheSize=%p", mon, cacheSize); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONGetMigrationCacheSize(mon, cacheSize); +} + +int +qemuMonitorSetMigrationCacheSize(qemuMonitorPtr mon, + unsigned long long cacheSize) +{ + VIR_DEBUG("mon=%p cacheSize=%llu", mon, cacheSize); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONSetMigrationCacheSize(mon, cacheSize); +} + + int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, qemuMonitorMigrationStatusPtr status) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 40e635d..ffb17c2 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -326,6 +326,11 @@ int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, int qemuMonitorSetMigrationDowntime(qemuMonitorPtr mon, unsigned long long downtime); +int qemuMonitorGetMigrationCacheSize(qemuMonitorPtr mon, + unsigned long long *cacheSize); +int qemuMonitorSetMigrationCacheSize(qemuMonitorPtr mon, + unsigned long long cacheSize); + enum { QEMU_MONITOR_MIGRATION_STATUS_INACTIVE, QEMU_MONITOR_MIGRATION_STATUS_ACTIVE, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index e8c1f81..af6683d 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2256,6 +2256,69 @@ int qemuMonitorJSONSetMigrationDowntime(qemuMonitorPtr mon, } +int +qemuMonitorJSONGetMigrationCacheSize(qemuMonitorPtr mon, + unsigned long long *cacheSize) +{ + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + *cacheSize = 0; + + cmd = qemuMonitorJSONMakeCommand("query-migrate-cache-size", NULL); + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret < 0) + goto cleanup; + + ret = virJSONValueObjectGetNumberUlong(reply, "return", cacheSize); + if (ret < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-migrate-cache-size reply was missing " + "'return' data")); + goto cleanup; + } + + ret = 0; +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int +qemuMonitorJSONSetMigrationCacheSize(qemuMonitorPtr mon, + unsigned long long cacheSize) +{ + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = qemuMonitorJSONMakeCommand("migrate-set-cache-size", + "U:value", cacheSize, + NULL); + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + static int qemuMonitorJSONGetMigrationStatusReply(virJSONValuePtr reply, qemuMonitorMigrationStatusPtr status) diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 66635fd..cfe9c19 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -120,6 +120,11 @@ int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon, int qemuMonitorJSONSetMigrationDowntime(qemuMonitorPtr mon, unsigned long long downtime); +int qemuMonitorJSONGetMigrationCacheSize(qemuMonitorPtr mon, + unsigned long long *cacheSize); +int qemuMonitorJSONSetMigrationCacheSize(qemuMonitorPtr mon, + unsigned long long cacheSize); + int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, qemuMonitorMigrationStatusPtr status); -- 1.8.1.2

On 02/19/13 13:35, Jiri Denemark wrote:
--- src/qemu/qemu_driver.c | 94 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 43 ++++++++++++++++++++ src/qemu/qemu_monitor.h | 5 +++ src/qemu/qemu_monitor_json.c | 63 +++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 5 +++ 5 files changed, 210 insertions(+)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 07f7061..5aef22d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10359,6 +10359,98 @@ cleanup: }
static int +qemuDomainMigrateGetCompressionCache(virDomainPtr dom, + unsigned long long *cacheSize, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm; + qemuDomainObjPrivatePtr priv; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + priv = vm->privateData; + + qemuDomainObjEnterMonitor(driver, vm); + + ret = qemuMonitorGetMigrationCapability( + priv->mon, + QEMU_MONITOR_MIGRATION_CAPS_XBZRLE); + if (ret == 0) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("Compressed migration is not supported by " + "QEMU binary")); + } else if (ret > 0) { + ret = qemuMonitorGetMigrationCacheSize(priv->mon, cacheSize); + } + + qemuDomainObjExitMonitor(driver, vm); + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) + vm = NULL; + +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static int +qemuDomainMigrateSetCompressionCache(virDomainPtr dom, + unsigned long long cacheSize, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm; + qemuDomainObjPrivatePtr priv; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MIGRATION_OP) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } +
Wouldn't it be better to check the xbzrle migration capability here too? To be able to provide better errors?
+ priv = vm->privateData; + + VIR_DEBUG("Setting compression cache to %llu B", cacheSize); + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorSetMigrationCacheSize(priv->mon, cacheSize); + qemuDomainObjExitMonitor(driver, vm); + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) + vm = NULL; + +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static int qemuDomainMigrateSetMaxSpeed(virDomainPtr dom, unsigned long bandwidth, unsigned int flags)
ACK with or without my suggestion implemented as I don't have a strong opinion on this. Peter

On Tue, Feb 19, 2013 at 13:35:38 +0100, Jiri Denemark wrote:
QEMU supports XBZRLE compression of repeatedly transferred pages during live migration and this series makes it usable through libvirt. The first two patches add support for VIR_MIGRATE_COMPRESSED flag that enables XBZRLE compression. Patches 3--9 add an enhanced and extensible version of virDomainGetJobInfo and make various migration statistics available through it. The rest of the patches (10--14) add APIs for querying and changing XBZRLE compression cache size.
I addressed the issues and pushed this series. Thanks for the reviews. Jirka
participants (4)
-
Daniel P. Berrange
-
Eric Blake
-
Jiri Denemark
-
Peter Krempa