From: Longjun Tang <lange_tang@163.com>
To: mst@redhat.com, kuba@kernel.org
Cc: jasowangio@gmail.com, xuanzhuo@linux.alibaba.com,
virtualization@lists.linux.dev, netdev@vger.kernel.org,
tanglongjun@kylinos.cn
Subject: [RFC RESEND 2/3] virtio_net: introduce an RX queue watchdog for stuck detection
Date: Tue, 8 Sep 2026 17:26:45 +0800 [thread overview]
Message-ID: <20260908092646.108865-3-lange_tang@163.com> (raw)
In-Reply-To: <20260908092646.108865-1-lange_tang@163.com>
From: Longjun Tang <tanglongjun@kylinos.cn>
The backend raises an interrupt after adding buffers to the used ring,
and the hypervisor injects it. If one is lost (e.g., a transient KVM
failure), NAPI will not be scheduled and the driver stops consuming
buffers. The ring eventually fills up, the backend stops notifying, and
the queue is left permanently stuck.
Add a per-device watchdog that scans the RX queues once per second and
detects a queue that has a non-zero backlog while making no consumption
progress and receiving no new interrupt for rx_watchdog_timeo seconds
(default 5, 0 disables it). On detection it logs a warning, rate-limited
to once per timeout.
Assisted-by: Kilo:deepseek-v4-pro
Signed-off-by: Longjun Tang <tanglongjun@kylinos.cn>
---
drivers/net/virtio_net.c | 88 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index e34c52d059d3..c28de03c177b 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -36,6 +36,10 @@ module_param(csum, bool, 0444);
module_param(gso, bool, 0444);
module_param(napi_tx, bool, 0644);
+/* RX queue stall timeout in seconds; 0 disables the watchdog. */
+static unsigned int rx_watchdog_timeo = 5;
+module_param(rx_watchdog_timeo, uint, 0644);
+
#define VIRTIO_OFFLOAD_MAP_MIN 46
#define VIRTIO_OFFLOAD_MAP_MAX 47
#define VIRTIO_FEATURES_MAP_MIN 65
@@ -330,6 +334,11 @@ struct receive_queue {
/* The number of rx notifications */
u16 calls;
+ /* RX watchdog state for stuck detection. */
+ u16 watchdog_last_used_idx;
+ u16 watchdog_calls;
+ unsigned long watchdog_jiffies;
+
/* Is dynamic interrupt moderation enabled? */
bool dim_enabled;
@@ -441,6 +450,9 @@ struct virtnet_info {
/* Work struct for setting rx mode */
struct work_struct rx_mode_work;
+ /* RX watchdog timer for stuck detection */
+ struct timer_list rx_watchdog;
+
/* OK to queue work setting RX mode? */
bool rx_mode_work_enabled;
@@ -3046,6 +3058,77 @@ static int virtnet_poll(struct napi_struct *napi, int budget)
return received;
}
+/*
+ * This watchdog detects that state: a queue is considered stalled when
+ * it has a non-zero backlog, makes no consumption progress and receives
+ * no new interrupt for rx_watchdog_timeo seconds. On detection it logs
+ * a warning.
+ */
+static void virtnet_rx_watchdog(struct timer_list *t)
+{
+ struct virtnet_info *vi = timer_container_of(vi, t, rx_watchdog);
+ unsigned long timeout = rx_watchdog_timeo * HZ;
+ int i;
+
+ if (!rx_watchdog_timeo)
+ return;
+
+ for (i = 0; i < vi->curr_queue_pairs; i++) {
+ struct receive_queue *rq = &vi->rq[i];
+ u16 last_used = virtqueue_get_last_used_idx(rq->vq);
+ u16 calls = rq->calls;
+ bool backlog = virtqueue_poll(rq->vq, last_used);
+
+ if (!backlog || last_used != rq->watchdog_last_used_idx ||
+ calls != rq->watchdog_calls) {
+ /* No pending data, or the queue made progress, or a
+ * new interrupt arrived: restart the window.
+ */
+ rq->watchdog_last_used_idx = last_used;
+ rq->watchdog_calls = calls;
+ rq->watchdog_jiffies = jiffies;
+ continue;
+ }
+
+ if (time_after(jiffies, rq->watchdog_jiffies + timeout)) {
+ unsigned int stall_ms =
+ jiffies_to_msecs(jiffies - rq->watchdog_jiffies);
+
+ netdev_warn(vi->dev, "RX queue %u stalled for %u ms\n",
+ i, stall_ms);
+
+ /* Rate-limit to one event per timeout. */
+ rq->watchdog_jiffies = jiffies;
+ }
+ }
+
+ mod_timer(&vi->rx_watchdog, jiffies + HZ);
+}
+
+static void virtnet_rx_watchdog_start(struct virtnet_info *vi)
+{
+ int i;
+
+ if (!rx_watchdog_timeo)
+ return;
+
+ for (i = 0; i < vi->curr_queue_pairs; i++) {
+ struct receive_queue *rq = &vi->rq[i];
+
+ rq->watchdog_last_used_idx =
+ virtqueue_get_last_used_idx(rq->vq);
+ rq->watchdog_calls = rq->calls;
+ rq->watchdog_jiffies = jiffies;
+ }
+
+ mod_timer(&vi->rx_watchdog, jiffies + HZ);
+}
+
+static void virtnet_rx_watchdog_stop(struct virtnet_info *vi)
+{
+ timer_delete_sync(&vi->rx_watchdog);
+}
+
static void virtnet_disable_queue_pair(struct virtnet_info *vi, int qp_index)
{
virtnet_napi_tx_disable(&vi->sq[qp_index]);
@@ -3209,6 +3292,8 @@ static int virtnet_open(struct net_device *dev)
netif_carrier_on(dev);
}
+ virtnet_rx_watchdog_start(vi);
+
return 0;
err_enable_qp:
@@ -3802,6 +3887,8 @@ static int virtnet_close(struct net_device *dev)
struct virtnet_info *vi = netdev_priv(dev);
int i;
+ virtnet_rx_watchdog_stop(vi);
+
/* Prevent the config change callback from changing carrier
* after close
*/
@@ -6869,6 +6956,7 @@ static int virtnet_probe(struct virtio_device *vdev)
INIT_WORK(&vi->config_work, virtnet_config_changed_work);
INIT_WORK(&vi->rx_mode_work, virtnet_rx_mode_work);
+ timer_setup(&vi->rx_watchdog, virtnet_rx_watchdog, 0);
if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) {
vi->mergeable_rx_bufs = true;
--
2.25.1
next prev parent reply other threads:[~2026-09-08 9:27 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-08 9:26 [RFC RESEND 0/3] virtio_net: introduce an RX queue watchdog for stuck detection Longjun Tang
2026-09-08 9:26 ` [RFC RESEND 1/3] virtio: add virtqueue_get_last_used_idx() helper Longjun Tang
2026-09-08 9:26 ` Longjun Tang [this message]
2026-09-08 9:26 ` [RFC RESEND 3/3] virtio_net: add rx_timeouts per-queue statistic Longjun Tang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260908092646.108865-3-lange_tang@163.com \
--to=lange_tang@163.com \
--cc=jasowangio@gmail.com \
--cc=kuba@kernel.org \
--cc=mst@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=tanglongjun@kylinos.cn \
--cc=virtualization@lists.linux.dev \
--cc=xuanzhuo@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox