virtnet_freeze() frees the receive and send queues and virtnet_restore()
allocates new ones, but AF_XDP sockets stay bound to the device in the
xsk core. Whatever the driver set up for a zero-copy pool is lost on
freeze: the pool's DMA mapping, the xsk_buffs array, the TX header and
its mapping are leaked, and the queue comes back with no pool. When the
socket is closed afterwards, the core asks the driver to disable the
pool again:
WARNING: net/core/xdp.c:140 ... Missing register, driver bug
BUG: KASAN: null-ptr-deref in xp_dma_unmap
Read of size 8 at addr 0000000000000090
as virtnet_xsk_pool_disable() unregisters an xdp_rxq_info that was
never registered and passes the NULL rq->xsk_pool to
xsk_pool_dma_unmap().
Release the driver side of all zero-copy pools before freezing and bind
the pools the core still has for the queues again after restore. Also
return early from virtnet_xsk_pool_disable() for a queue without a pool,
in case rebinding failed.
Fixes: 09d2b3182c8e ("virtio_net: xsk: bind/unbind xsk for rx")
Signed-off-by: Haishuang Yan <[email protected]>
---
drivers/net/virtio_net.c | 47 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 4f5d8812f75b..5995ad730a91 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -6037,6 +6037,9 @@ static int virtnet_xsk_pool_disable(struct net_device
*dev, u16 qid)
rq = &vi->rq[qid];
pool = rq->xsk_pool;
+ /* Not bound to the driver, e.g. if rebinding it on restore failed */
+ if (!pool)
+ return 0;
err = virtnet_rq_bind_xsk_pool(vi, rq, NULL);
err |= virtnet_sq_bind_xsk_pool(vi, sq, NULL);
@@ -6052,6 +6055,42 @@ static int virtnet_xsk_pool_disable(struct net_device
*dev, u16 qid)
return err;
}
+/*
+ * Freeze frees the receive and send queues while the AF_XDP sockets stay
+ * bound to the device, so release the driver side of the zero-copy pools
+ * before and bind them again after restore.
+ */
+static void __maybe_unused virtnet_xsk_pools_unbind(struct virtnet_info *vi)
+{
+ int i;
+
+ ASSERT_RTNL();
+
+ for (i = 0; i < vi->max_queue_pairs; i++)
+ if (vi->rq[i].xsk_pool)
+ virtnet_xsk_pool_disable(vi->dev, i);
+}
+
+static void __maybe_unused virtnet_xsk_pools_rebind(struct virtnet_info *vi)
+{
+ struct xsk_buff_pool *pool;
+ int i, err;
+
+ ASSERT_RTNL();
+
+ for (i = 0; i < vi->curr_queue_pairs; i++) {
+ pool = xsk_get_pool_from_qid(vi->dev, i);
+ if (!pool || !pool->umem->zc)
+ continue;
+
+ err = virtnet_xsk_pool_enable(vi->dev, pool, i);
+ if (err)
+ netdev_warn(vi->dev,
+ "failed to rebind AF_XDP pool to queue %d:
%d\n",
+ i, err);
+ }
+}
+
static int virtnet_xsk_pool_setup(struct net_device *dev, struct netdev_bpf
*xdp)
{
if (xdp->xsk.pool)
@@ -7246,6 +7285,10 @@ static __maybe_unused int virtnet_freeze(struct
virtio_device *vdev)
{
struct virtnet_info *vi = vdev->priv;
+ rtnl_lock();
+ virtnet_xsk_pools_unbind(vi);
+ rtnl_unlock();
+
virtnet_cpu_notif_remove(vi);
virtnet_freeze_down(vdev);
remove_vq_common(vi);
@@ -7270,6 +7313,10 @@ static __maybe_unused int virtnet_restore(struct
virtio_device *vdev)
return err;
}
+ rtnl_lock();
+ virtnet_xsk_pools_rebind(vi);
+ rtnl_unlock();
+
return 0;
}
--
2.43.0