From 727c5601ec7d1e21e38d219db2b5886ab4d79e78 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 26 Feb 2008 18:41:43 +0000 Subject: [PATCH] Implement virDomainBlockStats for QEMU/KVM. * src/qemu_driver.c: Implement virDomainBlockStats for QEMU/KVM using QEMU/KVM 'info blockstats' command (Richard Jones). * src/qemu_driver.c, src/qemu_conf.h: const-correctness fixes (Jim Meyering). --- ChangeLog | 8 +++ src/qemu_conf.h | 4 +- src/qemu_driver.c | 156 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 161 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4edc316c67..da1c3c1dfd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Tue Feb 26 18:38:00 UTC 2008 Richard W.M. Jones + + Implement virDomainBlockStats for QEMU/KVM. + * src/qemu_driver.c: Implement virDomainBlockStats for QEMU/KVM + using QEMU/KVM 'info blockstats' command (Richard Jones). + * src/qemu_driver.c, src/qemu_conf.h: const-correctness + fixes (Jim Meyering). + Tue Feb 26 18:28:00 UTC 2008 Richard W.M. Jones No address attribute in Xen domain XML. diff --git a/src/qemu_conf.h b/src/qemu_conf.h index 9f09ec1d15..735da4885f 100644 --- a/src/qemu_conf.h +++ b/src/qemu_conf.h @@ -318,13 +318,13 @@ struct qemud_driver { static inline int -qemudIsActiveVM(struct qemud_vm *vm) +qemudIsActiveVM(const struct qemud_vm *vm) { return vm->id != -1; } static inline int -qemudIsActiveNetwork(struct qemud_network *network) +qemudIsActiveNetwork(const struct qemud_network *network) { return network->active; } diff --git a/src/qemu_driver.c b/src/qemu_driver.c index b711dc1f0f..bfa5e397d6 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -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) @@ -1312,10 +1315,11 @@ static void qemudDispatchVMEvent(int fd, int events, void *opaque) { qemudDispatchVMFailure(driver, vm, fd); } -static int qemudMonitorCommand(struct qemud_driver *driver ATTRIBUTE_UNUSED, - struct qemud_vm *vm, - const char *cmd, - char **reply) { +static int +qemudMonitorCommand (const struct qemud_driver *driver ATTRIBUTE_UNUSED, + const struct qemud_vm *vm, + const char *cmd, + char **reply) { int size = 0; char *buf = NULL; size_t cmdlen = strlen(cmd); @@ -2527,6 +2531,148 @@ static int qemudDomainSetAutostart(virDomainPtr dom, 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) +{ + const struct qemud_driver *driver = + (struct qemud_driver *)dom->conn->privateData; + char *dummy, *info; + const char *p, *eol; + char qemu_dev_name[32]; + size_t len; + const 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, + "%s", _("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, + "%s", _("'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, + "%s", + _("'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 +3074,7 @@ static virDriver qemuDriver = { NULL, /* domainMigratePrepare */ NULL, /* domainMigratePerform */ NULL, /* domainMigrateFinish */ - NULL, /* domainBlockStats */ + qemudDomainBlockStats, /* domainBlockStats */ qemudDomainInterfaceStats, /* domainInterfaceStats */ NULL, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */