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

Daniel P. Berrange berrange at redhat.com
Fri May 14 13:10:01 UTC 2010


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




More information about the libvir-list mailing list