Blob Blame History Raw
From d1db5ddfebcab5075d25958dd7dfcaaf28238e72 Mon Sep 17 00:00:00 2001
Message-Id: <d1db5ddfebcab5075d25958dd7dfcaaf28238e72.1386348946.git.jdenemar@redhat.com>
From: "Daniel P. Berrange" <berrange@redhat.com>
Date: Mon, 2 Dec 2013 13:37:07 +0000
Subject: [PATCH] Fix busy wait loop in LXC container I/O handling

For

  https://bugzilla.redhat.com/show_bug.cgi?id=1032705

If the host side of an LXC container console disconnected
and the guest side continued to write data, until the PTY
buffer filled up, the LXC controller would busy wait. It
would repeatedly see POLLHUP from poll() and not disable
the watch.

This was due to some bogus logic detecting blocking
conditions. Upon seeing a POLLHUP we must disable all
reading & writing from the PTY, and setup the epoll to
wake us up again when the connection comes back.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
(cherry picked from commit 5087a5a0092853702eb5e0c0297937a7859bcab3)
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
 src/lxc/lxc_controller.c | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index 5754c92..cb2db82 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -77,13 +77,11 @@ struct _virLXCControllerConsole {
     int hostFd;  /* PTY FD in the host OS */
     bool hostClosed;
     int hostEpoll;
-    bool hostBlocking;
 
     int contWatch;
     int contFd;  /* PTY FD in the container */
     bool contClosed;
     int contEpoll;
-    bool contBlocking;
 
     int epollWatch;
     int epollFd; /* epoll FD for dealing with EOF */
@@ -805,12 +803,15 @@ static void virLXCControllerSignalChildIO(virNetServerPtr server,
     int status;
 
     ret = waitpid(-1, &status, WNOHANG);
+    VIR_DEBUG("Got sig child %d vs %lld", ret, (unsigned long long)ctrl->initpid);
     if (ret == ctrl->initpid) {
         virNetServerQuit(server);
         virMutexLock(&lock);
         if (WIFSIGNALED(status) &&
-            WTERMSIG(status) == SIGHUP)
+            WTERMSIG(status) == SIGHUP) {
+            VIR_DEBUG("Status indicates reboot");
             wantReboot = true;
+        }
         virMutexUnlock(&lock);
     }
 }
@@ -821,28 +822,32 @@ static void virLXCControllerConsoleUpdateWatch(virLXCControllerConsolePtr consol
     int hostEvents = 0;
     int contEvents = 0;
 
-    if (!console->hostClosed || (!console->hostBlocking && console->fromContLen)) {
+    /* If host console is open, then we can look to read/write */
+    if (!console->hostClosed) {
         if (console->fromHostLen < sizeof(console->fromHostBuf))
             hostEvents |= VIR_EVENT_HANDLE_READABLE;
         if (console->fromContLen)
             hostEvents |= VIR_EVENT_HANDLE_WRITABLE;
     }
-    if (!console->contClosed || (!console->contBlocking && console->fromHostLen)) {
+
+    /* If cont console is open, then we can look to read/write */
+    if (!console->contClosed) {
         if (console->fromContLen < sizeof(console->fromContBuf))
             contEvents |= VIR_EVENT_HANDLE_READABLE;
         if (console->fromHostLen)
             contEvents |= VIR_EVENT_HANDLE_WRITABLE;
     }
 
-    VIR_DEBUG("Container watch %d=%d host watch %d=%d",
-              console->contWatch, contEvents,
-              console->hostWatch, hostEvents);
+    VIR_DEBUG("Container watch=%d, events=%d closed=%d; host watch=%d events=%d closed=%d",
+              console->contWatch, contEvents, console->contClosed,
+              console->hostWatch, hostEvents, console->hostClosed);
     virEventUpdateHandle(console->contWatch, contEvents);
     virEventUpdateHandle(console->hostWatch, hostEvents);
 
     if (console->hostClosed) {
+        /* Must setup an epoll to detect when host becomes accessible again */
         int events = EPOLLIN | EPOLLET;
-        if (console->hostBlocking)
+        if (console->fromContLen)
             events |= EPOLLOUT;
 
         if (events != console->hostEpoll) {
@@ -878,8 +883,9 @@ static void virLXCControllerConsoleUpdateWatch(virLXCControllerConsolePtr consol
     }
 
     if (console->contClosed) {
+        /* Must setup an epoll to detect when guest becomes accessible again */
         int events = EPOLLIN | EPOLLET;
-        if (console->contBlocking)
+        if (console->fromHostLen)
             events |= EPOLLOUT;
 
         if (events != console->contEpoll) {
@@ -950,7 +956,7 @@ static void virLXCControllerConsoleEPoll(int watch, int fd, int events, void *op
 
         /* If we get HUP+dead PID, we just re-enable the main loop
          * which will see the PID has died and exit */
-        if ((event.events & EPOLLIN)) {
+        if ((event.events & (EPOLLIN|EPOLLOUT))) {
             if (event.data.fd == console->hostFd) {
                 console->hostClosed = false;
             } else {
@@ -1030,10 +1036,6 @@ static void virLXCControllerConsoleIO(int watch, int fd, int events, void *opaqu
             *len -= done;
         } else {
             VIR_DEBUG("Write fd %d done %d errno %d", fd, (int)done, errno);
-            if (watch == console->hostWatch)
-                console->hostBlocking = true;
-            else
-                console->contBlocking = true;
         }
     }
 
-- 
1.8.4.5