]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Add a mutex to serialize updates to firewall
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 22 Jan 2014 18:13:30 +0000 (18:13 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Mon, 10 Mar 2014 12:43:15 +0000 (12:43 +0000)
The nwfilter conf update mutex previously serialized
updates to the internal data structures for firewall
rules, and updates to the firewall itself. The latter
was recently turned into a read/write lock, and filter
instantiation allowed to proceed in parallel. It was
believed that this was ok, since each filter is created
on a separate iptables/ebtables chain.

It turns out that there is a subtle lock ordering problem
on virNWFilterObjPtr instances. __virNWFilterInstantiateFilter
will hold a lock on the virNWFilterObjPtr it is instantiating.
This in turn invokes virNWFilterInstantiate which then invokes
virNWFilterDetermineMissingVarsRec which then invokes
virNWFilterObjFindByName. This iterates over every single
virNWFilterObjPtr in the list, locking them and checking their
name. So if 2 or more threads try to instantiate a filter in
parallel, they'll all hold 1 lock at the top level in the
__virNWFilterInstantiateFilter method which will cause the
other thread to deadlock in virNWFilterObjFindByName.

The fix is to add an exclusive mutex to serialize the
execution of __virNWFilterInstantiateFilter.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
(cherry picked from commit 925de19ed7f13e0d12d0b993496d314bab886589)

Conflicts:
src/nwfilter/nwfilter_gentech_driver.c

src/nwfilter/nwfilter_driver.c
src/nwfilter/nwfilter_gentech_driver.c
src/nwfilter/nwfilter_gentech_driver.h

index 907169e48038d41a52bbd5b0874200e81cf2b050..9b6e84742438fe1d55d732692cac328b8ae04fd6 100644 (file)
@@ -198,7 +198,8 @@ nwfilterStateInitialize(bool privileged,
     if (virNWFilterDHCPSnoopInit() < 0)
         goto err_exit_learnshutdown;
 
-    virNWFilterTechDriversInit(privileged);
+    if (virNWFilterTechDriversInit(privileged) < 0)
+        goto err_dhcpsnoop_shutdown;
 
     if (virNWFilterConfLayerInit(virNWFilterDomainFWUpdateCB,
                                  driverState) < 0)
@@ -251,6 +252,7 @@ error:
 
 err_techdrivers_shutdown:
     virNWFilterTechDriversShutdown();
+err_dhcpsnoop_shutdown:
     virNWFilterDHCPSnoopShutdown();
 err_exit_learnshutdown:
     virNWFilterLearnShutdown();
@@ -333,10 +335,10 @@ nwfilterStateCleanup(void) {
 
     if (driverState->privileged) {
         virNWFilterConfLayerShutdown();
-        virNWFilterTechDriversShutdown();
         virNWFilterDHCPSnoopShutdown();
         virNWFilterLearnShutdown();
         virNWFilterIPAddrMapShutdown();
+        virNWFilterTechDriversShutdown();
 
         nwfilterDriverLock(driverState);
 
index 4361a9d7f1c049b78ca7231c19a70eb827024917..bd832f5037888c581dc1aa6156117d70e310e3f0 100644 (file)
@@ -55,30 +55,53 @@ static virNWFilterTechDriverPtr filter_tech_drivers[] = {
     NULL
 };
 
+/* Serializes instantiation of filters. This is necessary
+ * to avoid lock ordering deadlocks. eg __virNWFilterInstantiateFilter
+ * will hold a lock on a virNWFilterObjPtr. This in turn invokes
+ * virNWFilterInstantiate which invokes virNWFilterDetermineMissingVarsRec
+ * which invokes virNWFilterObjFindByName. This iterates over every single
+ * virNWFilterObjPtr in the list. So if 2 threads try to instantiate a
+ * filter in parallel, they'll both hold 1 lock at the top level in
+ * __virNWFilterInstantiateFilter which will cause the other thread
+ * to deadlock in virNWFilterObjFindByName.
+ *
+ * XXX better long term solution is to make virNWFilterObjList use a
+ * hash table as is done for virDomainObjList. You can then get
+ * lockless lookup of objects by name.
+ */
+static virMutex updateMutex;
 
-void virNWFilterTechDriversInit(bool privileged) {
+int virNWFilterTechDriversInit(bool privileged)
+{
     int i = 0;
     VIR_DEBUG("Initializing NWFilter technology drivers");
+    if (virMutexInitRecursive(&updateMutex) < 0)
+        return -1;
+
     while (filter_tech_drivers[i]) {
         if (!(filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED))
             filter_tech_drivers[i]->init(privileged);
         i++;
     }
+    return 0;
 }
 
 
-void virNWFilterTechDriversShutdown(void) {
+void virNWFilterTechDriversShutdown(void)
+{
     int i = 0;
     while (filter_tech_drivers[i]) {
         if ((filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED))
             filter_tech_drivers[i]->shutdown();
         i++;
     }
+    virMutexDestroy(&updateMutex);
 }
 
 
 virNWFilterTechDriverPtr
-virNWFilterTechDriverForName(const char *name) {
+virNWFilterTechDriverForName(const char *name)
+{
     int i = 0;
     while (filter_tech_drivers[i]) {
        if (STREQ(filter_tech_drivers[i]->name, name)) {
@@ -947,6 +970,8 @@ _virNWFilterInstantiateFilter(virNWFilterDriverStatePtr driver,
     int ifindex;
     int rc;
 
+    virMutexLock(&updateMutex);
+
     /* 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 */
@@ -974,6 +999,8 @@ _virNWFilterInstantiateFilter(virNWFilterDriverStatePtr driver,
                                         foundNewFilter);
 
 cleanup:
+    virMutexUnlock(&updateMutex);
+
     return rc;
 }
 
@@ -993,6 +1020,7 @@ virNWFilterInstantiateFilterLate(virNWFilterDriverStatePtr driver,
     bool foundNewFilter = false;
 
     virNWFilterReadLockFilterUpdates();
+    virMutexLock(&updateMutex);
 
     rc = __virNWFilterInstantiateFilter(driver,
                                         vmuuid,
@@ -1018,6 +1046,7 @@ virNWFilterInstantiateFilterLate(virNWFilterDriverStatePtr driver,
     }
 
     virNWFilterUnlockFilterUpdates();
+    virMutexUnlock(&updateMutex);
 
     return rc;
 }
@@ -1141,7 +1170,11 @@ _virNWFilterTeardownFilter(const char *ifname)
 int
 virNWFilterTeardownFilter(const virDomainNetDefPtr net)
 {
-    return _virNWFilterTeardownFilter(net->ifname);
+    int ret;
+    virMutexLock(&updateMutex);
+    ret = _virNWFilterTeardownFilter(net->ifname);
+    virMutexUnlock(&updateMutex);
+    return ret;
 }
 
 
index 8528e2a41eab8d912d8d1ca5e852787a4d797ba7..48ee1dfbfefbd1d82b09ff84d9e8e122cab06b83 100644 (file)
@@ -30,7 +30,7 @@ virNWFilterTechDriverPtr virNWFilterTechDriverForName(const char *name);
 int virNWFilterRuleInstAddData(virNWFilterRuleInstPtr res,
                                void *data);
 
-void virNWFilterTechDriversInit(bool privileged);
+int virNWFilterTechDriversInit(bool privileged);
 void virNWFilterTechDriversShutdown(void);
 
 enum instCase {