|
|
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 |
|