On Mon, Apr 14, 2025 at 12:08:35PM +0700, Bui Quang Minh wrote: > When pausing rx (e.g. set up xdp, xsk pool, rx resize), we call > napi_disable() on the receive queue's napi. In delayed refill_work, it > also calls napi_disable() on the receive queue's napi. When > napi_disable() is called on an already disabled napi, it will sleep in > napi_disable_locked while still holding the netdev_lock. As a result, > later napi_enable gets stuck too as it cannot acquire the netdev_lock. > This leads to refill_work and the pause-then-resume tx are stuck > altogether. > > This scenario can be reproducible by binding a XDP socket to virtio-net > interface without setting up the fill ring. As a result, try_fill_recv > will fail until the fill ring is set up and refill_work is scheduled. > > This commit adds virtnet_rx_(pause/resume)_all helpers and fixes up the > virtnet_rx_resume to disable future and cancel all inflights delayed > refill_work before calling napi_disable() to pause the rx. > > Fixes: 413f0271f396 ("net: protect NAPI enablement with netdev_lock()") > Signed-off-by: Bui Quang Minh <minhquangbu...@gmail.com> > --- > drivers/net/virtio_net.c | 60 ++++++++++++++++++++++++++++++++++------ > 1 file changed, 51 insertions(+), 9 deletions(-)
Thans for the patch! Yes something to improve: > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c > index 7e4617216a4b..4361b91ccc64 100644 > --- a/drivers/net/virtio_net.c > +++ b/drivers/net/virtio_net.c > @@ -3342,10 +3342,53 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, > struct net_device *dev) > return NETDEV_TX_OK; > } > > +static void virtnet_rx_pause_all(struct virtnet_info *vi) > +{ > + bool running = netif_running(vi->dev); > + > + /* > + * Make sure refill_work does not run concurrently to > + * avoid napi_disable race which leads to deadlock. > + */ > + disable_delayed_refill(vi); > + cancel_delayed_work_sync(&vi->refill); > + if (running) { > + int i; > + > + for (i = 0; i < vi->max_queue_pairs; i++) { > + virtnet_napi_disable(&vi->rq[i]); > + virtnet_cancel_dim(vi, &vi->rq[i].dim); duplicates a bit of code from virtnet_rx_pause_all. > + } > + } > +} > + > +static void virtnet_rx_resume_all(struct virtnet_info *vi) > +{ > + bool running = netif_running(vi->dev); > + int i; > + > + enable_delayed_refill(vi); > + for (i = 0; i < vi->max_queue_pairs; i++) { > + if (i < vi->curr_queue_pairs) { > + if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) > + schedule_delayed_work(&vi->refill, 0); > + } > + > + if (running) > + virtnet_napi_enable(&vi->rq[i]); > + } > +} > + > static void virtnet_rx_pause(struct virtnet_info *vi, struct receive_queue > *rq) > { > bool running = netif_running(vi->dev); > > + /* > + * Make sure refill_work does not run concurrently to > + * avoid napi_disable race which leads to deadlock. > + */ > + disable_delayed_refill(vi); > + cancel_delayed_work_sync(&vi->refill); Maybe rename this e.g. __virtnet_rx_pause ? > if (running) { > virtnet_napi_disable(rq); > virtnet_cancel_dim(vi, &rq->dim); > @@ -3356,6 +3399,7 @@ static void virtnet_rx_resume(struct virtnet_info *vi, > struct receive_queue *rq) > { > bool running = netif_running(vi->dev); > > + enable_delayed_refill(vi); > if (!try_fill_recv(vi, rq, GFP_KERNEL)) > schedule_delayed_work(&vi->refill, 0); > > @@ -5959,12 +6003,12 @@ static int virtnet_xdp_set(struct net_device *dev, > struct bpf_prog *prog, > if (prog) > bpf_prog_add(prog, vi->max_queue_pairs - 1); > > + virtnet_rx_pause_all(vi); > + > /* Make sure NAPI is not using any XDP TX queues for RX. */ > if (netif_running(dev)) { > - for (i = 0; i < vi->max_queue_pairs; i++) { > - virtnet_napi_disable(&vi->rq[i]); > + for (i = 0; i < vi->max_queue_pairs; i++) > virtnet_napi_tx_disable(&vi->sq[i]); > - } > } > > if (!prog) { > @@ -5996,13 +6040,12 @@ static int virtnet_xdp_set(struct net_device *dev, > struct bpf_prog *prog, > vi->xdp_enabled = false; > } > > + virtnet_rx_resume_all(vi); > for (i = 0; i < vi->max_queue_pairs; i++) { > if (old_prog) > bpf_prog_put(old_prog); > - if (netif_running(dev)) { > - virtnet_napi_enable(&vi->rq[i]); > + if (netif_running(dev)) > virtnet_napi_tx_enable(&vi->sq[i]); > - } > } > > return 0; > @@ -6014,11 +6057,10 @@ static int virtnet_xdp_set(struct net_device *dev, > struct bpf_prog *prog, > rcu_assign_pointer(vi->rq[i].xdp_prog, old_prog); > } > > + virtnet_rx_resume_all(vi); > if (netif_running(dev)) { > - for (i = 0; i < vi->max_queue_pairs; i++) { > - virtnet_napi_enable(&vi->rq[i]); > + for (i = 0; i < vi->max_queue_pairs; i++) > virtnet_napi_tx_enable(&vi->sq[i]); > - } > } > if (prog) > bpf_prog_sub(prog, vi->max_queue_pairs - 1); > -- > 2.43.0