getstats: crawl backing chain for qemu

Wire up backing chain recursion.  For the first time, it is now
possible to get libvirt to expose that qemu tracks read statistics
on backing files, as well as report maximum extent written on a
backing file during a block-commit operation.

For a running domain, where one of the two images has a backing
file, I see the traditional output:

$ virsh domstats --block testvm2
Domain: 'testvm2'
  block.count=2
  block.0.name=vda
  block.0.path=/tmp/wrapper.qcow2
  block.0.rd.reqs=1
  block.0.rd.bytes=512
  block.0.rd.times=28858
  block.0.wr.reqs=0
  block.0.wr.bytes=0
  block.0.wr.times=0
  block.0.fl.reqs=0
  block.0.fl.times=0
  block.0.allocation=0
  block.0.capacity=1310720000
  block.0.physical=200704
  block.1.name=vdb
  block.1.path=/dev/sda7
  block.1.rd.reqs=0
  block.1.rd.bytes=0
  block.1.rd.times=0
  block.1.wr.reqs=0
  block.1.wr.bytes=0
  block.1.wr.times=0
  block.1.fl.reqs=0
  block.1.fl.times=0
  block.1.allocation=0
  block.1.capacity=1310720000

vs. the new output:

$ virsh domstats --block --backing testvm2
Domain: 'testvm2'
  block.count=3
  block.0.name=vda
  block.0.path=/tmp/wrapper.qcow2
  block.0.rd.reqs=1
  block.0.rd.bytes=512
  block.0.rd.times=28858
  block.0.wr.reqs=0
  block.0.wr.bytes=0
  block.0.wr.times=0
  block.0.fl.reqs=0
  block.0.fl.times=0
  block.0.allocation=0
  block.0.capacity=1310720000
  block.0.physical=200704
  block.1.name=vda
  block.1.path=/dev/sda6
  block.1.backingIndex=1
  block.1.rd.reqs=0
  block.1.rd.bytes=0
  block.1.rd.times=0
  block.1.wr.reqs=0
  block.1.wr.bytes=0
  block.1.wr.times=0
  block.1.fl.reqs=0
  block.1.fl.times=0
  block.1.allocation=327680
  block.1.capacity=786432000
  block.2.name=vdb
  block.2.path=/dev/sda7
  block.2.rd.reqs=0
  block.2.rd.bytes=0
  block.2.rd.times=0
  block.2.wr.reqs=0
  block.2.wr.bytes=0
  block.2.wr.times=0
  block.2.fl.reqs=0
  block.2.fl.times=0
  block.2.allocation=0
  block.2.capacity=1310720000

I may later do a patch that trims the output to avoid 0 stats,
particularly for backing files (which are more likely to have
0 stats, at least for write statistics when no block-commit
is performed).  Also, I still plan to expose physical size
information (qemu doesn't expose it yet, so it requires a stat,
and for block devices, a further open/seek operation).  But
this patch is good enough without worrying about that yet.

* src/qemu/qemu_driver.c (QEMU_DOMAIN_STATS_BACKING): New internal
enum bit.
(qemuConnectGetAllDomainStats): Recognize new user flag, and pass
details to...
(qemuDomainGetStatsBlock): ...here, where we can do longer recursion.
(qemuDomainGetStatsOneBlock): Output new field.

Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Eric Blake 2014-12-06 00:14:43 -07:00
parent c2d380bff8
commit 3937ef9cf4

View File

@ -18277,8 +18277,10 @@ qemuDomainGetStatsState(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
typedef enum {
QEMU_DOMAIN_STATS_HAVE_JOB = (1 << 0), /* job is entered, monitor can be
accessed */
QEMU_DOMAIN_STATS_HAVE_JOB = 1 << 0, /* job is entered, monitor can be
accessed */
QEMU_DOMAIN_STATS_BACKING = 1 << 1, /* include backing chain in
block stats */
} qemuDomainStatsFlags;
@ -18525,6 +18527,19 @@ qemuDomainGetStatsInterface(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
#undef QEMU_ADD_NET_PARAM
#define QEMU_ADD_BLOCK_PARAM_UI(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 (virTypedParamsAddUInt(&(record)->params, \
&(record)->nparams, \
maxparams, \
param_name, \
value) < 0) \
goto cleanup; \
} while (0)
/* expects a LL, but typed parameter must be ULL */
#define QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, num, name, value) \
do { \
@ -18562,20 +18577,27 @@ qemuDomainGetStatsOneBlock(virQEMUDriverPtr driver,
virDomainDiskDefPtr disk,
virStorageSourcePtr src,
size_t block_idx,
unsigned int backing_idx,
bool abbreviated,
virHashTablePtr stats)
{
qemuBlockStats *entry;
int ret = -1;
char *alias = NULL;
if (disk->info.alias)
alias = qemuDomainStorageAlias(disk->info.alias, backing_idx);
QEMU_ADD_NAME_PARAM(record, maxparams, "block", "name", block_idx,
disk->dst);
if (virStorageSourceIsLocalStorage(src) && src->path)
QEMU_ADD_NAME_PARAM(record, maxparams, "block", "path",
block_idx, src->path);
if (backing_idx)
QEMU_ADD_BLOCK_PARAM_UI(record, maxparams, block_idx, "backingIndex",
backing_idx);
if (abbreviated || !disk->info.alias ||
!(entry = virHashLookup(stats, disk->info.alias))) {
if (abbreviated || !alias || !(entry = virHashLookup(stats, alias))) {
if (virStorageSourceIsEmpty(src)) {
ret = 0;
goto cleanup;
@ -18624,6 +18646,7 @@ qemuDomainGetStatsOneBlock(virQEMUDriverPtr driver,
ret = 0;
cleanup:
VIR_FREE(alias);
return ret;
}
@ -18643,14 +18666,17 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
bool abbreviated = false;
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
int count_index = -1;
size_t visited = 0;
bool visitBacking = !!(privflags & QEMU_DOMAIN_STATS_BACKING);
if (!HAVE_JOB(privflags) || !virDomainObjIsActive(dom)) {
abbreviated = true; /* it's ok, just go ahead silently */
} else {
qemuDomainObjEnterMonitor(driver, dom);
rc = qemuMonitorGetAllBlockStatsInfo(priv->mon, &stats, false);
rc = qemuMonitorGetAllBlockStatsInfo(priv->mon, &stats,
visitBacking);
ignore_value(qemuMonitorBlockStatsUpdateCapacity(priv->mon, stats,
false));
visitBacking));
qemuDomainObjExitMonitor(driver, dom);
if (rc < 0) {
@ -18667,14 +18693,21 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
for (i = 0; i < dom->def->ndisks; i++) {
virDomainDiskDefPtr disk = dom->def->disks[i];
virStorageSourcePtr src = disk->src;
unsigned int backing_idx = 0;
if (qemuDomainGetStatsOneBlock(driver, cfg, dom, record, maxparams,
disk, disk->src, i, abbreviated,
stats) < 0)
goto cleanup;
while (src && (backing_idx == 0 || visitBacking)) {
if (qemuDomainGetStatsOneBlock(driver, cfg, dom, record, maxparams,
disk, src, visited, backing_idx,
abbreviated, stats) < 0)
goto cleanup;
visited++;
backing_idx++;
src = src->backingStore;
}
}
record->params[count_index].value.ui = i;
record->params[count_index].value.ui = visited;
ret = 0;
cleanup:
@ -18818,11 +18851,13 @@ qemuConnectGetAllDomainStats(virConnectPtr conn,
unsigned int domflags = 0;
if (ndoms)
virCheckFlags(VIR_CONNECT_GET_ALL_DOMAINS_STATS_ENFORCE_STATS, -1);
virCheckFlags(VIR_CONNECT_GET_ALL_DOMAINS_STATS_BACKING |
VIR_CONNECT_GET_ALL_DOMAINS_STATS_ENFORCE_STATS, -1);
else
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE |
VIR_CONNECT_LIST_DOMAINS_FILTERS_PERSISTENT |
VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE |
VIR_CONNECT_GET_ALL_DOMAINS_STATS_BACKING |
VIR_CONNECT_GET_ALL_DOMAINS_STATS_ENFORCE_STATS, -1);
if (virConnectGetAllDomainStatsEnsureACL(conn) < 0)
@ -18872,6 +18907,8 @@ qemuConnectGetAllDomainStats(virConnectPtr conn,
domflags |= QEMU_DOMAIN_STATS_HAVE_JOB;
/* else: without a job it's still possible to gather some data */
if (flags & VIR_CONNECT_GET_ALL_DOMAINS_STATS_BACKING)
domflags |= QEMU_DOMAIN_STATS_BACKING;
if (qemuDomainGetStats(conn, dom, stats, &tmp, domflags) < 0)
goto endjob;