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(a)us.ibm.com>
To: Daniel Veillard <veillard(a)redhat.com>
Cc: Daniel P. Berrange <berrange(a)redhat.com>
Cc: libvirt list <libvir-list(a)redhat.com>
---
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