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