[libvirt] [PATCH] qemu: report vcpu state in virDomainGetVcpus

Boris Fiuczynski fiuczy at linux.vnet.ibm.com
Fri Jul 8 13:39:00 UTC 2016


From: Viktor Mihajlovski <mihajlov at linux.vnet.ibm.com>

Currently, the virVcpuInfo returned by virDomainGetVcpus() will always
report a state of VIR_VCPU_RUNNING for each defined domain vcpu even if
the vcpu is currently in the halted state.

As the monitor interface is in fact reporting the accurate state, it is
rather easy to transport this information with the existing API.

This is done by
- adding a new state of VIR_VCPU_HALTED
- extending the monitor to pass back the halted state for the vcpus
- adding a new array to the private domain object reflecting the halted
  state for the vcpus and update it along with the vcpu pids array
- modifying the driver code to report the vcpu state based on the halted
  indicator
- extending virsh vcpuinfo to also display the halted state
- modifying the monitor_json testcase

The vcpu state is however not recorded in the internal XML format, since
the state can change asynchronously (without notification).

Signed-off-by: Viktor Mihajlovski <mihajlov at linux.vnet.ibm.com>
Signed-off-by: Boris Fiuczynski <fiuczy at linux.vnet.ibm.com>
Reviewed-by: Bjoern Walk <bwalk at linux.vnet.ibm.com>
---
 include/libvirt/libvirt-domain.h |  1 +
 src/qemu/qemu_domain.c           | 26 +++++++++++++++++++++++++-
 src/qemu/qemu_domain.h           |  2 ++
 src/qemu/qemu_driver.c           | 14 +++++++++++++-
 src/qemu/qemu_monitor.c          |  7 ++++---
 src/qemu/qemu_monitor.h          |  3 ++-
 src/qemu/qemu_monitor_json.c     | 22 +++++++++++++++++++---
 src/qemu/qemu_monitor_json.h     |  3 ++-
 src/qemu/qemu_monitor_text.c     | 17 +++++++++++++++--
 src/qemu/qemu_monitor_text.h     |  3 ++-
 src/qemu/qemu_process.c          |  1 +
 tests/qemumonitorjsontest.c      | 18 +++++++++++++++---
 tools/virsh-domain.c             |  3 ++-
 13 files changed, 103 insertions(+), 17 deletions(-)

diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
index 7ea93aa..98b9420 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -1656,6 +1656,7 @@ typedef enum {
     VIR_VCPU_OFFLINE    = 0,    /* the virtual CPU is offline */
     VIR_VCPU_RUNNING    = 1,    /* the virtual CPU is running */
     VIR_VCPU_BLOCKED    = 2,    /* the virtual CPU is blocked on resource */
+    VIR_VCPU_HALTED     = 3,    /* the virtual CPU is halted */
 
 # ifdef VIR_ENUM_SENTINELS
     VIR_VCPU_LAST
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 42b5511..3428605 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -1256,6 +1256,7 @@ qemuDomainObjPrivateFree(void *data)
     virDomainChrSourceDefFree(priv->monConfig);
     qemuDomainObjFreeJob(priv);
     VIR_FREE(priv->vcpupids);
+    VIR_FREE(priv->vcpuhalted);
     VIR_FREE(priv->lockState);
     VIR_FREE(priv->origname);
 
@@ -5470,6 +5471,7 @@ qemuDomainDetectVcpuPids(virQEMUDriverPtr driver,
                          int asyncJob)
 {
     pid_t *cpupids = NULL;
+    bool *cpuhalted = NULL;
     int ncpupids = 0;
     qemuDomainObjPrivatePtr priv = vm->privateData;
 
@@ -5506,7 +5508,7 @@ qemuDomainDetectVcpuPids(virQEMUDriverPtr driver,
 
     if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
         return -1;
-    ncpupids = qemuMonitorGetCPUInfo(priv->mon, &cpupids);
+    ncpupids = qemuMonitorGetCPUInfo(priv->mon, &cpupids, &cpuhalted);
     if (qemuDomainObjExitMonitor(driver, vm) < 0) {
         VIR_FREE(cpupids);
         return -2;
@@ -5526,16 +5528,38 @@ qemuDomainDetectVcpuPids(virQEMUDriverPtr driver,
                          "got %d, wanted %d"),
                        ncpupids, virDomainDefGetVcpus(vm->def));
         VIR_FREE(cpupids);
+        VIR_FREE(cpuhalted);
         return -1;
     }
 
  done:
     VIR_FREE(priv->vcpupids);
+    VIR_FREE(priv->vcpuhalted);
     priv->nvcpupids = ncpupids;
     priv->vcpupids = cpupids;
+    priv->vcpuhalted = cpuhalted;
     return ncpupids;
 }
 
+/**
+ * qemuDomainGetVcpuHalted:
+ * @vm: domain object
+ * @vcpu: cpu id
+ *
+ * Returns the vCPU halted state.
+  */
+bool
+qemuDomainGetVcpuHalted(virDomainObjPtr vm,
+                        unsigned int vcpu)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    if (vcpu >= priv->nvcpupids)
+        return 0;
+
+    return priv->vcpuhalted[vcpu];
+}
+
 
 bool
 qemuDomainSupportsNicdev(virDomainDefPtr def,
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index c49f31c..8f7d421 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -186,6 +186,7 @@ struct _qemuDomainObjPrivate {
 
     int nvcpupids;
     int *vcpupids;
+    bool *vcpuhalted;
 
     virDomainPCIAddressSetPtr pciaddrs;
     virDomainCCWAddressSetPtr ccwaddrs;
@@ -639,6 +640,7 @@ bool qemuDomainHasVcpuPids(virDomainObjPtr vm);
 pid_t qemuDomainGetVcpuPid(virDomainObjPtr vm, unsigned int vcpu);
 int qemuDomainDetectVcpuPids(virQEMUDriverPtr driver, virDomainObjPtr vm,
                              int asyncJob);
+bool qemuDomainGetVcpuHalted(virDomainObjPtr vm, unsigned int vcpu);
 
 bool qemuDomainSupportsNicdev(virDomainDefPtr def,
                               virDomainNetDefPtr net);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index f9a3b15..f7ea745 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -1477,13 +1477,17 @@ qemuDomainHelperGetVcpus(virDomainObjPtr vm,
     for (i = 0; i < virDomainDefGetVcpusMax(vm->def) && ncpuinfo < maxinfo; i++) {
         virDomainVcpuInfoPtr vcpu = virDomainDefGetVcpu(vm->def, i);
         pid_t vcpupid = qemuDomainGetVcpuPid(vm, i);
+        bool vcpuhalted = qemuDomainGetVcpuHalted(vm, i);
 
         if (!vcpu->online)
             continue;
 
         if (info) {
             info[i].number = i;
-            info[i].state = VIR_VCPU_RUNNING;
+            if (vcpuhalted)
+                info[i].state = VIR_VCPU_HALTED;
+            else
+                info[i].state = VIR_VCPU_RUNNING;
 
             if (qemuGetProcessInfo(&(info[i].cpuTime), &(info[i].cpu), NULL,
                                    vm->pid, vcpupid) < 0) {
@@ -5272,6 +5276,7 @@ qemuDomainGetVcpus(virDomainPtr dom,
                    unsigned char *cpumaps,
                    int maplen)
 {
+    virQEMUDriverPtr driver = dom->conn->privateData;
     virDomainObjPtr vm;
     int ret = -1;
 
@@ -5288,6 +5293,13 @@ qemuDomainGetVcpus(virDomainPtr dom,
         goto cleanup;
     }
 
+    if (qemuDomainDetectVcpuPids(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s",
+                       _("could not detect vcpu pids"));
+        return -1;
+    }
+
     ret = qemuDomainHelperGetVcpus(vm, info, NULL, maxinfo, cpumaps, maplen);
 
  cleanup:
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 098e654..0272fb4 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1656,14 +1656,15 @@ qemuMonitorSystemReset(qemuMonitorPtr mon)
  */
 int
 qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
-                      int **pids)
+                      int **pids,
+                      bool **haltpids)
 {
     QEMU_CHECK_MONITOR(mon);
 
     if (mon->json)
-        return qemuMonitorJSONGetCPUInfo(mon, pids);
+        return qemuMonitorJSONGetCPUInfo(mon, pids, haltpids);
     else
-        return qemuMonitorTextGetCPUInfo(mon, pids);
+        return qemuMonitorTextGetCPUInfo(mon, pids, haltpids);
 }
 
 
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index cb4cca8..9f6ef72 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -391,7 +391,8 @@ int qemuMonitorSystemReset(qemuMonitorPtr mon);
 int qemuMonitorSystemPowerdown(qemuMonitorPtr mon);
 
 int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
-                          int **pids);
+                          int **pids,
+                          bool **haltpids);
 int qemuMonitorGetVirtType(qemuMonitorPtr mon,
                            virDomainVirtType *virtType);
 int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index bb426dc..87343c0 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1324,13 +1324,15 @@ int qemuMonitorJSONSystemReset(qemuMonitorPtr mon)
  */
 static int
 qemuMonitorJSONExtractCPUInfo(virJSONValuePtr reply,
-                              int **pids)
+                              int **pids,
+                              bool **haltpids)
 {
     virJSONValuePtr data;
     int ret = -1;
     size_t i;
     int *threads = NULL;
     ssize_t ncpus;
+    bool *haltedpids = NULL;
 
     if (!(data = virJSONValueObjectGetArray(reply, "return"))) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -1347,9 +1349,13 @@ qemuMonitorJSONExtractCPUInfo(virJSONValuePtr reply,
     if (VIR_ALLOC_N(threads, ncpus) < 0)
         goto cleanup;
 
+    if (VIR_ALLOC_N(haltedpids, ncpus) < 0)
+        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",
                            _("cpu information was missing an array element"));
@@ -1363,21 +1369,31 @@ qemuMonitorJSONExtractCPUInfo(virJSONValuePtr reply,
             goto cleanup;
         }
 
+        if (virJSONValueObjectGetBoolean(entry, "halted", &halted) < 0) {
+            /* Some older qemu versions may not report the halted state,
+             * for backword compatibility we assume it's not halted */
+            halted = false;
+        }
+
         threads[i] = thread;
+        haltedpids[i] = halted;
     }
 
     *pids = threads;
+    *haltpids = haltedpids;
     threads = NULL;
+    haltedpids = NULL;
     ret = ncpus;
 
  cleanup:
     VIR_FREE(threads);
+    VIR_FREE(haltedpids);
     return ret;
 }
 
 
 int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
-                              int **pids)
+                              int **pids, bool **haltpids)
 {
     int ret = -1;
     virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-cpus",
@@ -1395,7 +1411,7 @@ int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
     if (qemuMonitorJSONCheckError(cmd, reply) < 0)
         goto cleanup;
 
-    ret = qemuMonitorJSONExtractCPUInfo(reply, pids);
+    ret = qemuMonitorJSONExtractCPUInfo(reply, pids, haltpids);
  cleanup:
     virJSONValueFree(cmd);
     virJSONValueFree(reply);
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 37a739e..c7808aa 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -59,7 +59,8 @@ int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon);
 int qemuMonitorJSONSystemReset(qemuMonitorPtr mon);
 
 int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
-                              int **pids);
+                              int **pids,
+                              bool **haltpids);
 int qemuMonitorJSONGetVirtType(qemuMonitorPtr mon,
                                virDomainVirtType *virtType);
 int qemuMonitorJSONUpdateVideoMemorySize(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 9295219..fbacdbd 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -501,12 +501,15 @@ int qemuMonitorTextSystemReset(qemuMonitorPtr mon)
 
 
 int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
-                              int **pids)
+                              int **pids,
+                              bool **haltpids)
 {
     char *qemucpus = NULL;
     char *line;
     pid_t *cpupids = NULL;
     size_t ncpupids = 0;
+    bool *cpuhalted = NULL;
+    size_t ncpuhalted = 0;
 
     if (qemuMonitorHMPCommand(mon, "info cpus", &qemucpus) < 0)
         return -1;
@@ -517,7 +520,7 @@ int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
      * (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 #2: pc=0x00000000fffffff0 (halted) thread_id=30021
      *
      */
     line = qemucpus;
@@ -525,6 +528,7 @@ int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
         char *offset = NULL;
         char *end = NULL;
         int tid = 0;
+        bool halted = false;
 
         /* Extract host Thread ID */
         if ((offset = strstr(line, "thread_id=")) == NULL)
@@ -538,6 +542,13 @@ int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
         if (VIR_APPEND_ELEMENT_COPY(cpupids, ncpupids, tid) < 0)
             goto error;
 
+        /* Extract halted indicator */
+        if ((offset = strstr(line, "(halted)")) != NULL)
+            halted = true;
+
+        if (VIR_APPEND_ELEMENT_COPY(cpuhalted, ncpuhalted, halted) < 0)
+            goto error;
+
         VIR_DEBUG("tid=%d", tid);
 
         /* Skip to next data line */
@@ -549,11 +560,13 @@ int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
     /* Validate we got data for all VCPUs we expected */
     VIR_FREE(qemucpus);
     *pids = cpupids;
+    *haltpids = cpuhalted;
     return ncpupids;
 
  error:
     VIR_FREE(qemucpus);
     VIR_FREE(cpupids);
+    VIR_FREE(cpuhalted);
 
     /* Returning 0 to indicate non-fatal failure, since
      * older QEMU does not have VCPU<->PID mapping and
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index eeaca52..e7009b3 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -50,7 +50,8 @@ int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon);
 int qemuMonitorTextSystemReset(qemuMonitorPtr mon);
 
 int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
-                              int **pids);
+                              int **pids,
+                              bool **haltpids);
 int qemuMonitorTextGetVirtType(qemuMonitorPtr mon,
                                virDomainVirtType *virtType);
 int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 129c070..403b332 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -5900,6 +5900,7 @@ void qemuProcessStop(virQEMUDriverPtr driver,
     vm->pid = -1;
     virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
     VIR_FREE(priv->vcpupids);
+    VIR_FREE(priv->vcpuhalted);
     priv->nvcpupids = 0;
     for (i = 0; i < vm->def->niothreadids; i++)
         vm->def->iothreadids[i]->thread_id = 0;
diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c
index f698c14..441720e 100644
--- a/tests/qemumonitorjsontest.c
+++ b/tests/qemumonitorjsontest.c
@@ -1210,6 +1210,8 @@ testQemuMonitorJSONqemuMonitorJSONGetCPUInfo(const void *data)
     int ret = -1;
     pid_t *cpupids = NULL;
     pid_t expected_cpupids[] = {17622, 17624, 17626, 17628};
+    bool *cpuhalted = NULL;
+    bool expected_cpuhalted[] = {true, true, false, false};
     int ncpupids;
     size_t i;
 
@@ -1237,14 +1239,14 @@ testQemuMonitorJSONqemuMonitorJSONGetCPUInfo(const void *data)
                                "            \"current\": false,"
                                "            \"CPU\": 2,"
                                "            \"pc\": -2130530478,"
-                               "            \"halted\": true,"
+                               "            \"halted\": false,"
                                "            \"thread_id\": 17626"
                                "        },"
                                "        {"
                                "            \"current\": false,"
                                "            \"CPU\": 3,"
                                "            \"pc\": -2130530478,"
-                               "            \"halted\": true,"
+                               "            \"halted\": false,"
                                "            \"thread_id\": 17628"
                                "        }"
                                "    ],"
@@ -1252,7 +1254,7 @@ testQemuMonitorJSONqemuMonitorJSONGetCPUInfo(const void *data)
                                "}") < 0)
         goto cleanup;
 
-    ncpupids = qemuMonitorJSONGetCPUInfo(qemuMonitorTestGetMonitor(test), &cpupids);
+    ncpupids = qemuMonitorJSONGetCPUInfo(qemuMonitorTestGetMonitor(test), &cpupids, &cpuhalted);
 
     if (ncpupids != 4) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -1269,10 +1271,20 @@ testQemuMonitorJSONqemuMonitorJSONGetCPUInfo(const void *data)
         }
     }
 
+    for (i = 0; i < ncpupids; i++) {
+        if (cpuhalted[i] != expected_cpuhalted[i]) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "Expecting cpuhalted[%zu] = %d but got %d",
+                           i, expected_cpuhalted[i], cpuhalted[i]);
+            goto cleanup;
+        }
+    }
+
     ret = 0;
 
  cleanup:
     VIR_FREE(cpupids);
+    VIR_FREE(cpuhalted);
     qemuMonitorTestFree(test);
     return ret;
 }
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index dbdee5b..4650eb3 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -181,7 +181,8 @@ VIR_ENUM_IMPL(virshDomainVcpuState,
               VIR_VCPU_LAST,
               N_("offline"),
               N_("running"),
-              N_("blocked"))
+              N_("blocked"),
+              N_("halted"))
 
 static const char *
 virshDomainVcpuStateToString(int state)
-- 
2.9.0




More information about the libvir-list mailing list