c401cc
From 8d5bef256e0b58bb6f45910d0e9e724da72e100c Mon Sep 17 00:00:00 2001
c401cc
Message-Id: <8d5bef256e0b58bb6f45910d0e9e724da72e100c@dist-git>
c401cc
From: Michal Privoznik <mprivozn@redhat.com>
c401cc
Date: Wed, 26 Feb 2014 10:05:24 +0100
c401cc
Subject: [PATCH] virNetDevVethCreate: Serialize callers
c401cc
c401cc
https://bugzilla.redhat.com/show_bug.cgi?id=1014604
c401cc
c401cc
Consider dozen of LXC domains, each of them having this type of interface:
c401cc
c401cc
    <interface type='network'>
c401cc
      <mac address='52:54:00:a7:05:4b'/>
c401cc
      <source network='default'/>
c401cc
    </interface>
c401cc
c401cc
When starting these domain in parallel, all workers may meet in
c401cc
virNetDevVethCreate() where a race starts. Race over allocating veth
c401cc
pairs because allocation requires two steps:
c401cc
c401cc
  1) find first nonexistent '/sys/class/net/vnet%d/'
c401cc
  2) run 'ip link add ...' command
c401cc
c401cc
Now consider two threads. Both of them find N as the first unused veth
c401cc
index but only one of them succeeds allocating it. The other one fails.
c401cc
For such cases, we are running the allocation in a loop with 10 rounds.
c401cc
However this is very flaky synchronization. It should be rather used
c401cc
when libvirt is competing with other process than when libvirt threads
c401cc
fight each other. Therefore, internally we should use mutex to serialize
c401cc
callers, and do the allocation in loop (just in case we are competing
c401cc
with a different process). By the way we have something similar already
c401cc
since 1cf97c87.
c401cc
c401cc
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
c401cc
(cherry picked from commit c0d162c68c2f19af8d55a435a9e372da33857048)
c401cc
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
c401cc
---
c401cc
 src/util/virnetdevveth.c | 18 ++++++++++++++++++
c401cc
 1 file changed, 18 insertions(+)
c401cc
c401cc
diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
c401cc
index 25eb282..e698ce2 100644
c401cc
--- a/src/util/virnetdevveth.c
c401cc
+++ b/src/util/virnetdevveth.c
c401cc
@@ -39,6 +39,19 @@
c401cc
 
c401cc
 /* Functions */
c401cc
 
c401cc
+virMutex virNetDevVethCreateMutex;
c401cc
+
c401cc
+static int virNetDevVethCreateMutexOnceInit(void)
c401cc
+{
c401cc
+    if (virMutexInit(&virNetDevVethCreateMutex) < 0) {
c401cc
+        virReportSystemError(errno, "%s", _("unable to init mutex"));
c401cc
+        return -1;
c401cc
+    }
c401cc
+    return 0;
c401cc
+}
c401cc
+
c401cc
+VIR_ONCE_GLOBAL_INIT(virNetDevVethCreateMutex);
c401cc
+
c401cc
 static int virNetDevVethExists(int devNum)
c401cc
 {
c401cc
     int ret;
c401cc
@@ -117,6 +130,10 @@ int virNetDevVethCreate(char** veth1, char** veth2)
c401cc
      * We might race with other containers, but this is reasonably
c401cc
      * unlikely, so don't do too many retries for device creation
c401cc
      */
c401cc
+    if (virNetDevVethCreateMutexInitialize() < 0)
c401cc
+        return -1;
c401cc
+
c401cc
+    virMutexLock(&virNetDevVethCreateMutex);
c401cc
 #define MAX_VETH_RETRIES 10
c401cc
 
c401cc
     for (i = 0; i < MAX_VETH_RETRIES; i++) {
c401cc
@@ -179,6 +196,7 @@ int virNetDevVethCreate(char** veth1, char** veth2)
c401cc
                    MAX_VETH_RETRIES);
c401cc
 
c401cc
 cleanup:
c401cc
+    virMutexUnlock(&virNetDevVethCreateMutex);
c401cc
     virCommandFree(cmd);
c401cc
     VIR_FREE(veth1auto);
c401cc
     VIR_FREE(veth2auto);
c401cc
-- 
c401cc
1.9.0
c401cc