]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
Support VCPU hotplug in QEMU guests
authorDaniel P. Berrange <berrange@redhat.com>
Mon, 8 Feb 2010 16:37:17 +0000 (16:37 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Thu, 4 Mar 2010 13:19:29 +0000 (13:19 +0000)
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
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

index 2c048395b2299468cc70e5c7fa7fdb5c0832c1bc..b8b7916e51b1320e5d94a039655a96916e255dbc 100644 (file)
@@ -4494,10 +4494,56 @@ cleanup:
 }
 
 
-static int qemudDomainSetVcpus(virDomainPtr dom,
-                               ATTRIBUTE_UNUSED unsigned int nvcpus) {
+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:
+    qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                    _("cannot change vcpu count of this domain"));
+    goto cleanup;
+}
+
+
+static int qemudDomainSetVcpus(virDomainPtr dom, unsigned int nvcpus) {
     struct qemud_driver *driver = dom->conn->privateData;
     virDomainObjPtr vm;
+    const char * type;
+    int max;
     int ret = -1;
 
     qemuDriverLock(driver);
@@ -4518,8 +4564,31 @@ static int qemudDomainSetVcpus(virDomainPtr dom,
         goto cleanup;
     }
 
-    qemuReportError(VIR_ERR_NO_SUPPORT,
-                     "%s", _("cpu hotplug not yet supported"));
+    if (!(type = virDomainVirtTypeToString(vm->def->virtType))) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("unknown virt type in domain definition '%d'"),
+                        vm->def->virtType);
+        goto endjob;
+    }
+
+    if ((max = qemudGetMaxVCPUs(NULL, type)) < 0) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                        _("could not determine max vcpus for the domain"));
+        goto endjob;
+    }
+
+    if (nvcpus > max) {
+        qemuReportError(VIR_ERR_INVALID_ARG,
+                        _("requested vcpus is greater than max allowable"
+                          " vcpus for the domain: %d > %d"), nvcpus, max);
+        goto endjob;
+    }
+
+    ret = qemudDomainHotplugVcpus(vm, nvcpus);
+
+endjob:
+    if (qemuDomainObjEndJob(vm) == 0)
+        vm = NULL;
 
 cleanup:
     if (vm)
index a4d2b890e28e4ea854f79a6eccd4b41050aa56ee..c252bce5846b48138ca41abe8ea6ea949dae48cd 100644 (file)
@@ -925,6 +925,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)
 {
index 3e552360e27b8836a0f44fa4d962e800609406e6..15a2ff03d43ff3786ece9b46d701effa6a4df76e 100644 (file)
@@ -146,6 +146,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
index 9c0245c9b9cee7b3122774efc96bdc5eac7f3c0e..f04fd2eb5a813dd3903b53aec1ecb2a0d470f143 100644 (file)
@@ -914,6 +914,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)
 {
index 70a8dae6cb8f95a7b739609dbbb6e29a72781d3c..f18c7991b7d9809f8d915f5e33ccaa20cf033302 100644 (file)
@@ -59,6 +59,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);
index 82f7a9b9cb9422e18a0048b659754b9a945abcbd..7f0e7f65dc611d417c1b3002753ae920d11a6149 100644 (file)
@@ -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();
+        return -1;
+    }
+
+    if (qemuMonitorCommand(mon, cmd, &reply) < 0) {
+        qemuReportError(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)
 {
index 9dcb0c2e6c5f849956164d55dd49cecb3fde5909..ddacb42d6dbca5adaf496ad9b1fd5270954ac26a 100644 (file)
@@ -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);