[libvirt] [PATCH 2/3] util: add 30s connection timeout to virFDStreamConnectUNIX

Nikolay Shirokovskiy nshirokovskiy at virtuozzo.com
Thu Nov 16 15:04:31 UTC 2017


There is a bug in qemu currently on hanling agent channels.
As a result if you open agent channel for guest without running
an agent several times in raw you get hang on connect syscall
eventually.

Qemu will be fixed eventually but it is nice to handle hanging
connect condition in libvirt. Let's add 30s connect timeout.
---
 src/util/virfdstream.c | 17 +++++++++++++++
 src/util/virutil.c     | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/util/virutil.h     |  2 ++
 3 files changed, 78 insertions(+)

diff --git a/src/util/virfdstream.c b/src/util/virfdstream.c
index 1d064a7..029cc4e 100644
--- a/src/util/virfdstream.c
+++ b/src/util/virfdstream.c
@@ -1176,6 +1176,11 @@ int virFDStreamConnectUNIX(virStreamPtr st,
             goto error;
     }
 
+    if (virSetNonBlock(fd) < 0) {
+        virReportSystemError(errno, "%s", _("Unable to set non-blocking mode"));
+        goto error;
+    }
+
     if (virTimeBackOffStart(&timeout, 1, 3*1000 /* ms */) < 0)
         goto error;
     while (virTimeBackOffWait(&timeout)) {
@@ -1189,10 +1194,22 @@ int virFDStreamConnectUNIX(virStreamPtr st,
             continue;
         }
 
+        if (errno == EINPROGRESS || errno == EAGAIN) {
+            if (virConnectWait(fd, 30 * 1000) < 0)
+                goto error;
+
+            break;
+        }
+
         virReportSystemError(errno, "%s", _("Unable to connect to UNIX socket"));
         goto error;
     }
 
+    if (virSetBlocking(fd, true) < 0) {
+        virReportSystemError(errno, "%s", _("Unable to set blocking mode"));
+        goto error;
+    }
+
     if (virFDStreamOpenInternal(st, fd, NULL, 0) < 0)
         goto error;
     return 0;
diff --git a/src/util/virutil.c b/src/util/virutil.c
index 8bdcb02..75abd4f 100644
--- a/src/util/virutil.c
+++ b/src/util/virutil.c
@@ -80,6 +80,7 @@
 #include "nonblocking.h"
 #include "virprocess.h"
 #include "virstring.h"
+#include "virtime.h"
 #include "virutil.h"
 
 verify(sizeof(gid_t) <= sizeof(unsigned int) &&
@@ -195,6 +196,64 @@ int virSetSockReuseAddr(int fd, bool fatal)
 #endif
 
 int
+virConnectWait(int fd, unsigned long long timeout)
+{
+    struct pollfd fds[1];
+    unsigned long long then;
+    unsigned long long now;
+    int rc;
+    int optval;
+    socklen_t optlen = sizeof(optval);
+
+    fds[0].fd = fd;
+    fds[0].events = POLLOUT;
+    fds[0].revents = 0;
+
+    if (virTimeMillisNow(&now) < 0)
+        return -1;
+
+    then = now + timeout;
+
+ retry:
+    rc = poll(fds, 1, then - now);
+
+    if (rc < 0 && (errno == EAGAIN || errno == EINTR)) {
+        if (virTimeMillisNow(&now) < 0)
+            return -1;
+
+        if (now >= then) {
+            virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
+                           _("Connection timeout expired"));
+            return -1;
+        }
+
+        goto retry;
+    }
+
+    if (rc < 0) {
+        virReportSystemError(errno, "%s", _("Unable to connect to socket"));
+        return -1;
+    } else if (rc == 0) {
+        virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
+                       _("Connection timeout expired"));
+        return -1;
+    }
+
+    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Cannot check socket connection status"));
+        return -1;
+    }
+
+    if (optval != 0) {
+        virReportSystemError(optval, "%s", _("Unable to connect to socket"));
+        return -1;
+    }
+
+    return 0;
+}
+
+int
 virPipeReadUntilEOF(int outfd, int errfd,
                     char **outbuf, char **errbuf) {
 
diff --git a/src/util/virutil.h b/src/util/virutil.h
index ff89d1a..b0c9672 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -43,6 +43,8 @@ int virSetInherit(int fd, bool inherit) ATTRIBUTE_RETURN_CHECK;
 int virSetCloseExec(int fd) ATTRIBUTE_RETURN_CHECK;
 int virSetSockReuseAddr(int fd, bool fatal) ATTRIBUTE_RETURN_CHECK;
 
+int virConnectWait(int fd, unsigned long long timeout);
+
 int virPipeReadUntilEOF(int outfd, int errfd,
                         char **outbuf, char **errbuf);
 
-- 
1.8.3.1




More information about the libvir-list mailing list