Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

virtio_net: fix race in RX VQ processing

virtio net called virtqueue_enable_cq on RX path after napi_complete, so
with NAPI_STATE_SCHED clear - outside the implicit napi lock.
This violates the requirement to synchronize virtqueue_enable_cq wrt
virtqueue_add_buf. In particular, used event can move backwards,
causing us to lose interrupts.
In a debug build, this can trigger panic within START_USE.

Jason Wang reports that he can trigger the races artificially,
by adding udelay() in virtqueue_enable_cb() after virtio_mb().

However, we must call napi_complete to clear NAPI_STATE_SCHED before
polling the virtqueue for used buffers, otherwise napi_schedule_prep in
a callback will fail, causing us to lose RX events.

To fix, call virtqueue_enable_cb_prepare with NAPI_STATE_SCHED
set (under napi lock), later call virtqueue_poll with
NAPI_STATE_SCHED clear (outside the lock).

Reported-by: Jason Wang <jasowang@redhat.com>
Tested-by: Jason Wang <jasowang@redhat.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Michael S. Tsirkin and committed by
David S. Miller
cbdadbbf cc229884

+3 -2
+3 -2
drivers/net/virtio_net.c
··· 602 602 container_of(napi, struct receive_queue, napi); 603 603 struct virtnet_info *vi = rq->vq->vdev->priv; 604 604 void *buf; 605 - unsigned int len, received = 0; 605 + unsigned int r, len, received = 0; 606 606 607 607 again: 608 608 while (received < budget && ··· 619 619 620 620 /* Out of packets? */ 621 621 if (received < budget) { 622 + r = virtqueue_enable_cb_prepare(rq->vq); 622 623 napi_complete(napi); 623 - if (unlikely(!virtqueue_enable_cb(rq->vq)) && 624 + if (unlikely(virtqueue_poll(rq->vq, r)) && 624 625 napi_schedule_prep(napi)) { 625 626 virtqueue_disable_cb(rq->vq); 626 627 __napi_schedule(napi);