Re: [PATCH] api,qemu: add block latency histogram

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



04.09.2018 09:59, Nikolay Shirokovskiy wrote:
Hi, Peter. I have questions to several of your comments:

On 03.09.2018 14:59, Peter Krempa wrote:
On Mon, Sep 03, 2018 at 13:58:31 +0300, Nikolay Shirokovskiy wrote:
This patch adds option to configure/read latency histogram of
block device IO operations. The corresponding functionality
in qemu still has x- prefix AFAIK. So this patch should
help qemu functionality to become non experimental.
This can be used as a proof of concept for this but commiting this
feature will be possible only when qemu removes the experimental prefix.

Until then they are free to change the API and that would result in
multiple implementations in libvirt or they can even drop it.

In short histogram is configured thru new API virDomainSetBlockLatencyHistogram and
histogram itself is available as new fields of virConnectGetAllDomainStats
output.

...

  static int
+qemuDomainGetBlockLatency(virDomainStatsRecordPtr record,
+                          int *maxparams,
+                          size_t block_idx,
+                          const char *op,
+                          qemuBlockLatencyStatsPtr latency)
+{
+    char param_name[VIR_TYPED_PARAM_FIELD_LENGTH];
+    size_t i;
+    int ret = -1;
+
+    if (!latency->nbins)
+        return 0;
+
+    snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH,
+             "block.%zu.latency.%s.bincount", block_idx, op);
+    if (virTypedParamsAddUInt(&record->params, &record->nparams, maxparams,
+                              param_name, latency->nbins) < 0)
+        goto cleanup;
+
+    for (i = 0; i < latency->nbins; i++) {
+        snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH,
+                 "block.%zu.latency.%s.bin.%zu", block_idx, op, i);
+        if (virTypedParamsAddULLong(&record->params, &record->nparams, maxparams,
+                                    param_name, latency->bins[i]) < 0)
+            goto cleanup;
+    }
+
+    for (i = 0; i < latency->nbins - 1; i++) {
+        snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH,
+                 "block.%zu.latency.%s.boundary.%zu", block_idx, op, i);
+        if (virTypedParamsAddULLong(&record->params, &record->nparams, maxparams,
+                                    param_name, latency->boundaries[i]) < 0)
+            goto cleanup;
The two loops can be merged, so that the bin and value are together.
These loops counts differ by 1. I can either add check inside loop to skip
last iteration for boundaries or add record for last bin outside of the loop.
What is preferable?

...

+
+static
+int qemuDomainSetBlockLatencyHistogram(virDomainPtr dom,
+                                       const char *dev,
+                                       unsigned int op,
+                                       unsigned long long *boundaries,
+                                       int nboundaries,
+                                       unsigned int flags)
The setting and getting impl should be separated.

This API is only for setting bonudaries. After setting boundaries
are available in output of virConnectGetAllDomainStats. Example output:

virsh block-set-latency-histogram vm sda "10000, 50000"
virsh domstats vm | grep latency
   block.0.latency.rd.bincount=3
   block.0.latency.rd.bin.0=0
   block.0.latency.rd.bin.1=0
   block.0.latency.rd.bin.2=0
   block.0.latency.rd.boundary.0=10000
   block.0.latency.rd.boundary.1=50000
   block.0.latency.wr.bincount=3
   block.0.latency.wr.bin.0=0
   block.0.latency.wr.bin.1=0
   block.0.latency.wr.bin.2=0
   block.0.latency.wr.boundary.0=10000
   block.0.latency.wr.boundary.1=50000
   block.0.latency.fl.bincount=3
   block.0.latency.fl.bin.0=0
   block.0.latency.fl.bin.1=0
   block.0.latency.fl.bin.2=0
   block.0.latency.fl.boundary.0=10000
   block.0.latency.fl.boundary.1=50000
...


diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 48b142a..9295ecb 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -569,6 +569,14 @@ virHashTablePtr qemuMonitorGetBlockInfo(qemuMonitorPtr mon);
virJSONValuePtr qemuMonitorQueryBlockstats(qemuMonitorPtr mon); +typedef struct _qemuBlockLatencyStats qemuBlockLatencyStats;
+typedef qemuBlockLatencyStats *qemuBlockLatencyStatsPtr;
+struct _qemuBlockLatencyStats {
+    unsigned long long *boundaries;
+    unsigned long long *bins;
Since they are connected, you should use an array of structs containing
both, so that they can't be interpreted otherwise.
Unfortunately these arrays sizes differ by 1. nboundaries = nbins + 1.
That's why I don't introduce another structure here, it would be inconvinient.

+    unsigned int nbins;
+};
+
...

+static int
+qemuMonitorJSONGetBlockLatencyStats(virJSONValuePtr stats,
+                                    const char *name,
+                                    qemuBlockLatencyStatsPtr latency)
+{
+    virJSONValuePtr latencyJSON;
+    virJSONValuePtr bins;
+    virJSONValuePtr boundaries;
+    size_t i;
+
+    if (!(latencyJSON = virJSONValueObjectGetObject(stats, name)))
+        return 0;
+
+    if (!(bins = virJSONValueObjectGetArray(latencyJSON, "bins"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read bins in latency %s"), name);
+        return -1;
+    }
+
+    if (!(boundaries = virJSONValueObjectGetArray(latencyJSON, "boundaries"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("cannot read boundaries in latency %s"), name);
+        return -1;
+    }
+
+    if (virJSONValueArraySize(bins) != virJSONValueArraySize(boundaries) + 1) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("bins and boundaries size mismatch in latency %s"), name);
+        return -1;
Maybe the qemu API can be improved to return an array of objects rather
than two separate arrays?
Volodya?

Hmm, and what kind of objects?

something like

{
   .prev_bound
   .bin
}

?

Isn't it more weird than two different arrays?



+    }
+    latency->nbins = virJSONValueArraySize(bins);
+
+    if (VIR_ALLOC_N(latency->boundaries, latency->nbins - 1) < 0 ||
+        VIR_ALLOC_N(latency->bins, latency->nbins) < 0)
+        return -1;
+
+    for (i = 0; i < latency->nbins; i++) {
+        if (virJSONValueGetNumberUlong(virJSONValueArrayGet(bins, i),
+                                       &latency->bins[i]) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("invalid bins in latency %s"), name);
+            return -1;
+        }
+    }
+
+    for (i = 0; i < latency->nbins - 1; i++) {
+        if (virJSONValueGetNumberUlong(virJSONValueArrayGet(boundaries, i),
+                                       &latency->boundaries[i]) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("invalid boundaries in latency %s"), name);
+            return -1;
+        }
One loop ought to be enough.

+    }
+
+    return 0;
+}
+



--
Best regards,
Vladimir

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux