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 f21122d..03fadae 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6823,8 +6823,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 715b26e..77a2545 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 4bd761d..fab5e19 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list