Management software, want to be able to allocate disk space on demand.
To support this, they need keep track of the space occupation
of the block device.
This information is reported by qemu as part of block stats.
This patch extend the block information in the bulk stats with
the allocation information.
To keep the same behaviour, an helper is extracted from
qemuMonitorJSONGetBlockExtent in order to get per-device
allocation information.
Signed-off-by: Francesco Romani <fromani(a)redhat.com>
---
src/libvirt.c | 2 ++
src/qemu/qemu_driver.c | 15 +++++++++
src/qemu/qemu_monitor.h | 1 +
src/qemu/qemu_monitor_json.c | 76 ++++++++++++++++++++++++++++++++------------
4 files changed, 73 insertions(+), 21 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c
index e041fa2..ff8f891 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -21612,6 +21612,8 @@ virConnectGetDomainCapabilities(virConnectPtr conn,
* "block.<num>.fl.times" - total time (ns) spent on cache flushing
* as long long.
* "block.<num>.errors" - Xen only: the 'oo_req' value as long
long.
+ * "block.<num>.allocation" - offset of the highest written sector
+ * as unsigned long long.
*
* 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 93afb7e..86e7893 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -17658,6 +17658,18 @@ do { \
goto cleanup; \
} while (0)
+#define QEMU_ADD_BLOCK_PARAM_ULL(RECORD, MAXPARAMS, NUM, NAME, VALUE) \
+do { \
+ char param_name[NAME_MAX]; \
+ snprintf(param_name, NAME_MAX, "block.%lu.%s", NUM, NAME); \
+ if (virTypedParamsAddULLong(&RECORD->params, \
+ &RECORD->nparams, \
+ MAXPARAMS, \
+ param_name, \
+ VALUE) < 0) \
+ goto cleanup; \
+} while (0)
+
static int
qemuDomainGetStatsBlock(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainObjPtr dom,
@@ -17701,6 +17713,9 @@ qemuDomainGetStatsBlock(virConnectPtr conn ATTRIBUTE_UNUSED,
"fl.reqs", stats[i].flush_req);
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
"fl.times", stats[i].flush_total_times);
+
+ QEMU_ADD_BLOCK_PARAM_ULL(record, maxparams, i,
+ "allocation", stats[i].wr_highest_offset);
}
ret = 0;
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 8e3fb44..97d7336 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -358,6 +358,7 @@ struct _qemuBlockStats {
long long wr_total_times;
long long flush_req;
long long flush_total_times;
+ 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 aa95e71..bc5616d 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1776,6 +1776,55 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
}
+/* This helper function could be called in the
+ * qemuMonitorJSONGetAllBlockStatsInfo
+ * path - which is used also by the
+ * qemuMonitorJSONGetBlockStatsInfo
+ * path. In this case, we don't know in advance if the wr_highest_offset
+ * field is there, so it is OK to fail silently.
+ * However, we can get here by the
+ * qemuMonitorJSONGetBlockExtent
+ * path, and in that case we _must_ fail loudly.
+ */
+static int
+qemuMonitorJSONDevGetBlockExtent(virJSONValuePtr dev,
+ bool report_error,
+ unsigned long long *extent)
+{
+ virJSONValuePtr stats;
+ virJSONValuePtr parent;
+
+ if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL ||
+ parent->type != VIR_JSON_TYPE_OBJECT) {
+ if (report_error)
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("blockstats parent entry was not in "
+ "expected format"));
+ return -1;
+ }
+
+ if ((stats = virJSONValueObjectGet(parent, "stats")) == NULL ||
+ stats->type != VIR_JSON_TYPE_OBJECT) {
+ if (report_error)
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("blockstats stats entry was not in "
+ "expected format"));
+ return -1;
+ }
+
+ if (virJSONValueObjectGetNumberUlong(stats, "wr_highest_offset",
+ extent) < 0) {
+ if (report_error)
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot read %s statistic"),
+ "wr_highest_offset");
+ return -1;
+ }
+
+ return 0;
+}
+
+
int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
const char *dev_name,
qemuBlockStatsPtr bstats,
@@ -1919,6 +1968,10 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
goto cleanup;
}
+ /* it's ok to not have this information here. Just skip silently. */
+ qemuMonitorJSONDevGetBlockExtent(dev, false,
+ &bstats->wr_highest_offset);
+
ret++;
bstats++;
}
@@ -2010,6 +2063,7 @@ int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
return ret;
}
+
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
const char *dev_name,
unsigned long long *extent)
@@ -2044,8 +2098,6 @@ int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
for (i = 0; i < virJSONValueArraySize(devices); i++) {
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
- virJSONValuePtr stats;
- virJSONValuePtr parent;
const char *thisdev;
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -2070,26 +2122,8 @@ 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"));
+ if (qemuMonitorJSONDevGetBlockExtent(dev, true, extent) < 0)
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");
- goto cleanup;
- }
}
if (!found) {
--
1.9.3