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
