render / rpms / libvirt

Forked from rpms/libvirt 11 months ago
Clone
6ae9ed
From 45a8d716bb066cb05d59e5aeb9c46814f0de3b01 Mon Sep 17 00:00:00 2001
6ae9ed
Message-Id: <45a8d716bb066cb05d59e5aeb9c46814f0de3b01@dist-git>
6ae9ed
From: Peter Krempa <pkrempa@redhat.com>
6ae9ed
Date: Wed, 24 Aug 2016 16:11:24 -0400
6ae9ed
Subject: [PATCH] qemu: monitor: Add algorithm for combining
6ae9ed
 query-(hotpluggable-)-cpus data
6ae9ed
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1097930
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1224341
6ae9ed
6ae9ed
For hotplug purposes it's necessary to retrieve data using
6ae9ed
query-hotpluggable-cpus while the old query-cpus API report thread IDs
6ae9ed
and order of hotplug.
6ae9ed
6ae9ed
This patch adds code that merges the data using a rather non-trivial
6ae9ed
algorithm and fills the data to the qemuMonitorCPUInfo structure for
6ae9ed
adding to appropriate place in the domain definition.
6ae9ed
6ae9ed
(cherry picked from commit 9bbbc88a8f343be6217c49fc4eef033376c0c648)
6ae9ed
---
6ae9ed
 src/qemu/qemu_domain.c  |   2 +-
6ae9ed
 src/qemu/qemu_monitor.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++--
6ae9ed
 src/qemu/qemu_monitor.h |  23 +++++-
6ae9ed
 3 files changed, 220 insertions(+), 10 deletions(-)
6ae9ed
6ae9ed
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
6ae9ed
index be602ef..ebd0d9e 100644
6ae9ed
--- a/src/qemu/qemu_domain.c
6ae9ed
+++ b/src/qemu/qemu_domain.c
6ae9ed
@@ -5754,7 +5754,7 @@ qemuDomainRefreshVcpuInfo(virQEMUDriverPtr driver,
6ae9ed
     if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
6ae9ed
         return -1;
6ae9ed
 
6ae9ed
-    rc = qemuMonitorGetCPUInfo(qemuDomainGetMonitor(vm), &info, maxvcpus);
6ae9ed
+    rc = qemuMonitorGetCPUInfo(qemuDomainGetMonitor(vm), &info, maxvcpus, false);
6ae9ed
 
6ae9ed
     if (qemuDomainObjExitMonitor(driver, vm) < 0)
6ae9ed
         goto cleanup;
6ae9ed
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
6ae9ed
index 573c94a..1f633fc 100644
6ae9ed
--- a/src/qemu/qemu_monitor.c
6ae9ed
+++ b/src/qemu/qemu_monitor.c
6ae9ed
@@ -1651,13 +1651,36 @@ qemuMonitorSystemReset(qemuMonitorPtr mon)
6ae9ed
 }
6ae9ed
 
6ae9ed
 
6ae9ed
+static void
6ae9ed
+qemuMonitorCPUInfoClear(qemuMonitorCPUInfoPtr cpus,
6ae9ed
+                        size_t ncpus)
6ae9ed
+{
6ae9ed
+    size_t i;
6ae9ed
+
6ae9ed
+    for (i = 0; i < ncpus; i++) {
6ae9ed
+        cpus[i].id = 0;
6ae9ed
+        cpus[i].socket_id = -1;
6ae9ed
+        cpus[i].core_id = -1;
6ae9ed
+        cpus[i].thread_id = -1;
6ae9ed
+        cpus[i].vcpus = 0;
6ae9ed
+        cpus[i].tid = 0;
6ae9ed
+
6ae9ed
+        VIR_FREE(cpus[i].qom_path);
6ae9ed
+        VIR_FREE(cpus[i].alias);
6ae9ed
+        VIR_FREE(cpus[i].type);
6ae9ed
+    }
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
 void
6ae9ed
 qemuMonitorCPUInfoFree(qemuMonitorCPUInfoPtr cpus,
6ae9ed
-                       size_t ncpus ATTRIBUTE_UNUSED)
6ae9ed
+                       size_t ncpus)
6ae9ed
 {
6ae9ed
     if (!cpus)
6ae9ed
         return;
6ae9ed
 
6ae9ed
+    qemuMonitorCPUInfoClear(cpus, ncpus);
6ae9ed
+
6ae9ed
     VIR_FREE(cpus);
6ae9ed
 }
6ae9ed
 
6ae9ed
@@ -1678,10 +1701,156 @@ qemuMonitorQueryCpusFree(struct qemuMonitorQueryCpusEntry *entries,
6ae9ed
 
6ae9ed
 
6ae9ed
 /**
6ae9ed
+ * Legacy approach doesn't allow out of order cpus, thus no complex matching
6ae9ed
+ * algorithm is necessary */
6ae9ed
+static void
6ae9ed
+qemuMonitorGetCPUInfoLegacy(struct qemuMonitorQueryCpusEntry *cpuentries,
6ae9ed
+                            size_t ncpuentries,
6ae9ed
+                            qemuMonitorCPUInfoPtr vcpus,
6ae9ed
+                            size_t maxvcpus)
6ae9ed
+{
6ae9ed
+    size_t i;
6ae9ed
+
6ae9ed
+    for (i = 0; i < maxvcpus; i++) {
6ae9ed
+        if (i < ncpuentries)
6ae9ed
+            vcpus[i].tid = cpuentries[i].tid;
6ae9ed
+
6ae9ed
+        /* for legacy hotplug to work we need to fake the vcpu count added by
6ae9ed
+         * enabling a given vcpu */
6ae9ed
+        vcpus[i].vcpus = 1;
6ae9ed
+    }
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
+/**
6ae9ed
+ * qemuMonitorGetCPUInfoHotplug:
6ae9ed
+ *
6ae9ed
+ * This function stitches together data retrieved via query-hotpluggable-cpus
6ae9ed
+ * which returns entities on the hotpluggable level (which may describe more
6ae9ed
+ * than one guest logical vcpu) with the output of query-cpus, having an entry
6ae9ed
+ * per enabled guest logical vcpu.
6ae9ed
+ *
6ae9ed
+ * query-hotpluggable-cpus conveys following information:
6ae9ed
+ * - topology information and number of logical vcpus this entry creates
6ae9ed
+ * - device type name of the entry that needs to be used when hotplugging
6ae9ed
+ * - qom path in qemu which can be used to map the entry against query-cpus
6ae9ed
+ *
6ae9ed
+ * query-cpus conveys following information:
6ae9ed
+ * - thread id of a given guest logical vcpu
6ae9ed
+ * - order in which the vcpus were inserted
6ae9ed
+ * - qom path to allow mapping the two together
6ae9ed
+ *
6ae9ed
+ * The libvirt's internal structure has an entry for each possible (even
6ae9ed
+ * disabled) guest vcpu. The purpose is to map the data together so that we are
6ae9ed
+ * certain of the thread id mapping and the information required for vcpu
6ae9ed
+ * hotplug.
6ae9ed
+ *
6ae9ed
+ * This function returns 0 on success and -1 on error, but does not report
6ae9ed
+ * libvirt errors so that fallback approach can be used.
6ae9ed
+ */
6ae9ed
+static int
6ae9ed
+qemuMonitorGetCPUInfoHotplug(struct qemuMonitorQueryHotpluggableCpusEntry *hotplugvcpus,
6ae9ed
+                             size_t nhotplugvcpus,
6ae9ed
+                             struct qemuMonitorQueryCpusEntry *cpuentries,
6ae9ed
+                             size_t ncpuentries,
6ae9ed
+                             qemuMonitorCPUInfoPtr vcpus,
6ae9ed
+                             size_t maxvcpus)
6ae9ed
+{
6ae9ed
+    char *tmp;
6ae9ed
+    int order = 1;
6ae9ed
+    size_t totalvcpus = 0;
6ae9ed
+    size_t i;
6ae9ed
+    size_t j;
6ae9ed
+
6ae9ed
+    /* ensure that the total vcpu count reported by query-hotpluggable-cpus equals
6ae9ed
+     * to the libvirt maximum cpu count */
6ae9ed
+    for (i = 0; i < nhotplugvcpus; i++)
6ae9ed
+        totalvcpus += hotplugvcpus[i].vcpus;
6ae9ed
+
6ae9ed
+    /* trim '/thread...' suffix from the data returned by query-cpus */
6ae9ed
+    for (i = 0; i < ncpuentries; i++) {
6ae9ed
+        if (cpuentries[i].qom_path &&
6ae9ed
+            (tmp = strstr(cpuentries[i].qom_path, "/thread")))
6ae9ed
+            *tmp = '\0';
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (totalvcpus != maxvcpus) {
6ae9ed
+        VIR_DEBUG("expected '%zu' total vcpus got '%zu'", maxvcpus, totalvcpus);
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    /* Note the order in which the hotpluggable entities are inserted by
6ae9ed
+     * matching them to the query-cpus entries */
6ae9ed
+    for (i = 0; i < ncpuentries; i++) {
6ae9ed
+        for (j = 0; j < nhotplugvcpus; j++) {
6ae9ed
+            if (!cpuentries[i].qom_path ||
6ae9ed
+                !hotplugvcpus[j].qom_path ||
6ae9ed
+                STRNEQ(cpuentries[i].qom_path, hotplugvcpus[j].qom_path))
6ae9ed
+                continue;
6ae9ed
+
6ae9ed
+            /* add ordering info for hotpluggable entries */
6ae9ed
+            if (hotplugvcpus[j].enable_id == 0)
6ae9ed
+                hotplugvcpus[j].enable_id = order++;
6ae9ed
+
6ae9ed
+            break;
6ae9ed
+        }
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    /* transfer appropriate data from the hotpluggable list to corresponding
6ae9ed
+     * entries. the entries returned by qemu may in fact describe multiple
6ae9ed
+     * logical vcpus in the guest */
6ae9ed
+    j = 0;
6ae9ed
+    for (i = 0; i < nhotplugvcpus; i++) {
6ae9ed
+        vcpus[j].socket_id = hotplugvcpus[i].socket_id;
6ae9ed
+        vcpus[j].core_id = hotplugvcpus[i].core_id;
6ae9ed
+        vcpus[j].thread_id = hotplugvcpus[i].thread_id;
6ae9ed
+        vcpus[j].vcpus = hotplugvcpus[i].vcpus;
6ae9ed
+        VIR_STEAL_PTR(vcpus[j].qom_path, hotplugvcpus[i].qom_path);
6ae9ed
+        VIR_STEAL_PTR(vcpus[j].alias, hotplugvcpus[i].alias);
6ae9ed
+        VIR_STEAL_PTR(vcpus[j].type, hotplugvcpus[i].type);
6ae9ed
+        vcpus[j].id = hotplugvcpus[i].enable_id;
6ae9ed
+
6ae9ed
+        /* skip over vcpu entries covered by this hotpluggable entry */
6ae9ed
+        j += hotplugvcpus[i].vcpus;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    /* match entries from query cpus to the output array taking into account
6ae9ed
+     * multi-vcpu objects */
6ae9ed
+    for (j = 0; j < ncpuentries; j++) {
6ae9ed
+        /* find the correct entry or beginning of group of entries */
6ae9ed
+        for (i = 0; i < maxvcpus; i++) {
6ae9ed
+            if (cpuentries[j].qom_path && vcpus[i].qom_path &&
6ae9ed
+                STREQ(cpuentries[j].qom_path, vcpus[i].qom_path))
6ae9ed
+                break;
6ae9ed
+        }
6ae9ed
+
6ae9ed
+        if (i == maxvcpus) {
6ae9ed
+            VIR_DEBUG("too many query-cpus entries for a given "
6ae9ed
+                      "query-hotpluggable-cpus entry");
6ae9ed
+            return -1;
6ae9ed
+        }
6ae9ed
+
6ae9ed
+        if (vcpus[i].vcpus != 1) {
6ae9ed
+            /* find a possibly empty vcpu thread for core granularity systems */
6ae9ed
+            for (; i < maxvcpus; i++) {
6ae9ed
+                if (vcpus[i].tid == 0)
6ae9ed
+                    break;
6ae9ed
+            }
6ae9ed
+        }
6ae9ed
+
6ae9ed
+        vcpus[i].tid = cpuentries[j].tid;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    return 0;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
+/**
6ae9ed
  * qemuMonitorGetCPUInfo:
6ae9ed
  * @mon: monitor
6ae9ed
  * @vcpus: pointer filled by array of qemuMonitorCPUInfo structures
6ae9ed
  * @maxvcpus: total possible number of vcpus
6ae9ed
+ * @hotplug: query data relevant for hotplug support
6ae9ed
  *
6ae9ed
  * Detects VCPU information. If qemu doesn't support or fails reporting
6ae9ed
  * information this function will return success as other parts of libvirt
6ae9ed
@@ -1693,20 +1862,32 @@ qemuMonitorQueryCpusFree(struct qemuMonitorQueryCpusEntry *entries,
6ae9ed
 int
6ae9ed
 qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
6ae9ed
                       qemuMonitorCPUInfoPtr *vcpus,
6ae9ed
-                      size_t maxvcpus)
6ae9ed
+                      size_t maxvcpus,
6ae9ed
+                      bool hotplug)
6ae9ed
 {
6ae9ed
-    qemuMonitorCPUInfoPtr info = NULL;
6ae9ed
+    struct qemuMonitorQueryHotpluggableCpusEntry *hotplugcpus = NULL;
6ae9ed
+    size_t nhotplugcpus = 0;
6ae9ed
     struct qemuMonitorQueryCpusEntry *cpuentries = NULL;
6ae9ed
     size_t ncpuentries = 0;
6ae9ed
-    size_t i;
6ae9ed
     int ret = -1;
6ae9ed
     int rc;
6ae9ed
+    qemuMonitorCPUInfoPtr info = NULL;
6ae9ed
 
6ae9ed
-    QEMU_CHECK_MONITOR(mon);
6ae9ed
+    if (hotplug)
6ae9ed
+        QEMU_CHECK_MONITOR_JSON(mon);
6ae9ed
+    else
6ae9ed
+        QEMU_CHECK_MONITOR(mon);
6ae9ed
 
6ae9ed
     if (VIR_ALLOC_N(info, maxvcpus) < 0)
6ae9ed
         return -1;
6ae9ed
 
6ae9ed
+    /* initialize a few non-zero defaults */
6ae9ed
+    qemuMonitorCPUInfoClear(info, maxvcpus);
6ae9ed
+
6ae9ed
+    if (hotplug &&
6ae9ed
+        (qemuMonitorJSONGetHotpluggableCPUs(mon, &hotplugcpus, &nhotplugcpus)) < 0)
6ae9ed
+        goto cleanup;
6ae9ed
+
6ae9ed
     if (mon->json)
6ae9ed
         rc = qemuMonitorJSONQueryCPUs(mon, &cpuentries, &ncpuentries);
6ae9ed
     else
6ae9ed
@@ -1721,15 +1902,23 @@ qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
6ae9ed
         goto cleanup;
6ae9ed
     }
6ae9ed
 
6ae9ed
-    for (i = 0; i < ncpuentries; i++)
6ae9ed
-        info[i].tid = cpuentries[i].tid;
6ae9ed
+    if (!hotplugcpus ||
6ae9ed
+        qemuMonitorGetCPUInfoHotplug(hotplugcpus, nhotplugcpus,
6ae9ed
+                                     cpuentries, ncpuentries,
6ae9ed
+                                     info, maxvcpus) < 0) {
6ae9ed
+        /* Fallback to the legacy algorithm. Hotplug paths will make sure that
6ae9ed
+         * the apropriate data is present */
6ae9ed
+        qemuMonitorCPUInfoClear(info, maxvcpus);
6ae9ed
+        qemuMonitorGetCPUInfoLegacy(cpuentries, ncpuentries, info, maxvcpus);
6ae9ed
+    }
6ae9ed
 
6ae9ed
     VIR_STEAL_PTR(*vcpus, info);
6ae9ed
     ret = 0;
6ae9ed
 
6ae9ed
  cleanup:
6ae9ed
-    qemuMonitorCPUInfoFree(info, maxvcpus);
6ae9ed
+    qemuMonitorQueryHotpluggableCpusFree(hotplugcpus, nhotplugcpus);
6ae9ed
     qemuMonitorQueryCpusFree(cpuentries, ncpuentries);
6ae9ed
+    qemuMonitorCPUInfoFree(info, maxvcpus);
6ae9ed
     return ret;
6ae9ed
 }
6ae9ed
 
6ae9ed
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
6ae9ed
index 027a7a9..d07e60c 100644
6ae9ed
--- a/src/qemu/qemu_monitor.h
6ae9ed
+++ b/src/qemu/qemu_monitor.h
6ae9ed
@@ -409,6 +409,9 @@ struct qemuMonitorQueryHotpluggableCpusEntry {
6ae9ed
     int socket_id;
6ae9ed
     int core_id;
6ae9ed
     int thread_id;
6ae9ed
+
6ae9ed
+    /* internal data */
6ae9ed
+    int enable_id;
6ae9ed
 };
6ae9ed
 void qemuMonitorQueryHotpluggableCpusFree(struct qemuMonitorQueryHotpluggableCpusEntry *entries,
6ae9ed
                                           size_t nentries);
6ae9ed
@@ -416,6 +419,23 @@ void qemuMonitorQueryHotpluggableCpusFree(struct qemuMonitorQueryHotpluggableCpu
6ae9ed
 
6ae9ed
 struct _qemuMonitorCPUInfo {
6ae9ed
     pid_t tid;
6ae9ed
+    int id; /* order of enabling of the given cpu */
6ae9ed
+
6ae9ed
+    /* topology info for hotplug purposes. Hotplug of given vcpu impossible if
6ae9ed
+     * all entries are -1 */
6ae9ed
+    int socket_id;
6ae9ed
+    int core_id;
6ae9ed
+    int thread_id;
6ae9ed
+    unsigned int vcpus; /* number of vcpus added if given entry is hotplugged */
6ae9ed
+
6ae9ed
+    /* name of the qemu type to add in case of hotplug */
6ae9ed
+    char *type;
6ae9ed
+
6ae9ed
+    /* alias of an hotpluggable entry. Entries with alias can be hot-unplugged */
6ae9ed
+    char *alias;
6ae9ed
+
6ae9ed
+    /* internal for use in the matching code */
6ae9ed
+    char *qom_path;
6ae9ed
 };
6ae9ed
 typedef struct _qemuMonitorCPUInfo qemuMonitorCPUInfo;
6ae9ed
 typedef qemuMonitorCPUInfo *qemuMonitorCPUInfoPtr;
6ae9ed
@@ -424,7 +444,8 @@ void qemuMonitorCPUInfoFree(qemuMonitorCPUInfoPtr list,
6ae9ed
                             size_t nitems);
6ae9ed
 int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
6ae9ed
                           qemuMonitorCPUInfoPtr *vcpus,
6ae9ed
-                          size_t maxvcpus);
6ae9ed
+                          size_t maxvcpus,
6ae9ed
+                          bool hotplug);
6ae9ed
 
6ae9ed
 int qemuMonitorGetVirtType(qemuMonitorPtr mon,
6ae9ed
                            virDomainVirtType *virtType);
6ae9ed
-- 
6ae9ed
2.10.0
6ae9ed