diff --git a/src/libvirt.c b/src/libvirt.c index 245c373bcc135d4b657663ee37256f2b86717bfb..caacc2ebf397ed6d739559fc311fe7e1c3c2269b 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -21656,6 +21656,12 @@ virConnectGetDomainCapabilities(virConnectPtr conn, * unsigned long long. * "block..errors" - Xen only: the 'oo_req' value as * unsigned long long. + * "block..allocation" - offset of the highest written sector + * as unsigned long long. + * "block..capacity" - logical size in bytes of the block device backing + * image as unsigned long long. + * "block..physical" - physical size in bytes of the container of the + * backing image as unsigned long long. * * Note that entire stats groups or individual stat fields may be missing from * the output in case they are not supported by the given hypervisor, are not diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e873d4570824ac4c5a27e9c3eee5a84151108b2d..5cf235b4c8ffa5b5aada894d50dadb01b3e5a68f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17907,6 +17907,19 @@ do { \ goto cleanup; \ } while (0) +#define QEMU_ADD_BLOCK_PARAM_ULL(record, maxparams, num, name, value) \ +do { \ + char param_name[VIR_TYPED_PARAM_FIELD_LENGTH]; \ + snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH, \ + "block.%zu.%s", num, name); \ + if (virTypedParamsAddULLong(&(record)->params, \ + &(record)->nparams, \ + maxparams, \ + param_name, \ + value) < 0) \ + goto cleanup; \ +} while (0) + static int qemuDomainGetStatsBlock(virQEMUDriverPtr driver, virDomainObjPtr dom, @@ -17925,6 +17938,7 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver, qemuDomainObjEnterMonitor(driver, dom); rc = qemuMonitorGetAllBlockStatsInfo(priv->mon, &stats); + ignore_value(qemuMonitorBlockStatsUpdateCapacity(priv->mon, stats)); qemuDomainObjExitMonitor(driver, dom); if (rc < 0) { @@ -17961,6 +17975,17 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver, "fl.reqs", entry->flush_req); QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i, "fl.times", entry->flush_total_times); + + QEMU_ADD_BLOCK_PARAM_ULL(record, maxparams, i, + "allocation", entry->wr_highest_offset); + + if (entry->capacity) + QEMU_ADD_BLOCK_PARAM_ULL(record, maxparams, i, + "capacity", entry->capacity); + if (entry->physical) + QEMU_ADD_BLOCK_PARAM_ULL(record, maxparams, i, + "physical", entry->physical); + } ret = 0; @@ -17972,6 +17997,8 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver, #undef QEMU_ADD_BLOCK_PARAM_LL +#undef QEMU_ADD_BLOCK_PARAM_ULL + #undef QEMU_ADD_NAME_PARAM #undef QEMU_ADD_COUNT_PARAM diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 616d960b8fa409e257a9af57da45a516e7d28de0..f0c0b0af8531b8993b802aa4703ead51d9bd10ff 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -360,6 +360,7 @@ struct _qemuBlockStats { long long flush_total_times; unsigned long long capacity; unsigned long long physical; + unsigned long long wr_highest_offset; }; int qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 40446734d29fd4e9b909c96629cf03b493ab3c28..5f00101386b011d02cc298e8a80e1cb85f0d73b6 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -1782,6 +1782,40 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon, } +typedef enum { + QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK, + QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOPARENT, + QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOSTATS, + QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOOFFSET, +} qemuMonitorBlockExtentError; + + +static int +qemuMonitorJSONDevGetBlockExtent(virJSONValuePtr dev, + unsigned long long *extent) +{ + virJSONValuePtr stats; + virJSONValuePtr parent; + + if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL || + parent->type != VIR_JSON_TYPE_OBJECT) { + return QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOPARENT; + } + + if ((stats = virJSONValueObjectGet(parent, "stats")) == NULL || + stats->type != VIR_JSON_TYPE_OBJECT) { + return QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOSTATS; + } + + if (virJSONValueObjectGetNumberUlong(stats, "wr_highest_offset", + extent) < 0) { + return QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOOFFSET; + } + + return QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK; +} + + int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon, virHashTablePtr *ret_stats) { @@ -1907,6 +1941,9 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon, goto cleanup; } + /* it's ok to not have this information here. Just skip silently. */ + qemuMonitorJSONDevGetBlockExtent(dev, &bstats->wr_highest_offset); + if (virHashAddEntry(hash, dev_name, bstats) < 0) goto cleanup; bstats = NULL; @@ -2076,6 +2113,36 @@ int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon, return ret; } + +static int +qemuMonitorJSONReportBlockExtentError(qemuMonitorBlockExtentError error) +{ + switch (error) { + case QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOPARENT: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("blockstats parent entry was not in " + "expected format")); + break; + + case QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOSTATS: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("blockstats stats entry was not in " + "expected format")); + + case QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOOFFSET: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot read %s statistic"), + "wr_highest_offset"); + break; + + case QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK: + return 0; + } + + return -1; +} + + int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon, const char *dev_name, unsigned long long *extent) @@ -2110,9 +2177,8 @@ int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon, for (i = 0; i < virJSONValueArraySize(devices); i++) { virJSONValuePtr dev = virJSONValueArrayGet(devices, i); - virJSONValuePtr stats; - virJSONValuePtr parent; const char *thisdev; + int err; if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("blockstats device entry was not in expected format")); @@ -2136,24 +2202,9 @@ int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon, continue; found = true; - if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL || - parent->type != VIR_JSON_TYPE_OBJECT) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("blockstats parent entry was not in expected format")); - goto cleanup; - } - - if ((stats = virJSONValueObjectGet(parent, "stats")) == NULL || - stats->type != VIR_JSON_TYPE_OBJECT) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("blockstats stats entry was not in expected format")); - goto cleanup; - } - - if (virJSONValueObjectGetNumberUlong(stats, "wr_highest_offset", extent) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot read %s statistic"), - "wr_highest_offset"); + if ((err = qemuMonitorJSONDevGetBlockExtent(dev, extent)) != + QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK) { + qemuMonitorJSONReportBlockExtentError(err); goto cleanup; } }