The virDomainGetBlockInfo API allows query physical block extent and allocated block extent. These are normally the same value unless storing a special format like qcow2 inside a block device. In this scenario we can query QEMU to get the actual allocated extent. Since last time: - Return fatal error in text monitor - Only invoke monitor command for block devices - Fix error handling JSON code * src/qemu/qemu_driver.c: Fill in block aloction extent when VM is running * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h, src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add API to query the highest block extent via info blockstats --- src/qemu/qemu_driver.c | 33 ++++++++++++--- src/qemu/qemu_monitor.c | 16 +++++++ src/qemu/qemu_monitor.h | 4 ++ src/qemu/qemu_monitor_json.c | 94 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + src/qemu/qemu_monitor_text.c | 10 ++++ src/qemu/qemu_monitor_text.h | 4 +- 7 files changed, 156 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4ef2f57..356c4be 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9651,6 +9651,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom, int fd = -1; off_t end; virStorageFileMetadata meta; + virDomainDiskDefPtr disk = NULL; struct stat sb; int i; @@ -9677,19 +9678,17 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom, for (i = 0 ; i < vm->def->ndisks ; i++) { if (vm->def->disks[i]->src != NULL && STREQ (vm->def->disks[i]->src, path)) { - ret = 0; + disk = vm->def->disks[i]; break; } } - if (ret != 0) { + if (!disk) { qemuReportError(VIR_ERR_INVALID_ARG, _("invalid path %s not assigned to domain"), path); goto cleanup; } - ret = -1; - /* The path is correct, now try to open it and get its size. */ fd = open (path, O_RDONLY); if (fd == -1) { @@ -9740,11 +9739,31 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom, if (meta.capacity) info->capacity = meta.capacity; - /* XXX allocation will need to be pulled from QEMU for - * the qcow inside LVM case */ + /* Set default value .. */ info->allocation = info->physical; - ret = 0; + /* ..but if guest is running & not using raw + disk format and on a block device, then query + highest allocated extent from QEMU */ + if (virDomainObjIsActive(vm) && + disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK && + meta.format != VIR_STORAGE_FILE_RAW && + S_ISBLK(sb.st_mode)) { + qemuDomainObjPrivatePtr priv = vm->privateData; + if (qemuDomainObjBeginJob(vm) < 0) + goto cleanup; + + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorGetBlockExtent(priv->mon, + disk->info.alias, + &info->allocation); + qemuDomainObjExitMonitor(vm); + + if (qemuDomainObjEndJob(vm) == 0) + vm = NULL; + } else { + ret = 0; + } cleanup: if (fd != -1) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 582225e..efaf74a 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1009,6 +1009,22 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, return ret; } +int qemuMonitorGetBlockExtent(qemuMonitorPtr mon, + const char *devname, + unsigned long long *extent) +{ + int ret; + DEBUG("mon=%p, fd=%d, devname=%p", + mon, mon->fd, devname); + + if (mon->json) + ret = qemuMonitorJSONGetBlockExtent(mon, devname, extent); + else + ret = qemuMonitorTextGetBlockExtent(mon, devname, extent); + + return ret; +} + int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, const char *password) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 7b1589e..adfb3bc 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -185,6 +185,10 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, long long *wr_bytes, long long *errs); +int qemuMonitorGetBlockExtent(qemuMonitorPtr mon, + const char *devname, + unsigned long long *extent); + int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, const char *password); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 6d8f328..31a66f1 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -1186,6 +1186,100 @@ cleanup: } +int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon, + const char *devname, + unsigned long long *extent) +{ + int ret = -1; + int i; + int found = 0; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats", + NULL); + virJSONValuePtr reply = NULL; + virJSONValuePtr devices; + + *extent = 0; + + if (!cmd) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + goto cleanup; + + devices = virJSONValueObjectGet(reply, "return"); + if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("blockstats reply was missing device list")); + goto cleanup; + } + + 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) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("blockstats device entry was not in expected format")); + goto cleanup; + } + + if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) { + qemuReportError(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 devname is the guest side though + */ + if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX)) + thisdev += strlen(QEMU_DRIVE_HOST_PREFIX); + + if (STRNEQ(thisdev, devname)) + continue; + + found = 1; + if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL || + parent->type != VIR_JSON_TYPE_OBJECT) { + qemuReportError(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) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("blockstats stats entry was not in expected format")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberUlong(stats, "wr_highest_offset", extent) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot read %s statistic"), + "wr_highest_offset"); + goto cleanup; + } + } + + if (!found) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find statistics for device '%s'"), devname); + goto cleanup; + } + ret = 0; + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon, const char *password) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 26fc865..14597f4 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -56,6 +56,9 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon, long long *wr_req, long long *wr_bytes, long long *errs); +int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon, + const char *devname, + unsigned long long *extent); int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index d725d6d..72e3fd5 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -711,6 +711,16 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, } +int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *devname ATTRIBUTE_UNUSED, + unsigned long long *extent ATTRIBUTE_UNUSED) +{ + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to query block extent with this QEMU")); + return -1; +} + + static int qemuMonitorSendVNCPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, qemuMonitorMessagePtr msg, diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 2a62c7e..6fb7d7a 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -55,7 +55,9 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, long long *wr_req, long long *wr_bytes, long long *errs); - +int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon, + const char *devname, + unsigned long long *extent); int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon, const char *password); -- 1.6.6.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list