From: "Daniel P. Berrange" <berrange(a)redhat.com>
If QEMU supports the BALLOON_EVENT QMP event, then we can
avoid invoking 'query-balloon' when returning XML or the
domain info.
* src/qemu/qemu_capabilities.c, src/qemu/qemu_capabilities.h:
Add QEMU_CAPS_BALLOON_EVENT
* src/qemu/qemu_driver.c: Skip query-balloon in
qemudDomainGetInfo and qemuDomainGetXMLDesc if we have
QEMU_CAPS_BALLOON_EVENT set
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Check
for BALLOON_EVENT at connect to monitor. Add callback
for balloon change notifications
* src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h:
Add handling of BALLOON_EVENT and impl 'query-events'
check
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
src/qemu/qemu_capabilities.c | 2 +
src/qemu/qemu_capabilities.h | 1 +
src/qemu/qemu_driver.c | 7 ++++-
src/qemu/qemu_monitor.c | 18 +++++++++++-
src/qemu/qemu_monitor.h | 5 +++
src/qemu/qemu_monitor_json.c | 65 ++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_monitor_json.h | 2 +
src/qemu/qemu_process.c | 31 ++++++++++++++++++++
8 files changed, 129 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index a3c87d1..223852a 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -162,6 +162,8 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
"scsi-cd",
"ide-cd",
"no-user-config",
+
+ "balloon-event", /* 95 */
);
struct qemu_feature_flags {
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 0e0899e..8c829dc 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -130,6 +130,7 @@ enum qemuCapsFlags {
QEMU_CAPS_SCSI_CD = 92, /* -device scsi-cd */
QEMU_CAPS_IDE_CD = 93, /* -device ide-cd */
QEMU_CAPS_NO_USER_CONFIG = 94, /* -no-user-config */
+ QEMU_CAPS_BALLOON_EVENT = 95, /* Async event for balloon changes */
QEMU_CAPS_LAST, /* this must always be the last item */
};
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 0fd7de1..45eb16d 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -2226,6 +2226,8 @@ static int qemudDomainGetInfo(virDomainPtr dom,
if ((vm->def->memballoon != NULL) &&
(vm->def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_NONE)) {
info->memory = vm->def->mem.max_balloon;
+ } else if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_BALLOON_EVENT)) {
+ info->memory = vm->def->mem.cur_balloon;
} else if (qemuDomainJobAllowed(priv, QEMU_JOB_QUERY)) {
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
goto cleanup;
@@ -4500,6 +4502,7 @@ static char *qemuDomainGetXMLDesc(virDomainPtr dom,
char *ret = NULL;
unsigned long long balloon;
int err = 0;
+ qemuDomainObjPrivatePtr priv;
/* Flags checked by virDomainDefFormat */
@@ -4514,11 +4517,13 @@ static char *qemuDomainGetXMLDesc(virDomainPtr dom,
goto cleanup;
}
+ priv = vm->privateData;
+
/* Refresh current memory based on balloon info if supported */
if ((vm->def->memballoon != NULL) &&
(vm->def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_NONE)
&&
+ !qemuCapsGet(priv->qemuCaps, QEMU_CAPS_BALLOON_EVENT) &&
(virDomainObjIsActive(vm))) {
- qemuDomainObjPrivatePtr priv = vm->privateData;
/* Don't delay if someone's using the monitor, just use
* existing most recent data instead */
if (qemuDomainJobAllowed(priv, QEMU_JOB_QUERY)) {
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 7d69c67..2b04240 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1087,6 +1087,16 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
}
+int qemuMonitorEmitBalloonChange(qemuMonitorPtr mon,
+ unsigned long long actual)
+{
+ int ret = -1;
+ VIR_DEBUG("mon=%p", mon);
+
+ QEMU_MONITOR_CALLBACK(mon, ret, domainBalloonChange, mon->vm, actual);
+ return ret;
+}
+
int qemuMonitorSetCapabilities(qemuMonitorPtr mon,
virBitmapPtr qemuCaps)
@@ -1103,11 +1113,17 @@ int qemuMonitorSetCapabilities(qemuMonitorPtr mon,
if (mon->json) {
ret = qemuMonitorJSONSetCapabilities(mon);
- if (ret)
+ if (ret < 0)
goto cleanup;
ret = qemuMonitorJSONCheckCommands(mon, qemuCaps, &json_hmp);
+ if (ret < 0)
+ goto cleanup;
mon->json_hmp = json_hmp > 0;
+
+ ret = qemuMonitorJSONCheckEvents(mon, qemuCaps);
+ if (ret < 0)
+ goto cleanup;
} else {
ret = 0;
}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index ffe8fe7..3803ea6 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -133,6 +133,9 @@ struct _qemuMonitorCallbacks {
virDomainObjPtr vm);
int (*domainPMSuspend)(qemuMonitorPtr mon,
virDomainObjPtr vm);
+ int (*domainBalloonChange)(qemuMonitorPtr mon,
+ virDomainObjPtr vm,
+ unsigned long long actual);
};
char *qemuMonitorEscapeArg(const char *in);
@@ -208,6 +211,8 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
const char *diskAlias,
int type,
int status);
+int qemuMonitorEmitBalloonChange(qemuMonitorPtr mon,
+ unsigned long long actual);
int qemuMonitorStartCPUs(qemuMonitorPtr mon,
virConnectPtr conn);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index e1f5453..e3bdf51 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -66,6 +66,7 @@ static void qemuMonitorJSONHandlePMWakeup(qemuMonitorPtr mon,
virJSONValuePtr da
static void qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon, virJSONValuePtr data);
static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon, virJSONValuePtr
data);
static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONValuePtr
data);
+static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValuePtr
data);
typedef struct {
const char *type;
@@ -73,6 +74,7 @@ typedef struct {
} qemuEventHandler;
static qemuEventHandler eventHandlers[] = {
+ { "BALLOON_CHANGE", qemuMonitorJSONHandleBalloonChange, },
{ "BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError, },
{ "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, },
{ "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, },
@@ -872,6 +874,19 @@ qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon,
VIR_DOMAIN_BLOCK_JOB_CANCELED);
}
+static void
+qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon,
+ virJSONValuePtr data)
+{
+ unsigned long long actual = 0;
+ if (virJSONValueObjectGetNumberUlong(data, "actual", &actual) < 0)
{
+ VIR_WARN("missing actual in balloon change event");
+ return;
+ }
+ actual = (actual/1024);
+ qemuMonitorEmitBalloonChange(mon, actual);
+}
+
int
qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon,
const char *cmd_str,
@@ -999,6 +1014,56 @@ cleanup:
int
+qemuMonitorJSONCheckEvents(qemuMonitorPtr mon,
+ virBitmapPtr qemuCaps)
+{
+ int ret = -1;
+ virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-events", NULL);
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr data;
+ int i, n;
+
+ if (!cmd)
+ return ret;
+
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (qemuMonitorJSONCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ if (!(data = virJSONValueObjectGet(reply, "return")) ||
+ data->type != VIR_JSON_TYPE_ARRAY ||
+ (n = virJSONValueArraySize(data)) <= 0)
+ goto cleanup;
+
+ for (i = 0; i < n; i++) {
+ virJSONValuePtr entry;
+ const char *name;
+
+ if (!(entry = virJSONValueArrayGet(data, i)) ||
+ !(name = virJSONValueObjectGetString(entry, "name")))
+ goto cleanup;
+
+ if (STREQ(name, "BALLOON_CHANGE"))
+ qemuCapsSet(qemuCaps, QEMU_CAPS_BALLOON_EVENT);
+ }
+
+ ret = 0;
+
+cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+
+int
qemuMonitorJSONStartCPUs(qemuMonitorPtr mon,
virConnectPtr conn ATTRIBUTE_UNUSED)
{
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 22a3adf..46b6ab3 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -45,6 +45,8 @@ int qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon);
int qemuMonitorJSONCheckCommands(qemuMonitorPtr mon,
virBitmapPtr qemuCaps,
int *json_hmp);
+int qemuMonitorJSONCheckEvents(qemuMonitorPtr mon,
+ virBitmapPtr qemuCaps);
int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon,
virConnectPtr conn);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 58ba5bf..3248dbd 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1139,6 +1139,36 @@ qemuProcessHandlePMSuspend(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
return 0;
}
+static int
+qemuProcessHandleBalloonChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ unsigned long long actual)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainEventPtr event;
+
+ virDomainObjLock(vm);
+ event = virDomainEventBalloonChangeNewFromObj(vm, actual);
+
+ VIR_DEBUG("Updating balloon from %lld to %lld kb",
+ vm->def->mem.cur_balloon, actual);
+ vm->def->mem.cur_balloon = actual;
+
+ if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+ VIR_WARN("unable to save domain status with balloon change");
+
+ virDomainObjUnlock(vm);
+
+ if (event) {
+ qemuDriverLock(driver);
+ qemuDomainEventQueue(driver, event);
+ qemuDriverUnlock(driver);
+ }
+
+ return 0;
+}
+
+
static qemuMonitorCallbacks monitorCallbacks = {
.destroy = qemuProcessHandleMonitorDestroy,
.eofNotify = qemuProcessHandleMonitorEOF,
@@ -1155,6 +1185,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
.domainTrayChange = qemuProcessHandleTrayChange,
.domainPMWakeup = qemuProcessHandlePMWakeup,
.domainPMSuspend = qemuProcessHandlePMSuspend,
+ .domainBalloonChange = qemuProcessHandleBalloonChange,
};
static int
--
1.7.7.6