Management software wants 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 a 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 | 18 +++++++++
src/qemu/qemu_monitor.h | 1 +
src/qemu/qemu_monitor_json.c | 91 ++++++++++++++++++++++++++++++++++----------
4 files changed, 92 insertions(+), 20 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c
index ab10a3a..a8892a2 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -21654,6 +21654,8 @@ virConnectGetDomainCapabilities(virConnectPtr conn,
* unsigned long long.
* "block.<num>.errors" - Xen only: the 'oo_req' value as
* unsigned 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 446b04b..a34fffd 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -17636,6 +17636,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,
@@ -17690,6 +17703,9 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
"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;
@@ -17701,6 +17717,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 6eb0036..d0f0bf5 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 d45c41f..1948b85 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1774,6 +1774,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,
const char *dev_name,
qemuBlockStatsPtr bstats,
@@ -1910,6 +1944,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);
+
count++;
bstats++;
@@ -2005,6 +2042,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)
@@ -2039,9 +2106,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"));
@@ -2065,24 +2131,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;
}
}
--
1.9.3