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 <sebastian.eydam@cyberus-technology.de>
This commit is contained in:
Sebastian Eydam
2026-03-20 16:17:16 +01:00
committed by Rob Bradford
parent 92109136f1
commit 882f82f04b

View File

@@ -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();