From: Longjun Tang <[email protected]>
Hi Michael, Kuba,
I once ran into a problem where the virtio_net receive queue became
permanently stuck.
The virtio_net RX path relies on backend interrupt notifications.
The backend(vhost_net/vhost-user) appends buffers to the used ring
and then notifies the guest, relying on the hypervisor(KVM) to inject
the interrupt. On receiving it, virtio_net schedules NAPI, drains the
used ring and refills descriptors.
If the hypervisor fails to inject the interrupt(e.g. a transient KVM
failure), the guest never schedules NAPI, so it neither consumes buffers
nor returns descriptors. The backend keeps appending until the ring is full,
then stops and, with no free descriptors left, also stops sending
notifications. Both sides now wait for the other, and the RX queue is
permanently stuck.
The root cause is the lost interrupt in the hypervisor, and the proper
fix belongs there. Nevertheless, I believe virtio_net needs a way to
observe and diagnose it. Today, once a queue hangs, there is no signal
to the operator that anything is wrong: the RX path has no equivalent of
the TX watchdog (ndo_tx_timeout). This series lets virtio_net detect and
recover a stuck RX queue.
Implementation
--------------
Patch 1 exports virtqueue_get_last_used_idx(), a read-only accessor for
the last consumed used ring index. Together with the existing
virtqueue_poll(), it lets a driver ask whether the device has produced
buffers that the driver has not consumed yet ("backlog").
Patch 2 adds a per-device RX watchdog that scans all RX queues once per
second. A queue is considered stuck when, for rx_watchdog_timeo seconds
(default 5, 0 disables it), all of the following hold simultaneously:
- the queue has a non-zero backlog (unconsumed buffers in the used ring);
- the driver makes no consumption progress (last_used unchanged);
- no new interrupt arrives (rq->calls unchanged).
On detection it logs a warning, rate-limited to once per timeout, and
reschedules NAPI, which drains the used ring and refills the descriptors,
so the queue recovers.
Patch 3 adds a per-queue rx_timeouts statistic, exposed via ethtool -S,
so the number of stuck events can be observed per queue.
questions and comments are welcome.
Thanks!
Lange
Longjun Tang (3):
virtio: add virtqueue_get_last_used_idx() helper
virtio_net: introduce an RX queue watchdog for stuck detection
virtio_net: add rx_timeouts per-queue statistic
drivers/net/virtio_net.c | 112 +++++++++++++++++++++++++++++++++++
drivers/virtio/virtio_ring.c | 18 +++++-
include/linux/virtio.h | 2 +
3 files changed, 130 insertions(+), 2 deletions(-)
--
2.51.0