Skip to content

Commit c9b47cc

Browse files
magnus-karlssonborkmann
authored andcommitted
xsk: fix bug when trying to use both copy and zero-copy on one queue id
Previously, the xsk code did not record which umem was bound to a specific queue id. This was not required if all drivers were zero-copy enabled as this had to be recorded in the driver anyway. So if a user tried to bind two umems to the same queue, the driver would say no. But if copy-mode was first enabled and then zero-copy mode (or the reverse order), we mistakenly enabled both of them on the same umem leading to buggy behavior. The main culprit for this is that we did not store the association of umem to queue id in the copy case and only relied on the driver reporting this. As this relation was not stored in the driver for copy mode (it does not rely on the AF_XDP NDOs), this obviously could not work. This patch fixes the problem by always recording the umem to queue id relationship in the netdev_queue and netdev_rx_queue structs. This way we always know what kind of umem has been bound to a queue id and can act appropriately at bind time. Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
1 parent 661b8d1 commit c9b47cc

File tree

3 files changed

+64
-35
lines changed

3 files changed

+64
-35
lines changed

net/xdp/xdp_umem.c

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,45 +42,71 @@ void xdp_del_sk_umem(struct xdp_umem *umem, struct xdp_sock *xs)
4242
}
4343
}
4444

45-
int xdp_umem_query(struct net_device *dev, u16 queue_id)
45+
/* The umem is stored both in the _rx struct and the _tx struct as we do
46+
* not know if the device has more tx queues than rx, or the opposite.
47+
* This might also change during run time.
48+
*/
49+
static void xdp_reg_umem_at_qid(struct net_device *dev, struct xdp_umem *umem,
50+
u16 queue_id)
4651
{
47-
struct netdev_bpf bpf;
52+
if (queue_id < dev->real_num_rx_queues)
53+
dev->_rx[queue_id].umem = umem;
54+
if (queue_id < dev->real_num_tx_queues)
55+
dev->_tx[queue_id].umem = umem;
56+
}
4857

49-
ASSERT_RTNL();
58+
static struct xdp_umem *xdp_get_umem_from_qid(struct net_device *dev,
59+
u16 queue_id)
60+
{
61+
if (queue_id < dev->real_num_rx_queues)
62+
return dev->_rx[queue_id].umem;
63+
if (queue_id < dev->real_num_tx_queues)
64+
return dev->_tx[queue_id].umem;
5065

51-
memset(&bpf, 0, sizeof(bpf));
52-
bpf.command = XDP_QUERY_XSK_UMEM;
53-
bpf.xsk.queue_id = queue_id;
66+
return NULL;
67+
}
5468

55-
if (!dev->netdev_ops->ndo_bpf)
56-
return 0;
57-
return dev->netdev_ops->ndo_bpf(dev, &bpf) ?: !!bpf.xsk.umem;
69+
static void xdp_clear_umem_at_qid(struct net_device *dev, u16 queue_id)
70+
{
71+
/* Zero out the entry independent on how many queues are configured
72+
* at this point in time, as it might be used in the future.
73+
*/
74+
if (queue_id < dev->num_rx_queues)
75+
dev->_rx[queue_id].umem = NULL;
76+
if (queue_id < dev->num_tx_queues)
77+
dev->_tx[queue_id].umem = NULL;
5878
}
5979

6080
int xdp_umem_assign_dev(struct xdp_umem *umem, struct net_device *dev,
61-
u32 queue_id, u16 flags)
81+
u16 queue_id, u16 flags)
6282
{
6383
bool force_zc, force_copy;
6484
struct netdev_bpf bpf;
65-
int err;
85+
int err = 0;
6686

6787
force_zc = flags & XDP_ZEROCOPY;
6888
force_copy = flags & XDP_COPY;
6989

7090
if (force_zc && force_copy)
7191
return -EINVAL;
7292

73-
if (force_copy)
74-
return 0;
93+
rtnl_lock();
94+
if (xdp_get_umem_from_qid(dev, queue_id)) {
95+
err = -EBUSY;
96+
goto out_rtnl_unlock;
97+
}
7598

76-
if (!dev->netdev_ops->ndo_bpf || !dev->netdev_ops->ndo_xsk_async_xmit)
77-
return force_zc ? -EOPNOTSUPP : 0; /* fail or fallback */
99+
xdp_reg_umem_at_qid(dev, umem, queue_id);
100+
umem->dev = dev;
101+
umem->queue_id = queue_id;
102+
if (force_copy)
103+
/* For copy-mode, we are done. */
104+
goto out_rtnl_unlock;
78105

79-
rtnl_lock();
80-
err = xdp_umem_query(dev, queue_id);
81-
if (err) {
82-
err = err < 0 ? -EOPNOTSUPP : -EBUSY;
83-
goto err_rtnl_unlock;
106+
if (!dev->netdev_ops->ndo_bpf ||
107+
!dev->netdev_ops->ndo_xsk_async_xmit) {
108+
err = -EOPNOTSUPP;
109+
goto err_unreg_umem;
84110
}
85111

86112
bpf.command = XDP_SETUP_XSK_UMEM;
@@ -89,26 +115,28 @@ int xdp_umem_assign_dev(struct xdp_umem *umem, struct net_device *dev,
89115

90116
err = dev->netdev_ops->ndo_bpf(dev, &bpf);
91117
if (err)
92-
goto err_rtnl_unlock;
118+
goto err_unreg_umem;
93119
rtnl_unlock();
94120

95121
dev_hold(dev);
96-
umem->dev = dev;
97-
umem->queue_id = queue_id;
98122
umem->zc = true;
99123
return 0;
100124

101-
err_rtnl_unlock:
125+
err_unreg_umem:
126+
xdp_clear_umem_at_qid(dev, queue_id);
127+
if (!force_zc)
128+
err = 0; /* fallback to copy mode */
129+
out_rtnl_unlock:
102130
rtnl_unlock();
103-
return force_zc ? err : 0; /* fail or fallback */
131+
return err;
104132
}
105133

106134
static void xdp_umem_clear_dev(struct xdp_umem *umem)
107135
{
108136
struct netdev_bpf bpf;
109137
int err;
110138

111-
if (umem->dev) {
139+
if (umem->zc) {
112140
bpf.command = XDP_SETUP_XSK_UMEM;
113141
bpf.xsk.umem = NULL;
114142
bpf.xsk.queue_id = umem->queue_id;
@@ -119,9 +147,17 @@ static void xdp_umem_clear_dev(struct xdp_umem *umem)
119147

120148
if (err)
121149
WARN(1, "failed to disable umem!\n");
150+
}
151+
152+
if (umem->dev) {
153+
rtnl_lock();
154+
xdp_clear_umem_at_qid(umem->dev, umem->queue_id);
155+
rtnl_unlock();
156+
}
122157

158+
if (umem->zc) {
123159
dev_put(umem->dev);
124-
umem->dev = NULL;
160+
umem->zc = false;
125161
}
126162
}
127163

net/xdp/xdp_umem.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include <net/xdp_sock.h>
1010

1111
int xdp_umem_assign_dev(struct xdp_umem *umem, struct net_device *dev,
12-
u32 queue_id, u16 flags);
12+
u16 queue_id, u16 flags);
1313
bool xdp_umem_validate_queues(struct xdp_umem *umem);
1414
void xdp_get_umem(struct xdp_umem *umem);
1515
void xdp_put_umem(struct xdp_umem *umem);

net/xdp/xsk.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -419,13 +419,6 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
419419
}
420420

421421
qid = sxdp->sxdp_queue_id;
422-
423-
if ((xs->rx && qid >= dev->real_num_rx_queues) ||
424-
(xs->tx && qid >= dev->real_num_tx_queues)) {
425-
err = -EINVAL;
426-
goto out_unlock;
427-
}
428-
429422
flags = sxdp->sxdp_flags;
430423

431424
if (flags & XDP_SHARED_UMEM) {

0 commit comments

Comments
 (0)