On 28.09.2026 07:59, Refael Mantsur wrote:
hv-balloon runs its event loop, including the receive path, from contexts
other than the VMBus channel notify callback: the QMP "balloon" handler
(hv_balloon_to_target()), the post-init timer and the open/close/reset
paths. Receiving is only legal between ringbuf_start_io() and
ringbuf_end_io(), which channel_event_cb() provides for the notify path.
When the receive ring is empty, vmbus_channel_recv_peek() returns before
seeking, so this is usually harmless. When the guest has just posted a
Dynamic Memory message (e.g. the periodic STATUS report, or the response
to a previous hot-add) and the QMP "balloon" command arrives before the
notifier runs, QEMU aborts:
   hw/hyperv/vmbus.c:418: gpadl_iter_seek: Assertion `iter->active' failed.
Seen with a Windows Server 2022 guest (Hyper-V Dynamic Memory driver),
hv-balloon with a memdev and status-report=on, and a host-side controller
issuing a few "balloon" commands within a minute while an RDP user logged
in (the guest's committed memory jumped and hot-adds were in flight).
Make vmbus_channel_recv_start() start the ring I/O itself when it is
called outside the notify callback, remember that in the receive ring,
and end it in vmbus_channel_recv_done() (or right away if recv_start()
fails). The notify-callback path is unchanged.
Signed-off-by: FULL NAME <[email protected]>

                 ^^^^^^^^^ that looks wrong

It looks like your message arrived as a quoted-printable attachment,
probably mangled by Outlook.
GIT-SEND-EMAIL(1) should normally take care of sending a patch in the
proper format.

A nit there: it would greatly help readability to divide that commit
message into separate paragraphs and have an empty line before that
Signed-off-by.

---
  hw/hyperv/vmbus.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
  1 file changed, 47 insertions(+), 2 deletions(-)
--- a/hw/hyperv/vmbus.c
+++ b/hw/hyperv/vmbus.c
@@ -121,6 +121,12 @@
      uint32_t last_rd_idx;
      /* last seen write index */
      uint32_t last_seen_wr_idx;
+    /*
+     * recv_start was called outside the channel notify callback (e.g. from a
+     * QMP-triggered device path such as hv-balloon's balloon target change),
+     * so it started the I/O on the ring itself and recv_done must end it.
+     */
+    bool own_io;
  } VMBusRecvRingBuf;
@@ -1113,7 +1119,7 @@
      return req;
  }
-int vmbus_channel_recv_start(VMBusChannel *chan)
+static int vmbus_channel_recv_start_io(VMBusChannel *chan)
  {
      VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
      vmbus_ring_buffer *rb;
@@ -1132,6 +1138,33 @@
      /* prevent reorder of the following data operation with write_index read 
*/
      smp_mb();                   /* barrier pair [C] */
      return 0;
+}
+
+int vmbus_channel_recv_start(VMBusChannel *chan)
+{
+    VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
+    bool own_io = false;
+    int ret;
+
+    /*
+     * Receives normally happen within channel_event_cb(), which brackets them
+     * with ringbuf_start_io()/ringbuf_end_io().  Device code may also receive
+     * from other contexts (hv-balloon does from its QMP balloon handler); make
+     * that safe instead of tripping the iter->active assertion in
+     * gpadl_iter_seek() when a packet is pending.
+     */
+    if (!ringbuf->common.iter.active) {
+        ringbuf_start_io(&ringbuf->common);
+        own_io = true;
+    }
+
+    ret = vmbus_channel_recv_start_io(chan);
+    if (ret && own_io) {
+        ringbuf_end_io(&ringbuf->common);
+        own_io = false;
+    }
+    ringbuf->own_io = own_io;
+    return ret;
  }
  void *vmbus_channel_recv_peek(VMBusChannel *chan, uint32_t size)
@@ -1208,7 +1241,7 @@
      ringbuf->rd_idx = ringbuf_tell(&ringbuf->common);
  }
-ssize_t vmbus_channel_recv_done(VMBusChannel *chan)
+static ssize_t vmbus_channel_recv_done_io(VMBusChannel *chan)
  {
      VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
      vmbus_ring_buffer *rb;
@@ -1266,6 +1299,18 @@
      ringbuf_unmap_hdr(&ringbuf->common, rb, true);
      ringbuf->last_rd_idx = ringbuf->rd_idx;
      return read;
+}
+
+ssize_t vmbus_channel_recv_done(VMBusChannel *chan)
+{
+    VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
+    ssize_t ret = vmbus_channel_recv_done_io(chan);
+
+    if (ringbuf->own_io) {
+        ringbuf_end_io(&ringbuf->common);
+        ringbuf->own_io = false;
+    }
+    return ret;
  }
  void vmbus_free_req(void *req)


It looks like there should be an easier way to do it by adding
something like "do_recv" boolean parameter to hv_balloon_event_loop(),
skipping the hv_balloon_event_loop_recv() call there if
this parameter is false.

Then hv_balloon_event_loop() will be called with do_recv = false
from everywhere but hv_balloon_vmdev_chan_notify().

Care to submit an updated patch?

Thanks,
Maciej


Reply via email to