[Libvir] [PATCH] Implement virDomainBlockStats for QEMU/KVM

Richard W.M. Jones rjones at redhat.com
Tue Feb 26 11:32:20 UTC 2008


This little patch just implements the virDomainBlockStats call for
qemu & kvm.  It does this by using the new 'info blockstats' monitor
command which I added to qemu & KVM upstream some months back, and
(hopefully) it does the right thing if this command is not available.

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.55
diff -u -r1.55 qemu_driver.c
--- src/qemu_driver.c	26 Feb 2008 07:05:18 -0000	1.55
+++ src/qemu_driver.c	26 Feb 2008 11:31:53 -0000
@@ -59,6 +59,9 @@
 
 static int qemudShutdown(void);
 
+/* qemudDebug statements should be changed to use this macro instead. */
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
 
 #define qemudLog(level, msg...) fprintf(stderr, msg)
 
@@ -2527,6 +2530,144 @@
     return 0;
 }
 
+/* This uses the 'info blockstats' monitor command which was
+ * integrated into both qemu & kvm in late 2007.  If the command is
+ * not supported we detect this and return the appropriate error.
+ */
+static int
+qemudDomainBlockStats (virDomainPtr dom,
+                       const char *path,
+                       struct _virDomainBlockStats *stats)
+{
+    struct qemud_driver *driver =
+        (struct qemud_driver *)dom->conn->privateData;
+    char *info, *p, *dummy, *eol;
+    char qemu_dev_name[32];
+    int len;
+    struct qemud_vm *vm = qemudFindVMByID(driver, dom->id);
+
+    if (!vm) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                          _("no domain with matching id %d"), dom->id);
+        return -1;
+    }
+    if (!qemudIsActiveVM (vm)) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("domain is not running"));
+        return -1;
+    }
+
+    /*
+     * QEMU internal block device names are different from the device
+     * names we use in libvirt, so we need to map between them:
+     *
+     *   hd[a-]   to  ide0-hd[0-]
+     *   cdrom    to  ide1-cd0
+     *   fd[a-]   to  floppy[0-]
+     */
+    if (STREQLEN (path, "hd", 2) && path[2] >= 'a' && path[2] <= 'z')
+        snprintf (qemu_dev_name, sizeof (qemu_dev_name),
+                  "ide0-hd%d", path[2] - 'a');
+    else if (STREQ (path, "cdrom"))
+        strcpy (qemu_dev_name, "ide1-cd0");
+    else if (STREQLEN (path, "fd", 2) && path[2] >= 'a' && path[2] <= 'z')
+        snprintf (qemu_dev_name, sizeof (qemu_dev_name),
+                  "floppy%d", path[2] - 'a');
+    else {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+                          _("invalid path: %s"), path);
+        return -1;
+    }
+
+    len = strlen (qemu_dev_name);
+
+    if (qemudMonitorCommand (driver, vm, "info blockstats", &info) < 0) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("'info blockstats' command failed"));
+        return -1;
+    }
+
+    DEBUG ("info blockstats reply: %s", info);
+
+    /* If the command isn't supported then qemu prints the supported
+     * info commands, so the output starts "info ".  Since this is
+     * unlikely to be the name of a block device, we can use this
+     * to detect if qemu supports the command.
+     */
+    if (STREQLEN (info, "info ", 5)) {
+        free (info);
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+                          _("'info blockstats' not supported by this qemu"));
+        return -1;
+    }
+
+    stats->rd_req = -1;
+    stats->rd_bytes = -1;
+    stats->wr_req = -1;
+    stats->wr_bytes = -1;
+    stats->errs = -1;
+
+    /* The output format for both qemu & KVM is:
+     *   blockdevice: rd_bytes=% wr_bytes=% rd_operations=% wr_operations=%
+     *   (repeated for each block device)
+     * where '%' is a 64 bit number.
+     */
+    p = info;
+
+    while (*p) {
+        if (STREQLEN (p, qemu_dev_name, len)
+            && p[len] == ':' && p[len+1] == ' ') {
+
+            eol = strchr (p, '\n'); if (!eol) eol = p + strlen (p);
+
+            p += len+2;         /* Skip to first label. */
+
+            while (*p) {
+                if (STREQLEN (p, "rd_bytes=", 9)) {
+                    p += 9;
+                    if (virStrToLong_ll (p, &dummy, 10, &stats->rd_bytes) == -1)
+                        DEBUG ("error reading rd_bytes: %s", p);
+                } else if (STREQLEN (p, "wr_bytes=", 9)) {
+                    p += 9;
+                    if (virStrToLong_ll (p, &dummy, 10, &stats->wr_bytes) == -1)
+                        DEBUG ("error reading wr_bytes: %s", p);
+                } else if (STREQLEN (p, "rd_operations=", 14)) {
+                    p += 14;
+                    if (virStrToLong_ll (p, &dummy, 10, &stats->rd_req) == -1)
+                        DEBUG ("error reading rd_req: %s", p);
+                } else if (STREQLEN (p, "wr_operations=", 14)) {
+                    p += 14;
+                    if (virStrToLong_ll (p, &dummy, 10, &stats->wr_req) == -1)
+                        DEBUG ("error reading wr_req: %s", p);
+                } else
+                    DEBUG ("unknown block stat near %s", p);
+
+                /* Skip to next label. */
+                p = strchr (p, ' ');
+                if (!p || p >= eol) break;
+                p++;
+            }
+
+            goto done;
+        }
+
+        /* Skip to next line. */
+        p = strchr (p, '\n');
+        if (!p) break;
+        p++;
+    }
+
+    /* If we reach here then the device was not found. */
+    free (info);
+    qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+                      _("device not found: %s (%s)"), path, qemu_dev_name);
+    return -1;
+
+ done:
+    free (info);
+    return 0;
+}
+
 static int
 qemudDomainInterfaceStats (virDomainPtr dom,
                            const char *path,
@@ -2928,7 +3069,7 @@
     NULL, /* domainMigratePrepare */
     NULL, /* domainMigratePerform */
     NULL, /* domainMigrateFinish */
-    NULL, /* domainBlockStats */
+    qemudDomainBlockStats, /* domainBlockStats */
     qemudDomainInterfaceStats, /* domainInterfaceStats */
     NULL, /* nodeGetCellsFreeMemory */
     NULL, /* getFreeMemory */


More information about the libvir-list mailing list