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

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