[PATCH 4/5] migration: Fix rare hang of migration_channel_read_peek()

Peter Xu <[email protected]>
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
In an unlikely case, when a migration stream is attached to the destination
QEMU and only send <4 bytes to the channel as magic, it's possible that
migration_channel_read_peek() may spin forever without yielding in the main
thread causing two unwanted consequences:

- CPU will spin 100% waiting for the rest bytes until it reaches 4
- (more importantly..) Main thread is stuck during this process as the qio
  operation won't really yield the coroutine

Fix it by consuming the bytes that arrived.

Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3889
Cc: Daniel P. BerrangĂ© <[email protected]>
Reported-by: Feifan Qian <[email protected]>
Signed-off-by: Peter Xu <[email protected]>
---
 migration/channel.c | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/migration/channel.c b/migration/channel.c
index 1e2935f926..28fe1d2906 100644
--- a/migration/channel.c
+++ b/migration/channel.c
@@ -294,11 +294,31 @@ int migration_channel_read_peek(QIOChannel *ioc,
             return -1;
         }
 
-        if (len == buflen) {
+        if (len == iov.iov_len) {
             break;
-        }
+        } else if (len == 0) {
+            qio_channel_wait_cond(ioc, G_IO_IN);
+        } else {
+            ssize_t received = len;
 
-        qio_channel_wait_cond(ioc, G_IO_IN);
+            /*
+             * Partially arrived, read out to make qio_channel_wait_cond()
+             * won't return immediately, causing an unwanted spin on this
+             * CPU.
+             */
+            iov.iov_len = len;
+            len = qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, 0, errp);
+            /*
+             * QIO_CHANNEL_ERR_BLOCK also shouldn't happen, due to the
+             * prior peek just happened.  We should be pretty sure we will
+             * read what we peeked, or the channel was broken.
+             */
+            if (len != received) {
+                return -1;
+            }
+            iov.iov_base += received;
+            iov.iov_len = buflen - received;
+        }
     }
 
     return 0;
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.