render / rpms / libvirt

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