]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
qemu: monitor: Extract block latency histogram stats into 'qemuBlockStats'
authorPeter Krempa <pkrempa@redhat.com>
Thu, 29 Jan 2026 13:08:18 +0000 (14:08 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Mon, 16 Feb 2026 09:24:09 +0000 (10:24 +0100)
Extract the 'rd_latency_histogram', 'wr_latency_histogram',
'zone_append_latency_histogram', and 'flush_latency_histogram' stats
objects into our internal data.

Rather than storing 'boundaries' between bins we store them as start
points.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
src/qemu/qemu_monitor.c
src/qemu/qemu_monitor.h
src/qemu/qemu_monitor_json.c

index 1434da7f70f39847f24330c881b2ad2a62035690..24990372ddde9147c11cf2533b740daa04696439 100644 (file)
@@ -1943,6 +1943,26 @@ qemuBlockStatsFinalize(GObject *object)
     g_free(stats->limits);
     g_free(stats->timed_stats);
 
+    if (stats->histogram_read) {
+        g_free(stats->histogram_read->bins);
+        g_free(stats->histogram_read);
+    }
+
+    if (stats->histogram_write) {
+        g_free(stats->histogram_write->bins);
+        g_free(stats->histogram_write);
+    }
+
+    if (stats->histogram_zone) {
+        g_free(stats->histogram_zone->bins);
+        g_free(stats->histogram_zone);
+    }
+
+    if (stats->histogram_flush) {
+        g_free(stats->histogram_flush->bins);
+        g_free(stats->histogram_flush);
+    }
+
     G_OBJECT_CLASS(qemu_block_stats_parent_class)->finalize(object);
 }
 
index a35dbe560f45875d7dc95931540a816923ee3b9b..88a908c7822d52afd8a42691d5defc7f44446c0b 100644 (file)
@@ -803,6 +803,18 @@ struct qemuBlockStatsLimits {
 };
 
 
+struct qemuBlockStatsLatencyHistogramBin {
+    unsigned long long start;
+    unsigned long long value;
+};
+
+
+struct qemuBlockStatsLatencyHistogram {
+    struct qemuBlockStatsLatencyHistogramBin *bins;
+    size_t nbins;
+};
+
+
 struct qemuBlockStatsTimed {
     unsigned long long interval_length;
 
@@ -855,6 +867,12 @@ struct _qemuBlockStats {
     /* block accounting/timed stats from qemu - one entry per interval configured */
     size_t n_timed_stats;
     struct qemuBlockStatsTimed *timed_stats;
+
+    /* latency histograms */
+    struct qemuBlockStatsLatencyHistogram *histogram_read;
+    struct qemuBlockStatsLatencyHistogram *histogram_write;
+    struct qemuBlockStatsLatencyHistogram *histogram_zone;
+    struct qemuBlockStatsLatencyHistogram *histogram_flush;
 };
 G_DECLARE_FINAL_TYPE(qemuBlockStats, qemu_block_stats, QEMU, BLOCK_STATS, GObject);
 
index 971e8dd63fcfd39580625e387a797f89c9abfc8c..0bb1ca7957c535f6389e86a044555941daa6d617 100644 (file)
@@ -2462,6 +2462,52 @@ qemuMonitorJSONBlockStatsCollectDataTimed(virJSONValue *timed_stats,
 }
 
 
+static void
+qemuMonitorJSONBlockStatsCollectDataLatencyHistogram(virJSONValue *stats,
+                                                     const char *histogram_field,
+                                                     struct qemuBlockStatsLatencyHistogram **histogram_data)
+{
+    virJSONValue *hist;
+    virJSONValue *hist_bins;
+    virJSONValue *hist_bounds;
+    g_autofree struct qemuBlockStatsLatencyHistogramBin *bins = NULL;
+    size_t nbins = 0;
+    size_t i;
+
+    if (!(hist = virJSONValueObjectGetObject(stats, histogram_field)))
+        return;
+
+    if (!(hist_bins = virJSONValueObjectGetArray(hist, "bins")) ||
+        !(hist_bounds = virJSONValueObjectGetArray(hist, "boundaries")) ||
+        virJSONValueArraySize(hist_bins) != (virJSONValueArraySize(hist_bounds) + 1)) {
+        VIR_DEBUG("malformed latency histogram container");
+        return;
+    }
+
+    nbins = virJSONValueArraySize(hist_bins);
+    bins = g_new0(struct qemuBlockStatsLatencyHistogramBin, nbins);
+
+    for (i = 0; i < nbins; i++) {
+        virJSONValue *bin = virJSONValueArrayGet(hist_bins, i);
+        virJSONValue *bound = NULL;
+
+        if (i > 0)
+            bound = virJSONValueArrayGet(hist_bounds, i - 1);
+
+        if (!bin ||
+            virJSONValueGetNumberUlong(bin, &(bins[i].value)) < 0 ||
+            (bound && virJSONValueGetNumberUlong(bound, &(bins[i].start)) < 0)) {
+            VIR_DEBUG("malformed latency histogram container");
+            return;
+        }
+    }
+
+    *histogram_data = g_new0(struct qemuBlockStatsLatencyHistogram, 1);
+    (*histogram_data)->bins = g_steal_pointer(&bins);
+    (*histogram_data)->nbins = nbins;
+}
+
+
 static qemuBlockStats *
 qemuMonitorJSONBlockStatsCollectData(virJSONValue *dev,
                                      int *nstats)
@@ -2506,6 +2552,15 @@ qemuMonitorJSONBlockStatsCollectData(virJSONValue *dev,
             bstats->wr_highest_offset_valid = true;
     }
 
+    qemuMonitorJSONBlockStatsCollectDataLatencyHistogram(stats, "rd_latency_histogram",
+                                                         &bstats->histogram_read);
+    qemuMonitorJSONBlockStatsCollectDataLatencyHistogram(stats, "wr_latency_histogram",
+                                                         &bstats->histogram_write);
+    qemuMonitorJSONBlockStatsCollectDataLatencyHistogram(stats, "zone_append_latency_histogram",
+                                                         &bstats->histogram_zone);
+    qemuMonitorJSONBlockStatsCollectDataLatencyHistogram(stats, "flush_latency_histogram",
+                                                         &bstats->histogram_flush);
+
     if ((timed_stats = virJSONValueObjectGetArray(stats, "timed_stats")) &&
         virJSONValueArraySize(timed_stats) > 0)
         qemuMonitorJSONBlockStatsCollectDataTimed(timed_stats, bstats);