[libvirt] [PATCHv2 03/14] qemu: Extract more information about vCPUs and threads

Peter Krempa pkrempa at redhat.com
Wed Jun 5 13:43:54 UTC 2013


The qemu monitor provides more information about vCPUs of a guest than
we needed currently. This patch upgrades the extraction function to
easily extract new data about the vCPUs and fixes code to cope with the
new structure. The information extracted here will be later used for
mapping of vCPUs of a guest.

This patch also refactors the function used to parse data from the text
monitor.
---
 src/qemu/qemu_driver.c       | 31 ++++++++-------
 src/qemu/qemu_monitor.c      |  9 +++--
 src/qemu/qemu_monitor.h      | 11 +++++-
 src/qemu/qemu_monitor_json.c | 47 +++++++++++++---------
 src/qemu/qemu_monitor_json.h |  2 +-
 src/qemu/qemu_monitor_text.c | 92 +++++++++++++++++++++++++-------------------
 src/qemu/qemu_monitor_text.h |  2 +-
 src/qemu/qemu_process.c      | 63 ++++++++++++++++++++----------
 8 files changed, 159 insertions(+), 98 deletions(-)

diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index e13b914..3db21d4 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3526,8 +3526,8 @@ static int qemuDomainHotplugVcpus(virQEMUDriverPtr driver,
     int ret = -1;
     int oldvcpus = vm->def->vcpus;
     int vcpus = oldvcpus;
-    pid_t *cpupids = NULL;
-    int ncpupids;
+    qemuMonitorCPUInfoPtr cpuinfo = NULL;
+    int ncpuinfo;
     virCgroupPtr cgroup_vcpu = NULL;

     qemuDomainObjEnterMonitor(driver, vm);
@@ -3568,13 +3568,13 @@ static int qemuDomainHotplugVcpus(virQEMUDriverPtr driver,
      * or don't have the "info cpus" command (and they don't support multiple
      * CPUs anyways), so errors in the re-detection will not be treated
      * fatal */
-    if ((ncpupids = qemuMonitorGetCPUInfo(priv->mon, &cpupids)) <= 0) {
+    if ((ncpuinfo = qemuMonitorGetCPUInfo(priv->mon, &cpuinfo)) <= 0) {
         virResetLastError();
         goto cleanup;
     }

     /* check if hotplug has failed */
-    if (vcpus < oldvcpus && ncpupids == oldvcpus) {
+    if (vcpus < oldvcpus && ncpuinfo == oldvcpus) {
         virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
                        _("qemu didn't unplug the vCPUs properly"));
         vcpus = oldvcpus;
@@ -3582,11 +3582,11 @@ static int qemuDomainHotplugVcpus(virQEMUDriverPtr driver,
         goto cleanup;
     }

-    if (ncpupids != vcpus) {
+    if (ncpuinfo != vcpus) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("got wrong number of vCPU pids from QEMU monitor. "
                          "got %d, wanted %d"),
-                       ncpupids, vcpus);
+                       ncpuinfo, vcpus);
         ret = -1;
         goto cleanup;
     }
@@ -3606,11 +3606,11 @@ static int qemuDomainHotplugVcpus(virQEMUDriverPtr driver,
                 }

                 /* Add vcpu thread to the cgroup */
-                rv = virCgroupAddTask(cgroup_vcpu, cpupids[i]);
+                rv = virCgroupAddTask(cgroup_vcpu, cpuinfo[i].thread_id);
                 if (rv < 0) {
                     virReportSystemError(-rv,
                                          _("unable to add vcpu %d task %d to cgroup"),
-                                         i, cpupids[i]);
+                                         i, cpuinfo[i].thread_id);
                     virCgroupRemove(cgroup_vcpu);
                     goto cleanup;
                 }
@@ -3650,7 +3650,7 @@ static int qemuDomainHotplugVcpus(virQEMUDriverPtr driver,
                         goto cleanup;
                     }
                 } else {
-                    if (virProcessSetAffinity(cpupids[i],
+                    if (virProcessSetAffinity(cpuinfo[i].thread_id,
                                               vcpupin->cpumask) < 0) {
                         virReportError(VIR_ERR_SYSTEM_ERROR,
                                        _("failed to set cpu affinity for vcpu %d"),
@@ -3691,15 +3691,18 @@ static int qemuDomainHotplugVcpus(virQEMUDriverPtr driver,
         }
     }

-    priv->nvcpupids = ncpupids;
-    VIR_FREE(priv->vcpupids);
-    priv->vcpupids = cpupids;
-    cpupids = NULL;
+    if (VIR_REALLOC_N(priv->vcpupids, ncpuinfo) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    /* copy the thread id's */
+    for (i = 0; i < ncpuinfo; i++)
+        priv->vcpupids[i] = cpuinfo[i].thread_id;

 cleanup:
     qemuDomainObjExitMonitor(driver, vm);
     vm->def->vcpus = vcpus;
-    VIR_FREE(cpupids);
     virDomainAuditVcpu(vm, oldvcpus, nvcpus, "update", rc == 1);
     if (cgroup_vcpu)
         virCgroupFree(&cgroup_vcpu);
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 9733aeb..07f89a2 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1282,8 +1282,9 @@ int qemuMonitorSystemReset(qemuMonitorPtr mon)
 }


-int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
-                          int **pids)
+int
+qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
+                      qemuMonitorCPUInfoPtr *info)
 {
     int ret;
     VIR_DEBUG("mon=%p", mon);
@@ -1295,9 +1296,9 @@ int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
     }

     if (mon->json)
-        ret = qemuMonitorJSONGetCPUInfo(mon, pids);
+        ret = qemuMonitorJSONGetCPUInfo(mon, info);
     else
-        ret = qemuMonitorTextGetCPUInfo(mon, pids);
+        ret = qemuMonitorTextGetCPUInfo(mon, info);
     return ret;
 }

diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 3d9afa3..c51fee3 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -252,8 +252,17 @@ int qemuMonitorGetStatus(qemuMonitorPtr mon,
 int qemuMonitorSystemReset(qemuMonitorPtr mon);
 int qemuMonitorSystemPowerdown(qemuMonitorPtr mon);

+typedef struct _qemuMonitorCPUInfo qemuMonitorCPUInfo;
+typedef qemuMonitorCPUInfo *qemuMonitorCPUInfoPtr;
+struct _qemuMonitorCPUInfo {
+    unsigned int id;
+    bool active;
+    pid_t thread_id;
+};
+
 int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
-                          int **pids);
+                          qemuMonitorCPUInfoPtr *info);
+
 int qemuMonitorGetVirtType(qemuMonitorPtr mon,
                            int *virtType);
 int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 26bf09b..4a69fec 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1164,17 +1164,17 @@ int qemuMonitorJSONSystemReset(qemuMonitorPtr mon)


 /*
- * [ { "CPU": 0, "current": true, "halted": false, "pc": 3227107138 },
- *   { "CPU": 1, "current": false, "halted": true, "pc": 7108165 } ]
+ * [ { "CPU": 0, "current": true, "halted": false, "pc": 3227107138, "thread_id": 1234},
+ *   { "CPU": 1, "current": false, "halted": true, "pc": 7108165, "thread_id": 5678 } ]
  */
 static int
 qemuMonitorJSONExtractCPUInfo(virJSONValuePtr reply,
-                              int **pids)
+                              qemuMonitorCPUInfoPtr *info)
 {
     virJSONValuePtr data;
     int ret = -1;
     int i;
-    int *threads = NULL;
+    qemuMonitorCPUInfoPtr cpus = NULL;
     int ncpus;

     if (!(data = virJSONValueObjectGet(reply, "return"))) {
@@ -1195,49 +1195,60 @@ qemuMonitorJSONExtractCPUInfo(virJSONValuePtr reply,
         goto cleanup;
     }

-    if (VIR_REALLOC_N(threads, ncpus) < 0) {
+    if (VIR_ALLOC_N(cpus, ncpus) < 0) {
         virReportOOMError();
         goto cleanup;
     }

     for (i = 0; i < ncpus; i++) {
         virJSONValuePtr entry = virJSONValueArrayGet(data, i);
-        int thread;
+        bool halted;
+
         if (!entry) {
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("character device information was missing array element"));
+                           _("cpu information was missing array element"));
             goto cleanup;
         }

-        if (virJSONValueObjectGetNumberInt(entry, "thread_id", &thread) < 0) {
-            /* Only qemu-kvm tree includs thread_id, so treat this as
-               non-fatal, simply returning no data */
-            ret = 0;
+        if (virJSONValueObjectGetNumberUint(entry, "CPU", &cpus[i].id) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("CPU id missing in cpu information"));
             goto cleanup;
         }

-        threads[i] = thread;
+        if (virJSONValueObjectGetNumberInt(entry, "thread_id",
+                                           &cpus[i].thread_id) < 0) {
+            /* Some versions of qemu don't provide this information */
+            cpus[i].thread_id = 0;
+        }
+
+        if (virJSONValueObjectGetBoolean(entry, "halted", &halted) < 0) {
+            /* Not that important that we should fail */
+            halted = false;
+        }
+
+        cpus[i].active = !halted;
     }

-    *pids = threads;
-    threads = NULL;
+    *info = cpus;
+    cpus = NULL;
     ret = ncpus;

 cleanup:
-    VIR_FREE(threads);
+    VIR_FREE(cpus);
     return ret;
 }


 int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
-                              int **pids)
+                              qemuMonitorCPUInfoPtr *info)
 {
     int ret;
     virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-cpus",
                                                      NULL);
     virJSONValuePtr reply = NULL;

-    *pids = NULL;
+    *info = NULL;

     if (!cmd)
         return -1;
@@ -1248,7 +1259,7 @@ int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
         ret = qemuMonitorJSONCheckError(cmd, reply);

     if (ret == 0)
-        ret = qemuMonitorJSONExtractCPUInfo(reply, pids);
+        ret = qemuMonitorJSONExtractCPUInfo(reply, info);

     virJSONValueFree(cmd);
     virJSONValueFree(reply);
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index d79b86b..ac077b1 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -53,7 +53,7 @@ int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon);
 int qemuMonitorJSONSystemReset(qemuMonitorPtr mon);

 int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
-                              int **pids);
+                              qemuMonitorCPUInfoPtr *info);
 int qemuMonitorJSONGetVirtType(qemuMonitorPtr mon,
                                int *virtType);
 int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index aa4145e..a967ae6 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -501,13 +501,16 @@ int qemuMonitorTextSystemReset(qemuMonitorPtr mon) {
 }


-int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
-                              int **pids)
+int
+qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
+                          qemuMonitorCPUInfoPtr *info)
 {
     char *qemucpus = NULL;
-    char *line;
-    pid_t *cpupids = NULL;
-    size_t ncpupids = 0;
+    char **cpulines = NULL;
+    char **cpuline;
+    qemuMonitorCPUInfoPtr cpus = NULL;
+    size_t ncpus = 0;
+    int ret = -1;

     if (qemuMonitorHMPCommand(mon, "info cpus", &qemucpus) < 0)
         return -1;
@@ -516,52 +519,63 @@ int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
      * This is the gross format we're about to parse :-{
      *
      * (qemu) info cpus
-     * * CPU #0: pc=0x00000000000f0c4a thread_id=30019
-     *   CPU #1: pc=0x00000000fffffff0 thread_id=30020
-     *   CPU #2: pc=0x00000000fffffff0 thread_id=30021
+     * * CPU #0: pc=0xffffffff8103b90b (halted) thread_id=874519
+     *   CPU #1: pc=0xffffffff8102e191 (halted) thread_id=874520
+     *   CPU #2: pc=0xffffffff812c12ae thread_id=874521
+     *   CPU #3: pc=0xffffffff810521cf thread_id=874522
      *
      */
-    line = qemucpus;
-    do {
-        char *offset = NULL;
-        char *end = NULL;
+    if (!(cpulines = virStringSplit(qemucpus, "\n", 0)))
+        goto cleanup;
+
+    cpuline = cpulines;
+
+    while (*cpuline++) {
+        char *offset;
+        char *end;
+        int id;
         int tid = 0;
+        bool halted = false;

-        /* Extract host Thread ID */
-        if ((offset = strstr(line, "thread_id=")) == NULL)
-            goto error;
+        /* Extract vCPU id */
+        if (!(offset = strchr(*cpuline, '#')) ||
+            virStrToLong_i(offset + 1, &end, 10, &id) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("failed to extract vCPU id"));
+            goto cleanup;
+        }

-        if (virStrToLong_i(offset + strlen("thread_id="), &end, 10, &tid) < 0)
-            goto error;
-        if (end == NULL || !c_isspace(*end))
-            goto error;
+        /* extract cpu state */
+        if (strstr(*cpuline, "(halted)"))
+            halted = true;

-        if (VIR_REALLOC_N(cpupids, ncpupids+1) < 0)
-            goto error;
+        /* Extract host Thread ID, some versions of qemu may lack this info */
+        if ((offset = strstr(*cpuline, "thread_id="))) {
+            if (virStrToLong_i(offset + strlen("thread_id="), &end, 10, &tid) < 0)
+                tid = 0;
+        }

-        VIR_DEBUG("tid=%d", tid);
-        cpupids[ncpupids++] = tid;
+        ncpus++;

-        /* Skip to next data line */
-        line = strchr(offset, '\r');
-        if (line == NULL)
-            line = strchr(offset, '\n');
-    } while (line != NULL);
+        if (VIR_REALLOC_N(cpus, ncpus) < 0) {
+            virReportOOMError();
+            goto cleanup;
+        }

-    /* Validate we got data for all VCPUs we expected */
-    VIR_FREE(qemucpus);
-    *pids = cpupids;
-    return ncpupids;
+        cpus[ncpus].id = id;
+        cpus[ncpus].thread_id = tid;
+        cpus[ncpus].active = !halted;
+    }

-error:
+    *info = cpus;
+    ret = ncpus;
+
+cleanup:
     VIR_FREE(qemucpus);
-    VIR_FREE(cpupids);
+    VIR_FREE(cpus);
+    virStringFreeList(cpulines);

-    /* Returning 0 to indicate non-fatal failure, since
-     * older QEMU does not have VCPU<->PID mapping and
-     * we don't want to fail on that
-     */
-    return 0;
+    return ret;
 }


diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 5218a8b..08f4280 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -50,7 +50,7 @@ int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon);
 int qemuMonitorTextSystemReset(qemuMonitorPtr mon);

 int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
-                              int **pids);
+                              qemuMonitorCPUInfoPtr *info);
 int qemuMonitorTextGetVirtType(qemuMonitorPtr mon,
                                int *virtType);
 int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index d4fd4fb..8de14c3 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1755,40 +1755,63 @@ static int
 qemuProcessDetectVcpuPIDs(virQEMUDriverPtr driver,
                           virDomainObjPtr vm)
 {
-    pid_t *cpupids = NULL;
-    int ncpupids;
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuMonitorCPUInfoPtr info = NULL;
+    int ninfo;
+    int i;
+    int ret = -1;

-    qemuDomainObjEnterMonitor(driver, vm);
     /* failure to get the VCPU<-> PID mapping or to execute the query
      * command will not be treated fatal as some versions of qemu don't
      * support this command */
-    if ((ncpupids = qemuMonitorGetCPUInfo(priv->mon, &cpupids)) <= 0) {
-        qemuDomainObjExitMonitor(driver, vm);
-        virResetLastError();
+    qemuDomainObjEnterMonitor(driver, vm);
+    ninfo = qemuMonitorGetCPUInfo(priv->mon, &info);
+    qemuDomainObjExitMonitor(driver, vm);

-        priv->nvcpupids = 1;
-        if (VIR_ALLOC_N(priv->vcpupids, priv->nvcpupids) < 0) {
-            virReportOOMError();
-            return -1;
-        }
-        priv->vcpupids[0] = vm->pid;
-        return 0;
+    if (ninfo < 0)
+        goto fallback;
+
+    if (VIR_ALLOC_N(priv->vcpupids, ninfo) < 0) {
+        virReportOOMError();
+        goto cleanup;
     }
-    qemuDomainObjExitMonitor(driver, vm);

-    if (ncpupids != vm->def->vcpus) {
+    for (i = 0; i < ninfo; i++) {
+        /* some versions of qemu don't expose thread IDs. Fall back gracefully */
+        if (info[i].thread_id == 0)
+            goto fallback;
+
+        priv->vcpupids[i] = info[i].thread_id;
+    }
+
+    if (ninfo != vm->def->vcpus) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("got wrong number of vCPU pids from QEMU monitor. "
                          "got %d, wanted %d"),
-                       ncpupids, vm->def->vcpus);
-        VIR_FREE(cpupids);
-        return -1;
+                       ninfo, vm->def->vcpus);
+        VIR_FREE(priv->vcpupids);
+        goto cleanup;
     }

-    priv->nvcpupids = ncpupids;
-    priv->vcpupids = cpupids;
+    priv->nvcpupids = ninfo;
+    ret = 0;
+
+cleanup:
+    VIR_FREE(info);
+    return ret;
+
+fallback:
+    VIR_FREE(info);
+    VIR_FREE(priv->vcpupids);
+    virResetLastError();
+    priv->nvcpupids = 1;
+    if (VIR_ALLOC_N(priv->vcpupids, priv->nvcpupids) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+    priv->vcpupids[0] = vm->pid;
     return 0;
+
 }


-- 
1.8.2.1




More information about the libvir-list mailing list