By probing for qemu machine types, we increased the time of a
GetCapabilities call from 100us to a whopping 60ms.
This patch takes the approach of only probing for machine types
when the mtime of the emulator binary changed since the last time
the capabilities were generated.
This brings the time of the call back to what it was for the most
common case.
* src/capabilities.h: cache the emulator binary mtime
* src/qemu_conf.c: add qemudGetOldMachines() to copy the machine
types from the old caps struct if the mtime for the binary hasn't
changed
* src/qemu_conf.h, src/qemu_driver.c: pass the old caps pointer to
qemudCapsInit()
---
src/capabilities.h | 1 +
src/qemu_conf.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++---
src/qemu_conf.h | 2 +-
src/qemu_driver.c | 4 +-
4 files changed, 95 insertions(+), 9 deletions(-)
diff --git a/src/capabilities.h b/src/capabilities.h
index aab084c..b958d95 100644
--- a/src/capabilities.h
+++ b/src/capabilities.h
@@ -49,6 +49,7 @@ struct _virCapsGuestDomainInfo {
char *loader;
int nmachines;
virCapsGuestMachinePtr *machines;
+ time_t emulator_mtime; /* do @machines need refreshing? */
};
typedef struct _virCapsGuestDomain virCapsGuestDomain;
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
index a3986df..a9d559e 100644
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -457,7 +457,72 @@ rewait:
}
static int
+qemudGetOldMachines(const char *ostype,
+ const char *arch,
+ int wordsize,
+ const char *emulator,
+ time_t emulator_mtime,
+ virCapsPtr old_caps,
+ virCapsGuestMachinePtr **machines,
+ int *nmachines)
+{
+ int i;
+
+ for (i = 0; i < old_caps->nguests; i++) {
+ virCapsGuestPtr guest = old_caps->guests[i];
+ virCapsGuestDomainInfoPtr info = &guest->arch.defaultInfo;
+ virCapsGuestMachinePtr *list;
+
+ if (!STREQ(ostype, guest->ostype) ||
+ !STREQ(arch, guest->arch.name) ||
+ wordsize != guest->arch.wordsize ||
+ !STREQ(emulator, info->emulator))
+ continue;
+
+ if (emulator_mtime != info->emulator_mtime) {
+ VIR_DEBUG("mtime on %s has changed, refreshing machine types",
+ info->emulator);
+ return 0;
+ }
+
+ /* It sucks to have to dup these, when we're most likely going
+ * to free the old caps anyway - except if an error occurs, we'll
+ * stick with the old caps.
+ * Also, if we get OOM here, just let the caller try and probe
+ * the binary directly, which will probably fail too.
+ */
+ if (VIR_ALLOC_N(list, info->nmachines) < 0)
+ return 0;
+
+ for (i = 0; i < info->nmachines; i++) {
+ if (VIR_ALLOC(list[i]) < 0) {
+ virCapabilitiesFreeMachines(list, info->nmachines);
+ return 0;
+ }
+ if (info->machines[i]->name &&
+ !(list[i]->name = strdup(info->machines[i]->name))) {
+ virCapabilitiesFreeMachines(list, info->nmachines);
+ return 0;
+ }
+ if (info->machines[i]->canonical &&
+ !(list[i]->canonical = strdup(info->machines[i]->canonical))) {
+ virCapabilitiesFreeMachines(list, info->nmachines);
+ return 0;
+ }
+ }
+
+ *machines = list;
+ *nmachines = info->nmachines;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
qemudCapsInitGuest(virCapsPtr caps,
+ virCapsPtr old_caps,
const char *hostmachine,
const struct qemu_arch_info *info,
int hvm) {
@@ -467,8 +532,10 @@ qemudCapsInitGuest(virCapsPtr caps,
int haskqemu = 0;
const char *kvmbin = NULL;
const char *binary = NULL;
+ time_t binary_mtime;
virCapsGuestMachinePtr *machines = NULL;
int nmachines = 0;
+ struct stat st;
/* Check for existance of base emulator, or alternate base
* which can be used with magic cpu choice
@@ -507,6 +574,15 @@ qemudCapsInitGuest(virCapsPtr caps,
if (!binary)
return 0;
+ if (stat(binary, &st) == 0) {
+ binary_mtime = st.st_mtime;
+ } else {
+ char ebuf[1024];
+ VIR_WARN(_("Failed to stat %s, most peculiar : %s"),
+ binary, virStrerror(errno, ebuf, sizeof(ebuf)));
+ binary_mtime = 0;
+ }
+
if (info->machine) {
virCapsGuestMachinePtr machine;
@@ -526,9 +602,16 @@ qemudCapsInitGuest(virCapsPtr caps,
machines[0] = machine;
nmachines = 1;
-
- } else if (qemudProbeMachineTypes(binary, &machines, &nmachines) < 0)
- return -1;
+ } else {
+ int probe = 1;
+ if (old_caps && binary_mtime)
+ probe = !qemudGetOldMachines(hvm ? "hvm" : "xen",
info->arch,
+ info->wordsize, binary, binary_mtime,
+ old_caps, &machines, &nmachines);
+ if (probe &&
+ qemudProbeMachineTypes(binary, &machines, &nmachines) < 0)
+ return -1;
+ }
/* We register kvm as the base emulator too, since we can
* just give -no-kvm to disable acceleration if required */
@@ -548,6 +631,8 @@ qemudCapsInitGuest(virCapsPtr caps,
return -1;
}
+ guest->arch.defaultInfo.emulator_mtime = binary_mtime;
+
if (hvm) {
if (virCapabilitiesAddGuestDomain(guest,
"qemu",
@@ -597,7 +682,7 @@ qemudCapsInitGuest(virCapsPtr caps,
return 0;
}
-virCapsPtr qemudCapsInit(void) {
+virCapsPtr qemudCapsInit(virCapsPtr old_caps) {
struct utsname utsname;
virCapsPtr caps;
int i;
@@ -626,7 +711,7 @@ virCapsPtr qemudCapsInit(void) {
/* First the pure HVM guests */
for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_hvm) ; i++)
- if (qemudCapsInitGuest(caps,
+ if (qemudCapsInitGuest(caps, old_caps,
utsname.machine,
&arch_info_hvm[i], 1) < 0)
goto no_memory;
@@ -639,7 +724,7 @@ virCapsPtr qemudCapsInit(void) {
if (STREQ(arch_info_xen[i].arch, utsname.machine) ||
(STREQ(utsname.machine, "x86_64") &&
STREQ(arch_info_xen[i].arch, "i686"))) {
- if (qemudCapsInitGuest(caps,
+ if (qemudCapsInitGuest(caps, old_caps,
utsname.machine,
&arch_info_xen[i], 0) < 0)
goto no_memory;
diff --git a/src/qemu_conf.h b/src/qemu_conf.h
index 316a787..ad68e31 100644
--- a/src/qemu_conf.h
+++ b/src/qemu_conf.h
@@ -125,7 +125,7 @@ struct qemud_driver {
int qemudLoadDriverConfig(struct qemud_driver *driver,
const char *filename);
-virCapsPtr qemudCapsInit (void);
+virCapsPtr qemudCapsInit (virCapsPtr old_caps);
int qemudExtractVersion (virConnectPtr conn,
struct qemud_driver *driver);
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index d10c0a1..95ee0b4 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -516,7 +516,7 @@ qemudStartup(int privileged) {
virStrerror(-rc, buf, sizeof(buf)));
}
- if ((qemu_driver->caps = qemudCapsInit()) == NULL)
+ if ((qemu_driver->caps = qemudCapsInit(NULL)) == NULL)
goto out_of_memory;
if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) {
@@ -2440,7 +2440,7 @@ static char *qemudGetCapabilities(virConnectPtr conn) {
char *xml = NULL;
qemuDriverLock(driver);
- if ((caps = qemudCapsInit()) == NULL) {
+ if ((caps = qemudCapsInit(qemu_driver->caps)) == NULL) {
virReportOOMError(conn);
goto cleanup;
}
--
1.6.2.5