mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-10-30 09:53:10 +00:00
qemu: bulk stats: add block allocation information
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@redhat.com> Signed-off-by: Peter Krempa <pkrempa@redhat.com>
This commit is contained in:
parent
8caded6b8e
commit
7557ddf8be
@ -21656,6 +21656,12 @@ virConnectGetDomainCapabilities(virConnectPtr conn,
|
|||||||
* unsigned long long.
|
* unsigned long long.
|
||||||
* "block.<num>.errors" - Xen only: the 'oo_req' value as
|
* "block.<num>.errors" - Xen only: the 'oo_req' value as
|
||||||
* unsigned long long.
|
* unsigned long long.
|
||||||
|
* "block.<num>.allocation" - offset of the highest written sector
|
||||||
|
* as unsigned long long.
|
||||||
|
* "block.<num>.capacity" - logical size in bytes of the block device backing
|
||||||
|
* image as unsigned long long.
|
||||||
|
* "block.<num>.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
|
* 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
|
* the output in case they are not supported by the given hypervisor, are not
|
||||||
|
@ -17907,6 +17907,19 @@ do { \
|
|||||||
goto cleanup; \
|
goto cleanup; \
|
||||||
} while (0)
|
} 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
|
static int
|
||||||
qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
|
qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
|
||||||
virDomainObjPtr dom,
|
virDomainObjPtr dom,
|
||||||
@ -17925,6 +17938,7 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
|
|||||||
|
|
||||||
qemuDomainObjEnterMonitor(driver, dom);
|
qemuDomainObjEnterMonitor(driver, dom);
|
||||||
rc = qemuMonitorGetAllBlockStatsInfo(priv->mon, &stats);
|
rc = qemuMonitorGetAllBlockStatsInfo(priv->mon, &stats);
|
||||||
|
ignore_value(qemuMonitorBlockStatsUpdateCapacity(priv->mon, stats));
|
||||||
qemuDomainObjExitMonitor(driver, dom);
|
qemuDomainObjExitMonitor(driver, dom);
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
@ -17961,6 +17975,17 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
|
|||||||
"fl.reqs", entry->flush_req);
|
"fl.reqs", entry->flush_req);
|
||||||
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
||||||
"fl.times", entry->flush_total_times);
|
"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;
|
ret = 0;
|
||||||
@ -17972,6 +17997,8 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
|
|||||||
|
|
||||||
#undef QEMU_ADD_BLOCK_PARAM_LL
|
#undef QEMU_ADD_BLOCK_PARAM_LL
|
||||||
|
|
||||||
|
#undef QEMU_ADD_BLOCK_PARAM_ULL
|
||||||
|
|
||||||
#undef QEMU_ADD_NAME_PARAM
|
#undef QEMU_ADD_NAME_PARAM
|
||||||
|
|
||||||
#undef QEMU_ADD_COUNT_PARAM
|
#undef QEMU_ADD_COUNT_PARAM
|
||||||
|
@ -360,6 +360,7 @@ struct _qemuBlockStats {
|
|||||||
long long flush_total_times;
|
long long flush_total_times;
|
||||||
unsigned long long capacity;
|
unsigned long long capacity;
|
||||||
unsigned long long physical;
|
unsigned long long physical;
|
||||||
|
unsigned long long wr_highest_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
int qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
int qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
||||||
|
@ -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,
|
int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
||||||
virHashTablePtr *ret_stats)
|
virHashTablePtr *ret_stats)
|
||||||
{
|
{
|
||||||
@ -1907,6 +1941,9 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
goto cleanup;
|
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)
|
if (virHashAddEntry(hash, dev_name, bstats) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
bstats = NULL;
|
bstats = NULL;
|
||||||
@ -2076,6 +2113,36 @@ int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
|
|||||||
return ret;
|
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,
|
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
|
||||||
const char *dev_name,
|
const char *dev_name,
|
||||||
unsigned long long *extent)
|
unsigned long long *extent)
|
||||||
@ -2110,9 +2177,8 @@ int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
|
|||||||
|
|
||||||
for (i = 0; i < virJSONValueArraySize(devices); i++) {
|
for (i = 0; i < virJSONValueArraySize(devices); i++) {
|
||||||
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
||||||
virJSONValuePtr stats;
|
|
||||||
virJSONValuePtr parent;
|
|
||||||
const char *thisdev;
|
const char *thisdev;
|
||||||
|
int err;
|
||||||
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
_("blockstats device entry was not in expected format"));
|
_("blockstats device entry was not in expected format"));
|
||||||
@ -2136,24 +2202,9 @@ int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL ||
|
if ((err = qemuMonitorJSONDevGetBlockExtent(dev, extent)) !=
|
||||||
parent->type != VIR_JSON_TYPE_OBJECT) {
|
QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK) {
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
qemuMonitorJSONReportBlockExtentError(err);
|
||||||
_("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");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user