[Devel] [PATCH RH7] virtio_net: fix hang on vhost backend setting

Pavel Tikhomirov ptikhomirov at virtuozzo.com
Tue May 17 19:29:49 MSK 2022


We can see a hang for 40 sec in (vz7.185.3) in vhost_net_set_backend:

crash> ps -m | grep UN
[0 00:00:40.157] [UN]  PID: 38937  TASK: ffff9b7f54ca8000  CPU: 2   COMMAND: "qemu-kvm"

[<ffffffffc09c04a5>] vhost_net_ubuf_put_and_wait+0x65/0xb0 [vhost_net]
[<ffffffffc09c2451>] vhost_net_ioctl+0x661/0x890 [vhost_net]
[<ffffffff8fc89d80>] do_vfs_ioctl+0x3b0/0x5b0
[<ffffffff8fc8a021>] SyS_ioctl+0xa1/0xc0

while waiting for ubufs->refcount to get to zero.

In crash we see an inprogress ubuf_info generated by handle_tx(), which
is holding a refcount on ubufs, and should release it from
vhost_zerocopy_callback() but yet didn't to it. We also see the
skb_shared_info referencing this ubuf_info with zerocopy flag set. And
finally we see an sk_buff referencing this skb_shared_info. This sk_buff
had passed through validate_xmit_skb_list() as it has ->next == NULL and
->prev == skb. So it also passed dev_hard_start_xmit() already in
sch_direct_xmit(), and by looking on sk_buff's dev's netdev_queue's
trans_start we can see that it happened exactly 40 sec earlier. The
netdev_queue has virtnet_netdev ops so everything happens in virtio_net.

Considering all the above I believe that we have the same thing as in
tun devices, seems that after virtio_net xmit the sk_buff can be held
for long and thus we need to orphan frags to release ubufs earlier.

See similar patches for tun:
868eefeb17d4 ("tun: orphan frags on xmit")
149d36f7187c ("tun: report orphan frags errors to zero copy callback")

https://pmc.acronis.com/browse/VSTOR-52993

Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
---
 drivers/net/virtio_net.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 652f5b1b218c..5d22e19ad161 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -790,16 +790,21 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
 	/* Free up any pending old buffers before queueing new ones. */
 	free_old_xmit_skbs(sq);
 
+	if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
+		goto drop;
+
 	/* Try to transmit */
 	err = xmit_skb(sq, skb);
 
 	/* This should not happen! */
 	if (unlikely(err)) {
+drop:
 		dev->stats.tx_fifo_errors++;
 		if (net_ratelimit())
 			dev_warn(&dev->dev,
 				 "Unexpected TXQ (%d) queue failure: %d\n", qnum, err);
 		dev->stats.tx_dropped++;
+		skb_tx_error(skb);
 		kfree_skb(skb);
 		return NETDEV_TX_OK;
 	}
-- 
2.35.1



More information about the Devel mailing list