mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-03 11:51:11 +00:00
qemu: monitor: return block stats data as a hash to avoid disk mixup
The current block stats code matched up the disk name with the actual stats by the order in the data returned from qemu. This unfortunately isn't right as qemu may return the disks in any order. Fix this by returning a hash of stats and index them by the disk alias.
This commit is contained in:
parent
f53bb1af90
commit
96c0f57a82
@ -17922,24 +17922,18 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
|
|||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
int nstats = dom->def->ndisks;
|
int rc;
|
||||||
qemuBlockStatsPtr stats = NULL;
|
virHashTablePtr stats = NULL;
|
||||||
qemuDomainObjPrivatePtr priv = dom->privateData;
|
qemuDomainObjPrivatePtr priv = dom->privateData;
|
||||||
|
|
||||||
if (!HAVE_JOB(privflags) || !virDomainObjIsActive(dom))
|
if (!HAVE_JOB(privflags) || !virDomainObjIsActive(dom))
|
||||||
return 0; /* it's ok, just go ahead silently */
|
return 0; /* it's ok, just go ahead silently */
|
||||||
|
|
||||||
if (VIR_ALLOC_N(stats, nstats) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
qemuDomainObjEnterMonitor(driver, dom);
|
qemuDomainObjEnterMonitor(driver, dom);
|
||||||
|
rc = qemuMonitorGetAllBlockStatsInfo(priv->mon, &stats);
|
||||||
nstats = qemuMonitorGetAllBlockStatsInfo(priv->mon, NULL,
|
|
||||||
stats, nstats);
|
|
||||||
|
|
||||||
qemuDomainObjExitMonitor(driver, dom);
|
qemuDomainObjExitMonitor(driver, dom);
|
||||||
|
|
||||||
if (nstats < 0) {
|
if (rc < 0) {
|
||||||
virResetLastError();
|
virResetLastError();
|
||||||
ret = 0; /* still ok, again go ahead silently */
|
ret = 0; /* still ok, again go ahead silently */
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -17947,32 +17941,38 @@ qemuDomainGetStatsBlock(virQEMUDriverPtr driver,
|
|||||||
|
|
||||||
QEMU_ADD_COUNT_PARAM(record, maxparams, "block", dom->def->ndisks);
|
QEMU_ADD_COUNT_PARAM(record, maxparams, "block", dom->def->ndisks);
|
||||||
|
|
||||||
for (i = 0; i < nstats; i++) {
|
for (i = 0; i < dom->def->ndisks; i++) {
|
||||||
QEMU_ADD_NAME_PARAM(record, maxparams,
|
qemuBlockStats *entry;
|
||||||
"block", i, dom->def->disks[i]->dst);
|
virDomainDiskDefPtr disk = dom->def->disks[i];
|
||||||
|
|
||||||
|
QEMU_ADD_NAME_PARAM(record, maxparams, "block", i, disk->dst);
|
||||||
|
|
||||||
|
if (!disk->info.alias ||
|
||||||
|
!(entry = virHashLookup(stats, disk->info.alias)))
|
||||||
|
continue;
|
||||||
|
|
||||||
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
||||||
"rd.reqs", stats[i].rd_req);
|
"rd.reqs", entry->rd_req);
|
||||||
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
||||||
"rd.bytes", stats[i].rd_bytes);
|
"rd.bytes", entry->rd_bytes);
|
||||||
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
||||||
"rd.times", stats[i].rd_total_times);
|
"rd.times", entry->rd_total_times);
|
||||||
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
||||||
"wr.reqs", stats[i].wr_req);
|
"wr.reqs", entry->wr_req);
|
||||||
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
||||||
"wr.bytes", stats[i].wr_bytes);
|
"wr.bytes", entry->wr_bytes);
|
||||||
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
||||||
"wr.times", stats[i].wr_total_times);
|
"wr.times", entry->wr_total_times);
|
||||||
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
QEMU_ADD_BLOCK_PARAM_LL(record, maxparams, i,
|
||||||
"fl.reqs", stats[i].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", stats[i].flush_total_times);
|
"fl.times", entry->flush_total_times);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
VIR_FREE(stats);
|
virHashFree(stats);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1763,23 +1763,17 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
||||||
const char *dev_name,
|
virHashTablePtr *ret_stats)
|
||||||
qemuBlockStatsPtr stats,
|
|
||||||
int nstats)
|
|
||||||
{
|
{
|
||||||
int ret;
|
VIR_DEBUG("mon=%p ret_stats=%p", mon, ret_stats);
|
||||||
VIR_DEBUG("mon=%p dev=%s", mon, dev_name);
|
|
||||||
|
|
||||||
if (mon->json) {
|
if (!mon->json) {
|
||||||
ret = qemuMonitorJSONGetAllBlockStatsInfo(mon, dev_name,
|
|
||||||
stats, nstats);
|
|
||||||
} else {
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
_("unable to query all block stats with this QEMU"));
|
_("unable to query all block stats with this QEMU"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return qemuMonitorJSONGetAllBlockStatsInfo(mon, ret_stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return 0 and update @nparams with the number of block stats
|
/* Return 0 and update @nparams with the number of block stats
|
||||||
|
@ -361,10 +361,8 @@ struct _qemuBlockStats {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
int qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
||||||
const char *dev_name,
|
virHashTablePtr *ret_stats)
|
||||||
qemuBlockStatsPtr stats,
|
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
|
||||||
int nstats)
|
|
||||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
|
|
||||||
|
|
||||||
int qemuMonitorGetBlockStatsParamsNumber(qemuMonitorPtr mon,
|
int qemuMonitorGetBlockStatsParamsNumber(qemuMonitorPtr mon,
|
||||||
int *nparams);
|
int *nparams);
|
||||||
|
@ -1734,7 +1734,8 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
long long *flush_total_times,
|
long long *flush_total_times,
|
||||||
long long *errs)
|
long long *errs)
|
||||||
{
|
{
|
||||||
qemuBlockStats stats;
|
qemuBlockStats *stats;
|
||||||
|
virHashTablePtr blockstats = NULL;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
*rd_req = *rd_bytes = -1;
|
*rd_req = *rd_bytes = -1;
|
||||||
@ -1749,56 +1750,61 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
if (flush_total_times)
|
if (flush_total_times)
|
||||||
*flush_total_times = -1;
|
*flush_total_times = -1;
|
||||||
|
|
||||||
if (qemuMonitorJSONGetAllBlockStatsInfo(mon, dev_name, &stats, 1) != 1)
|
if (qemuMonitorJSONGetAllBlockStatsInfo(mon, &blockstats) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
*rd_req = stats.rd_req;
|
if (!(stats = virHashLookup(blockstats, dev_name))) {
|
||||||
*rd_bytes = stats.rd_bytes;
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
*wr_req = stats.wr_req;
|
_("cannot find statistics for device '%s'"), dev_name);
|
||||||
*wr_bytes = stats.wr_bytes;
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
*rd_req = stats->rd_req;
|
||||||
|
*rd_bytes = stats->rd_bytes;
|
||||||
|
*wr_req = stats->wr_req;
|
||||||
|
*wr_bytes = stats->wr_bytes;
|
||||||
*errs = -1; /* QEMU does not have this */
|
*errs = -1; /* QEMU does not have this */
|
||||||
|
|
||||||
if (rd_total_times)
|
if (rd_total_times)
|
||||||
*rd_total_times = stats.rd_total_times;
|
*rd_total_times = stats->rd_total_times;
|
||||||
if (wr_total_times)
|
if (wr_total_times)
|
||||||
*wr_total_times = stats.wr_total_times;
|
*wr_total_times = stats->wr_total_times;
|
||||||
if (flush_req)
|
if (flush_req)
|
||||||
*flush_req = stats.flush_req;
|
*flush_req = stats->flush_req;
|
||||||
if (flush_total_times)
|
if (flush_total_times)
|
||||||
*flush_total_times = stats.flush_total_times;
|
*flush_total_times = stats->flush_total_times;
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
virHashFree(blockstats);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
||||||
const char *dev_name,
|
virHashTablePtr *ret_stats)
|
||||||
qemuBlockStatsPtr bstats,
|
|
||||||
int nstats)
|
|
||||||
{
|
{
|
||||||
int ret, count;
|
int ret = -1;
|
||||||
|
int rc;
|
||||||
size_t i;
|
size_t i;
|
||||||
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
|
virJSONValuePtr cmd;
|
||||||
NULL);
|
|
||||||
virJSONValuePtr reply = NULL;
|
virJSONValuePtr reply = NULL;
|
||||||
virJSONValuePtr devices;
|
virJSONValuePtr devices;
|
||||||
|
qemuBlockStatsPtr bstats = NULL;
|
||||||
|
virHashTablePtr hash = NULL;
|
||||||
|
|
||||||
if (!cmd)
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-blockstats", NULL)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!bstats || nstats <= 0)
|
if (!(hash = virHashCreate(10, virHashValueFree)))
|
||||||
return -1;
|
goto cleanup;
|
||||||
|
|
||||||
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
if ((rc = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
||||||
|
goto cleanup;
|
||||||
if (ret == 0)
|
|
||||||
ret = qemuMonitorJSONCheckError(cmd, reply);
|
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
|
||||||
if (ret < 0)
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
ret = -1;
|
|
||||||
|
|
||||||
devices = virJSONValueObjectGet(reply, "return");
|
devices = virJSONValueObjectGet(reply, "return");
|
||||||
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
|
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
|
||||||
@ -1807,10 +1813,14 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = 0;
|
for (i = 0; i < virJSONValueArraySize(devices); i++) {
|
||||||
for (i = 0; i < virJSONValueArraySize(devices) && count < nstats; i++) {
|
|
||||||
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
||||||
virJSONValuePtr stats;
|
virJSONValuePtr stats;
|
||||||
|
const char *devname;
|
||||||
|
|
||||||
|
if (VIR_ALLOC(bstats) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
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 "
|
_("blockstats device entry was not "
|
||||||
@ -1818,29 +1828,16 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If dev_name is specified, we are looking for a specific device,
|
if (!(devname = virJSONValueObjectGetString(dev, "device"))) {
|
||||||
* so we must be stricter.
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
*/
|
_("blockstats device entry was not "
|
||||||
if (dev_name) {
|
"in expected format"));
|
||||||
const char *thisdev = virJSONValueObjectGetString(dev, "device");
|
goto cleanup;
|
||||||
if (!thisdev) {
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
||||||
_("blockstats device entry was not "
|
|
||||||
"in expected format"));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* New QEMU has separate names for host & guest side of the disk
|
|
||||||
* and libvirt gives the host side a 'drive-' prefix. The passed
|
|
||||||
* in dev_name is the guest side though
|
|
||||||
*/
|
|
||||||
if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
|
|
||||||
thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
|
|
||||||
|
|
||||||
if (STRNEQ(thisdev, dev_name))
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (STRPREFIX(devname, QEMU_DRIVE_HOST_PREFIX))
|
||||||
|
devname += strlen(QEMU_DRIVE_HOST_PREFIX);
|
||||||
|
|
||||||
if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
|
if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
|
||||||
stats->type != VIR_JSON_TYPE_OBJECT) {
|
stats->type != VIR_JSON_TYPE_OBJECT) {
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
@ -1910,22 +1907,18 @@ int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
count++;
|
if (virHashAddEntry(hash, devname, bstats) < 0)
|
||||||
bstats++;
|
goto cleanup;
|
||||||
|
bstats = NULL;
|
||||||
if (dev_name && count)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev_name && !count) {
|
*ret_stats = hash;
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
hash = NULL;
|
||||||
_("cannot find statistics for device '%s'"), dev_name);
|
ret = 0;
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = count;
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
VIR_FREE(bstats);
|
||||||
|
virHashFree(hash);
|
||||||
virJSONValueFree(cmd);
|
virJSONValueFree(cmd);
|
||||||
virJSONValueFree(reply);
|
virJSONValueFree(reply);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -80,9 +80,7 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
long long *flush_total_times,
|
long long *flush_total_times,
|
||||||
long long *errs);
|
long long *errs);
|
||||||
int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
||||||
const char *dev_name,
|
virHashTablePtr *ret_stats);
|
||||||
qemuBlockStatsPtr stats,
|
|
||||||
int nstats);
|
|
||||||
int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
|
int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
|
||||||
int *nparams);
|
int *nparams);
|
||||||
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
|
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
|
||||||
|
Loading…
Reference in New Issue
Block a user