render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
9119d9
From 59a6145c0f1fe4e7ef35970578b8eb9837145bc0 Mon Sep 17 00:00:00 2001
9119d9
Message-Id: <59a6145c0f1fe4e7ef35970578b8eb9837145bc0@dist-git>
9119d9
From: Francesco Romani <fromani@redhat.com>
9119d9
Date: Wed, 1 Oct 2014 11:20:16 +0200
9119d9
Subject: [PATCH] qemu: bulk stats: implement block group
9119d9
9119d9
https://bugzilla.redhat.com/show_bug.cgi?id=1113116
9119d9
9119d9
This patch implements the VIR_DOMAIN_STATS_BLOCK group of statistics.
9119d9
9119d9
To do so, a helper function to get the block stats of all the disks of
9119d9
a domain is added.
9119d9
9119d9
Signed-off-by: Francesco Romani <fromani@redhat.com>
9119d9
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
9119d9
(cherry picked from commit 290e3c6b07a273444f681de198d2416ccd2d4702)
9119d9
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
9119d9
---
9119d9
 include/libvirt/libvirt.h.in |   1 +
9119d9
 src/libvirt.c                |  21 +++++++
9119d9
 src/qemu/qemu_driver.c       |  81 ++++++++++++++++++++++++++
9119d9
 src/qemu/qemu_monitor.c      |  26 +++++++++
9119d9
 src/qemu/qemu_monitor.h      |  20 +++++++
9119d9
 src/qemu/qemu_monitor_json.c | 136 +++++++++++++++++++++++++++++--------------
9119d9
 src/qemu/qemu_monitor_json.h |   4 ++
9119d9
 7 files changed, 246 insertions(+), 43 deletions(-)
9119d9
9119d9
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
9119d9
index 72aa47d..60b3e0b 100644
9119d9
--- a/include/libvirt/libvirt.h.in
9119d9
+++ b/include/libvirt/libvirt.h.in
9119d9
@@ -2518,6 +2518,7 @@ typedef enum {
9119d9
     VIR_DOMAIN_STATS_BALLOON = (1 << 2), /* return domain balloon info */
9119d9
     VIR_DOMAIN_STATS_VCPU = (1 << 3), /* return domain virtual CPU info */
9119d9
     VIR_DOMAIN_STATS_INTERFACE = (1 << 4), /* return domain interfaces info */
9119d9
+    VIR_DOMAIN_STATS_BLOCK = (1 << 5), /* return domain block info */
9119d9
 } virDomainStatsTypes;
9119d9
 
9119d9
 typedef enum {
9119d9
diff --git a/src/libvirt.c b/src/libvirt.c
9119d9
index 46d3e22..f62ecf7 100644
9119d9
--- a/src/libvirt.c
9119d9
+++ b/src/libvirt.c
9119d9
@@ -21595,6 +21595,27 @@ virConnectGetDomainCapabilities(virConnectPtr conn,
9119d9
  * "net.<num>.tx.errs" - transmission errors as unsigned long long.
9119d9
  * "net.<num>.tx.drop" - transmit packets dropped as unsigned long long.
9119d9
  *
9119d9
+ * VIR_DOMAIN_STATS_BLOCK: Return block devices statistics.
9119d9
+ * The typed parameter keys are in this format:
9119d9
+ * "block.count" - number of block devices on this domain
9119d9
+ *                 as unsigned int.
9119d9
+ * "block.<num>.name" - name of the block device <num> as string.
9119d9
+ *                      matches the target name (vda/sda/hda) of the
9119d9
+ *                      block device.
9119d9
+ * "block.<num>.rd.reqs" - number of read requests as unsigned long long.
9119d9
+ * "block.<num>.rd.bytes" - number of read bytes as unsigned long long.
9119d9
+ * "block.<num>.rd.times" - total time (ns) spent on reads as
9119d9
+ *                          unsigned long long.
9119d9
+ * "block.<num>.wr.reqs" - number of write requests as unsigned long long.
9119d9
+ * "block.<num>.wr.bytes" - number of written bytes as unsigned long long.
9119d9
+ * "block.<num>.wr.times" - total time (ns) spent on writes as
9119d9
+ *                          unsigned long long.
9119d9
+ * "block.<num>.fl.reqs" - total flush requests as unsigned long long.
9119d9
+ * "block.<num>.fl.times" - total time (ns) spent on cache flushing as
9119d9
+ *                          unsigned long long.
9119d9
+ * "block.<num>.errors" - Xen only: the 'oo_req' value as
9119d9
+ *                        unsigned long long.
9119d9
+ *
9119d9
  * Using 0 for @stats returns all stats groups supported by the given
9119d9
  * hypervisor.
9119d9
  *
9119d9
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
9119d9
index 3a46c6d..8e90cc6 100644
9119d9
--- a/src/qemu/qemu_driver.c
9119d9
+++ b/src/qemu/qemu_driver.c
9119d9
@@ -9866,6 +9866,7 @@ qemuDomainBlockStats(virDomainPtr dom,
9119d9
     return ret;
9119d9
 }
9119d9
 
9119d9
+
9119d9
 static int
9119d9
 qemuDomainBlockStatsFlags(virDomainPtr dom,
9119d9
                           const char *path,
9119d9
@@ -17690,6 +17691,85 @@ qemuDomainGetStatsInterface(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
9119d9
 
9119d9
 #undef QEMU_ADD_NET_PARAM
9119d9
 
9119d9
+/* expects a LL, but typed parameter must be ULL */
9119d9
+#define QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, num, name, value) \
9119d9
+do { \
9119d9
+    char param_name[VIR_TYPED_PARAM_FIELD_LENGTH]; \
9119d9
+    snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH, \
9119d9
+             "block.%zu.%s", num, name); \
9119d9
+    if (value >= 0 && virTypedParamsAddULLong(&(record)->params, \
9119d9
+                                              &(record)->nparams, \
9119d9
+                                              maxparams, \
9119d9
+                                              param_name, \
9119d9
+                                              value) < 0) \
9119d9
+        goto cleanup; \
9119d9
+} while (0)
9119d9
+
9119d9
+static int
9119d9
+qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
9119d9
+                        virDomainObjPtr dom,
9119d9
+                        virDomainStatsRecordPtr record,
9119d9
+                        int *maxparams,
9119d9
+                        unsigned int privflags)
9119d9
+{
9119d9
+    size_t i;
9119d9
+    int ret = -1;
9119d9
+    int nstats = dom->def->ndisks;
9119d9
+    qemuBlockStatsPtr stats = NULL;
9119d9
+    qemuDomainObjPrivatePtr priv = dom->privateData;
9119d9
+
9119d9
+    if (!HAVE_JOB(privflags) || !virDomainObjIsActive(dom))
9119d9
+        return 0; /* it's ok, just go ahead silently */
9119d9
+
9119d9
+    if (VIR_ALLOC_N(stats, nstats) < 0)
9119d9
+        return -1;
9119d9
+
9119d9
+    qemuDomainObjEnterMonitor(driver, dom);
9119d9
+
9119d9
+    nstats = qemuMonitorGetAllBlockStatsInfo(priv->mon, NULL,
9119d9
+                                             stats, nstats);
9119d9
+
9119d9
+    qemuDomainObjExitMonitor(driver, dom);
9119d9
+
9119d9
+    if (nstats < 0) {
9119d9
+        virResetLastError();
9119d9
+        ret = 0; /* still ok, again go ahead silently */
9119d9
+        goto cleanup;
9119d9
+    }
9119d9
+
9119d9
+    QEMU_ADD_COUNT_PARAM(record, maxparams, "block", dom->def->ndisks);
9119d9
+
9119d9
+    for (i = 0; i < nstats; i++) {
9119d9
+        QEMU_ADD_NAME_PARAM(record, maxparams,
9119d9
+                            "block", i, dom->def->disks[i]->dst);
9119d9
+
9119d9
+        QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
9119d9
+                                "rd.reqs", stats[i].rd_req);
9119d9
+        QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
9119d9
+                                "rd.bytes", stats[i].rd_bytes);
9119d9
+        QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
9119d9
+                                "rd.times", stats[i].rd_total_times);
9119d9
+        QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
9119d9
+                                "wr.reqs", stats[i].wr_req);
9119d9
+        QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
9119d9
+                                "wr.bytes", stats[i].wr_bytes);
9119d9
+        QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
9119d9
+                                "wr.times", stats[i].wr_total_times);
9119d9
+        QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
9119d9
+                                "fl.reqs", stats[i].flush_req);
9119d9
+        QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
9119d9
+                                "fl.times", stats[i].flush_total_times);
9119d9
+    }
9119d9
+
9119d9
+    ret = 0;
9119d9
+
9119d9
+ cleanup:
9119d9
+    VIR_FREE(stats);
9119d9
+    return ret;
9119d9
+}
9119d9
+
9119d9
+#undef QEMU_ADD_BLOCK_PARAM_LL
9119d9
+
9119d9
 #undef QEMU_ADD_NAME_PARAM
9119d9
 
9119d9
 #undef QEMU_ADD_COUNT_PARAM
9119d9
@@ -17713,6 +17793,7 @@ static struct qemuDomainGetStatsWorker qemuDomainGetStatsWorkers[] = {
9119d9
     { qemuDomainGetStatsBalloon, VIR_DOMAIN_STATS_BALLOON, true },
9119d9
     { qemuDomainGetStatsVcpu, VIR_DOMAIN_STATS_VCPU, false },
9119d9
     { qemuDomainGetStatsInterface, VIR_DOMAIN_STATS_INTERFACE, false },
9119d9
+    { qemuDomainGetStatsBlock, VIR_DOMAIN_STATS_BLOCK, true },
9119d9
     { NULL, 0, false }
9119d9
 };
9119d9
 
9119d9
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
9119d9
index fea551d..afe7959 100644
9119d9
--- a/src/qemu/qemu_monitor.c
9119d9
+++ b/src/qemu/qemu_monitor.c
9119d9
@@ -1760,6 +1760,32 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
9119d9
     return ret;
9119d9
 }
9119d9
 
9119d9
+/* Fills the first 'nstats' block stats. 'stats' must be an array.
9119d9
+ * Returns <0 on error, otherwise the number of block stats retrieved.
9119d9
+ * if 'dev_name' is != NULL, look for this device only and skip
9119d9
+ * any other. In that case return value cannot be greater than 1.
9119d9
+ */
9119d9
+int
9119d9
+qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
9119d9
+                                const char *dev_name,
9119d9
+                                qemuBlockStatsPtr stats,
9119d9
+                                int nstats)
9119d9
+{
9119d9
+    int ret;
9119d9
+    VIR_DEBUG("mon=%p dev=%s", mon, dev_name);
9119d9
+
9119d9
+    if (mon->json) {
9119d9
+        ret = qemuMonitorJSONGetAllBlockStatsInfo(mon, dev_name,
9119d9
+                                                  stats, nstats);
9119d9
+    } else {
9119d9
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
9119d9
+                       _("unable to query all block stats with this QEMU"));
9119d9
+        return -1;
9119d9
+    }
9119d9
+
9119d9
+    return ret;
9119d9
+}
9119d9
+
9119d9
 /* Return 0 and update @nparams with the number of block stats
9119d9
  * QEMU supports if success. Return -1 if failure.
9119d9
  */
9119d9
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
9119d9
index 62c9f2e..b7f9fd1 100644
9119d9
--- a/src/qemu/qemu_monitor.h
9119d9
+++ b/src/qemu/qemu_monitor.h
9119d9
@@ -346,6 +346,26 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
9119d9
                                  long long *flush_req,
9119d9
                                  long long *flush_total_times,
9119d9
                                  long long *errs);
9119d9
+
9119d9
+typedef struct _qemuBlockStats qemuBlockStats;
9119d9
+typedef qemuBlockStats *qemuBlockStatsPtr;
9119d9
+struct _qemuBlockStats {
9119d9
+    long long rd_req;
9119d9
+    long long rd_bytes;
9119d9
+    long long wr_req;
9119d9
+    long long wr_bytes;
9119d9
+    long long rd_total_times;
9119d9
+    long long wr_total_times;
9119d9
+    long long flush_req;
9119d9
+    long long flush_total_times;
9119d9
+};
9119d9
+
9119d9
+int qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
9119d9
+                                    const char *dev_name,
9119d9
+                                    qemuBlockStatsPtr stats,
9119d9
+                                    int nstats)
9119d9
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
9119d9
+
9119d9
 int qemuMonitorGetBlockStatsParamsNumber(qemuMonitorPtr mon,
9119d9
                                          int *nparams);
9119d9
 
9119d9
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
9119d9
index 17b082c..c013d49 100644
9119d9
--- a/src/qemu/qemu_monitor_json.c
9119d9
+++ b/src/qemu/qemu_monitor_json.c
9119d9
@@ -1708,13 +1708,8 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
9119d9
                                      long long *flush_total_times,
9119d9
                                      long long *errs)
9119d9
 {
9119d9
-    int ret;
9119d9
-    size_t i;
9119d9
-    bool found = false;
9119d9
-    virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
9119d9
-                                                     NULL);
9119d9
-    virJSONValuePtr reply = NULL;
9119d9
-    virJSONValuePtr devices;
9119d9
+    qemuBlockStats stats;
9119d9
+    int ret = -1;
9119d9
 
9119d9
     *rd_req = *rd_bytes = -1;
9119d9
     *wr_req = *wr_bytes = *errs = -1;
9119d9
@@ -1728,9 +1723,49 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
9119d9
     if (flush_total_times)
9119d9
         *flush_total_times = -1;
9119d9
 
9119d9
+    if (qemuMonitorJSONGetAllBlockStatsInfo(mon, dev_name, &stats, 1) != 1)
9119d9
+        goto cleanup;
9119d9
+
9119d9
+    *rd_req = stats.rd_req;
9119d9
+    *rd_bytes = stats.rd_bytes;
9119d9
+    *wr_req = stats.wr_req;
9119d9
+    *wr_bytes = stats.wr_bytes;
9119d9
+    *errs = -1; /* QEMU does not have this */
9119d9
+
9119d9
+    if (rd_total_times)
9119d9
+        *rd_total_times = stats.rd_total_times;
9119d9
+    if (wr_total_times)
9119d9
+        *wr_total_times = stats.wr_total_times;
9119d9
+    if (flush_req)
9119d9
+        *flush_req = stats.flush_req;
9119d9
+    if (flush_total_times)
9119d9
+        *flush_total_times = stats.flush_total_times;
9119d9
+
9119d9
+    ret = 0;
9119d9
+
9119d9
+ cleanup:
9119d9
+    return ret;
9119d9
+}
9119d9
+
9119d9
+
9119d9
+int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
9119d9
+                                        const char *dev_name,
9119d9
+                                        qemuBlockStatsPtr bstats,
9119d9
+                                        int nstats)
9119d9
+{
9119d9
+    int ret, count;
9119d9
+    size_t i;
9119d9
+    virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
9119d9
+                                                     NULL);
9119d9
+    virJSONValuePtr reply = NULL;
9119d9
+    virJSONValuePtr devices;
9119d9
+
9119d9
     if (!cmd)
9119d9
         return -1;
9119d9
 
9119d9
+    if (!bstats || nstats <= 0)
9119d9
+        return -1;
9119d9
+
9119d9
     ret = qemuMonitorJSONCommand(mon, cmd, &reply);
9119d9
 
9119d9
     if (ret == 0)
9119d9
@@ -1746,108 +1781,123 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
9119d9
         goto cleanup;
9119d9
     }
9119d9
 
9119d9
-    for (i = 0; i < virJSONValueArraySize(devices); i++) {
9119d9
+    count = 0;
9119d9
+    for (i = 0; i < virJSONValueArraySize(devices) && count < nstats; i++) {
9119d9
         virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
9119d9
         virJSONValuePtr stats;
9119d9
-        const char *thisdev;
9119d9
         if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
9119d9
-                           _("blockstats device entry was not in expected format"));
9119d9
+                           _("blockstats device entry was not "
9119d9
+                             "in expected format"));
9119d9
             goto cleanup;
9119d9
         }
9119d9
 
9119d9
-        if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
9119d9
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
9119d9
-                           _("blockstats device entry was not in expected format"));
9119d9
-            goto cleanup;
9119d9
-        }
9119d9
-
9119d9
-        /* New QEMU has separate names for host & guest side of the disk
9119d9
-         * and libvirt gives the host side a 'drive-' prefix. The passed
9119d9
-         * in dev_name is the guest side though
9119d9
+        /* If dev_name is specified, we are looking for a specific device,
9119d9
+         * so we must be stricter.
9119d9
          */
9119d9
-        if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
9119d9
-            thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
9119d9
+        if (dev_name) {
9119d9
+            const char *thisdev = virJSONValueObjectGetString(dev, "device");
9119d9
+            if (!thisdev) {
9119d9
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
9119d9
+                               _("blockstats device entry was not "
9119d9
+                                 "in expected format"));
9119d9
+                goto cleanup;
9119d9
+            }
9119d9
 
9119d9
-        if (STRNEQ(thisdev, dev_name))
9119d9
-            continue;
9119d9
+            /* New QEMU has separate names for host & guest side of the disk
9119d9
+             * and libvirt gives the host side a 'drive-' prefix. The passed
9119d9
+             * in dev_name is the guest side though
9119d9
+             */
9119d9
+            if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
9119d9
+                thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
9119d9
+
9119d9
+            if (STRNEQ(thisdev, dev_name))
9119d9
+                continue;
9119d9
+        }
9119d9
 
9119d9
-        found = true;
9119d9
         if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
9119d9
             stats->type != VIR_JSON_TYPE_OBJECT) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
9119d9
-                           _("blockstats stats entry was not in expected format"));
9119d9
+                           _("blockstats stats entry was not "
9119d9
+                             "in expected format"));
9119d9
             goto cleanup;
9119d9
         }
9119d9
 
9119d9
-        if (virJSONValueObjectGetNumberLong(stats, "rd_bytes", rd_bytes) < 0) {
9119d9
+        if (virJSONValueObjectGetNumberLong(stats, "rd_bytes",
9119d9
+                                            &bstats->rd_bytes) < 0) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
                            _("cannot read %s statistic"),
9119d9
                            "rd_bytes");
9119d9
             goto cleanup;
9119d9
         }
9119d9
-        if (virJSONValueObjectGetNumberLong(stats, "rd_operations", rd_req) < 0) {
9119d9
+        if (virJSONValueObjectGetNumberLong(stats, "rd_operations",
9119d9
+                                            &bstats->rd_req) < 0) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
                            _("cannot read %s statistic"),
9119d9
                             "rd_operations");
9119d9
             goto cleanup;
9119d9
         }
9119d9
-        if (rd_total_times &&
9119d9
-            virJSONValueObjectHasKey(stats, "rd_total_time_ns") &&
9119d9
+        if (virJSONValueObjectHasKey(stats, "rd_total_time_ns") &&
9119d9
             (virJSONValueObjectGetNumberLong(stats, "rd_total_time_ns",
9119d9
-                                             rd_total_times) < 0)) {
9119d9
+                                             &bstats->rd_total_times) < 0)) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
                            _("cannot read %s statistic"),
9119d9
                            "rd_total_time_ns");
9119d9
             goto cleanup;
9119d9
         }
9119d9
-        if (virJSONValueObjectGetNumberLong(stats, "wr_bytes", wr_bytes) < 0) {
9119d9
+        if (virJSONValueObjectGetNumberLong(stats, "wr_bytes",
9119d9
+                                            &bstats->wr_bytes) < 0) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
                            _("cannot read %s statistic"),
9119d9
                            "wr_bytes");
9119d9
             goto cleanup;
9119d9
         }
9119d9
-        if (virJSONValueObjectGetNumberLong(stats, "wr_operations", wr_req) < 0) {
9119d9
+        if (virJSONValueObjectGetNumberLong(stats, "wr_operations",
9119d9
+                                            &bstats->wr_req) < 0) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
                            _("cannot read %s statistic"),
9119d9
                            "wr_operations");
9119d9
             goto cleanup;
9119d9
         }
9119d9
-        if (wr_total_times &&
9119d9
-            virJSONValueObjectHasKey(stats, "wr_total_time_ns") &&
9119d9
+        if (virJSONValueObjectHasKey(stats, "wr_total_time_ns") &&
9119d9
             (virJSONValueObjectGetNumberLong(stats, "wr_total_time_ns",
9119d9
-                                             wr_total_times) < 0)) {
9119d9
+                                             &bstats->wr_total_times) < 0)) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
                            _("cannot read %s statistic"),
9119d9
                            "wr_total_time_ns");
9119d9
             goto cleanup;
9119d9
         }
9119d9
-        if (flush_req &&
9119d9
-            virJSONValueObjectHasKey(stats, "flush_operations") &&
9119d9
+        if (virJSONValueObjectHasKey(stats, "flush_operations") &&
9119d9
             (virJSONValueObjectGetNumberLong(stats, "flush_operations",
9119d9
-                                            flush_req) < 0)) {
9119d9
+                                             &bstats->flush_req) < 0)) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
                            _("cannot read %s statistic"),
9119d9
                            "flush_operations");
9119d9
             goto cleanup;
9119d9
         }
9119d9
-        if (flush_total_times &&
9119d9
-            virJSONValueObjectHasKey(stats, "flush_total_time_ns") &&
9119d9
+        if (virJSONValueObjectHasKey(stats, "flush_total_time_ns") &&
9119d9
             (virJSONValueObjectGetNumberLong(stats, "flush_total_time_ns",
9119d9
-                                            flush_total_times) < 0)) {
9119d9
+                                             &bstats->flush_total_times) < 0)) {
9119d9
             virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
                            _("cannot read %s statistic"),
9119d9
                            "flush_total_time_ns");
9119d9
             goto cleanup;
9119d9
         }
9119d9
+
9119d9
+        count++;
9119d9
+        bstats++;
9119d9
+
9119d9
+        if (dev_name && count)
9119d9
+            break;
9119d9
     }
9119d9
 
9119d9
-    if (!found) {
9119d9
+    if (dev_name && !count) {
9119d9
         virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
                        _("cannot find statistics for device '%s'"), dev_name);
9119d9
         goto cleanup;
9119d9
     }
9119d9
-    ret = 0;
9119d9
+
9119d9
+    ret = count;
9119d9
 
9119d9
  cleanup:
9119d9
     virJSONValueFree(cmd);
9119d9
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
9119d9
index c4e795c..2402b5a 100644
9119d9
--- a/src/qemu/qemu_monitor_json.h
9119d9
+++ b/src/qemu/qemu_monitor_json.h
9119d9
@@ -79,6 +79,10 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
9119d9
                                      long long *flush_req,
9119d9
                                      long long *flush_total_times,
9119d9
                                      long long *errs);
9119d9
+int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
9119d9
+                                        const char *dev_name,
9119d9
+                                        qemuBlockStatsPtr stats,
9119d9
+                                        int nstats);
9119d9
 int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
9119d9
                                              int *nparams);
9119d9
 int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
9119d9
-- 
9119d9
2.1.2
9119d9