Blob Blame History Raw
From d86dcee6e419259d365eaaa69a4d71c48491e95f Mon Sep 17 00:00:00 2001
Message-Id: <d86dcee6e419259d365eaaa69a4d71c48491e95f@dist-git>
From: Eric Blake <eblake@redhat.com>
Date: Wed, 17 Dec 2014 03:09:04 -0700
Subject: [PATCH] getstats: prepare monitor collection for recursion

https://bugzilla.redhat.com/show_bug.cgi?id=1041569

A future patch will allow recursion into backing chains when
collecting block stats.  This patch should not change behavior,
but merely moves out the common code that will be reused once
recursion is enabled, and adds the parameter that will turn on
recursion.

* src/qemu/qemu_monitor.h (qemuMonitorGetAllBlockStatsInfo)
(qemuMonitorBlockStatsUpdateCapacity): Add recursion parameter,
although it is ignored for now.
* src/qemu/qemu_monitor.h (qemuMonitorGetAllBlockStatsInfo)
(qemuMonitorBlockStatsUpdateCapacity): Likewise.
* src/qemu/qemu_monitor_json.h
(qemuMonitorJSONGetAllBlockStatsInfo)
(qemuMonitorJSONBlockStatsUpdateCapacity): Likewise.
* src/qemu/qemu_monitor_json.c
(qemuMonitorJSONGetAllBlockStatsInfo)
(qemuMonitorJSONBlockStatsUpdateCapacity): Add parameter, and
split...
(qemuMonitorJSONGetOneBlockStatsInfo)
(qemuMonitorJSONBlockStatsUpdateCapacityOne): ...into helpers.
(qemuMonitorJSONGetBlockStatsInfo): Update caller.
* src/qemu/qemu_driver.c (qemuDomainGetStatsBlock): Update caller.
* src/qemu/qemu_migration.c (qemuMigrationCookieAddNBD): Likewise.

Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit 7b11f5e5547ded860692caa2cefae1a856920336)

Conflicts:
	src/qemu/qemu_migration.c - no disk precreation (commit e1466dc)
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
 src/qemu/qemu_driver.c       |   5 +-
 src/qemu/qemu_monitor.c      |  24 ++--
 src/qemu/qemu_monitor.h      |   6 +-
 src/qemu/qemu_monitor_json.c | 257 +++++++++++++++++++++++++------------------
 src/qemu/qemu_monitor_json.h |   6 +-
 5 files changed, 173 insertions(+), 125 deletions(-)

diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 03b62d8..8376486 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -18025,8 +18025,9 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
         abbreviated = true; /* it's ok, just go ahead silently */
     } else {
         qemuDomainObjEnterMonitor(driver, dom);
-        rc = qemuMonitorGetAllBlockStatsInfo(priv->mon, &stats);
-        ignore_value(qemuMonitorBlockStatsUpdateCapacity(priv->mon, stats));
+        rc = qemuMonitorGetAllBlockStatsInfo(priv->mon, &stats, false);
+        ignore_value(qemuMonitorBlockStatsUpdateCapacity(priv->mon, stats,
+                                                         false));
         qemuDomainObjExitMonitor(driver, dom);
 
         if (rc < 0) {
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 38399ba..f797f4f 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1787,16 +1787,16 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
     return ret;
 }
 
-/* Fills the first 'nstats' block stats. 'stats' must be an array.
- * Returns <0 on error, otherwise the number of block stats retrieved.
- * if 'dev_name' is != NULL, look for this device only and skip
- * any other. In that case return value cannot be greater than 1.
+
+/* Creates a hash table in 'ret_stats' with all block stats.
+ * Returns <0 on error, 0 on success.
  */
 int
 qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
-                                virHashTablePtr *ret_stats)
+                                virHashTablePtr *ret_stats,
+                                bool backingChain)
 {
-    VIR_DEBUG("mon=%p ret_stats=%p", mon, ret_stats);
+    VIR_DEBUG("mon=%p ret_stats=%p, backing=%d", mon, ret_stats, backingChain);
 
     if (!mon->json) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -1804,15 +1804,17 @@ qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
         return -1;
     }
 
-    return qemuMonitorJSONGetAllBlockStatsInfo(mon, ret_stats);
+    return qemuMonitorJSONGetAllBlockStatsInfo(mon, ret_stats, backingChain);
 }
 
 
 /* Updates "stats" to fill virtual and physical size of the image */
-int qemuMonitorBlockStatsUpdateCapacity(qemuMonitorPtr mon,
-                                        virHashTablePtr stats)
+int
+qemuMonitorBlockStatsUpdateCapacity(qemuMonitorPtr mon,
+                                    virHashTablePtr stats,
+                                    bool backingChain)
 {
-    VIR_DEBUG("mon=%p, stats=%p", mon, stats);
+    VIR_DEBUG("mon=%p, stats=%p, backing=%d", mon, stats, backingChain);
 
     if (!mon->json) {
         virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
@@ -1820,7 +1822,7 @@ int qemuMonitorBlockStatsUpdateCapacity(qemuMonitorPtr mon,
         return -1;
     }
 
-    return qemuMonitorJSONBlockStatsUpdateCapacity(mon, stats);
+    return qemuMonitorJSONBlockStatsUpdateCapacity(mon, stats, backingChain);
 }
 
 
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index d1dc242..b08d74d 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -382,11 +382,13 @@ struct _qemuBlockStats {
 };
 
 int qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
-                                    virHashTablePtr *ret_stats)
+                                    virHashTablePtr *ret_stats,
+                                    bool backingChain)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
 
 int qemuMonitorBlockStatsUpdateCapacity(qemuMonitorPtr mon,
-                                        virHashTablePtr stats)
+                                        virHashTablePtr stats,
+                                        bool backingChain)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
 
 int qemuMonitorGetBlockStatsParamsNumber(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 3cf67aa..7088409 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1770,7 +1770,7 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
     if (flush_total_times)
         *flush_total_times = -1;
 
-    if (qemuMonitorJSONGetAllBlockStatsInfo(mon, &blockstats) < 0)
+    if (qemuMonitorJSONGetAllBlockStatsInfo(mon, &blockstats, false) < 0)
         goto cleanup;
 
     if (!(stats = virHashLookup(blockstats, dev_name))) {
@@ -1836,16 +1836,112 @@ qemuMonitorJSONDevGetBlockExtent(virJSONValuePtr dev,
 }
 
 
-int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
-                                        virHashTablePtr *ret_stats)
+static int
+qemuMonitorJSONGetOneBlockStatsInfo(virJSONValuePtr dev,
+                                    const char *dev_name,
+                                    virHashTablePtr hash,
+                                    bool backingChain ATTRIBUTE_UNUSED)
 {
-    int ret = -1;
-    int rc;
-    size_t i;
-    virJSONValuePtr cmd;
-    virJSONValuePtr reply = NULL;
-    virJSONValuePtr devices;
     qemuBlockStatsPtr bstats = NULL;
+    virJSONValuePtr stats;
+    int ret = -1;
+
+    if (VIR_ALLOC(bstats) < 0)
+        goto cleanup;
+
+    if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
+        stats->type != VIR_JSON_TYPE_OBJECT) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("blockstats stats entry was not "
+                         "in expected format"));
+        goto cleanup;
+    }
+
+    if (virJSONValueObjectGetNumberLong(stats, "rd_bytes",
+                                        &bstats->rd_bytes) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read %s statistic"),
+                       "rd_bytes");
+        goto cleanup;
+    }
+    if (virJSONValueObjectGetNumberLong(stats, "rd_operations",
+                                        &bstats->rd_req) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read %s statistic"),
+                       "rd_operations");
+        goto cleanup;
+    }
+    if (virJSONValueObjectHasKey(stats, "rd_total_time_ns") &&
+        (virJSONValueObjectGetNumberLong(stats, "rd_total_time_ns",
+                                         &bstats->rd_total_times) < 0)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read %s statistic"),
+                       "rd_total_time_ns");
+        goto cleanup;
+    }
+    if (virJSONValueObjectGetNumberLong(stats, "wr_bytes",
+                                        &bstats->wr_bytes) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read %s statistic"),
+                       "wr_bytes");
+        goto cleanup;
+    }
+    if (virJSONValueObjectGetNumberLong(stats, "wr_operations",
+                                        &bstats->wr_req) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read %s statistic"),
+                       "wr_operations");
+        goto cleanup;
+    }
+    if (virJSONValueObjectHasKey(stats, "wr_total_time_ns") &&
+        (virJSONValueObjectGetNumberLong(stats, "wr_total_time_ns",
+                                         &bstats->wr_total_times) < 0)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read %s statistic"),
+                       "wr_total_time_ns");
+        goto cleanup;
+    }
+    if (virJSONValueObjectHasKey(stats, "flush_operations") &&
+        (virJSONValueObjectGetNumberLong(stats, "flush_operations",
+                                         &bstats->flush_req) < 0)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read %s statistic"),
+                       "flush_operations");
+        goto cleanup;
+    }
+    if (virJSONValueObjectHasKey(stats, "flush_total_time_ns") &&
+        (virJSONValueObjectGetNumberLong(stats, "flush_total_time_ns",
+                                         &bstats->flush_total_times) < 0)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read %s statistic"),
+                       "flush_total_time_ns");
+        goto cleanup;
+    }
+
+    /* it's ok to not have this information here. Just skip silently. */
+    qemuMonitorJSONDevGetBlockExtent(dev, &bstats->wr_highest_offset);
+
+    if (virHashAddEntry(hash, dev_name, bstats) < 0)
+        goto cleanup;
+    bstats = NULL;
+    ret = 0;
+ cleanup:
+    VIR_FREE(bstats);
+    return ret;
+}
+
+
+int
+qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
+                                    virHashTablePtr *ret_stats,
+                                    bool backingChain)
+{
+    int ret = -1;
+    int rc;
+    size_t i;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+    virJSONValuePtr devices;
     virHashTablePtr hash = NULL;
 
     if (!(cmd = qemuMonitorJSONMakeCommand("query-blockstats", NULL)))
@@ -1869,12 +1965,8 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
 
     for (i = 0; i < virJSONValueArraySize(devices); i++) {
         virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
-        virJSONValuePtr stats;
         const char *dev_name;
 
-        if (VIR_ALLOC(bstats) < 0)
-            goto cleanup;
-
         if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                            _("blockstats device entry was not "
@@ -1892,81 +1984,10 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
         if (STRPREFIX(dev_name, QEMU_DRIVE_HOST_PREFIX))
             dev_name += strlen(QEMU_DRIVE_HOST_PREFIX);
 
-        if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
-            stats->type != VIR_JSON_TYPE_OBJECT) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("blockstats stats entry was not "
-                             "in expected format"));
+        if (qemuMonitorJSONGetOneBlockStatsInfo(dev, dev_name, hash,
+                                                backingChain) < 0)
             goto cleanup;
-        }
 
-        if (virJSONValueObjectGetNumberLong(stats, "rd_bytes",
-                                            &bstats->rd_bytes) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("cannot read %s statistic"),
-                           "rd_bytes");
-            goto cleanup;
-        }
-        if (virJSONValueObjectGetNumberLong(stats, "rd_operations",
-                                            &bstats->rd_req) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("cannot read %s statistic"),
-                            "rd_operations");
-            goto cleanup;
-        }
-        if (virJSONValueObjectHasKey(stats, "rd_total_time_ns") &&
-            (virJSONValueObjectGetNumberLong(stats, "rd_total_time_ns",
-                                             &bstats->rd_total_times) < 0)) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("cannot read %s statistic"),
-                           "rd_total_time_ns");
-            goto cleanup;
-        }
-        if (virJSONValueObjectGetNumberLong(stats, "wr_bytes",
-                                            &bstats->wr_bytes) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("cannot read %s statistic"),
-                           "wr_bytes");
-            goto cleanup;
-        }
-        if (virJSONValueObjectGetNumberLong(stats, "wr_operations",
-                                            &bstats->wr_req) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("cannot read %s statistic"),
-                           "wr_operations");
-            goto cleanup;
-        }
-        if (virJSONValueObjectHasKey(stats, "wr_total_time_ns") &&
-            (virJSONValueObjectGetNumberLong(stats, "wr_total_time_ns",
-                                             &bstats->wr_total_times) < 0)) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("cannot read %s statistic"),
-                           "wr_total_time_ns");
-            goto cleanup;
-        }
-        if (virJSONValueObjectHasKey(stats, "flush_operations") &&
-            (virJSONValueObjectGetNumberLong(stats, "flush_operations",
-                                             &bstats->flush_req) < 0)) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("cannot read %s statistic"),
-                           "flush_operations");
-            goto cleanup;
-        }
-        if (virJSONValueObjectHasKey(stats, "flush_total_time_ns") &&
-            (virJSONValueObjectGetNumberLong(stats, "flush_total_time_ns",
-                                             &bstats->flush_total_times) < 0)) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("cannot read %s statistic"),
-                           "flush_total_time_ns");
-            goto cleanup;
-        }
-
-        /* it's ok to not have this information here. Just skip silently. */
-        qemuMonitorJSONDevGetBlockExtent(dev, &bstats->wr_highest_offset);
-
-        if (virHashAddEntry(hash, dev_name, bstats) < 0)
-            goto cleanup;
-        bstats = NULL;
     }
 
     *ret_stats = hash;
@@ -1974,7 +1995,6 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
     ret = 0;
 
  cleanup:
-    VIR_FREE(bstats);
     virHashFree(hash);
     virJSONValueFree(cmd);
     virJSONValueFree(reply);
@@ -1982,8 +2002,45 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
 }
 
 
-int qemuMonitorJSONBlockStatsUpdateCapacity(qemuMonitorPtr mon,
-                                            virHashTablePtr stats)
+static int
+qemuMonitorJSONBlockStatsUpdateCapacityOne(virJSONValuePtr image,
+                                           const char *dev_name,
+                                           virHashTablePtr stats,
+                                           bool backingChain ATTRIBUTE_UNUSED)
+{
+    qemuBlockStatsPtr bstats;
+    int ret = -1;
+
+    if (!(bstats = virHashLookup(stats, dev_name))) {
+        if (VIR_ALLOC(bstats) < 0)
+            goto cleanup;
+
+        if (virHashAddEntry(stats, dev_name, bstats) < 0) {
+            VIR_FREE(bstats);
+            goto cleanup;
+        }
+    }
+
+    /* After this point, we ignore failures; the stats were
+     * zero-initialized when created which is a sane fallback.  */
+    ret = 0;
+    if (virJSONValueObjectGetNumberUlong(image, "virtual-size",
+                                         &bstats->capacity) < 0)
+        goto cleanup;
+
+    /* if actual-size is missing, image is not thin provisioned */
+    if (virJSONValueObjectGetNumberUlong(image, "actual-size",
+                                         &bstats->physical) < 0)
+        bstats->physical = bstats->capacity;
+ cleanup:
+    return ret;
+}
+
+
+int
+qemuMonitorJSONBlockStatsUpdateCapacity(qemuMonitorPtr mon,
+                                        virHashTablePtr stats,
+                                        bool backingChain)
 {
     int ret = -1;
     int rc;
@@ -2012,7 +2069,6 @@ int qemuMonitorJSONBlockStatsUpdateCapacity(qemuMonitorPtr mon,
         virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
         virJSONValuePtr inserted;
         virJSONValuePtr image;
-        qemuBlockStatsPtr bstats;
         const char *dev_name;
 
         if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
@@ -2037,24 +2093,9 @@ int qemuMonitorJSONBlockStatsUpdateCapacity(qemuMonitorPtr mon,
             !(image = virJSONValueObjectGet(inserted, "image")))
             continue;
 
-        if (!(bstats = virHashLookup(stats, dev_name))) {
-            if (VIR_ALLOC(bstats) < 0)
-                goto cleanup;
-
-            if (virHashAddEntry(stats, dev_name, bstats) < 0) {
-                VIR_FREE(bstats);
-                goto cleanup;
-            }
-        }
-
-        if (virJSONValueObjectGetNumberUlong(image, "virtual-size",
-                                             &bstats->capacity) < 0)
-            continue;
-
-        /* if actual-size is missing, image is not thin provisioned */
-        if (virJSONValueObjectGetNumberUlong(image, "actual-size",
-                                             &bstats->physical) < 0)
-            bstats->physical = bstats->capacity;
+        if (qemuMonitorJSONBlockStatsUpdateCapacityOne(image, dev_name, stats,
+                                                       backingChain) < 0)
+            goto cleanup;
     }
 
     ret = 0;
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 17f9718..d039991 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -80,9 +80,11 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
                                      long long *flush_total_times,
                                      long long *errs);
 int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
-                                        virHashTablePtr *ret_stats);
+                                        virHashTablePtr *ret_stats,
+                                        bool backingChain);
 int qemuMonitorJSONBlockStatsUpdateCapacity(qemuMonitorPtr mon,
-                                            virHashTablePtr stats);
+                                            virHashTablePtr stats,
+                                            bool backingChain);
 int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
                                              int *nparams);
 int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
-- 
2.2.0