[libvirt] [PATCH 1/2] qemu: implement block group for bulk stats.

Li Wei lw at cn.fujitsu.com
Fri Aug 29 07:56:02 UTC 2014


This patch add the block group for bulk stats.
The following typed parameter used for each block stats:
    block.count - number of block devices in this domain
    block.0.name - name of the block device
    block.0.rd_bytes - number of read bytes
    block.0.rd_operations - number of read requests
    block.0.rd_total_time -  total time spend on cache reads in nano-seconds
    block.0.wr_bytes - number of write bytes
    block.0.wr_operations - number of write requests
    block.0.wr_total_time - total time spend on cache write in nano-seconds
    block.0.flush_operations - total flush requests
    block.0.flush_total_time - total time spend on cache flushing in nano-seconds

Signed-off-by: Li Wei <lw at cn.fujitsu.com>
---
 include/libvirt/libvirt.h.in |   1 +
 src/libvirt.c                |  13 ++++
 src/qemu/qemu_driver.c       |  31 ++++++++
 src/qemu/qemu_monitor.c      |  23 ++++++
 src/qemu/qemu_monitor.h      |   5 ++
 src/qemu/qemu_monitor_json.c | 170 +++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_monitor_json.h |   5 ++
 7 files changed, 248 insertions(+)

diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 9358314..36c4fec 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2511,6 +2511,7 @@ struct _virDomainStatsRecord {
 
 typedef enum {
     VIR_DOMAIN_STATS_STATE = (1 << 0), /* return domain state */
+    VIR_DOMAIN_STATS_BLOCK = (1 << 1), /* return block stats */
 } virDomainStatsTypes;
 
 typedef enum {
diff --git a/src/libvirt.c b/src/libvirt.c
index 5d8f01c..ca0d071 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -21632,6 +21632,19 @@ virConnectGetAllDomainStats(virConnectPtr conn,
  * "state.reason" - reason for entering given state, returned as int from
  *                  virDomain*Reason enum corresponding to given state.
  *
+ * VIR_DOMAIN_STATS_BLOCK: Return block device stats. The typed parameter keys
+ * are in this format:
+ * "block.count" - number of block devices in this domain
+ * "block.0.name" - name of the block device
+ * "block.0.rd_bytes" - number of read bytes
+ * "block.0.rd_operations" - number of read requests
+ * "block.0.rd_total_time" -  total time spend on cache reads in nano-seconds
+ * "block.0.wr_bytes" - number of write bytes
+ * "block.0.wr_operations" - number of write requests
+ * "block.0.wr_total_time" - total time spend on cache write in nano-seconds
+ * "block.0.flush_operations" - total flush requests
+ * "block.0.flush_total_time" - total time spend on cache flushing in nano-seconds
+ *
  * Using 0 for @stats returns all stats groups supported by the given
  * hypervisor.
  *
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 239a300..ef4d3be 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -17221,6 +17221,36 @@ qemuDomainGetStatsState(virDomainObjPtr dom,
 }
 
 
+static int
+qemuDomainGetStatsBlock(virDomainObjPtr dom,
+                        virDomainStatsRecordPtr record,
+                        int *maxparams,
+                        unsigned int flags)
+{
+    int ret;
+    qemuDomainObjPrivatePtr priv = dom->privateData;
+
+    /* only valid for active domain, ignore inactive ones silently */
+    if (!virDomainObjIsActive(dom))
+        return 0;
+
+    if (qemuDomainObjBeginJob(qemu_driver, dom, QEMU_JOB_QUERY) < 0)
+        return -1;
+
+    qemuDomainObjEnterMonitor(qemu_driver, dom);
+    ret = qemuMonitorDomainGetStatsBlock(priv->mon,
+                                         record,
+                                         maxparams,
+                                         flags);
+    qemuDomainObjExitMonitor(qemu_driver, dom);
+
+    if (qemuDomainObjEndJob(qemu_driver, dom) < 0)
+        return -1;
+
+    return ret;
+}
+
+
 typedef int
 (*qemuDomainGetStatsFunc)(virDomainObjPtr dom,
                           virDomainStatsRecordPtr record,
@@ -17234,6 +17264,7 @@ struct qemuDomainGetStatsWorker {
 
 static struct qemuDomainGetStatsWorker qemuDomainGetStatsWorkers[] = {
     { qemuDomainGetStatsState, VIR_DOMAIN_STATS_STATE},
+    { qemuDomainGetStatsBlock, VIR_DOMAIN_STATS_BLOCK},
     { NULL, 0 }
 };
 
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 5b2952a..83d1dc3 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -4071,3 +4071,26 @@ qemuMonitorRTCResetReinjection(qemuMonitorPtr mon)
 
     return qemuMonitorJSONRTCResetReinjection(mon);
 }
+
+int
+qemuMonitorDomainGetStatsBlock(qemuMonitorPtr mon,
+                               virDomainStatsRecordPtr record,
+                               int *maxparams,
+                               unsigned int flags)
+{
+    VIR_DEBUG("mon=%p", mon);
+
+    if (!mon) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("monitor must not be NULL"));
+        return -1;
+    }
+
+    if (!mon->json) {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+                       _("JSON monitor is required"));
+        return -1;
+    }
+
+    return qemuMonitorJSONDomainGetStatsBlock(mon, record, maxparams, flags);
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 4fd6f01..6f77ecc 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -792,6 +792,11 @@ int qemuMonitorGetGuestCPU(qemuMonitorPtr mon,
 
 int qemuMonitorRTCResetReinjection(qemuMonitorPtr mon);
 
+int
+qemuMonitorDomainGetStatsBlock(qemuMonitorPtr mon,
+                               virDomainStatsRecordPtr record,
+                               int *maxparams,
+                               unsigned int flags);
 /**
  * When running two dd process and using <> redirection, we need a
  * shell that will not truncate files.  These two strings serve that
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 62e7d5d..282635a 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -5872,3 +5872,173 @@ qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon)
     virJSONValueFree(reply);
     return ret;
 }
+
+int
+qemuMonitorJSONDomainGetStatsBlock(qemuMonitorPtr mon,
+                               virDomainStatsRecordPtr record,
+                               int *maxparams,
+                               unsigned int privflags ATTRIBUTE_UNUSED)
+{
+    int ret, i, nblocks = 0;
+    virJSONValuePtr cmd, devices;
+    virJSONValuePtr reply = NULL;
+    char field[VIR_TYPED_PARAM_FIELD_LENGTH];
+
+    if (!(cmd = qemuMonitorJSONMakeCommand("query-blockstats", NULL)))
+        return -1;
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+    if (ret < 0)
+        goto cleanup;
+
+    ret = -1;
+
+    devices = virJSONValueObjectGet(reply, "return");
+    if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("blockstats reply was missing device list"));
+        goto cleanup;
+    }
+
+    if (virTypedParamsAddInt(&record->params,
+                               &record->nparams,
+                               maxparams,
+                               "block.count",
+                               virJSONValueArraySize(devices)) < 0)
+        goto cleanup;
+
+    for (i = 0; i < virJSONValueArraySize(devices); i++) {
+        virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
+        virJSONValuePtr stats;
+        long long int llvalue;
+        const char *block_name;
+
+        if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("blockstats device entry was not in expected format"));
+            goto cleanup;
+        }
+
+        if ((block_name = virJSONValueObjectGetString(dev, "device")) == NULL) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("blockstats device entry was not in expected format"));
+            goto cleanup;
+        }
+
+        if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
+            stats->type != VIR_JSON_TYPE_OBJECT) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("blockstats device entry was not in expected format"));
+            goto cleanup;
+        }
+
+        snprintf(field, VIR_TYPED_PARAM_FIELD_LENGTH, "block.%d.name", nblocks);
+        if (virTypedParamsAddString(&record->params,
+                                    &record->nparams,
+                                    maxparams,
+                                    field,
+                                    block_name) < 0)
+            goto cleanup;
+
+        if (virJSONValueObjectGetNumberLong(stats, "rd_bytes", &llvalue) < 0)
+            goto cleanup;
+
+        snprintf(field, VIR_TYPED_PARAM_FIELD_LENGTH, "block.%d.rd_bytes", nblocks);
+        if (virTypedParamsAddLLong(&record->params,
+                                   &record->nparams,
+                                   maxparams,
+                                   field,
+                                   llvalue) < 0)
+            goto cleanup;
+
+        if (virJSONValueObjectGetNumberLong(stats, "rd_operations", &llvalue) < 0)
+            goto cleanup;
+
+        snprintf(field, VIR_TYPED_PARAM_FIELD_LENGTH, "block.%d.rd_operations", nblocks);
+        if (virTypedParamsAddLLong(&record->params,
+                                   &record->nparams,
+                                   maxparams,
+                                   field,
+                                   llvalue) < 0)
+            goto cleanup;
+
+        if (virJSONValueObjectGetNumberLong(stats, "rd_total_time_ns", &llvalue) < 0)
+            goto cleanup;
+
+        snprintf(field, VIR_TYPED_PARAM_FIELD_LENGTH, "block.%d.rd_total_time", nblocks);
+        if (virTypedParamsAddLLong(&record->params,
+                                   &record->nparams,
+                                   maxparams,
+                                   field,
+                                   llvalue) < 0)
+            goto cleanup;
+
+        if (virJSONValueObjectGetNumberLong(stats, "wr_bytes", &llvalue) < 0)
+            goto cleanup;
+
+        snprintf(field, VIR_TYPED_PARAM_FIELD_LENGTH, "block.%d.wr_bytes", nblocks);
+        if (virTypedParamsAddLLong(&record->params,
+                                   &record->nparams,
+                                   maxparams,
+                                   field,
+                                   llvalue) < 0)
+            goto cleanup;
+
+        if (virJSONValueObjectGetNumberLong(stats, "wr_operations", &llvalue) < 0)
+            goto cleanup;
+
+        snprintf(field, VIR_TYPED_PARAM_FIELD_LENGTH, "block.%d.wr_operations", nblocks);
+        if (virTypedParamsAddLLong(&record->params,
+                                   &record->nparams,
+                                   maxparams,
+                                   field,
+                                   llvalue) < 0)
+            goto cleanup;
+
+        if (virJSONValueObjectGetNumberLong(stats, "wr_total_time_ns", &llvalue) < 0)
+            goto cleanup;
+
+        snprintf(field, VIR_TYPED_PARAM_FIELD_LENGTH, "block.%d.wr_total_time", nblocks);
+        if (virTypedParamsAddLLong(&record->params,
+                                   &record->nparams,
+                                   maxparams,
+                                   field,
+                                   llvalue) < 0)
+            goto cleanup;
+
+        if (virJSONValueObjectGetNumberLong(stats, "flush_operations", &llvalue) < 0)
+            goto cleanup;
+
+        snprintf(field, VIR_TYPED_PARAM_FIELD_LENGTH, "block.%d.flush_operations", nblocks);
+        if (virTypedParamsAddLLong(&record->params,
+                                   &record->nparams,
+                                   maxparams,
+                                   field,
+                                   llvalue) < 0)
+            goto cleanup;
+
+        if (virJSONValueObjectGetNumberLong(stats, "flush_total_time_ns", &llvalue) < 0)
+            goto cleanup;
+
+        snprintf(field, VIR_TYPED_PARAM_FIELD_LENGTH, "block.%d.flush_total_time", nblocks);
+        if (virTypedParamsAddLLong(&record->params,
+                                   &record->nparams,
+                                   maxparams,
+                                   field,
+                                   llvalue) < 0)
+            goto cleanup;
+
+        nblocks++;
+    }
+
+    ret = 0;
+
+ cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+
+    return ret;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index d8c9308..a763c82 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -439,4 +439,9 @@ int qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon,
                                virCPUDataPtr *data);
 
 int qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon);
+
+int qemuMonitorJSONDomainGetStatsBlock(qemuMonitorPtr mon,
+                                       virDomainStatsRecordPtr record,
+                                       int *maxparams,
+                                       unsigned int flags);
 #endif /* QEMU_MONITOR_JSON_H */
-- 
1.9.3




More information about the libvir-list mailing list