diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 9e4d927dd5..d438c06112 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -167,6 +167,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "dump-guest-memory", "nec-usb-xhci", "virtio-s390", + "balloon-event", ); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 9b5ff302b7..6526ea838d 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -134,6 +134,7 @@ enum qemuCapsFlags { QEMU_CAPS_DUMP_GUEST_MEMORY = 96, /* dump-guest-memory command */ QEMU_CAPS_NEC_USB_XHCI = 97, /* -device nec-usb-xhci */ QEMU_CAPS_VIRTIO_S390 = 98, /* -device virtio-*-s390 */ + QEMU_CAPS_BALLOON_EVENT = 99, /* 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 dc04d133c7..29ebb4c540 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2234,6 +2234,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; @@ -4560,6 +4562,7 @@ static char *qemuDomainGetXMLDesc(virDomainPtr dom, char *ret = NULL; unsigned long long balloon; int err = 0; + qemuDomainObjPrivatePtr priv; /* Flags checked by virDomainDefFormat */ @@ -4574,11 +4577,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 b8a2f2fbcd..42a5d6cbe8 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1086,6 +1086,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) @@ -1102,11 +1112,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 66bec38982..c96282b230 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 7317c42c45..a99bf60ed1 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -69,6 +69,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; @@ -76,6 +77,7 @@ typedef struct { } qemuEventHandler; static qemuEventHandler eventHandlers[] = { + { "BALLOON_CHANGE", qemuMonitorJSONHandleBalloonChange, }, { "BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError, }, { "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, }, { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, }, @@ -875,6 +877,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 = VIR_DIV_UP(actual, 1024); + qemuMonitorEmitBalloonChange(mon, actual); +} + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, const char *cmd_str, @@ -1003,6 +1018,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 e8bd9b8841..52938957d8 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 bf324870f1..c120517d16 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1148,6 +1148,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, @@ -1164,6 +1194,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainTrayChange = qemuProcessHandleTrayChange, .domainPMWakeup = qemuProcessHandlePMWakeup, .domainPMSuspend = qemuProcessHandlePMSuspend, + .domainBalloonChange = qemuProcessHandleBalloonChange, }; static int