[libvirt] [PATCH] Query block allocation extent from QEMU monitor

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

On Fri, May 14, 2010 at 09:10:01AM -0400, Daniel P. Berrange wrote:
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
Looks fine to me, ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On 05/14/2010 07:10 AM, Daniel P. Berrange wrote:
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
Good, but...
- 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);
that means you now propagate that fatal error out of the call, rather than falling back on the default. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Fri, May 14, 2010 at 10:39:08AM -0600, Eric Blake wrote:
On 05/14/2010 07:10 AM, Daniel P. Berrange wrote:
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
Good, but...
- 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);
that means you now propagate that fatal error out of the call, rather than falling back on the default.
Yes, that is the intended behaviour here. If it is a block device + not using the raw format, then we want the caller to be able to see the error Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On 05/16/2010 03:03 AM, Daniel P. Berrange wrote:
that means you now propagate that fatal error out of the call, rather than falling back on the default.
Yes, that is the intended behaviour here. If it is a block device + not using the raw format, then we want the caller to be able to see the error
Fair enough. By the way, I never saw this one applied, even though DV gave it an ACK... -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 05/14/2010 07:10 AM, Daniel P. Berrange wrote:
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
On 05/18/2010 02:32 PM, Eric Blake wrote:
On 05/16/2010 03:03 AM, Daniel P. Berrange wrote:
that means you now propagate that fatal error out of the call, rather than falling back on the default.
Yes, that is the intended behaviour here. If it is a block device + not using the raw format, then we want the caller to be able to see the error
Fair enough. By the way, I never saw this one applied, even though DV gave it an ACK...
I applied this on Daniel's behalf, as part of cleaning out some of my inbox, and after double-checking that everything builds cleanly after rebasing on latest sources. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, May 24, 2010 at 04:05:41PM -0600, Eric Blake wrote:
On 05/14/2010 07:10 AM, Daniel P. Berrange wrote:
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
On 05/18/2010 02:32 PM, Eric Blake wrote:
On 05/16/2010 03:03 AM, Daniel P. Berrange wrote:
that means you now propagate that fatal error out of the call, rather than falling back on the default.
Yes, that is the intended behaviour here. If it is a block device + not using the raw format, then we want the caller to be able to see the error
Fair enough. By the way, I never saw this one applied, even though DV gave it an ACK...
I applied this on Daniel's behalf, as part of cleaning out some of my inbox, and after double-checking that everything builds cleanly after rebasing on latest sources.
Thanks, I forgot I'd not pushed that here..... -ETOOMANYBRANCHES Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Eric Blake