QEMU has a monitor command 'set_cpu' which allows a specific
CPU to be toggled between online& offline state. libvirt CPU
hotplug does not work in terms of individual indexes CPUs.
Thus to support this, we iteratively toggle the online state
when the total number of vCPUs is adjusted via libvirt
NB, currently untested since QEMU segvs when running this!
* src/qemu/qemu_driver.c: Toggle online state for CPUs when
doing hotplug
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
monitor API for toggling a CPU's online status via 'set_cpu
---
src/qemu/qemu_driver.c | 59 ++++++++++++++++++++++++++++++++++++-----
src/qemu/qemu_monitor.c | 14 ++++++++++
src/qemu/qemu_monitor.h | 2 +
src/qemu/qemu_monitor_json.c | 44 +++++++++++++++++++++++++++++++
src/qemu/qemu_monitor_json.h | 1 +
src/qemu/qemu_monitor_text.c | 38 +++++++++++++++++++++++++++
src/qemu/qemu_monitor_text.h | 1 +
7 files changed, 151 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 03feaf4..6ef784f 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -4206,6 +4206,51 @@ cleanup:
}
+static int qemudDomainHotplugVcpus(virDomainObjPtr vm, unsigned int nvcpus)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int i, rc;
+ int ret = -1;
+
+ /* We need different branches here, because we want to offline
+ * in reverse order to onlining, so any partial fail leaves us in a
+ * reasonably sensible state */
+ if (nvcpus > vm->def->vcpus) {
+ for (i = vm->def->vcpus ; i < nvcpus ; i++) {
+ /* Online new CPU */
+ rc = qemuMonitorSetCPU(priv->mon, i, 1);
+ if (rc == 0)
+ goto unsupported;
+ if (rc < 0)
+ goto cleanup;
+
+ vm->def->vcpus++;
+ }
+ } else {
+ for (i = vm->def->vcpus - 1 ; i >= nvcpus ; i--) {
+ /* Offline old CPU */
+ rc = qemuMonitorSetCPU(priv->mon, i, 0);
+ if (rc == 0)
+ goto unsupported;
+ if (rc < 0)
+ goto cleanup;
+
+ vm->def->vcpus--;
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ return ret;
+
+unsupported:
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_INVALID, "%s",
+ _("cannot change vcpu count of an active domain"));
+ goto cleanup;
+}
+
+
static int qemudDomainSetVcpus(virDomainPtr dom, unsigned int nvcpus) {
struct qemud_driver *driver = dom->conn->privateData;
virDomainObjPtr vm;
@@ -4228,12 +4273,6 @@ static int qemudDomainSetVcpus(virDomainPtr dom, unsigned int
nvcpus) {
if (qemuDomainObjBeginJob(vm) < 0)
goto cleanup;
- if (virDomainObjIsActive(vm)) {
- qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
- _("cannot change vcpu count of an active domain"));
- goto endjob;
- }
-
if (!(type = virDomainVirtTypeToString(vm->def->virtType))) {
qemuReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown virt type in domain definition
'%d'"),
@@ -4254,8 +4293,12 @@ static int qemudDomainSetVcpus(virDomainPtr dom, unsigned int
nvcpus) {
goto endjob;
}
- vm->def->vcpus = nvcpus;
- ret = 0;
+ if (virDomainObjIsActive(vm)) {
+ ret = qemudDomainHotplugVcpus(vm, nvcpus);
+ } else {
+ vm->def->vcpus = nvcpus;
+ ret = 0;
+ }
endjob:
if (qemuDomainObjEndJob(vm) == 0)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index c1d369b..541d7a1 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -912,6 +912,20 @@ int qemuMonitorSetBalloon(qemuMonitorPtr mon,
return ret;
}
+
+int qemuMonitorSetCPU(qemuMonitorPtr mon, int cpu, int online)
+{
+ int ret;
+ DEBUG("mon=%p, fd=%d cpu=%d online=%d", mon, mon->fd, cpu, online);
+
+ if (mon->json)
+ ret = qemuMonitorJSONSetCPU(mon, cpu, online);
+ else
+ ret = qemuMonitorTextSetCPU(mon, cpu, online);
+ return ret;
+}
+
+
int qemuMonitorEjectMedia(qemuMonitorPtr mon,
const char *devname)
{
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 786ad7a..d560c63 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -144,6 +144,8 @@ int qemuMonitorSetVNCPassword(qemuMonitorPtr mon,
const char *password);
int qemuMonitorSetBalloon(qemuMonitorPtr mon,
unsigned long newmem);
+int qemuMonitorSetCPU(qemuMonitorPtr mon, int cpu, int online);
+
/* XXX should we pass the virDomainDiskDefPtr instead
* and hide devname details inside monitor. Reconsider
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index c9b8d60..35e529e 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -873,6 +873,50 @@ cleanup:
}
+/*
+ * Returns: 0 if CPU hotplug not supported, +1 if CPU hotplug worked
+ * or -1 on failure
+ */
+int qemuMonitorJSONSetCPU(qemuMonitorPtr mon,
+ int cpu, int online)
+{
+ int ret;
+ virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("balloon",
+ "U:cpu", (unsigned long
long)cpu,
+ "s:state", online ?
"online" : "offline",
+ NULL);
+ virJSONValuePtr reply = NULL;
+ if (!cmd)
+ return -1;
+
+ ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+ if (ret == 0) {
+ /* XXX See if CPU soft-failed due to lack of ACPI */
+#if 0
+ if (qemuMonitorJSONHasError(reply, "DeviceNotActive") ||
+ qemuMonitorJSONHasError(reply, "KVMMissingCap"))
+ goto cleanup;
+#endif
+
+ /* See if any other fatal error occurred */
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+
+ /* Real success */
+ if (ret == 0)
+ ret = 1;
+ }
+
+#if 0
+cleanup:
+#endif
+
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+
int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon,
const char *devname)
{
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 65a70e3..88dfd3c 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -57,6 +57,7 @@ int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
const char *password);
int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon,
unsigned long newmem);
+int qemuMonitorJSONSetCPU(qemuMonitorPtr mon, int cpu, int online);
int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon,
const char *devname);
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index e993699..1745b20 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -797,6 +797,44 @@ int qemuMonitorTextSetBalloon(qemuMonitorPtr mon,
return ret;
}
+
+/*
+ * Returns: 0 if balloon not supported, +1 if balloon adjust worked
+ * or -1 on failure
+ */
+int qemuMonitorTextSetCPU(qemuMonitorPtr mon, int cpu, int online)
+{
+ char *cmd;
+ char *reply = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&cmd, "set_cpu %d %s", cpu, online ? "online"
: "offline") < 0) {
+ virReportOOMError(NULL);
+ return -1;
+ }
+
+ if (qemuMonitorCommand(mon, cmd, &reply) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+ "%s", _("could nt change CPU online
status"));
+ VIR_FREE(cmd);
+ return -1;
+ }
+ VIR_FREE(cmd);
+
+ /* If the command failed qemu prints: 'unknown command'
+ * No message is printed on success it seems */
+ if (strstr(reply, "\nunknown command:")) {
+ /* Don't set error - it is expected CPU onlining fails on many qemu - caller
will handle */
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+
+ VIR_FREE(reply);
+ return ret;
+}
+
+
int qemuMonitorTextEjectMedia(qemuMonitorPtr mon,
const char *devname)
{
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 1937e99..77b8b0f 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -61,6 +61,7 @@ int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon,
const char *password);
int qemuMonitorTextSetBalloon(qemuMonitorPtr mon,
unsigned long newmem);
+int qemuMonitorTextSetCPU(qemuMonitorPtr mon, int cpu, int online);
int qemuMonitorTextEjectMedia(qemuMonitorPtr mon,
const char *devname);
--
1.6.6