d76c62
From dbc6ca0acac24c12b30b74b706c848489f008d71 Mon Sep 17 00:00:00 2001
d76c62
Message-Id: <dbc6ca0acac24c12b30b74b706c848489f008d71@dist-git>
d76c62
From: Michal Privoznik <mprivozn@redhat.com>
d76c62
Date: Fri, 24 Jan 2020 15:05:51 +0100
d76c62
Subject: [PATCH] qemu_capabilities: Rework domain caps cache
d76c62
d76c62
Since v5.6.0-48-g270583ed98 we try to cache domain capabilities,
d76c62
i.e. store filled virDomainCaps in a hash table in virQEMUCaps
d76c62
for future use. However, there's a race condition in the way it's
d76c62
implemented. We use virQEMUCapsGetDomainCapsCache() to obtain the
d76c62
pointer to the hash table, then we search the hash table for
d76c62
cached data and if none is found the domcaps is constructed and
d76c62
put into the table. Problem is that this is all done without any
d76c62
locking, so if there are two threads trying to do the same, one
d76c62
will succeed and the other will fail inserting the data into the
d76c62
table.
d76c62
d76c62
Also, the API looks a bit fishy - obtaining pointer to the hash
d76c62
table is dangerous.
d76c62
d76c62
The solution is to use a mutex that guards the whole operation
d76c62
with the hash table. Then, the API can be changes to return
d76c62
virDomainCapsPtr directly.
d76c62
d76c62
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1791790
d76c62
d76c62
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
d76c62
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
d76c62
(cherry picked from commit c76009313f8068c848cad6cb517daf42e6716bb9)
d76c62
d76c62
https://bugzilla.redhat.com/show_bug.cgi?id=1794691
d76c62
d76c62
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
d76c62
Message-Id: <48a4b2f9ab1e8157e4b7baf1b506e90861a39308.1579874719.git.mprivozn@redhat.com>
d76c62
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
d76c62
---
d76c62
 src/qemu/qemu_capabilities.c | 122 +++++++++++++++++++++++++++++++++--
d76c62
 src/qemu/qemu_capabilities.h |  12 +++-
d76c62
 src/qemu/qemu_conf.c         |  65 +++----------------
d76c62
 3 files changed, 136 insertions(+), 63 deletions(-)
d76c62
d76c62
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
d76c62
index 84c62a4e28..edefb70309 100644
d76c62
--- a/src/qemu/qemu_capabilities.c
d76c62
+++ b/src/qemu/qemu_capabilities.c
d76c62
@@ -594,6 +594,26 @@ struct _virQEMUCapsAccel {
d76c62
     qemuMonitorCPUDefsPtr cpuModels;
d76c62
 };
d76c62
 
d76c62
+
d76c62
+typedef struct _virQEMUDomainCapsCache virQEMUDomainCapsCache;
d76c62
+typedef virQEMUDomainCapsCache *virQEMUDomainCapsCachePtr;
d76c62
+struct _virQEMUDomainCapsCache {
d76c62
+    virObjectLockable parent;
d76c62
+
d76c62
+    virHashTablePtr cache;
d76c62
+};
d76c62
+
d76c62
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virQEMUDomainCapsCache, virObjectUnref);
d76c62
+
d76c62
+static virClassPtr virQEMUDomainCapsCacheClass;
d76c62
+static void virQEMUDomainCapsCacheDispose(void *obj)
d76c62
+{
d76c62
+    virQEMUDomainCapsCachePtr cache = obj;
d76c62
+
d76c62
+    virHashFree(cache->cache);
d76c62
+}
d76c62
+
d76c62
+
d76c62
 /*
d76c62
  * Update the XML parser/formatter when adding more
d76c62
  * information to this struct so that it gets cached
d76c62
@@ -625,7 +645,7 @@ struct _virQEMUCaps {
d76c62
 
d76c62
     virArch arch;
d76c62
 
d76c62
-    virHashTablePtr domCapsCache;
d76c62
+    virQEMUDomainCapsCachePtr domCapsCache;
d76c62
 
d76c62
     size_t ngicCapabilities;
d76c62
     virGICCapability *gicCapabilities;
d76c62
@@ -651,6 +671,9 @@ static int virQEMUCapsOnceInit(void)
d76c62
     if (!VIR_CLASS_NEW(virQEMUCaps, virClassForObject()))
d76c62
         return -1;
d76c62
 
d76c62
+    if (!(VIR_CLASS_NEW(virQEMUDomainCapsCache, virClassForObjectLockable())))
d76c62
+        return -1;
d76c62
+
d76c62
     return 0;
d76c62
 }
d76c62
 
d76c62
@@ -1620,6 +1643,22 @@ int virQEMUCapsGetDefaultVersion(virCapsPtr caps,
d76c62
 }
d76c62
 
d76c62
 
d76c62
+static virQEMUDomainCapsCachePtr
d76c62
+virQEMUDomainCapsCacheNew(void)
d76c62
+{
d76c62
+    g_autoptr(virQEMUDomainCapsCache) cache = NULL;
d76c62
+
d76c62
+    if (virQEMUCapsInitialize() < 0)
d76c62
+        return NULL;
d76c62
+
d76c62
+    if (!(cache = virObjectLockableNew(virQEMUDomainCapsCacheClass)))
d76c62
+        return NULL;
d76c62
+
d76c62
+    if (!(cache->cache = virHashCreate(5, virObjectFreeHashData)))
d76c62
+        return NULL;
d76c62
+
d76c62
+    return g_steal_pointer(&cache);
d76c62
+}
d76c62
 
d76c62
 
d76c62
 virQEMUCapsPtr
d76c62
@@ -1637,7 +1676,7 @@ virQEMUCapsNew(void)
d76c62
     if (!(qemuCaps->flags = virBitmapNew(QEMU_CAPS_LAST)))
d76c62
         goto error;
d76c62
 
d76c62
-    if (!(qemuCaps->domCapsCache = virHashCreate(5, virObjectFreeHashData)))
d76c62
+    if (!(qemuCaps->domCapsCache = virQEMUDomainCapsCacheNew()))
d76c62
         goto error;
d76c62
 
d76c62
     return qemuCaps;
d76c62
@@ -1827,7 +1866,7 @@ void virQEMUCapsDispose(void *obj)
d76c62
 {
d76c62
     virQEMUCapsPtr qemuCaps = obj;
d76c62
 
d76c62
-    virHashFree(qemuCaps->domCapsCache);
d76c62
+    virObjectUnref(qemuCaps->domCapsCache);
d76c62
     virBitmapFree(qemuCaps->flags);
d76c62
 
d76c62
     VIR_FREE(qemuCaps->package);
d76c62
@@ -1987,9 +2026,82 @@ const char *virQEMUCapsGetPackage(virQEMUCapsPtr qemuCaps)
d76c62
 }
d76c62
 
d76c62
 
d76c62
-virHashTablePtr virQEMUCapsGetDomainCapsCache(virQEMUCapsPtr qemuCaps)
d76c62
+struct virQEMUCapsSearchDomcapsData {
d76c62
+    const char *path;
d76c62
+    const char *machine;
d76c62
+    virArch arch;
d76c62
+    virDomainVirtType virttype;
d76c62
+};
d76c62
+
d76c62
+
d76c62
+static int
d76c62
+virQEMUCapsSearchDomcaps(const void *payload,
d76c62
+                         const void *name G_GNUC_UNUSED,
d76c62
+                         const void *opaque)
d76c62
 {
d76c62
-    return qemuCaps->domCapsCache;
d76c62
+    virDomainCapsPtr domCaps = (virDomainCapsPtr) payload;
d76c62
+    struct virQEMUCapsSearchDomcapsData *data = (struct virQEMUCapsSearchDomcapsData *) opaque;
d76c62
+
d76c62
+    if (STREQ_NULLABLE(data->path, domCaps->path) &&
d76c62
+        STREQ_NULLABLE(data->machine, domCaps->machine) &&
d76c62
+        data->arch == domCaps->arch &&
d76c62
+        data->virttype == domCaps->virttype)
d76c62
+        return 1;
d76c62
+
d76c62
+    return 0;
d76c62
+}
d76c62
+
d76c62
+
d76c62
+virDomainCapsPtr
d76c62
+virQEMUCapsGetDomainCapsCache(virQEMUCapsPtr qemuCaps,
d76c62
+                              const char *machine,
d76c62
+                              virArch arch,
d76c62
+                              virDomainVirtType virttype,
d76c62
+                              virArch hostarch,
d76c62
+                              bool privileged,
d76c62
+                              virFirmwarePtr *firmwares,
d76c62
+                              size_t nfirmwares)
d76c62
+{
d76c62
+    virQEMUDomainCapsCachePtr cache = qemuCaps->domCapsCache;
d76c62
+    virDomainCapsPtr domCaps = NULL;
d76c62
+    const char *path = virQEMUCapsGetBinary(qemuCaps);
d76c62
+    struct virQEMUCapsSearchDomcapsData data = {
d76c62
+        .path = path,
d76c62
+        .machine = machine,
d76c62
+        .arch = arch,
d76c62
+        .virttype = virttype,
d76c62
+    };
d76c62
+
d76c62
+    virObjectLock(cache);
d76c62
+
d76c62
+    domCaps = virHashSearch(cache->cache, virQEMUCapsSearchDomcaps, &data, NULL);
d76c62
+
d76c62
+    if (!domCaps) {
d76c62
+        g_autoptr(virDomainCaps) tempDomCaps = NULL;
d76c62
+        g_autofree char *key = NULL;
d76c62
+
d76c62
+        /* hash miss, build new domcaps */
d76c62
+        if (!(tempDomCaps = virDomainCapsNew(path, machine,
d76c62
+                                             arch, virttype)))
d76c62
+            goto cleanup;
d76c62
+
d76c62
+        if (virQEMUCapsFillDomainCaps(qemuCaps, hostarch, tempDomCaps,
d76c62
+                                      privileged, firmwares, nfirmwares) < 0)
d76c62
+            goto cleanup;
d76c62
+
d76c62
+        key = g_strdup_printf("%d:%d:%s:%s", arch, virttype,
d76c62
+                              NULLSTR(machine), path);
d76c62
+
d76c62
+        if (virHashAddEntry(cache->cache, key, tempDomCaps) < 0)
d76c62
+            goto cleanup;
d76c62
+
d76c62
+        domCaps = g_steal_pointer(&tempDomCaps);
d76c62
+    }
d76c62
+
d76c62
+    virObjectRef(domCaps);
d76c62
+ cleanup:
d76c62
+    virObjectUnlock(cache);
d76c62
+    return domCaps;
d76c62
 }
d76c62
 
d76c62
 
d76c62
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
d76c62
index 193c19fc81..d76c1dbfa9 100644
d76c62
--- a/src/qemu/qemu_capabilities.h
d76c62
+++ b/src/qemu/qemu_capabilities.h
d76c62
@@ -571,7 +571,17 @@ const char *virQEMUCapsGetBinary(virQEMUCapsPtr qemuCaps);
d76c62
 virArch virQEMUCapsGetArch(virQEMUCapsPtr qemuCaps);
d76c62
 unsigned int virQEMUCapsGetVersion(virQEMUCapsPtr qemuCaps);
d76c62
 const char *virQEMUCapsGetPackage(virQEMUCapsPtr qemuCaps);
d76c62
-virHashTablePtr virQEMUCapsGetDomainCapsCache(virQEMUCapsPtr qemuCaps);
d76c62
+
d76c62
+virDomainCapsPtr
d76c62
+virQEMUCapsGetDomainCapsCache(virQEMUCapsPtr qemuCaps,
d76c62
+                              const char *machine,
d76c62
+                              virArch arch,
d76c62
+                              virDomainVirtType virttype,
d76c62
+                              virArch hostarch,
d76c62
+                              bool privileged,
d76c62
+                              virFirmwarePtr *firmwares,
d76c62
+                              size_t nfirmwares);
d76c62
+
d76c62
 unsigned int virQEMUCapsGetKVMVersion(virQEMUCapsPtr qemuCaps);
d76c62
 int virQEMUCapsAddCPUDefinitions(virQEMUCapsPtr qemuCaps,
d76c62
                                  virDomainVirtType type,
d76c62
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
d76c62
index e33ef4895e..029996427e 100644
d76c62
--- a/src/qemu/qemu_conf.c
d76c62
+++ b/src/qemu/qemu_conf.c
d76c62
@@ -1338,31 +1338,6 @@ virCapsPtr virQEMUDriverGetCapabilities(virQEMUDriverPtr driver,
d76c62
 }
d76c62
 
d76c62
 
d76c62
-struct virQEMUDriverSearchDomcapsData {
d76c62
-    const char *path;
d76c62
-    const char *machine;
d76c62
-    virArch arch;
d76c62
-    virDomainVirtType virttype;
d76c62
-};
d76c62
-
d76c62
-
d76c62
-static int
d76c62
-virQEMUDriverSearchDomcaps(const void *payload,
d76c62
-                           const void *name G_GNUC_UNUSED,
d76c62
-                           const void *opaque)
d76c62
-{
d76c62
-    virDomainCapsPtr domCaps = (virDomainCapsPtr) payload;
d76c62
-    struct virQEMUDriverSearchDomcapsData *data = (struct virQEMUDriverSearchDomcapsData *) opaque;
d76c62
-
d76c62
-    if (STREQ_NULLABLE(data->path, domCaps->path) &&
d76c62
-        STREQ_NULLABLE(data->machine, domCaps->machine) &&
d76c62
-        data->arch == domCaps->arch &&
d76c62
-        data->virttype == domCaps->virttype)
d76c62
-        return 1;
d76c62
-
d76c62
-    return 0;
d76c62
-}
d76c62
-
d76c62
 /**
d76c62
  * virQEMUDriverGetDomainCapabilities:
d76c62
  *
d76c62
@@ -1381,40 +1356,16 @@ virQEMUDriverGetDomainCapabilities(virQEMUDriverPtr driver,
d76c62
                                    virArch arch,
d76c62
                                    virDomainVirtType virttype)
d76c62
 {
d76c62
-    g_autoptr(virDomainCaps) domCaps = NULL;
d76c62
     g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
d76c62
-    virHashTablePtr domCapsCache = virQEMUCapsGetDomainCapsCache(qemuCaps);
d76c62
-    struct virQEMUDriverSearchDomcapsData data = {
d76c62
-        .path = virQEMUCapsGetBinary(qemuCaps),
d76c62
-        .machine = machine,
d76c62
-        .arch = arch,
d76c62
-        .virttype = virttype,
d76c62
-    };
d76c62
 
d76c62
-    domCaps = virHashSearch(domCapsCache,
d76c62
-                            virQEMUDriverSearchDomcaps, &data, NULL);
d76c62
-    if (!domCaps) {
d76c62
-        g_autofree char *key = NULL;
d76c62
-
d76c62
-        /* hash miss, build new domcaps */
d76c62
-        if (!(domCaps = virDomainCapsNew(data.path, data.machine,
d76c62
-                                         data.arch, data.virttype)))
d76c62
-            return NULL;
d76c62
-
d76c62
-        if (virQEMUCapsFillDomainCaps(qemuCaps, driver->hostarch, domCaps,
d76c62
-                                      driver->privileged,
d76c62
-                                      cfg->firmwares, cfg->nfirmwares) < 0)
d76c62
-            return NULL;
d76c62
-
d76c62
-        key = g_strdup_printf("%d:%d:%s:%s", data.arch, data.virttype,
d76c62
-                              NULLSTR(data.machine), NULLSTR(data.path));
d76c62
-
d76c62
-        if (virHashAddEntry(domCapsCache, key, domCaps) < 0)
d76c62
-            return NULL;
d76c62
-    }
d76c62
-
d76c62
-    virObjectRef(domCaps);
d76c62
-    return g_steal_pointer(&domCaps);
d76c62
+    return virQEMUCapsGetDomainCapsCache(qemuCaps,
d76c62
+                                         machine,
d76c62
+                                         arch,
d76c62
+                                         virttype,
d76c62
+                                         driver->hostarch,
d76c62
+                                         driver->privileged,
d76c62
+                                         cfg->firmwares,
d76c62
+                                         cfg->nfirmwares);
d76c62
 }
d76c62
 
d76c62
 
d76c62
-- 
d76c62
2.25.0
d76c62