6ae9ed
From 4da15317e206ea8dede92b8b6e37805473df5a2f Mon Sep 17 00:00:00 2001
6ae9ed
Message-Id: <4da15317e206ea8dede92b8b6e37805473df5a2f@dist-git>
6ae9ed
From: Peter Krempa <pkrempa@redhat.com>
6ae9ed
Date: Wed, 24 Aug 2016 16:11:23 -0400
6ae9ed
Subject: [PATCH] qemu: monitor: Add support for calling
6ae9ed
 query-hotpluggable-cpus
6ae9ed
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1097930
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1224341
6ae9ed
6ae9ed
Add support for retrieving information regarding hotpluggable cpu units
6ae9ed
supported by qemu. Data returned by the command carries information
6ae9ed
needed to figure out the granularity of hotplug, the necessary cpu type
6ae9ed
name and the topology information.
6ae9ed
6ae9ed
Note that qemu doesn't specify any particular order of the entries thus
6ae9ed
it's necessary sort them by socket_id, core_id and thread_id to the
6ae9ed
order libvirt expects.
6ae9ed
6ae9ed
(cherry picked from commit 1213f0f8a50d9d3b782e3c336e3373c35c652a55)
6ae9ed
---
6ae9ed
 src/qemu/qemu_monitor.h      |  16 ++++
6ae9ed
 src/qemu/qemu_monitor_json.c | 170 +++++++++++++++++++++++++++++++++++++++++++
6ae9ed
 src/qemu/qemu_monitor_json.h |   5 ++
6ae9ed
 3 files changed, 191 insertions(+)
6ae9ed
6ae9ed
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
6ae9ed
index 83396a4..027a7a9 100644
6ae9ed
--- a/src/qemu/qemu_monitor.h
6ae9ed
+++ b/src/qemu/qemu_monitor.h
6ae9ed
@@ -398,6 +398,22 @@ void qemuMonitorQueryCpusFree(struct qemuMonitorQueryCpusEntry *entries,
6ae9ed
                               size_t nentries);
6ae9ed
 
6ae9ed
 
6ae9ed
+struct qemuMonitorQueryHotpluggableCpusEntry {
6ae9ed
+    char *type; /* name of the cpu to use with device_add */
6ae9ed
+    unsigned int vcpus; /* count of virtual cpus in the guest this entry adds */
6ae9ed
+    char *qom_path; /* full device qom path only present for online cpus */
6ae9ed
+    char *alias; /* device alias, may be NULL for non-hotpluggable entities */
6ae9ed
+
6ae9ed
+    /* topology information -1 if qemu didn't report given parameter */
6ae9ed
+    int node_id;
6ae9ed
+    int socket_id;
6ae9ed
+    int core_id;
6ae9ed
+    int thread_id;
6ae9ed
+};
6ae9ed
+void qemuMonitorQueryHotpluggableCpusFree(struct qemuMonitorQueryHotpluggableCpusEntry *entries,
6ae9ed
+                                          size_t nentries);
6ae9ed
+
6ae9ed
+
6ae9ed
 struct _qemuMonitorCPUInfo {
6ae9ed
     pid_t tid;
6ae9ed
 };
6ae9ed
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
6ae9ed
index a59b195..1871723 100644
6ae9ed
--- a/src/qemu/qemu_monitor_json.c
6ae9ed
+++ b/src/qemu/qemu_monitor_json.c
6ae9ed
@@ -7209,3 +7209,173 @@ qemuMonitorJSONGetRTCTime(qemuMonitorPtr mon,
6ae9ed
     virJSONValueFree(reply);
6ae9ed
     return ret;
6ae9ed
 }
6ae9ed
+
6ae9ed
+
6ae9ed
+void
6ae9ed
+qemuMonitorQueryHotpluggableCpusFree(struct qemuMonitorQueryHotpluggableCpusEntry *entries,
6ae9ed
+                                     size_t nentries)
6ae9ed
+{
6ae9ed
+    struct qemuMonitorQueryHotpluggableCpusEntry *entry;
6ae9ed
+    size_t i;
6ae9ed
+
6ae9ed
+    if (!entries)
6ae9ed
+        return;
6ae9ed
+
6ae9ed
+    for (i = 0; i < nentries; i++) {
6ae9ed
+        entry = entries + i;
6ae9ed
+
6ae9ed
+        VIR_FREE(entry->type);
6ae9ed
+        VIR_FREE(entry->qom_path);
6ae9ed
+        VIR_FREE(entry->alias);
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    VIR_FREE(entries);
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
+/**
6ae9ed
+ * [{
6ae9ed
+ *    "props": {
6ae9ed
+ *      "core-id": 0,
6ae9ed
+ *      "thread-id": 0,
6ae9ed
+ *      "socket-id": 0
6ae9ed
+ *    },
6ae9ed
+ *    "vcpus-count": 1,
6ae9ed
+ *    "qom-path": "/machine/unattached/device[0]",
6ae9ed
+ *    "type": "qemu64-x86_64-cpu"
6ae9ed
+ *  },
6ae9ed
+ *  {...}
6ae9ed
+ * ]
6ae9ed
+ */
6ae9ed
+static int
6ae9ed
+qemuMonitorJSONProcessHotpluggableCpusReply(virJSONValuePtr vcpu,
6ae9ed
+                                            struct qemuMonitorQueryHotpluggableCpusEntry *entry)
6ae9ed
+{
6ae9ed
+    virJSONValuePtr props;
6ae9ed
+    const char *tmp;
6ae9ed
+
6ae9ed
+    if (!(tmp = virJSONValueObjectGetString(vcpu, "type"))) {
6ae9ed
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
6ae9ed
+                       _("query-hotpluggable-cpus didn't return device type"));
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (VIR_STRDUP(entry->type, tmp) < 0)
6ae9ed
+        return -1;
6ae9ed
+
6ae9ed
+    if (virJSONValueObjectGetNumberUint(vcpu, "vcpus-count", &entry->vcpus) < 0) {
6ae9ed
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
6ae9ed
+                       _("query-hotpluggable-cpus didn't return vcpus-count"));
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (!(props = virJSONValueObjectGetObject(vcpu, "props"))) {
6ae9ed
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
6ae9ed
+                       _("query-hotpluggable-cpus didn't return device props"));
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    entry->node_id = -1;
6ae9ed
+    entry->socket_id = -1;
6ae9ed
+    entry->core_id = -1;
6ae9ed
+    entry->thread_id = -1;
6ae9ed
+
6ae9ed
+    ignore_value(virJSONValueObjectGetNumberInt(props, "node-id", &entry->node_id));
6ae9ed
+    ignore_value(virJSONValueObjectGetNumberInt(props, "socket-id", &entry->socket_id));
6ae9ed
+    ignore_value(virJSONValueObjectGetNumberInt(props, "core-id", &entry->core_id));
6ae9ed
+    ignore_value(virJSONValueObjectGetNumberInt(props, "thread-id", &entry->thread_id));
6ae9ed
+
6ae9ed
+    if (entry->node_id == -1 && entry->socket_id == -1 &&
6ae9ed
+        entry->core_id == -1 && entry->thread_id == -1) {
6ae9ed
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
6ae9ed
+                       _("query-hotpluggable-cpus entry doesn't report "
6ae9ed
+                         "topology information"));
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    /* qom path is not present unless the vCPU is online */
6ae9ed
+    if ((tmp = virJSONValueObjectGetString(vcpu, "qom-path"))) {
6ae9ed
+        if (VIR_STRDUP(entry->qom_path, tmp) < 0)
6ae9ed
+            return -1;
6ae9ed
+
6ae9ed
+        /* alias is the part after last slash having a "vcpu" prefix */
6ae9ed
+        if ((tmp = strrchr(tmp, '/')) && STRPREFIX(tmp + 1, "vcpu")) {
6ae9ed
+            if (VIR_STRDUP(entry->alias, tmp + 1) < 0)
6ae9ed
+                return -1;
6ae9ed
+        }
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    return 0;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
+static int
6ae9ed
+qemuMonitorQueryHotpluggableCpusEntrySort(const void *p1,
6ae9ed
+                                          const void *p2)
6ae9ed
+{
6ae9ed
+    const struct qemuMonitorQueryHotpluggableCpusEntry *a = p1;
6ae9ed
+    const struct qemuMonitorQueryHotpluggableCpusEntry *b = p2;
6ae9ed
+
6ae9ed
+    if (a->socket_id != b->socket_id)
6ae9ed
+        return a->socket_id - b->socket_id;
6ae9ed
+
6ae9ed
+    if (a->core_id != b->core_id)
6ae9ed
+        return a->core_id - b->core_id;
6ae9ed
+
6ae9ed
+    return a->thread_id - b->thread_id;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
+int
6ae9ed
+qemuMonitorJSONGetHotpluggableCPUs(qemuMonitorPtr mon,
6ae9ed
+                                   struct qemuMonitorQueryHotpluggableCpusEntry **entries,
6ae9ed
+                                   size_t *nentries)
6ae9ed
+{
6ae9ed
+    struct qemuMonitorQueryHotpluggableCpusEntry *info = NULL;
6ae9ed
+    ssize_t ninfo = 0;
6ae9ed
+    int ret = -1;
6ae9ed
+    size_t i;
6ae9ed
+    virJSONValuePtr data;
6ae9ed
+    virJSONValuePtr cmd;
6ae9ed
+    virJSONValuePtr reply = NULL;
6ae9ed
+    virJSONValuePtr vcpu;
6ae9ed
+
6ae9ed
+    if (!(cmd = qemuMonitorJSONMakeCommand("query-hotpluggable-cpus", NULL)))
6ae9ed
+        return -1;
6ae9ed
+
6ae9ed
+    if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
6ae9ed
+        goto cleanup;
6ae9ed
+
6ae9ed
+    if (qemuMonitorJSONCheckError(cmd, reply) < 0)
6ae9ed
+        goto cleanup;
6ae9ed
+
6ae9ed
+    data = virJSONValueObjectGet(reply, "return");
6ae9ed
+
6ae9ed
+    if ((ninfo = virJSONValueArraySize(data)) < 0) {
6ae9ed
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
6ae9ed
+                       _("query-hotpluggable-cpus reply is not an array"));
6ae9ed
+        goto cleanup;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (VIR_ALLOC_N(info, ninfo) < 0)
6ae9ed
+        goto cleanup;
6ae9ed
+
6ae9ed
+    for (i = 0; i < ninfo; i++) {
6ae9ed
+        vcpu = virJSONValueArrayGet(data, i);
6ae9ed
+
6ae9ed
+        if (qemuMonitorJSONProcessHotpluggableCpusReply(vcpu, info + i) < 0)
6ae9ed
+            goto cleanup;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    qsort(info, ninfo, sizeof(*info), qemuMonitorQueryHotpluggableCpusEntrySort);
6ae9ed
+
6ae9ed
+    VIR_STEAL_PTR(*entries, info);
6ae9ed
+    *nentries = ninfo;
6ae9ed
+    ret = 0;
6ae9ed
+
6ae9ed
+ cleanup:
6ae9ed
+    qemuMonitorQueryHotpluggableCpusFree(info, ninfo);
6ae9ed
+    virJSONValueFree(cmd);
6ae9ed
+    virJSONValueFree(reply);
6ae9ed
+    return ret;
6ae9ed
+}
6ae9ed
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
6ae9ed
index 2a439da..4db6067 100644
6ae9ed
--- a/src/qemu/qemu_monitor_json.h
6ae9ed
+++ b/src/qemu/qemu_monitor_json.h
6ae9ed
@@ -504,4 +504,9 @@ int qemuMonitorJSONMigrateStartPostCopy(qemuMonitorPtr mon)
6ae9ed
 int qemuMonitorJSONGetRTCTime(qemuMonitorPtr mon,
6ae9ed
                               struct tm *tm)
6ae9ed
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
6ae9ed
+
6ae9ed
+int qemuMonitorJSONGetHotpluggableCPUs(qemuMonitorPtr mon,
6ae9ed
+                                       struct qemuMonitorQueryHotpluggableCpusEntry **entries,
6ae9ed
+                                       size_t *nentries)
6ae9ed
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
6ae9ed
 #endif /* QEMU_MONITOR_JSON_H */
6ae9ed
-- 
6ae9ed
2.10.0
6ae9ed