From 882f82f04bffa3ae9dc3591217aff40787528958 Mon Sep 17 00:00:00 2001 From: Sebastian Eydam Date: Fri, 20 Mar 2026 16:17:16 +0100 Subject: [PATCH] virtio-devices: fix barrier handling in virtio-net When configuring multiple queues for a virtio device, the guest can activate between 1 and the configured amout of queues. The firmware, for example, may activate only one queue, while a Linux guest would likely activate all available queues. The constructor of virtio-net initializes the `paused_sync` barrier using the configured queue count (plus one for the main thread). This can be wrong if the guest enables a different number of queues at activation time, which can make pause hang. Thus, we now recompute the barrier size from the queues that are actually activated. On-behalf-of: SAP sebastian.eydam@sap.com Signed-off-by: Sebastian Eydam --- virtio-devices/src/net.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/virtio-devices/src/net.rs b/virtio-devices/src/net.rs index ec8afc2cc..3bb360c64 100644 --- a/virtio-devices/src/net.rs +++ b/virtio-devices/src/net.rs @@ -752,7 +752,15 @@ impl VirtioDevice for Net { let num_queues = queues.len(); let event_idx = self.common.feature_acked(VIRTIO_RING_F_EVENT_IDX.into()); - if self.common.feature_acked(VIRTIO_NET_F_CTRL_VQ.into()) && !num_queues.is_multiple_of(2) { + + // Recompute the barrier size from the queues that are actually activated. + let has_ctrl_queue = + self.common.feature_acked(VIRTIO_NET_F_CTRL_VQ.into()) && !num_queues.is_multiple_of(2); + let ctrl_threads = if has_ctrl_queue { 1 } else { 0 }; + let qp_threads = (num_queues - ctrl_threads) / 2; + self.common.paused_sync = Some(Arc::new(Barrier::new(1 + qp_threads + ctrl_threads))); + + if has_ctrl_queue { let ctrl_queue_index = num_queues - 1; let (_, mut ctrl_queue, ctrl_queue_evt) = queues.remove(ctrl_queue_index); @@ -772,10 +780,6 @@ impl VirtioDevice for Net { }; let paused = self.common.paused.clone(); - // Let's update the barrier as we need 1 for each RX/TX pair + - // 1 for the control queue + 1 for the main thread signalling - // the pause. - self.common.paused_sync = Some(Arc::new(Barrier::new(self.taps.len() + 2))); let paused_sync = self.common.paused_sync.clone(); let mut epoll_threads = Vec::new();