6ae9ed
From f44af1507adb8d05bd5e2992734d7f275c34fc6a Mon Sep 17 00:00:00 2001
6ae9ed
Message-Id: <f44af1507adb8d05bd5e2992734d7f275c34fc6a@dist-git>
6ae9ed
From: Peter Krempa <pkrempa@redhat.com>
6ae9ed
Date: Wed, 24 Aug 2016 16:11:38 -0400
6ae9ed
Subject: [PATCH] qemu: Use modern vcpu hotplug approach if possible
6ae9ed
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1097930
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1224341
6ae9ed
6ae9ed
To allow unplugging the vcpus, hotplugging of vcpus on platforms which
6ae9ed
require to plug multiple logical vcpus at once or plugging them in an
6ae9ed
arbitrary order it's necessary to use the new device_add interface for
6ae9ed
vcpu hotplug.
6ae9ed
6ae9ed
This patch adds support for the device_add interface using the old
6ae9ed
setvcpus API by implementing an algorithm to select the appropriate
6ae9ed
entities to plug in.
6ae9ed
6ae9ed
(cherry picked from commit 6d4ee77d754df83a0d006c8b503042b437c14df8)
6ae9ed
---
6ae9ed
 src/qemu/qemu_driver.c | 157 +++++++++++++++++++++++++++++++++++++++++++------
6ae9ed
 1 file changed, 140 insertions(+), 17 deletions(-)
6ae9ed
6ae9ed
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
6ae9ed
index 43e00ff..900aacd 100644
6ae9ed
--- a/src/qemu/qemu_driver.c
6ae9ed
+++ b/src/qemu/qemu_driver.c
6ae9ed
@@ -4600,46 +4600,67 @@ qemuDomainHotplugAddVcpu(virQEMUDriverPtr driver,
6ae9ed
                          virDomainObjPtr vm,
6ae9ed
                          unsigned int vcpu)
6ae9ed
 {
6ae9ed
-    qemuDomainObjPrivatePtr priv = vm->privateData;
6ae9ed
+    virJSONValuePtr vcpuprops = NULL;
6ae9ed
     virDomainVcpuDefPtr vcpuinfo = virDomainDefGetVcpu(vm->def, vcpu);
6ae9ed
     qemuDomainVcpuPrivatePtr vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpuinfo);
6ae9ed
+    unsigned int nvcpus = vcpupriv->vcpus;
6ae9ed
+    bool newhotplug = qemuDomainSupportsNewVcpuHotplug(vm);
6ae9ed
     int ret = -1;
6ae9ed
     int rc;
6ae9ed
     int oldvcpus = virDomainDefGetVcpus(vm->def);
6ae9ed
+    size_t i;
6ae9ed
 
6ae9ed
-    if (vcpuinfo->online) {
6ae9ed
-        virReportError(VIR_ERR_INVALID_ARG,
6ae9ed
-                       _("vCPU '%u' is already online"), vcpu);
6ae9ed
-        return -1;
6ae9ed
+    if (newhotplug) {
6ae9ed
+        if (virAsprintf(&vcpupriv->alias, "vcpu%u", vcpu) < 0)
6ae9ed
+            goto cleanup;
6ae9ed
+
6ae9ed
+        if (!(vcpuprops = qemuBuildHotpluggableCPUProps(vcpuinfo)))
6ae9ed
+            goto cleanup;
6ae9ed
     }
6ae9ed
 
6ae9ed
     qemuDomainObjEnterMonitor(driver, vm);
6ae9ed
 
6ae9ed
-    rc = qemuMonitorSetCPU(priv->mon, vcpu, true);
6ae9ed
+    if (newhotplug) {
6ae9ed
+        rc = qemuMonitorAddDeviceArgs(qemuDomainGetMonitor(vm), vcpuprops);
6ae9ed
+        vcpuprops = NULL;
6ae9ed
+    } else {
6ae9ed
+        rc = qemuMonitorSetCPU(qemuDomainGetMonitor(vm), vcpu, true);
6ae9ed
+    }
6ae9ed
 
6ae9ed
     if (qemuDomainObjExitMonitor(driver, vm) < 0)
6ae9ed
         goto cleanup;
6ae9ed
 
6ae9ed
-    virDomainAuditVcpu(vm, oldvcpus, oldvcpus + 1, "update", rc == 0);
6ae9ed
+    virDomainAuditVcpu(vm, oldvcpus, oldvcpus + nvcpus, "update", rc == 0);
6ae9ed
 
6ae9ed
     if (rc < 0)
6ae9ed
         goto cleanup;
6ae9ed
 
6ae9ed
-    vcpuinfo->online = true;
6ae9ed
+    /* start outputting of the new XML element to allow keeping unpluggability */
6ae9ed
+    if (newhotplug)
6ae9ed
+        vm->def->individualvcpus = true;
6ae9ed
 
6ae9ed
     if (qemuDomainRefreshVcpuInfo(driver, vm, QEMU_ASYNC_JOB_NONE, false) < 0)
6ae9ed
         goto cleanup;
6ae9ed
 
6ae9ed
-    if (qemuDomainValidateVcpuInfo(vm) < 0)
6ae9ed
-        goto cleanup;
6ae9ed
+    /* validation requires us to set the expected state prior to calling it */
6ae9ed
+    for (i = vcpu; i < vcpu + nvcpus; i++) {
6ae9ed
+        vcpuinfo = virDomainDefGetVcpu(vm->def, i);
6ae9ed
+        vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpuinfo);
6ae9ed
 
6ae9ed
-    if (vcpupriv->tid > 0 &&
6ae9ed
-        qemuProcessSetupVcpu(vm, vcpu) < 0)
6ae9ed
+        vcpuinfo->online = true;
6ae9ed
+
6ae9ed
+        if (vcpupriv->tid > 0 &&
6ae9ed
+            qemuProcessSetupVcpu(vm, i) < 0)
6ae9ed
+            goto cleanup;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (qemuDomainValidateVcpuInfo(vm) < 0)
6ae9ed
         goto cleanup;
6ae9ed
 
6ae9ed
     ret = 0;
6ae9ed
 
6ae9ed
  cleanup:
6ae9ed
+    virJSONValueFree(vcpuprops);
6ae9ed
     return ret;
6ae9ed
 }
6ae9ed
 
6ae9ed
@@ -4777,6 +4798,96 @@ qemuDomainSetVcpusMax(virQEMUDriverPtr driver,
6ae9ed
 }
6ae9ed
 
6ae9ed
 
6ae9ed
+/**
6ae9ed
+ * qemuDomainSelectHotplugVcpuEntities:
6ae9ed
+ *
6ae9ed
+ * @def: domain definition
6ae9ed
+ * @nvcpus: target vcpu count
6ae9ed
+ *
6ae9ed
+ * Tries to find which vcpu entities need to be enabled or disabled to reach
6ae9ed
+ * @nvcpus. This function works in order of the legacy hotplug but is able to
6ae9ed
+ * skip over entries that are added out of order.
6ae9ed
+ *
6ae9ed
+ * Returns the bitmap of vcpus to modify on success, NULL on error.
6ae9ed
+ */
6ae9ed
+static virBitmapPtr
6ae9ed
+qemuDomainSelectHotplugVcpuEntities(virDomainDefPtr def,
6ae9ed
+                                    unsigned int nvcpus)
6ae9ed
+{
6ae9ed
+    virBitmapPtr ret = NULL;
6ae9ed
+    virDomainVcpuDefPtr vcpu;
6ae9ed
+    qemuDomainVcpuPrivatePtr vcpupriv;
6ae9ed
+    unsigned int maxvcpus = virDomainDefGetVcpusMax(def);
6ae9ed
+    unsigned int curvcpus = virDomainDefGetVcpus(def);
6ae9ed
+    ssize_t i;
6ae9ed
+
6ae9ed
+    if (!(ret = virBitmapNew(maxvcpus)))
6ae9ed
+        return NULL;
6ae9ed
+
6ae9ed
+    if (nvcpus > curvcpus) {
6ae9ed
+        for (i = 0; i < maxvcpus && curvcpus < nvcpus; i++) {
6ae9ed
+            vcpu = virDomainDefGetVcpu(def, i);
6ae9ed
+            vcpupriv =  QEMU_DOMAIN_VCPU_PRIVATE(vcpu);
6ae9ed
+
6ae9ed
+            if (vcpu->online)
6ae9ed
+                continue;
6ae9ed
+
6ae9ed
+            if (vcpupriv->vcpus == 0)
6ae9ed
+                continue;
6ae9ed
+
6ae9ed
+            curvcpus += vcpupriv->vcpus;
6ae9ed
+
6ae9ed
+            if (curvcpus > nvcpus) {
6ae9ed
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
6ae9ed
+                               _("target vm vcpu granularity does not allow the "
6ae9ed
+                                 "desired vcpu count"));
6ae9ed
+                goto error;
6ae9ed
+            }
6ae9ed
+
6ae9ed
+            ignore_value(virBitmapSetBit(ret, i));
6ae9ed
+        }
6ae9ed
+    } else {
6ae9ed
+        for (i = maxvcpus - 1; i >= 0 && curvcpus > nvcpus; i--) {
6ae9ed
+            vcpu = virDomainDefGetVcpu(def, i);
6ae9ed
+            vcpupriv =  QEMU_DOMAIN_VCPU_PRIVATE(vcpu);
6ae9ed
+
6ae9ed
+            if (!vcpu->online)
6ae9ed
+                continue;
6ae9ed
+
6ae9ed
+            if (vcpupriv->vcpus == 0)
6ae9ed
+                continue;
6ae9ed
+
6ae9ed
+            if (!vcpupriv->alias)
6ae9ed
+                continue;
6ae9ed
+
6ae9ed
+            curvcpus -= vcpupriv->vcpus;
6ae9ed
+
6ae9ed
+            if (curvcpus < nvcpus) {
6ae9ed
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
6ae9ed
+                               _("target vm vcpu granularity does not allow the "
6ae9ed
+                                 "desired vcpu count"));
6ae9ed
+                goto error;
6ae9ed
+            }
6ae9ed
+
6ae9ed
+            ignore_value(virBitmapSetBit(ret, i));
6ae9ed
+        }
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (curvcpus != nvcpus) {
6ae9ed
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
6ae9ed
+                       _("failed to find appropriate hotpluggable vcpus to "
6ae9ed
+                         "reach the desired target vcpu count"));
6ae9ed
+        goto error;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    return ret;
6ae9ed
+
6ae9ed
+ error:
6ae9ed
+    virBitmapFree(ret);
6ae9ed
+    return NULL;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
 static int
6ae9ed
 qemuDomainSetVcpusLive(virQEMUDriverPtr driver,
6ae9ed
                        virQEMUDriverConfigPtr cfg,
6ae9ed
@@ -4790,8 +4901,14 @@ qemuDomainSetVcpusLive(virQEMUDriverPtr driver,
6ae9ed
     char *all_nodes_str = NULL;
6ae9ed
     virBitmapPtr all_nodes = NULL;
6ae9ed
     virErrorPtr err = NULL;
6ae9ed
+    virBitmapPtr vcpumap = NULL;
6ae9ed
+    ssize_t nextvcpu = -1;
6ae9ed
+    int rc = 0;
6ae9ed
     int ret = -1;
6ae9ed
 
6ae9ed
+    if (!(vcpumap = qemuDomainSelectHotplugVcpuEntities(vm->def, nvcpus)))
6ae9ed
+        goto cleanup;
6ae9ed
+
6ae9ed
     if (virNumaIsAvailable() &&
6ae9ed
         virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPUSET)) {
6ae9ed
         if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_EMULATOR, 0,
6ae9ed
@@ -4810,20 +4927,25 @@ qemuDomainSetVcpusLive(virQEMUDriverPtr driver,
6ae9ed
     }
6ae9ed
 
6ae9ed
     if (nvcpus > virDomainDefGetVcpus(vm->def)) {
6ae9ed
-        for (i = virDomainDefGetVcpus(vm->def); i < nvcpus; i++) {
6ae9ed
-            if (qemuDomainHotplugAddVcpu(driver, vm, i) < 0)
6ae9ed
-                goto cleanup;
6ae9ed
+        while ((nextvcpu = virBitmapNextSetBit(vcpumap, nextvcpu)) != -1) {
6ae9ed
+            if ((rc = qemuDomainHotplugAddVcpu(driver, vm, nextvcpu)) < 0)
6ae9ed
+                break;
6ae9ed
         }
6ae9ed
     } else {
6ae9ed
         for (i = virDomainDefGetVcpus(vm->def) - 1; i >= nvcpus; i--) {
6ae9ed
-            if (qemuDomainHotplugDelVcpu(driver, vm, i) < 0)
6ae9ed
-                goto cleanup;
6ae9ed
+            if ((rc = qemuDomainHotplugDelVcpu(driver, vm, i)) < 0)
6ae9ed
+                break;
6ae9ed
         }
6ae9ed
     }
6ae9ed
 
6ae9ed
+    qemuDomainVcpuPersistOrder(vm->def);
6ae9ed
+
6ae9ed
     if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0)
6ae9ed
         goto cleanup;
6ae9ed
 
6ae9ed
+    if (rc < 0)
6ae9ed
+        goto cleanup;
6ae9ed
+
6ae9ed
     ret = 0;
6ae9ed
 
6ae9ed
  cleanup:
6ae9ed
@@ -4838,6 +4960,7 @@ qemuDomainSetVcpusLive(virQEMUDriverPtr driver,
6ae9ed
     VIR_FREE(all_nodes_str);
6ae9ed
     virBitmapFree(all_nodes);
6ae9ed
     virCgroupFree(&cgroup_temp);
6ae9ed
+    virBitmapFree(vcpumap);
6ae9ed
 
6ae9ed
     return ret;
6ae9ed
 }
6ae9ed
-- 
6ae9ed
2.10.0
6ae9ed