From: "Daniel P. Berrange" <berrange(a)redhat.com>
(see notes about conflict resolution at the end. I'm not sending the
other 5 patches required, since they were all simple cherry-picks of:
4f2094346d98f4ed6a2de115d204c166cc563496
b77b16ce4166dcc87963ae5d279b77b162ddbb55
ebca369e3fe5ac999c261c2d44e60a1bac3cfe65
999d72fbd59ea712128ae294b69b6a54039d757b
c065984b58000a44c90588198d222a314ac532fd
)
The NWFilter code has as a deadlock race condition between
the virNWFilter{Define,Undefine} APIs and starting of guest
VMs due to mis-matched lock ordering.
In the virNWFilter{Define,Undefine} codepaths the lock ordering
is
1. nwfilter driver lock
2. virt driver lock
3. nwfilter update lock
4. domain object lock
In the VM guest startup paths the lock ordering is
1. virt driver lock
2. domain object lock
3. nwfilter update lock
As can be seen the domain object and nwfilter update locks are
not acquired in a consistent order.
The fix used is to push the nwfilter update lock upto the top
level resulting in a lock ordering for virNWFilter{Define,Undefine}
of
1. nwfilter driver lock
2. nwfilter update lock
3. virt driver lock
4. domain object lock
and VM start using
1. nwfilter update lock
2. virt driver lock
3. domain object lock
This has the effect of serializing VM startup once again, even if
no nwfilters are applied to the guest. There is also the possibility
of deadlock due to a call graph loop via virNWFilterInstantiate
and virNWFilterInstantiateFilterLate.
These two problems mean the lock must be turned into a read/write
lock instead of a plain mutex at the same time. The lock is used to
serialize changes to the "driver->nwfilters" hash, so the write lock
only needs to be held by the define/undefine methods. All other
methods can rely on a read lock which allows good concurrency.
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
(cherry picked from commit 6e5c79a1b5a8b3a23e7df7ffe58fb272aa17fbfb)
Conflicts:
src/conf/nwfilter_conf.c
- virReportOOMError() in context of one hunk.
src/lxc/lxc_driver.c
- functions renamed, and lxc object locking changed, creating
a conflict in the context.
---
src/conf/nwfilter_conf.c | 25 ++++++++++++-------------
src/conf/nwfilter_conf.h | 3 ++-
src/libvirt_private.syms | 3 ++-
src/lxc/lxc_driver.c | 8 +++++++-
src/nwfilter/nwfilter_driver.c | 10 ++++++----
src/nwfilter/nwfilter_gentech_driver.c | 6 +-----
src/qemu/qemu_driver.c | 6 ++++++
src/uml/uml_driver.c | 4 ++++
8 files changed, 40 insertions(+), 25 deletions(-)
diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c
index 94b4fe9..e943bab 100644
--- a/src/conf/nwfilter_conf.c
+++ b/src/conf/nwfilter_conf.c
@@ -2,7 +2,7 @@
* nwfilter_conf.c: network filter XML processing
* (derived from storage_conf.c)
*
- * Copyright (C) 2006-2012 Red Hat, Inc.
+ * Copyright (C) 2006-2012, 2014 Red Hat, Inc.
* Copyright (C) 2006-2008 Daniel P. Berrange
*
* Copyright (C) 2010-2011 IBM Corporation
@@ -143,17 +143,22 @@ static const struct int_map chain_priorities[] = {
/*
* only one filter update allowed
*/
-static virMutex updateMutex;
+static virRWLock updateLock;
static bool initialized = false;
void
-virNWFilterLockFilterUpdates(void) {
- virMutexLock(&updateMutex);
+virNWFilterReadLockFilterUpdates(void) {
+ virRWLockRead(&updateLock);
+}
+
+void
+virNWFilterWriteLockFilterUpdates(void) {
+ virRWLockWrite(&updateLock);
}
void
virNWFilterUnlockFilterUpdates(void) {
- virMutexUnlock(&updateMutex);
+ virRWLockUnlock(&updateLock);
}
@@ -2992,14 +2997,12 @@ virNWFilterObjAssignDef(virNWFilterObjListPtr nwfilters,
return NULL;
}
- virNWFilterLockFilterUpdates();
if ((nwfilter = virNWFilterObjFindByName(nwfilters, def->name))) {
if (virNWFilterDefEqual(def, nwfilter->def, false)) {
virNWFilterDefFree(nwfilter->def);
nwfilter->def = def;
- virNWFilterUnlockFilterUpdates();
return nwfilter;
}
@@ -3007,7 +3010,6 @@ virNWFilterObjAssignDef(virNWFilterObjListPtr nwfilters,
/* trigger the update on VMs referencing the filter */
if (virNWFilterTriggerVMFilterRebuild()) {
nwfilter->newDef = NULL;
- virNWFilterUnlockFilterUpdates();
virNWFilterObjUnlock(nwfilter);
return NULL;
}
@@ -3015,12 +3017,9 @@ virNWFilterObjAssignDef(virNWFilterObjListPtr nwfilters,
virNWFilterDefFree(nwfilter->def);
nwfilter->def = def;
nwfilter->newDef = NULL;
- virNWFilterUnlockFilterUpdates();
return nwfilter;
}
- virNWFilterUnlockFilterUpdates();
-
if (VIR_ALLOC(nwfilter) < 0) {
virReportOOMError();
return NULL;
@@ -3492,7 +3491,7 @@ int virNWFilterConfLayerInit(virDomainObjListIterator domUpdateCB,
initialized = true;
- if (virMutexInitRecursive(&updateMutex) < 0)
+ if (virRWLockInit(&updateLock) < 0)
return -1;
return 0;
@@ -3504,7 +3503,7 @@ void virNWFilterConfLayerShutdown(void)
if (!initialized)
return;
- virMutexDestroy(&updateMutex);
+ virRWLockDestroy(&updateLock);
initialized = false;
virNWFilterDomainFWUpdateOpaque = NULL;
diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h
index 29906f1..d460a08 100644
--- a/src/conf/nwfilter_conf.h
+++ b/src/conf/nwfilter_conf.h
@@ -716,7 +716,8 @@ virNWFilterDefPtr virNWFilterDefParseFile(const char *filename);
void virNWFilterObjLock(virNWFilterObjPtr obj);
void virNWFilterObjUnlock(virNWFilterObjPtr obj);
-void virNWFilterLockFilterUpdates(void);
+void virNWFilterWriteLockFilterUpdates(void);
+void virNWFilterReadLockFilterUpdates(void);
void virNWFilterUnlockFilterUpdates(void);
int virNWFilterConfLayerInit(virDomainObjListIterator domUpdateCB, void *opaque);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index a6348bc..753c698 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -551,7 +551,6 @@ virNWFilterDefParseString;
virNWFilterInstFiltersOnAllVMs;
virNWFilterJumpTargetTypeToString;
virNWFilterLoadAllConfigs;
-virNWFilterLockFilterUpdates;
virNWFilterObjAssignDef;
virNWFilterObjDeleteDef;
virNWFilterObjFindByName;
@@ -563,6 +562,7 @@ virNWFilterObjSaveDef;
virNWFilterObjUnlock;
virNWFilterPrintStateMatchFlags;
virNWFilterPrintTCPFlags;
+virNWFilterReadLockFilterUpdates;
virNWFilterRegisterCallbackDriver;
virNWFilterRuleActionTypeToString;
virNWFilterRuleDirectionTypeToString;
@@ -570,6 +570,7 @@ virNWFilterRuleProtocolTypeToString;
virNWFilterTestUnassignDef;
virNWFilterUnlockFilterUpdates;
virNWFilterUnRegisterCallbackDriver;
+virNWFilterWriteLockFilterUpdates;
# conf/nwfilter_ipaddrmap.h
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index fed3775..d59f0f6 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 Red Hat, Inc.
+ * Copyright (C) 2010-2014 Red Hat, Inc.
* Copyright IBM Corp. 2008
*
* lxc_driver.c: linux container driver functions
@@ -1102,6 +1102,8 @@ static int lxcDomainCreateWithFlags(virDomainPtr dom, unsigned int
flags)
virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, -1);
+ virNWFilterReadLockFilterUpdates();
+
lxcDriverLock(driver);
vm = virDomainObjListFindByUUID(driver->domains, dom->uuid);
if (!vm) {
@@ -1146,6 +1148,7 @@ cleanup:
if (event)
virDomainEventStateQueue(driver->domainEventState, event);
lxcDriverUnlock(driver);
+ virNWFilterUnlockFilterUpdates();
return ret;
}
@@ -1184,6 +1187,8 @@ lxcDomainCreateXML(virConnectPtr conn,
virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, NULL);
+ virNWFilterReadLockFilterUpdates();
+
lxcDriverLock(driver);
if (!(def = virDomainDefParseString(xml, driver->caps, driver->xmlopt,
1 << VIR_DOMAIN_VIRT_LXC,
@@ -1235,6 +1240,7 @@ cleanup:
if (event)
virDomainEventStateQueue(driver->domainEventState, event);
lxcDriverUnlock(driver);
+ virNWFilterUnlockFilterUpdates();
return dom;
}
diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c
index 630f622..2d5dcaf 100644
--- a/src/nwfilter/nwfilter_driver.c
+++ b/src/nwfilter/nwfilter_driver.c
@@ -284,12 +284,14 @@ nwfilterStateReload(void)
virNWFilterLearnThreadsTerminate(true);
nwfilterDriverLock(driverState);
+ virNWFilterWriteLockFilterUpdates();
virNWFilterCallbackDriversLock();
virNWFilterLoadAllConfigs(&driverState->nwfilters,
driverState->configDir);
virNWFilterCallbackDriversUnlock();
+ virNWFilterUnlockFilterUpdates();
nwfilterDriverUnlock(driverState);
virNWFilterInstFiltersOnAllVMs();
@@ -540,6 +542,7 @@ nwfilterDefineXML(virConnectPtr conn,
virNWFilterPtr ret = NULL;
nwfilterDriverLock(driver);
+ virNWFilterWriteLockFilterUpdates();
virNWFilterCallbackDriversLock();
if (!(def = virNWFilterDefParseString(xml)))
@@ -566,6 +569,7 @@ cleanup:
virNWFilterObjUnlock(nwfilter);
virNWFilterCallbackDriversUnlock();
+ virNWFilterUnlockFilterUpdates();
nwfilterDriverUnlock(driver);
return ret;
}
@@ -578,10 +582,9 @@ nwfilterUndefine(virNWFilterPtr obj) {
int ret = -1;
nwfilterDriverLock(driver);
+ virNWFilterWriteLockFilterUpdates();
virNWFilterCallbackDriversLock();
- virNWFilterLockFilterUpdates();
-
nwfilter = virNWFilterObjFindByUUID(&driver->nwfilters, obj->uuid);
if (!nwfilter) {
virReportError(VIR_ERR_NO_NWFILTER,
@@ -612,9 +615,8 @@ cleanup:
if (nwfilter)
virNWFilterObjUnlock(nwfilter);
- virNWFilterUnlockFilterUpdates();
-
virNWFilterCallbackDriversUnlock();
+ virNWFilterUnlockFilterUpdates();
nwfilterDriverUnlock(driver);
return ret;
}
diff --git a/src/nwfilter/nwfilter_gentech_driver.c
b/src/nwfilter/nwfilter_gentech_driver.c
index 637e647..c9ce514 100644
--- a/src/nwfilter/nwfilter_gentech_driver.c
+++ b/src/nwfilter/nwfilter_gentech_driver.c
@@ -947,8 +947,6 @@ _virNWFilterInstantiateFilter(virNWFilterDriverStatePtr driver,
int ifindex;
int rc;
- virNWFilterLockFilterUpdates();
-
/* after grabbing the filter update lock check for the interface; if
it's not there anymore its filters will be or are being removed
(while holding the lock) and we don't want to build new ones */
@@ -976,8 +974,6 @@ _virNWFilterInstantiateFilter(virNWFilterDriverStatePtr driver,
foundNewFilter);
cleanup:
- virNWFilterUnlockFilterUpdates();
-
return rc;
}
@@ -996,7 +992,7 @@ virNWFilterInstantiateFilterLate(virNWFilterDriverStatePtr driver,
int rc;
bool foundNewFilter = false;
- virNWFilterLockFilterUpdates();
+ virNWFilterReadLockFilterUpdates();
rc = __virNWFilterInstantiateFilter(driver,
vmuuid,
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 8ebfadd..b209614 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -1542,6 +1542,8 @@ static virDomainPtr qemuDomainCreateXML(virConnectPtr conn,
if (flags & VIR_DOMAIN_START_AUTODESTROY)
start_flags |= VIR_QEMU_PROCESS_START_AUTODESTROY;
+ virNWFilterReadLockFilterUpdates();
+
if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
goto cleanup;
@@ -1619,6 +1621,7 @@ cleanup:
}
virObjectUnref(caps);
virObjectUnref(qemuCaps);
+ virNWFilterUnlockFilterUpdates();
return dom;
}
@@ -5846,6 +5849,8 @@ qemuDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
VIR_DOMAIN_START_BYPASS_CACHE |
VIR_DOMAIN_START_FORCE_BOOT, -1);
+ virNWFilterReadLockFilterUpdates();
+
if (!(vm = qemuDomObjFromDomain(dom)))
return -1;
@@ -5873,6 +5878,7 @@ endjob:
cleanup:
if (vm)
virObjectUnlock(vm);
+ virNWFilterUnlockFilterUpdates();
return ret;
}
diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c
index 74aa94a..5947082 100644
--- a/src/uml/uml_driver.c
+++ b/src/uml/uml_driver.c
@@ -1572,6 +1572,7 @@ static virDomainPtr umlDomainCreateXML(virConnectPtr conn, const
char *xml,
virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, NULL);
+ virNWFilterReadLockFilterUpdates();
umlDriverLock(driver);
if (!(def = virDomainDefParseString(xml, driver->caps, driver->xmlopt,
1 << VIR_DOMAIN_VIRT_UML,
@@ -1611,6 +1612,7 @@ cleanup:
if (event)
umlDomainEventQueue(driver, event);
umlDriverUnlock(driver);
+ virNWFilterUnlockFilterUpdates();
return dom;
}
@@ -1993,6 +1995,7 @@ static int umlDomainCreateWithFlags(virDomainPtr dom, unsigned int
flags) {
virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, -1);
+ virNWFilterReadLockFilterUpdates();
umlDriverLock(driver);
vm = virDomainObjListFindByUUID(driver->domains, dom->uuid);
@@ -2019,6 +2022,7 @@ cleanup:
if (event)
umlDomainEventQueue(driver, event);
umlDriverUnlock(driver);
+ virNWFilterUnlockFilterUpdates();
return ret;
}
--
1.8.5.3