Support for memory statistics reporting is accepted for qemu inclusion. Statistics are reported via the monitor command 'info balloon' as a comma seprated list: (qemu) info balloon balloon: actual=1024,mem_swapped_in=0,mem_swapped_out=0,major_page_faults=88,minor_page_faults=105535,free_mem=1017065472,total_mem=1045229568 Libvirt, qemu, and the guest operating system may support a subset of the statistics defined by the virtio spec. Thus, only statistics recognized by components will be reported. Signed-off-by: Adam Litke <agl@xxxxxxxxxx> To: libvirt list <libvir-list@xxxxxxxxxx> Cc: Daniel Veillard <veillard@xxxxxxxxxx> Cc: Daniel P. Berrange <berrange@xxxxxxxxxx> --- src/qemu/qemu_driver.c | 39 +++++++++++++++++++- src/qemu/qemu_monitor_text.c | 83 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 3 ++ 3 files changed, 124 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1ddc23e..562a20b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6235,6 +6235,43 @@ qemudDomainInterfaceStats (virDomainPtr dom, #endif static int +qemudDomainMemoryStats (virDomainPtr dom, + struct _virDomainMemoryStat *stats, + unsigned int nr_stats) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + unsigned int ret = -1; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorTextGetMemoryStats(priv->mon, stats, nr_stats); + qemuDomainObjExitMonitor(vm); + } else { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int qemudDomainBlockPeek (virDomainPtr dom, const char *path, unsigned long long offset, size_t size, @@ -7907,7 +7944,7 @@ static virDriver qemuDriver = { NULL, /* domainMigrateFinish */ qemudDomainBlockStats, /* domainBlockStats */ qemudDomainInterfaceStats, /* domainInterfaceStats */ - NULL, /* domainMemoryStats */ + qemudDomainMemoryStats, /* domainMemoryStats */ qemudDomainBlockPeek, /* domainBlockPeek */ qemudDomainMemoryPeek, /* domainMemoryPeek */ nodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 0cb9ea6..ab361c6 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -454,6 +454,65 @@ error: return 0; } +static int parseMemoryStat(char **text, unsigned int tag, + const char *search, virDomainMemoryStatPtr stat) +{ + char *dummy; + unsigned long long value; + + if (STRPREFIX (*text, search)) { + *text += strlen(search); + if (virStrToLong_ull (*text, &dummy, 10, &value)) { + DEBUG ("error reading %s: %s", search, *text); + return 0; + } + + /* Convert bytes to kilobytes for libvirt */ + switch (tag) { + case VIR_DOMAIN_MEMORY_STAT_SWAP_IN: + case VIR_DOMAIN_MEMORY_STAT_SWAP_OUT: + case VIR_DOMAIN_MEMORY_STAT_UNUSED: + case VIR_DOMAIN_MEMORY_STAT_AVAILABLE: + value = value >> 10; + } + stat->tag = tag; + stat->val = value; + return 1; + } + return 0; +} + +/* The reply from the 'info balloon' command may contain additional memory + * statistics in the form: '[,<tag>=<val>]*' + */ +static int qemuMonitorParseExtraBalloonInfo(char *text, + virDomainMemoryStatPtr stats, + unsigned int nr_stats) +{ + char *p = text; + unsigned int nr_stats_found = 0; + + while (*p && nr_stats_found < nr_stats) { + if (parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_SWAP_IN, + ",mem_swapped_in=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_SWAP_OUT, + ",mem_swapped_out=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT, + ",major_page_faults=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT, + ",minor_page_faults=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_UNUSED, + ",free_mem=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_AVAILABLE, + ",total_mem=", &stats[nr_stats_found])) + nr_stats_found++; + + /* Skip to the next label */ + p = strchr (p, ','); + if (!p) break; + } + return nr_stats_found; +} /* The reply from QEMU contains 'ballon: actual=421' where value is in MB */ @@ -499,6 +558,30 @@ cleanup: return ret; } +int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, + virDomainMemoryStatPtr stats, + unsigned int nr_stats) +{ + char *reply = NULL; + int ret = 0; + char *offset; + + if (qemuMonitorCommand(mon, "info balloon", &reply) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("could not query memory balloon statistics")); + return -1; + } + + if ((offset = strstr(reply, BALLOON_PREFIX)) != NULL) { + if ((offset = strchr(reply, ',')) != NULL) { + ret = qemuMonitorParseExtraBalloonInfo(offset, stats, nr_stats); + } + } + + VIR_FREE(reply); + return ret; +} + int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, const char *devname, diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 519edf4..5897a03 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -45,6 +45,9 @@ int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon, int **pids); int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon, unsigned long *currmem); +int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, + virDomainMemoryStatPtr stats, + unsigned int nr_stats); int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, const char *devname, long long *rd_req, -- 1.6.5 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list