The mainly changes are:
1) Update qemuMonitorGetBlockStatsInfo and it's children (Text/JSON)
functions to return the value of new latency fields.
2) Add new function qemuMonitorGetBlockStatsParamsNumber, which is
to count how many parameters the underlying QEMU supports.
3) Update virDomainBlockStats in src/qemu/qemu_driver.c to be
compatible with the changes by 1).
---
src/qemu/qemu_driver.c | 4 ++
src/qemu/qemu_monitor.c | 35 ++++++++++++
src/qemu/qemu_monitor.h | 6 ++
src/qemu/qemu_monitor_json.c | 124 +++++++++++++++++++++++++++++++++++++++++-
src/qemu/qemu_monitor_json.h | 6 ++
src/qemu/qemu_monitor_text.c | 121 +++++++++++++++++++++++++++++++++++++----
src/qemu/qemu_monitor_text.h | 6 ++
7 files changed, 291 insertions(+), 11 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 7028d72..c5809d2 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7224,8 +7224,12 @@ qemudDomainBlockStats (virDomainPtr dom,
disk->info.alias,
&stats->rd_req,
&stats->rd_bytes,
+ NULL,
&stats->wr_req,
&stats->wr_bytes,
+ NULL,
+ NULL,
+ NULL,
&stats->errs);
qemuDomainObjExitMonitor(driver, vm);
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index db6107c..92631ae 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1201,8 +1201,12 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
const char *devname,
long long *rd_req,
long long *rd_bytes,
+ long long *rd_total_times,
long long *wr_req,
long long *wr_bytes,
+ long long *wr_total_times,
+ long long *flush_req,
+ long long *flush_total_times,
long long *errs)
{
int ret;
@@ -1217,16 +1221,47 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
if (mon->json)
ret = qemuMonitorJSONGetBlockStatsInfo(mon, devname,
rd_req, rd_bytes,
+ rd_total_times,
wr_req, wr_bytes,
+ wr_total_times,
+ flush_req,
+ flush_total_times,
errs);
else
ret = qemuMonitorTextGetBlockStatsInfo(mon, devname,
rd_req, rd_bytes,
+ rd_total_times,
wr_req, wr_bytes,
+ wr_total_times,
+ flush_req,
+ flush_total_times,
errs);
return ret;
}
+/* Return 0 and update @nparams with the number of block stats
+ * QEMU supports if success. Return -1 if failure.
+ */
+int qemuMonitorGetBlockStatsParamsNumber(qemuMonitorPtr mon,
+ int *nparams)
+{
+ int ret;
+ VIR_DEBUG("mon=%p nparams=%p", mon, nparams);
+
+ if (!mon) {
+ qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("monitor must not be NULL"));
+ return -1;
+ }
+
+ if (mon->json)
+ ret = qemuMonitorJSONGetBlockStatsParamsNumber(mon, nparams);
+ else
+ ret = qemuMonitorTextGetBlockStatsParamsNumber(mon, nparams);
+
+ return ret;
+}
+
int qemuMonitorGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
unsigned long long *extent)
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index f241c9e..1b9d98d 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -212,9 +212,15 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
const char *devname,
long long *rd_req,
long long *rd_bytes,
+ long long *rd_total_times,
long long *wr_req,
long long *wr_bytes,
+ long long *wr_total_times,
+ long long *flush_req,
+ long long *flush_total_times,
long long *errs);
+int qemuMonitorGetBlockStatsParamsNumber(qemuMonitorPtr mon,
+ int *nparams);
int qemuMonitorGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 4ceb536..0cc4702 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1318,8 +1318,12 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
const char *devname,
long long *rd_req,
long long *rd_bytes,
+ long long *rd_total_times,
long long *wr_req,
long long *wr_bytes,
+ long long *wr_total_times,
+ long long *flush_req,
+ long long *flush_total_times,
long long *errs)
{
int ret;
@@ -1330,7 +1334,17 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
virJSONValuePtr reply = NULL;
virJSONValuePtr devices;
- *rd_req = *rd_bytes = *wr_req = *wr_bytes = *errs = 0;
+ *rd_req = *rd_bytes = -1;
+ *wr_req = *wr_bytes = *errs = -1;
+
+ if (rd_total_times)
+ *rd_total_times = -1;
+ if (wr_total_times)
+ *wr_total_times = -1;
+ if (flush_req)
+ *flush_req = -1;
+ if (flush_total_times)
+ *flush_total_times = -1;
if (!cmd)
return -1;
@@ -1396,6 +1410,15 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
"rd_operations");
goto cleanup;
}
+ if (rd_total_times &&
+ virJSONValueObjectHasKey(stats, "rd_total_times_ns") &&
+ (virJSONValueObjectGetNumberLong(stats, "rd_total_times_ns",
+ rd_total_times) < 0)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot read %s statistic"),
+ "rd_total_times_ns");
+ goto cleanup;
+ }
if (virJSONValueObjectGetNumberLong(stats, "wr_bytes", wr_bytes) <
0) {
qemuReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
@@ -1408,6 +1431,33 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
"wr_operations");
goto cleanup;
}
+ if (wr_total_times &&
+ virJSONValueObjectHasKey(stats, "wr_total_times_ns") &&
+ (virJSONValueObjectGetNumberLong(stats, "wr_total_times_ns",
+ wr_total_times) < 0)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot read %s statistic"),
+ "wr_total_times_ns");
+ goto cleanup;
+ }
+ if (flush_req &&
+ virJSONValueObjectHasKey(stats, "flush_operations") &&
+ (virJSONValueObjectGetNumberLong(stats, "flush_operations",
+ flush_req) < 0)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot read %s statistic"),
+ "flush_operations");
+ goto cleanup;
+ }
+ if (flush_total_times &&
+ virJSONValueObjectHasKey(stats, "flush_total_times_ns") &&
+ (virJSONValueObjectGetNumberLong(stats, "flush_total_times_ns",
+ flush_total_times) < 0)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot read %s statistic"),
+ "flush_total_times_ns");
+ goto cleanup;
+ }
}
if (!found) {
@@ -1424,6 +1474,78 @@ cleanup:
}
+int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
+ int *nparams)
+{
+ int ret, i, num = 0;
+ virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
+ NULL);
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr devices = NULL;
+ virJSONValuePtr dev = NULL;
+ virJSONValuePtr stats = NULL;
+
+ if (!cmd)
+ 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) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("blockstats reply was missing device list"));
+ goto cleanup;
+ }
+
+ dev = virJSONValueArrayGet(devices, 0);
+
+ if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
+ qemuReportError(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) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("blockstats stats entry was not in expected
format"));
+ goto cleanup;
+ }
+
+ for (i = 0 ; i < stats->data.object.npairs; i++) {
+ const char *key = stats->data.object.pairs[i].key;
+
+ if (STREQ(key, "rd_bytes") ||
+ STREQ(key, "rd_operations") ||
+ STREQ(key, "rd_total_times_ns") ||
+ STREQ(key, "wr_bytes") ||
+ STREQ(key, "wr_operations") ||
+ STREQ(key, "wr_total_times_ns") ||
+ STREQ(key, "flush_operations") ||
+ STREQ(key, "flush_total_times_ns")) {
+ num++;
+ } else {
+ /* wr_highest_offset is parsed by qemuMonitorJSONGetBlockExtent. */
+ if (STRNEQ(key, "wr_highest_offset"))
+ VIR_DEBUG("Missed block stat: %s", key);
+ }
+ }
+
+ *nparams = num;
+ ret = 0;
+
+cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
unsigned long long *extent)
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 9512793..ec9fecb 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -64,9 +64,15 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
const char *devname,
long long *rd_req,
long long *rd_bytes,
+ long long *rd_total_times,
long long *wr_req,
long long *wr_bytes,
+ long long *wr_total_times,
+ long long *flush_req,
+ long long *flush_total_times,
long long *errs);
+int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
+ int *nparams);
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
unsigned long long *extent);
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 854ee7f..9bbb8b0 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -708,8 +708,12 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
const char *devname,
long long *rd_req,
long long *rd_bytes,
+ long long *rd_total_times,
long long *wr_req,
long long *wr_bytes,
+ long long *wr_total_times,
+ long long *flush_req,
+ long long *flush_total_times,
long long *errs)
{
char *info = NULL;
@@ -736,11 +740,17 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
goto cleanup;
}
- *rd_req = -1;
- *rd_bytes = -1;
- *wr_req = -1;
- *wr_bytes = -1;
- *errs = -1;
+ *rd_req = *rd_bytes = -1;
+ *wr_req = *wr_bytes = *errs = -1;
+
+ if (rd_total_times)
+ *rd_total_times = -1;
+ if (wr_total_times)
+ *wr_total_times = -1;
+ if (flush_req)
+ *flush_req = -1;
+ if (flush_total_times)
+ *flush_total_times = -1;
/* The output format for both qemu & KVM is:
* blockdevice: rd_bytes=% wr_bytes=% rd_operations=% wr_operations=%
@@ -768,23 +778,44 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
while (*p) {
if (STRPREFIX (p, "rd_bytes=")) {
- p += 9;
+ p += strlen("rd_bytes=");
if (virStrToLong_ll (p, &dummy, 10, rd_bytes) == -1)
VIR_DEBUG ("error reading rd_bytes: %s", p);
} else if (STRPREFIX (p, "wr_bytes=")) {
- p += 9;
+ p += strlen("wr_bytes=");
if (virStrToLong_ll (p, &dummy, 10, wr_bytes) == -1)
VIR_DEBUG ("error reading wr_bytes: %s", p);
} else if (STRPREFIX (p, "rd_operations=")) {
- p += 14;
+ p += strlen("rd_operations=");
if (virStrToLong_ll (p, &dummy, 10, rd_req) == -1)
VIR_DEBUG ("error reading rd_req: %s", p);
} else if (STRPREFIX (p, "wr_operations=")) {
- p += 14;
+ p += strlen("wr_operations=");
if (virStrToLong_ll (p, &dummy, 10, wr_req) == -1)
VIR_DEBUG ("error reading wr_req: %s", p);
- } else
+ } else if (rd_total_times &&
+ STRPREFIX (p, "rd_total_times_ns=")) {
+ p += strlen("rd_total_times_ns=");
+ if (virStrToLong_ll (p, &dummy, 10, rd_total_times) == -1)
+ VIR_DEBUG ("error reading rd_total_times: %s", p);
+ } else if (wr_total_times &&
+ STRPREFIX (p, "wr_total_times_ns=")) {
+ p += strlen("wr_total_times_ns=");
+ if (virStrToLong_ll (p, &dummy, 10, wr_total_times) == -1)
+ VIR_DEBUG ("error reading wr_total_times: %s", p);
+ } else if (flush_req &&
+ STRPREFIX (p, "flush_operations=")) {
+ p += strlen("flush_operations=");
+ if (virStrToLong_ll (p, &dummy, 10, flush_req) == -1)
+ VIR_DEBUG ("error reading flush_req: %s", p);
+ } else if (flush_total_times &&
+ STRPREFIX (p, "flush_total_times_ns=")) {
+ p += strlen("flush_total_times_ns=");
+ if (virStrToLong_ll (p, &dummy, 10, flush_total_times) == -1)
+ VIR_DEBUG ("error reading flush_total_times: %s", p);
+ } else {
VIR_DEBUG ("unknown block stat near %s", p);
+ }
/* Skip to next label. */
p = strchr (p, ' ');
@@ -810,6 +841,76 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
return ret;
}
+int qemuMonitorTextGetBlockStatsParamsNumber(qemuMonitorPtr mon,
+ int *nparams)
+{
+ char *info = NULL;
+ int ret = -1;
+ int num = 0;
+ const char *p, *eol;
+
+ if (qemuMonitorHMPCommand (mon, "info blockstats", &info) < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("'info blockstats' command
failed"));
+ goto cleanup;
+ }
+
+ /* If the command isn't supported then qemu prints the supported
+ * info commands, so the output starts "info ". Since this is
+ * unlikely to be the name of a block device, we can use this
+ * to detect if qemu supports the command.
+ */
+ if (strstr(info, "\ninfo ")) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s",
+ _("'info blockstats' not supported by this
qemu"));
+ goto cleanup;
+ }
+
+ /* The output format for both qemu & KVM is:
+ * blockdevice: rd_bytes=% wr_bytes=% rd_operations=% wr_operations=%
+ * (repeated for each block device)
+ * where '%' is a 64 bit number.
+ */
+ p = info;
+
+ eol = strchr (p, '\n');
+ if (!eol)
+ eol = p + strlen (p);
+
+ /* Skip the device name and following ":", and spaces (e.g.
+ * "floppy0: ")
+ */
+ p = strchr(p, ' ');
+ p++;
+
+ while (*p) {
+ if (STRPREFIX (p, "rd_bytes=") ||
+ STRPREFIX (p, "wr_bytes=") ||
+ STRPREFIX (p, "rd_operations=") ||
+ STRPREFIX (p, "wr_operations=") ||
+ STRPREFIX (p, "rd_total_times_ns=") ||
+ STRPREFIX (p, "wr_total_times_ns=") ||
+ STRPREFIX (p, "flush_operations=") ||
+ STRPREFIX (p, "flush_total_times_ns=")) {
+ num++;
+ } else {
+ VIR_DEBUG ("unknown block stat near %s", p);
+ }
+
+ /* Skip to next label. */
+ p = strchr (p, ' ');
+ if (!p || p >= eol) break;
+ p++;
+ }
+
+ *nparams = num;
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(info);
+ return ret;
+}
int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
const char *devname ATTRIBUTE_UNUSED,
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index b250738..de6fbcc 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -61,9 +61,15 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
const char *devname,
long long *rd_req,
long long *rd_bytes,
+ long long *rd_total_times,
long long *wr_req,
long long *wr_bytes,
+ long long *wr_total_times,
+ long long *flush_req,
+ long long *flush_total_times,
long long *errs);
+int qemuMonitorTextGetBlockStatsParamsNumber(qemuMonitorPtr mon,
+ int *nparams);
int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
unsigned long long *extent);
--
1.7.6