From 7b31f94d1a005dc24e316c4c9999e18d59dc76f2 Mon Sep 17 00:00:00 2001 From: Harshal Patil Date: Tue, 11 Aug 2026 17:39:32 +0530 Subject: [PATCH] VIRTWINKVM-2680: [QMP] flush pending events in wait_for to handle guest-side crashes When a crash is triggered from inside the guest (e.g. via NotMyFault over WinRM) rather than via a QMP command (e.g. inject-nmi), the QMP socket has no recent I/O activity. This can cause QEMU to not flush pending events like GUEST_PANICKED to the socket, making wait_for_new_event block until timeout. Fix this by: 1. Sending a query-status command in wait_for before entering the blocking wait loop, which forces QMP negotiation and flushes any pending events into the cache. 2. Using IO.wait_readable with a 2 sec interval in wait_for_new_event so that if the event hasn't arrived yet, a periodic query-status poke forces QEMU to flush its event buffer. After each poke, the event cache is re-checked since send_cmd's internal read loop may have consumed and buffered the target event. Signed-off-by: Harshal Patil --- lib/setupmanagers/qemuhck/qmp.rb | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/setupmanagers/qemuhck/qmp.rb b/lib/setupmanagers/qemuhck/qmp.rb index 9b2781fe..6a2c47b0 100644 --- a/lib/setupmanagers/qemuhck/qmp.rb +++ b/lib/setupmanagers/qemuhck/qmp.rb @@ -44,6 +44,11 @@ def wait_for(name, accepted, timeout = 60) cached = find_cached_event(name, accepted) return cached if cached + run_cmd('query-status') + + cached = find_cached_event(name, accepted) + return cached if cached + wait_for_new_event(name, accepted, timeout) end @@ -57,12 +62,19 @@ def find_cached_event(name, accepted) def wait_for_new_event(name, accepted, timeout) Timeout.timeout(timeout) do loop do - response = JSON.parse(@socket_internal.readline) - @logger.debug("Received QMP message: #{response}") - return response if accepted.include?(response[name]) + if @socket_internal.wait_readable(2) + response = JSON.parse(@socket_internal.readline) + @logger.debug("Received QMP message: #{response}") + return response if accepted.include?(response[name]) + + @events << response if response.key?('event') + raise(QMPError, response['error'].to_s) if response.key?('error') + else + run_cmd('query-status') - @events << response if response.key?('event') - raise(QMPError, response['error'].to_s) if response.key?('error') + cached = find_cached_event(name, accepted) + return cached if cached + end end end end