Blame SOURCES/libvirt-logging-ensure-pending-I-O-is-drained-before-reading-position.patch

0a7476
From c90c82df8b2ce989e628338a6f43b75acdd5a868 Mon Sep 17 00:00:00 2001
0a7476
Message-Id: <c90c82df8b2ce989e628338a6f43b75acdd5a868@dist-git>
0a7476
From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
0a7476
Date: Wed, 3 Apr 2019 17:03:46 +0200
0a7476
Subject: [PATCH] logging: ensure pending I/O is drained before reading
0a7476
 position
0a7476
MIME-Version: 1.0
0a7476
Content-Type: text/plain; charset=UTF-8
0a7476
Content-Transfer-Encoding: 8bit
0a7476
0a7476
The virtualization driver has two connections to the virtlogd daemon,
0a7476
one pipe fd for writing to the log file, and one socket fd for making
0a7476
RPC calls. The typical sequence is to write some data to the pipe fd and
0a7476
then make an RPC call to determine the current log file offset.
0a7476
0a7476
Unfortunately these two operations are not guaranteed to be handling in
0a7476
order by virtlogd. The event loop for virtlogd may identify an incoming
0a7476
event on both the pipe fd and socket fd in the same iteration of the
0a7476
event loop. It is then entirely possible that it will process the socket
0a7476
fd RPC call before reading the pending log data from the pipe fd.
0a7476
0a7476
As a result the virtualization driver will get an outdated log file
0a7476
offset reported back.
0a7476
0a7476
This can be seen with the QEMU driver where, when a guest fails to
0a7476
start, it will randomly include too much data in the error message it
0a7476
has fetched from the log file.
0a7476
0a7476
The solution is to ensure we have drained all pending data from the pipe
0a7476
fd before reporting the log file offset. The pipe fd is always in
0a7476
blocking mode, so cares needs to be taken to avoid blocking. When
0a7476
draining this is taken care of by using poll(). The extra complication
0a7476
is that they might already be an event loop dispatch pending on the pipe
0a7476
fd. If we have just drained the pipe this pending event will be invalid
0a7476
so must be discarded.
0a7476
0a7476
See also https://bugzilla.redhat.com/show_bug.cgi?id=1356108
0a7476
0a7476
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
0a7476
0a7476
(cherry picked from commit cc9e80c59368478d179ee3eb7bf8106664c56870)
0a7476
0a7476
https://bugzilla.redhat.com/show_bug.cgi?id=1660531
0a7476
0a7476
Signed-off-by: Andrea Bolognani <abologna@redhat.com>
0a7476
Message-Id: <20190403150346.20086-1-abologna@redhat.com>
0a7476
Reviewed-by: Erik Skultety <eskultet@redhat.com>
0a7476
---
0a7476
 src/logging/log_handler.c | 48 +++++++++++++++++++++++++++++++++++++++
0a7476
 1 file changed, 48 insertions(+)
0a7476
0a7476
diff --git a/src/logging/log_handler.c b/src/logging/log_handler.c
0a7476
index 5364e06dff..25065ed177 100644
0a7476
--- a/src/logging/log_handler.c
0a7476
+++ b/src/logging/log_handler.c
0a7476
@@ -34,6 +34,7 @@
0a7476
 
0a7476
 #include <unistd.h>
0a7476
 #include <fcntl.h>
0a7476
+#include <poll.h>
0a7476
 
0a7476
 #include "configmake.h"
0a7476
 
0a7476
@@ -50,6 +51,7 @@ struct _virLogHandlerLogFile {
0a7476
     virRotatingFileWriterPtr file;
0a7476
     int watch;
0a7476
     int pipefd; /* Read from QEMU via this */
0a7476
+    bool drained;
0a7476
 
0a7476
     char *driver;
0a7476
     unsigned char domuuid[VIR_UUID_BUFLEN];
0a7476
@@ -153,6 +155,11 @@ virLogHandlerDomainLogFileEvent(int watch,
0a7476
         return;
0a7476
     }
0a7476
 
0a7476
+    if (logfile->drained) {
0a7476
+        logfile->drained = false;
0a7476
+        goto cleanup;
0a7476
+    }
0a7476
+
0a7476
  reread:
0a7476
     len = read(fd, buf, sizeof(buf));
0a7476
     if (len < 0) {
0a7476
@@ -170,6 +177,7 @@ virLogHandlerDomainLogFileEvent(int watch,
0a7476
     if (events & VIR_EVENT_HANDLE_HANGUP)
0a7476
         goto error;
0a7476
 
0a7476
+ cleanup:
0a7476
     virObjectUnlock(handler);
0a7476
     return;
0a7476
 
0a7476
@@ -435,6 +443,44 @@ virLogHandlerDomainOpenLogFile(virLogHandlerPtr handler,
0a7476
 }
0a7476
 
0a7476
 
0a7476
+static void
0a7476
+virLogHandlerDomainLogFileDrain(virLogHandlerLogFilePtr file)
0a7476
+{
0a7476
+    char buf[1024];
0a7476
+    ssize_t len;
0a7476
+    struct pollfd pfd;
0a7476
+    int ret;
0a7476
+
0a7476
+    for (;;) {
0a7476
+        pfd.fd = file->pipefd;
0a7476
+        pfd.events = POLLIN;
0a7476
+        pfd.revents = 0;
0a7476
+
0a7476
+        ret = poll(&pfd, 1, 0);
0a7476
+        if (ret < 0) {
0a7476
+            if (errno == EINTR)
0a7476
+                continue;
0a7476
+
0a7476
+            return;
0a7476
+        }
0a7476
+
0a7476
+        if (ret == 0)
0a7476
+            return;
0a7476
+
0a7476
+        len = read(file->pipefd, buf, sizeof(buf));
0a7476
+        file->drained = true;
0a7476
+        if (len < 0) {
0a7476
+            if (errno == EINTR)
0a7476
+                continue;
0a7476
+            return;
0a7476
+        }
0a7476
+
0a7476
+        if (virRotatingFileWriterAppend(file->file, buf, len) != len)
0a7476
+            return;
0a7476
+    }
0a7476
+}
0a7476
+
0a7476
+
0a7476
 int
0a7476
 virLogHandlerDomainGetLogFilePosition(virLogHandlerPtr handler,
0a7476
                                       const char *path,
0a7476
@@ -465,6 +511,8 @@ virLogHandlerDomainGetLogFilePosition(virLogHandlerPtr handler,
0a7476
         goto cleanup;
0a7476
     }
0a7476
 
0a7476
+    virLogHandlerDomainLogFileDrain(file);
0a7476
+
0a7476
     *inode = virRotatingFileWriterGetINode(file->file);
0a7476
     *offset = virRotatingFileWriterGetOffset(file->file);
0a7476
 
0a7476
-- 
0a7476
2.21.0
0a7476