c401cc
From 681c822ef653930b15c7568a0d9a0f4248dbe61e Mon Sep 17 00:00:00 2001
c401cc
Message-Id: <681c822ef653930b15c7568a0d9a0f4248dbe61e.1391615407.git.jdenemar@redhat.com>
c401cc
From: "Daniel P. Berrange" <berrange@redhat.com>
c401cc
Date: Tue, 4 Feb 2014 06:23:07 -0700
c401cc
Subject: [PATCH] Push nwfilter update locking up to top level
c401cc
c401cc
https://bugzilla.redhat.com/show_bug.cgi?id=1034807
c401cc
c401cc
The NWFilter code has as a deadlock race condition between
c401cc
the virNWFilter{Define,Undefine} APIs and starting of guest
c401cc
VMs due to mis-matched lock ordering.
c401cc
c401cc
In the virNWFilter{Define,Undefine} codepaths the lock ordering
c401cc
is
c401cc
c401cc
  1. nwfilter driver lock
c401cc
  2. virt driver lock
c401cc
  3. nwfilter update lock
c401cc
  4. domain object lock
c401cc
c401cc
In the VM guest startup paths the lock ordering is
c401cc
c401cc
  1. virt driver lock
c401cc
  2. domain object lock
c401cc
  3. nwfilter update lock
c401cc
c401cc
As can be seen the domain object and nwfilter update locks are
c401cc
not acquired in a consistent order.
c401cc
c401cc
The fix used is to push the nwfilter update lock upto the top
c401cc
level resulting in a lock ordering for virNWFilter{Define,Undefine}
c401cc
of
c401cc
c401cc
  1. nwfilter driver lock
c401cc
  2. nwfilter update lock
c401cc
  3. virt driver lock
c401cc
  4. domain object lock
c401cc
c401cc
and VM start using
c401cc
c401cc
  1. nwfilter update lock
c401cc
  2. virt driver lock
c401cc
  3. domain object lock
c401cc
c401cc
This has the effect of serializing VM startup once again, even if
c401cc
no nwfilters are applied to the guest. There is also the possibility
c401cc
of deadlock due to a call graph loop via virNWFilterInstantiate
c401cc
and virNWFilterInstantiateFilterLate.
c401cc
c401cc
These two problems mean the lock must be turned into a read/write
c401cc
lock instead of a plain mutex at the same time. The lock is used to
c401cc
serialize changes to the "driver->nwfilters" hash, so the write lock
c401cc
only needs to be held by the define/undefine methods. All other
c401cc
methods can rely on a read lock which allows good concurrency.
c401cc
c401cc
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
c401cc
(cherry picked from commit 6e5c79a1b5a8b3a23e7df7ffe58fb272aa17fbfb)
c401cc
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
c401cc
---
c401cc
 src/conf/nwfilter_conf.c               | 23 +++++++++++------------
c401cc
 src/conf/nwfilter_conf.h               |  3 ++-
c401cc
 src/libvirt_private.syms               |  3 ++-
c401cc
 src/lxc/lxc_driver.c                   |  6 ++++++
c401cc
 src/nwfilter/nwfilter_driver.c         | 10 ++++++----
c401cc
 src/nwfilter/nwfilter_gentech_driver.c |  6 +-----
c401cc
 src/qemu/qemu_driver.c                 |  6 ++++++
c401cc
 src/uml/uml_driver.c                   |  4 ++++
c401cc
 8 files changed, 38 insertions(+), 23 deletions(-)
c401cc
c401cc
diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c
c401cc
index 1b86d36..5599443 100644
c401cc
--- a/src/conf/nwfilter_conf.c
c401cc
+++ b/src/conf/nwfilter_conf.c
c401cc
@@ -143,17 +143,22 @@ static const struct int_map chain_priorities[] = {
c401cc
 /*
c401cc
  * only one filter update allowed
c401cc
  */
c401cc
-static virMutex updateMutex;
c401cc
+static virRWLock updateLock;
c401cc
 static bool initialized = false;
c401cc
 
c401cc
 void
c401cc
-virNWFilterLockFilterUpdates(void) {
c401cc
-    virMutexLock(&updateMutex);
c401cc
+virNWFilterReadLockFilterUpdates(void) {
c401cc
+    virRWLockRead(&updateLock);
c401cc
+}
c401cc
+
c401cc
+void
c401cc
+virNWFilterWriteLockFilterUpdates(void) {
c401cc
+    virRWLockWrite(&updateLock);
c401cc
 }
c401cc
 
c401cc
 void
c401cc
 virNWFilterUnlockFilterUpdates(void) {
c401cc
-    virMutexUnlock(&updateMutex);
c401cc
+    virRWLockUnlock(&updateLock);
c401cc
 }
c401cc
 
c401cc
 
c401cc
@@ -2981,14 +2986,12 @@ virNWFilterObjAssignDef(virNWFilterObjListPtr nwfilters,
c401cc
         return NULL;
c401cc
     }
c401cc
 
c401cc
-    virNWFilterLockFilterUpdates();
c401cc
 
c401cc
     if ((nwfilter = virNWFilterObjFindByName(nwfilters, def->name))) {
c401cc
 
c401cc
         if (virNWFilterDefEqual(def, nwfilter->def, false)) {
c401cc
             virNWFilterDefFree(nwfilter->def);
c401cc
             nwfilter->def = def;
c401cc
-            virNWFilterUnlockFilterUpdates();
c401cc
             return nwfilter;
c401cc
         }
c401cc
 
c401cc
@@ -2996,7 +2999,6 @@ virNWFilterObjAssignDef(virNWFilterObjListPtr nwfilters,
c401cc
         /* trigger the update on VMs referencing the filter */
c401cc
         if (virNWFilterTriggerVMFilterRebuild()) {
c401cc
             nwfilter->newDef = NULL;
c401cc
-            virNWFilterUnlockFilterUpdates();
c401cc
             virNWFilterObjUnlock(nwfilter);
c401cc
             return NULL;
c401cc
         }
c401cc
@@ -3004,12 +3006,9 @@ virNWFilterObjAssignDef(virNWFilterObjListPtr nwfilters,
c401cc
         virNWFilterDefFree(nwfilter->def);
c401cc
         nwfilter->def = def;
c401cc
         nwfilter->newDef = NULL;
c401cc
-        virNWFilterUnlockFilterUpdates();
c401cc
         return nwfilter;
c401cc
     }
c401cc
 
c401cc
-    virNWFilterUnlockFilterUpdates();
c401cc
-
c401cc
     if (VIR_ALLOC(nwfilter) < 0)
c401cc
         return NULL;
c401cc
 
c401cc
@@ -3474,7 +3473,7 @@ int virNWFilterConfLayerInit(virDomainObjListIterator domUpdateCB,
c401cc
 
c401cc
     initialized = true;
c401cc
 
c401cc
-    if (virMutexInitRecursive(&updateMutex) < 0)
c401cc
+    if (virRWLockInit(&updateLock) < 0)
c401cc
         return -1;
c401cc
 
c401cc
     return 0;
c401cc
@@ -3486,7 +3485,7 @@ void virNWFilterConfLayerShutdown(void)
c401cc
     if (!initialized)
c401cc
         return;
c401cc
 
c401cc
-    virMutexDestroy(&updateMutex);
c401cc
+    virRWLockDestroy(&updateLock);
c401cc
 
c401cc
     initialized = false;
c401cc
     virNWFilterDomainFWUpdateOpaque = NULL;
c401cc
diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h
c401cc
index 29906f1..d460a08 100644
c401cc
--- a/src/conf/nwfilter_conf.h
c401cc
+++ b/src/conf/nwfilter_conf.h
c401cc
@@ -716,7 +716,8 @@ virNWFilterDefPtr virNWFilterDefParseFile(const char *filename);
c401cc
 void virNWFilterObjLock(virNWFilterObjPtr obj);
c401cc
 void virNWFilterObjUnlock(virNWFilterObjPtr obj);
c401cc
 
c401cc
-void virNWFilterLockFilterUpdates(void);
c401cc
+void virNWFilterWriteLockFilterUpdates(void);
c401cc
+void virNWFilterReadLockFilterUpdates(void);
c401cc
 void virNWFilterUnlockFilterUpdates(void);
c401cc
 
c401cc
 int virNWFilterConfLayerInit(virDomainObjListIterator domUpdateCB, void *opaque);
c401cc
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
c401cc
index fa56bfd..a07b52e 100644
c401cc
--- a/src/libvirt_private.syms
c401cc
+++ b/src/libvirt_private.syms
c401cc
@@ -566,7 +566,6 @@ virNWFilterDefParseString;
c401cc
 virNWFilterInstFiltersOnAllVMs;
c401cc
 virNWFilterJumpTargetTypeToString;
c401cc
 virNWFilterLoadAllConfigs;
c401cc
-virNWFilterLockFilterUpdates;
c401cc
 virNWFilterObjAssignDef;
c401cc
 virNWFilterObjDeleteDef;
c401cc
 virNWFilterObjFindByName;
c401cc
@@ -578,6 +577,7 @@ virNWFilterObjSaveDef;
c401cc
 virNWFilterObjUnlock;
c401cc
 virNWFilterPrintStateMatchFlags;
c401cc
 virNWFilterPrintTCPFlags;
c401cc
+virNWFilterReadLockFilterUpdates;
c401cc
 virNWFilterRegisterCallbackDriver;
c401cc
 virNWFilterRuleActionTypeToString;
c401cc
 virNWFilterRuleDirectionTypeToString;
c401cc
@@ -585,6 +585,7 @@ virNWFilterRuleProtocolTypeToString;
c401cc
 virNWFilterTestUnassignDef;
c401cc
 virNWFilterUnlockFilterUpdates;
c401cc
 virNWFilterUnRegisterCallbackDriver;
c401cc
+virNWFilterWriteLockFilterUpdates;
c401cc
 
c401cc
 
c401cc
 # conf/nwfilter_ipaddrmap.h
c401cc
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
c401cc
index cddf9d3..27f39a9 100644
c401cc
--- a/src/lxc/lxc_driver.c
c401cc
+++ b/src/lxc/lxc_driver.c
c401cc
@@ -1015,6 +1015,8 @@ static int lxcDomainCreateWithFiles(virDomainPtr dom,
c401cc
 
c401cc
     virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, -1);
c401cc
 
c401cc
+    virNWFilterReadLockFilterUpdates();
c401cc
+
c401cc
     if (!(vm = lxcDomObjFromDomain(dom)))
c401cc
         goto cleanup;
c401cc
 
c401cc
@@ -1053,6 +1055,7 @@ cleanup:
c401cc
     if (event)
c401cc
         virDomainEventStateQueue(driver->domainEventState, event);
c401cc
     virObjectUnref(cfg);
c401cc
+    virNWFilterUnlockFilterUpdates();
c401cc
     return ret;
c401cc
 }
c401cc
 
c401cc
@@ -1109,6 +1112,8 @@ lxcDomainCreateXMLWithFiles(virConnectPtr conn,
c401cc
 
c401cc
     virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, NULL);
c401cc
 
c401cc
+    virNWFilterReadLockFilterUpdates();
c401cc
+
c401cc
     if (!(caps = virLXCDriverGetCapabilities(driver, false)))
c401cc
         goto cleanup;
c401cc
 
c401cc
@@ -1164,6 +1169,7 @@ cleanup:
c401cc
         virDomainEventStateQueue(driver->domainEventState, event);
c401cc
     virObjectUnref(caps);
c401cc
     virObjectUnref(cfg);
c401cc
+    virNWFilterUnlockFilterUpdates();
c401cc
     return dom;
c401cc
 }
c401cc
 
c401cc
diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c
c401cc
index 6602d73..77fc608 100644
c401cc
--- a/src/nwfilter/nwfilter_driver.c
c401cc
+++ b/src/nwfilter/nwfilter_driver.c
c401cc
@@ -285,12 +285,14 @@ nwfilterStateReload(void)
c401cc
     virNWFilterLearnThreadsTerminate(true);
c401cc
 
c401cc
     nwfilterDriverLock(driverState);
c401cc
+    virNWFilterWriteLockFilterUpdates();
c401cc
     virNWFilterCallbackDriversLock();
c401cc
 
c401cc
     virNWFilterLoadAllConfigs(&driverState->nwfilters,
c401cc
                               driverState->configDir);
c401cc
 
c401cc
     virNWFilterCallbackDriversUnlock();
c401cc
+    virNWFilterUnlockFilterUpdates();
c401cc
     nwfilterDriverUnlock(driverState);
c401cc
 
c401cc
     virNWFilterInstFiltersOnAllVMs();
c401cc
@@ -556,6 +558,7 @@ nwfilterDefineXML(virConnectPtr conn,
c401cc
     virNWFilterPtr ret = NULL;
c401cc
 
c401cc
     nwfilterDriverLock(driver);
c401cc
+    virNWFilterWriteLockFilterUpdates();
c401cc
     virNWFilterCallbackDriversLock();
c401cc
 
c401cc
     if (!(def = virNWFilterDefParseString(xml)))
c401cc
@@ -582,6 +585,7 @@ cleanup:
c401cc
         virNWFilterObjUnlock(nwfilter);
c401cc
 
c401cc
     virNWFilterCallbackDriversUnlock();
c401cc
+    virNWFilterUnlockFilterUpdates();
c401cc
     nwfilterDriverUnlock(driver);
c401cc
     return ret;
c401cc
 }
c401cc
@@ -594,10 +598,9 @@ nwfilterUndefine(virNWFilterPtr obj) {
c401cc
     int ret = -1;
c401cc
 
c401cc
     nwfilterDriverLock(driver);
c401cc
+    virNWFilterWriteLockFilterUpdates();
c401cc
     virNWFilterCallbackDriversLock();
c401cc
 
c401cc
-    virNWFilterLockFilterUpdates();
c401cc
-
c401cc
     nwfilter = virNWFilterObjFindByUUID(&driver->nwfilters, obj->uuid);
c401cc
     if (!nwfilter) {
c401cc
         virReportError(VIR_ERR_NO_NWFILTER,
c401cc
@@ -628,9 +631,8 @@ cleanup:
c401cc
     if (nwfilter)
c401cc
         virNWFilterObjUnlock(nwfilter);
c401cc
 
c401cc
-    virNWFilterUnlockFilterUpdates();
c401cc
-
c401cc
     virNWFilterCallbackDriversUnlock();
c401cc
+    virNWFilterUnlockFilterUpdates();
c401cc
     nwfilterDriverUnlock(driver);
c401cc
     return ret;
c401cc
 }
c401cc
diff --git a/src/nwfilter/nwfilter_gentech_driver.c b/src/nwfilter/nwfilter_gentech_driver.c
c401cc
index f517569..203ba0e 100644
c401cc
--- a/src/nwfilter/nwfilter_gentech_driver.c
c401cc
+++ b/src/nwfilter/nwfilter_gentech_driver.c
c401cc
@@ -934,8 +934,6 @@ _virNWFilterInstantiateFilter(virNWFilterDriverStatePtr driver,
c401cc
     int ifindex;
c401cc
     int rc;
c401cc
 
c401cc
-    virNWFilterLockFilterUpdates();
c401cc
-
c401cc
     /* after grabbing the filter update lock check for the interface; if
c401cc
        it's not there anymore its filters will be or are being removed
c401cc
        (while holding the lock) and we don't want to build new ones */
c401cc
@@ -963,8 +961,6 @@ _virNWFilterInstantiateFilter(virNWFilterDriverStatePtr driver,
c401cc
                                         foundNewFilter);
c401cc
 
c401cc
 cleanup:
c401cc
-    virNWFilterUnlockFilterUpdates();
c401cc
-
c401cc
     return rc;
c401cc
 }
c401cc
 
c401cc
@@ -983,7 +979,7 @@ virNWFilterInstantiateFilterLate(virNWFilterDriverStatePtr driver,
c401cc
     int rc;
c401cc
     bool foundNewFilter = false;
c401cc
 
c401cc
-    virNWFilterLockFilterUpdates();
c401cc
+    virNWFilterReadLockFilterUpdates();
c401cc
 
c401cc
     rc = __virNWFilterInstantiateFilter(driver,
c401cc
                                         vmuuid,
c401cc
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
c401cc
index e2919a4..3e041c5 100644
c401cc
--- a/src/qemu/qemu_driver.c
c401cc
+++ b/src/qemu/qemu_driver.c
c401cc
@@ -1578,6 +1578,8 @@ static virDomainPtr qemuDomainCreateXML(virConnectPtr conn,
c401cc
     if (flags & VIR_DOMAIN_START_AUTODESTROY)
c401cc
         start_flags |= VIR_QEMU_PROCESS_START_AUTODESTROY;
c401cc
 
c401cc
+    virNWFilterReadLockFilterUpdates();
c401cc
+
c401cc
     if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
c401cc
         goto cleanup;
c401cc
 
c401cc
@@ -1658,6 +1660,7 @@ cleanup:
c401cc
     }
c401cc
     virObjectUnref(caps);
c401cc
     virObjectUnref(qemuCaps);
c401cc
+    virNWFilterUnlockFilterUpdates();
c401cc
     return dom;
c401cc
 }
c401cc
 
c401cc
@@ -6166,6 +6169,8 @@ qemuDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
c401cc
                   VIR_DOMAIN_START_BYPASS_CACHE |
c401cc
                   VIR_DOMAIN_START_FORCE_BOOT, -1);
c401cc
 
c401cc
+    virNWFilterReadLockFilterUpdates();
c401cc
+
c401cc
     if (!(vm = qemuDomObjFromDomain(dom)))
c401cc
         return -1;
c401cc
 
c401cc
@@ -6193,6 +6198,7 @@ endjob:
c401cc
 cleanup:
c401cc
     if (vm)
c401cc
         virObjectUnlock(vm);
c401cc
+    virNWFilterUnlockFilterUpdates();
c401cc
     return ret;
c401cc
 }
c401cc
 
c401cc
diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c
c401cc
index cf7f334..7860614 100644
c401cc
--- a/src/uml/uml_driver.c
c401cc
+++ b/src/uml/uml_driver.c
c401cc
@@ -1574,6 +1574,7 @@ static virDomainPtr umlDomainCreateXML(virConnectPtr conn, const char *xml,
c401cc
 
c401cc
     virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, NULL);
c401cc
 
c401cc
+    virNWFilterReadLockFilterUpdates();
c401cc
     umlDriverLock(driver);
c401cc
     if (!(def = virDomainDefParseString(xml, driver->caps, driver->xmlopt,
c401cc
                                         1 << VIR_DOMAIN_VIRT_UML,
c401cc
@@ -1613,6 +1614,7 @@ cleanup:
c401cc
     if (event)
c401cc
         umlDomainEventQueue(driver, event);
c401cc
     umlDriverUnlock(driver);
c401cc
+    virNWFilterUnlockFilterUpdates();
c401cc
     return dom;
c401cc
 }
c401cc
 
c401cc
@@ -1997,6 +1999,7 @@ static int umlDomainCreateWithFlags(virDomainPtr dom, unsigned int flags) {
c401cc
 
c401cc
     virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, -1);
c401cc
 
c401cc
+    virNWFilterReadLockFilterUpdates();
c401cc
     umlDriverLock(driver);
c401cc
     vm = virDomainObjListFindByUUID(driver->domains, dom->uuid);
c401cc
 
c401cc
@@ -2023,6 +2026,7 @@ cleanup:
c401cc
     if (event)
c401cc
         umlDomainEventQueue(driver, event);
c401cc
     umlDriverUnlock(driver);
c401cc
+    virNWFilterUnlockFilterUpdates();
c401cc
     return ret;
c401cc
 }
c401cc
 
c401cc
-- 
c401cc
1.8.5.3
c401cc