* [PATCH net] xsk: defer software checksum until the skb is complete
@ 2026-10-24 9:12 Rongzheng Wang
2026-10-24 9:15 ` netdev-bot+sinfo
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Rongzheng Wang @ 2026-10-24 9:12 UTC (permalink / raw)
To: Magnus Karlsson, Maciej Fijalkowski
Cc: Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf,
linux-kernel
When XDP_UMEM_TX_SW_CSUM is used with multi-buffer TX, xsk_skb_metadata()
calls skb_checksum_help() while only the first descriptor has been added to
the skb. The checksum therefore covers just the first fragment, and
skb_checksum_help() sets ip_summed to CHECKSUM_NONE, so the remaining
fragments are never included in the checksum.
Move the software checksum calculation to the point where the last
descriptor has been added to the skb.
On a checksum failure the packet is now dropped instead of being returned
to the application for a retry. This path is not reachable with the
existing metadata validation, but the behavior change is intentional.
Add a selftest that sends the same packet as a single descriptor and as two
descriptors, verifies the expected UDP checksum, and compares the frames
captured on the peer.
Fixes: 11614723af26 ("xsk: Add option to calculate TX checksum in SW")
Cc: stable@vger.kernel.org
Signed-off-by: Rongzheng Wang <testwrzwrz@gmail.com>
---
net/xdp/xsk.c | 25 +-
.../selftests/bpf/prog_tests/xdp_metadata.c | 299 ++++++++++++++++++
2 files changed, 316 insertions(+), 8 deletions(-)
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index 33475b180ea6..845ae6bde987 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -1011,14 +1011,6 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
skb->csum_start = hr + csum_start;
skb->csum_offset = csum_offset;
skb->ip_summed = CHECKSUM_PARTIAL;
-
- if (unlikely(pool->tx_sw_csum)) {
- int err;
-
- err = skb_checksum_help(skb);
- if (err)
- return err;
- }
}
if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME)
@@ -1030,6 +1022,14 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
return 0;
}
+static int xsk_skb_sw_csum(struct sk_buff *skb, struct xsk_buff_pool *pool)
+{
+ if (!pool->tx_sw_csum || skb->ip_summed != CHECKSUM_PARTIAL)
+ return 0;
+
+ return skb_checksum_help(skb);
+}
+
static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
struct xdp_desc *desc)
{
@@ -1270,6 +1270,15 @@ static int __xsk_generic_xmit(struct sock *sk)
continue;
}
+ if (!xp_mb_desc(&desc)) {
+ err = xsk_skb_sw_csum(skb, xs->pool);
+ if (unlikely(err)) {
+ xskq_cons_release(xs->tx);
+ xsk_drop_skb(skb);
+ goto out;
+ }
+ }
+
xskq_cons_release(xs->tx);
if (xp_mb_desc(&desc)) {
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c
index 5c31054ad4a4..21a11300076a 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c
@@ -6,9 +6,12 @@
#include "xdp_metadata.h"
#include "xsk.h"
+#include <arpa/inet.h>
#include <bpf/btf.h>
#include <linux/errqueue.h>
+#include <linux/if_ether.h>
#include <linux/if_link.h>
+#include <linux/if_packet.h>
#include <linux/net_tstamp.h>
#include <netinet/udp.h>
#include <sys/mman.h>
@@ -38,6 +41,22 @@
#define TX_MAC "00:00:00:00:00:01"
#define RX_MAC "00:00:00:00:00:02"
+#define MB_TX_NAME "mb_tx0"
+#define MB_RX_NAME "mb_rx0"
+#define MB_NETNS_NAME "xdp_mb_csum"
+#define MB_UMEM_NUM 4
+#define MB_UMEM_FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE
+#define MB_UMEM_SIZE (MB_UMEM_FRAME_SIZE * MB_UMEM_NUM)
+#define MB_METADATA_LEN sizeof(struct xsk_tx_metadata)
+#define MB_PAYLOAD_LEN 128
+#define MB_SPLIT_LEN 64
+#define MB_PKT_LEN (sizeof(struct ethhdr) + sizeof(struct iphdr) + \
+ sizeof(struct udphdr) + MB_PAYLOAD_LEN)
+#define MB_TX_ADDR "10.1.0.1"
+#define MB_RX_ADDR "10.1.0.2"
+#define MB_SOURCE_PORT 1234
+#define MB_DEST_PORT 8080
+
#define VLAN_ID 59
#define VLAN_PROTO "802.1Q"
#define VLAN_PID htons(ETH_P_8021Q)
@@ -543,3 +562,283 @@ void test_xdp_metadata(void)
SYS_NOFAIL("ip netns del " RX_NETNS_NAME);
SYS_NOFAIL("ip netns del " TX_NETNS_NAME);
}
+
+static int open_xsk_mb(int ifindex, struct xsk *xsk)
+{
+ const struct xsk_socket_config socket_config = {
+ .rx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
+ .tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
+ .bind_flags = XDP_COPY | XDP_USE_SG,
+ };
+ const struct xsk_umem_config umem_config = {
+ .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
+ .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
+ .frame_size = MB_UMEM_FRAME_SIZE,
+ .flags = XDP_UMEM_UNALIGNED_CHUNK_FLAG | XDP_UMEM_TX_SW_CSUM |
+ XDP_UMEM_TX_METADATA_LEN,
+ .tx_metadata_len = sizeof(struct xsk_tx_metadata),
+ };
+ int ret;
+
+ xsk->umem_area = mmap(NULL, MB_UMEM_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
+ if (!ASSERT_NEQ(xsk->umem_area, MAP_FAILED, "mmap"))
+ return -1;
+
+ ret = xsk_umem__create(&xsk->umem, xsk->umem_area, MB_UMEM_SIZE,
+ &xsk->fill, &xsk->comp, &umem_config);
+ if (!ASSERT_OK(ret, "xsk_umem__create"))
+ return ret;
+
+ ret = xsk_socket__create(&xsk->socket, ifindex, QUEUE_ID, xsk->umem,
+ &xsk->rx, &xsk->tx, &socket_config);
+ if (!ASSERT_OK(ret, "xsk_socket__create"))
+ return ret;
+
+ return 0;
+}
+
+static void close_xsk_mb(struct xsk *xsk)
+{
+ if (xsk->socket)
+ xsk_socket__delete(xsk->socket);
+ if (xsk->umem)
+ xsk_umem__delete(xsk->umem);
+ if (xsk->umem_area && xsk->umem_area != MAP_FAILED)
+ munmap(xsk->umem_area, MB_UMEM_SIZE);
+}
+
+static __sum16 mb_build_packet(u8 *pkt)
+{
+ struct ethhdr *eth = (void *)pkt;
+ struct iphdr *iph = (void *)(eth + 1);
+ struct udphdr *udph = (void *)(iph + 1);
+ __sum16 expected;
+
+ memset(pkt, 0, MB_PKT_LEN);
+ memcpy(eth->h_dest, RX_MAC, ETH_ALEN);
+ memcpy(eth->h_source, TX_MAC, ETH_ALEN);
+ eth->h_proto = htons(ETH_P_IP);
+
+ iph->version = 4;
+ iph->ihl = 5;
+ iph->tot_len = htons((u16)(MB_PKT_LEN - sizeof(*eth)));
+ iph->id = htons(1);
+ iph->ttl = 64;
+ iph->protocol = IPPROTO_UDP;
+ iph->saddr = inet_addr(MB_TX_ADDR);
+ iph->daddr = inet_addr(MB_RX_ADDR);
+ iph->check = build_ip_csum(iph);
+
+ udph->source = htons(MB_SOURCE_PORT);
+ udph->dest = htons(MB_DEST_PORT);
+ udph->len = htons((u16)(sizeof(*udph) + MB_PAYLOAD_LEN));
+ memset(udph + 1, 0xaa, MB_PAYLOAD_LEN);
+
+ expected = build_udp_v4_csum(iph, udph);
+ udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+ ntohs(udph->len), IPPROTO_UDP, 0);
+ return expected;
+}
+
+static int mb_send_packet(struct xsk *xsk, const u8 *pkt, bool split)
+{
+ struct xsk_tx_metadata *meta;
+ struct xdp_desc *tx_desc;
+ u32 idx, num = split ? 2 : 1;
+ u8 *frame;
+ int ret;
+
+ ret = xsk_ring_prod__reserve(&xsk->tx, num, &idx);
+ if (!ASSERT_EQ(ret, num, "xsk_ring_prod__reserve"))
+ return -1;
+
+ if (split) {
+ frame = xsk_umem__get_data(xsk->umem_area, MB_UMEM_FRAME_SIZE);
+ meta = (struct xsk_tx_metadata *)frame;
+ memcpy(frame + MB_METADATA_LEN, pkt, MB_SPLIT_LEN);
+
+ tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx);
+ tx_desc->addr = MB_UMEM_FRAME_SIZE + MB_METADATA_LEN;
+ tx_desc->len = MB_SPLIT_LEN;
+ tx_desc->options = XDP_TX_METADATA | XDP_PKT_CONTD;
+
+ frame = xsk_umem__get_data(xsk->umem_area,
+ 2 * MB_UMEM_FRAME_SIZE);
+ memcpy(frame + MB_METADATA_LEN, pkt + MB_SPLIT_LEN,
+ MB_PKT_LEN - MB_SPLIT_LEN);
+
+ tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + 1);
+ tx_desc->addr = 2 * MB_UMEM_FRAME_SIZE + MB_METADATA_LEN;
+ tx_desc->len = (u32)(MB_PKT_LEN - MB_SPLIT_LEN);
+ tx_desc->options = 0;
+ } else {
+ frame = xsk_umem__get_data(xsk->umem_area, 0);
+ meta = (struct xsk_tx_metadata *)frame;
+ memcpy(frame + MB_METADATA_LEN, pkt, MB_PKT_LEN);
+
+ tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx);
+ tx_desc->addr = MB_METADATA_LEN;
+ tx_desc->len = (u32)MB_PKT_LEN;
+ tx_desc->options = XDP_TX_METADATA;
+ }
+
+ memset(meta, 0, sizeof(*meta));
+ meta->flags = XDP_TXMD_FLAGS_CHECKSUM;
+ meta->request.csum_start = (u16)(sizeof(struct ethhdr) + sizeof(struct iphdr));
+ meta->request.csum_offset = (u16)offsetof(struct udphdr, check);
+
+ xsk_ring_prod__submit(&xsk->tx, num);
+
+ ret = sendto(xsk_socket__fd(xsk->socket), NULL, 0, MSG_DONTWAIT,
+ NULL, 0);
+ if (!ASSERT_GE(ret, 0, "sendto"))
+ return -1;
+
+ return 0;
+}
+
+static int mb_wait_completion(struct xsk *xsk, u32 num)
+{
+ struct pollfd pfd = {
+ .fd = xsk_socket__fd(xsk->socket),
+ .events = POLLOUT,
+ };
+ u32 idx;
+ int ret, i;
+
+ for (i = 0; i < 100; i++) {
+ ret = xsk_ring_cons__peek(&xsk->comp, num, &idx);
+ if (ret == num) {
+ xsk_ring_cons__release(&xsk->comp, num);
+ return 0;
+ }
+ poll(&pfd, 1, 1);
+ }
+
+ return -1;
+}
+
+static int mb_recv_packet(int fd, u8 *buf, size_t len)
+{
+ struct sockaddr_ll sll;
+ socklen_t sll_len;
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
+ struct ethhdr *eth;
+ struct iphdr *iph;
+ struct udphdr *udph;
+ int ret;
+
+ for (;;) {
+ ret = poll(&pfd, 1, 1000);
+ if (ret <= 0)
+ return -1;
+
+ sll_len = sizeof(sll);
+ ret = recvfrom(fd, buf, len, 0, (struct sockaddr *)&sll,
+ &sll_len);
+ if (ret < 0)
+ return -1;
+ if (ret < (int)(sizeof(*eth) + sizeof(*iph) + sizeof(*udph)))
+ continue;
+
+ eth = (void *)buf;
+ if (eth->h_proto != htons(ETH_P_IP))
+ continue;
+ iph = (void *)(eth + 1);
+ if (iph->protocol != IPPROTO_UDP)
+ continue;
+ udph = (void *)(iph + 1);
+ if (udph->dest != htons(MB_DEST_PORT))
+ continue;
+
+ return ret;
+ }
+}
+
+void test_xdp_metadata_mb_csum(void)
+{
+ struct nstoken *tok = NULL;
+ struct sockaddr_ll sll = {};
+ struct udphdr *single_udp;
+ struct udphdr *multi_udp;
+ __sum16 expected_csum;
+ u8 pkt[MB_PKT_LEN];
+ u8 single[MB_PKT_LEN];
+ u8 multi[MB_PKT_LEN];
+ struct xsk xsk = {};
+ int tx_ifindex, rx_ifindex;
+ int pkt_fd = -1;
+ int single_len;
+ int multi_len;
+ int ret;
+
+ SYS_NOFAIL("ip netns del " MB_NETNS_NAME);
+ SYS(out, "ip netns add " MB_NETNS_NAME);
+ tok = open_netns(MB_NETNS_NAME);
+ if (!ASSERT_OK_PTR(tok, "setns"))
+ goto out;
+
+ SYS(out, "ip link add " MB_TX_NAME " type veth peer name " MB_RX_NAME);
+ SYS(out, "ip link set dev " MB_TX_NAME " up");
+ SYS(out, "ip link set dev " MB_RX_NAME " up");
+
+ tx_ifindex = if_nametoindex(MB_TX_NAME);
+ rx_ifindex = if_nametoindex(MB_RX_NAME);
+ if (!ASSERT_NEQ(tx_ifindex, 0, "if_nametoindex(tx)") ||
+ !ASSERT_NEQ(rx_ifindex, 0, "if_nametoindex(rx)"))
+ goto out;
+
+ ret = open_xsk_mb(tx_ifindex, &xsk);
+ if (!ASSERT_OK(ret, "open_xsk_mb"))
+ goto out;
+
+ pkt_fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC, htons(ETH_P_ALL));
+ if (!ASSERT_GE(pkt_fd, 0, "socket(AF_PACKET)"))
+ goto out;
+
+ sll.sll_family = AF_PACKET;
+ sll.sll_protocol = htons(ETH_P_ALL);
+ sll.sll_ifindex = rx_ifindex;
+ if (!ASSERT_OK(bind(pkt_fd, (struct sockaddr *)&sll, sizeof(sll)),
+ "bind(AF_PACKET)"))
+ goto out;
+
+ expected_csum = mb_build_packet(pkt);
+
+ if (!ASSERT_OK(mb_send_packet(&xsk, pkt, false), "send single"))
+ goto out;
+ single_len = mb_recv_packet(pkt_fd, single, sizeof(single));
+ if (!ASSERT_EQ(single_len, (int)MB_PKT_LEN, "recv single"))
+ goto out;
+ single_udp = (void *)(single + sizeof(struct ethhdr) + sizeof(struct iphdr));
+ if (!ASSERT_EQ(single_udp->check, expected_csum, "single checksum"))
+ goto out;
+ if (!ASSERT_OK(mb_wait_completion(&xsk, 1), "complete single"))
+ goto out;
+
+ if (!ASSERT_OK(mb_send_packet(&xsk, pkt, true), "send multi"))
+ goto out;
+ multi_len = mb_recv_packet(pkt_fd, multi, sizeof(multi));
+ if (!ASSERT_EQ(multi_len, (int)MB_PKT_LEN, "recv multi"))
+ goto out;
+ multi_udp = (void *)(multi + sizeof(struct ethhdr) + sizeof(struct iphdr));
+ if (!ASSERT_EQ(multi_udp->check, expected_csum, "multi checksum"))
+ goto out;
+ if (!ASSERT_OK(mb_wait_completion(&xsk, 2), "complete multi"))
+ goto out;
+
+ ASSERT_EQ(memcmp(single, multi, MB_PKT_LEN), 0,
+ "single and multi-buffer packets differ");
+
+out:
+ if (pkt_fd >= 0)
+ close(pkt_fd);
+ close_xsk_mb(&xsk);
+ if (tok)
+ close_netns(tok);
+ SYS_NOFAIL("ip netns del " MB_NETNS_NAME);
+}
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH net] xsk: defer software checksum until the skb is complete 2026-10-24 9:12 [PATCH net] xsk: defer software checksum until the skb is complete Rongzheng Wang @ 2026-10-24 9:15 ` netdev-bot+sinfo 2026-10-24 11:35 ` 王荣峥 2026-10-24 12:25 ` Jason Xing 2026-10-24 14:11 ` [PATCH net v2 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang 2 siblings, 1 reply; 8+ messages in thread From: netdev-bot+sinfo @ 2026-10-24 9:15 UTC (permalink / raw) To: Rongzheng Wang Cc: Magnus Karlsson, Maciej Fijalkowski, Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf, linux-kernel Hi! This is an automated message. This series looks like a fix, but its commit messages seem to be missing some information: - How the issue was discovered, e.g. hit in production, hit during development, syzbot report, manual code inspection, LLM or static analysis tool scan. Please do not repost the series just to address the above. Instead, reply to this email with the missing information, so that reviewers can take it into account. If the series needs another revision for other reasons, please include the information in the commit messages then. The evaluation is done by an LLM so it may be wrong, if you think that is the case please reply and explain. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net] xsk: defer software checksum until the skb is complete 2026-10-24 9:15 ` netdev-bot+sinfo @ 2026-10-24 11:35 ` 王荣峥 0 siblings, 0 replies; 8+ messages in thread From: 王荣峥 @ 2026-10-24 11:35 UTC (permalink / raw) To: netdev-bot+sinfo Cc: Magnus Karlsson, Maciej Fijalkowski, Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf, linux-kernel Hi, I found this while studying the AF_XDP kernel implementation and running my own multi-buffer TX tests. When I enabled XDP_UMEM_TX_SW_CSUM and sent a UDP packet as two descriptors, the checksum on the wire did not match the expected value; the same packet as a single descriptor was correct. I then traced it to xsk_skb_metadata() calling skb_checksum_help() before the remaining fragments were added. This was not a production or syzbot report. I reproduced it with a standalone AF_XDP TAP test and extended the kernel selftest: the unpatched kernel gives actual 8817 vs expected 52507, while the patched kernel gives the same correct checksum for both single- and multi-buffer TX. Thanks, Rongzheng <netdev-bot+sinfo@kernel.org> 于2026年10月24日周六 17:15写道: > > Hi! > > This is an automated message. This series looks like a fix, but its > commit messages seem to be missing some information: > > - How the issue was discovered, e.g. hit in production, hit during > development, syzbot report, manual code inspection, LLM or static > analysis tool scan. > > Please do not repost the series just to address the above. Instead, > reply to this email with the missing information, so that reviewers > can take it into account. If the series needs another revision for > other reasons, please include the information in the commit messages > then. > > The evaluation is done by an LLM so it may be wrong, if you think > that is the case please reply and explain. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net] xsk: defer software checksum until the skb is complete 2026-10-24 9:12 [PATCH net] xsk: defer software checksum until the skb is complete Rongzheng Wang 2026-10-24 9:15 ` netdev-bot+sinfo @ 2026-10-24 12:25 ` Jason Xing 2026-10-24 14:11 ` [PATCH net v2 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang 2 siblings, 0 replies; 8+ messages in thread From: Jason Xing @ 2026-10-24 12:25 UTC (permalink / raw) To: Rongzheng Wang Cc: Magnus Karlsson, Maciej Fijalkowski, Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf, linux-kernel On Sat, Oct 24, 2026 at 5:13 PM Rongzheng Wang <testwrzwrz@gmail.com> wrote: > > When XDP_UMEM_TX_SW_CSUM is used with multi-buffer TX, xsk_skb_metadata() > calls skb_checksum_help() while only the first descriptor has been added to > the skb. The checksum therefore covers just the first fragment, and > skb_checksum_help() sets ip_summed to CHECKSUM_NONE, so the remaining > fragments are never included in the checksum. > > Move the software checksum calculation to the point where the last > descriptor has been added to the skb. > > On a checksum failure the packet is now dropped instead of being returned > to the application for a retry. This path is not reachable with the > existing metadata validation, but the behavior change is intentional. > > Add a selftest that sends the same packet as a single descriptor and as two > descriptors, verifies the expected UDP checksum, and compares the frames > captured on the peer. > > Fixes: 11614723af26 ("xsk: Add option to calculate TX checksum in SW") > Cc: stable@vger.kernel.org > Signed-off-by: Rongzheng Wang <testwrzwrz@gmail.com> > --- > net/xdp/xsk.c | 25 +- > .../selftests/bpf/prog_tests/xdp_metadata.c | 299 ++++++++++++++++++ > 2 files changed, 316 insertions(+), 8 deletions(-) > > diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c > index 33475b180ea6..845ae6bde987 100644 > --- a/net/xdp/xsk.c > +++ b/net/xdp/xsk.c > @@ -1011,14 +1011,6 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, > skb->csum_start = hr + csum_start; > skb->csum_offset = csum_offset; > skb->ip_summed = CHECKSUM_PARTIAL; > - > - if (unlikely(pool->tx_sw_csum)) { > - int err; > - > - err = skb_checksum_help(skb); > - if (err) > - return err; > - } > } > > if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME) > @@ -1030,6 +1022,14 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, > return 0; > } > > +static int xsk_skb_sw_csum(struct sk_buff *skb, struct xsk_buff_pool *pool) > +{ > + if (!pool->tx_sw_csum || skb->ip_summed != CHECKSUM_PARTIAL) > + return 0; > + > + return skb_checksum_help(skb); > +} > + > static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, > struct xdp_desc *desc) > { > @@ -1270,6 +1270,15 @@ static int __xsk_generic_xmit(struct sock *sk) > continue; > } > > + if (!xp_mb_desc(&desc)) { > + err = xsk_skb_sw_csum(skb, xs->pool); > + if (unlikely(err)) { > + xskq_cons_release(xs->tx); > + xsk_drop_skb(skb); > + goto out; > + } > + } > + > xskq_cons_release(xs->tx); > > if (xp_mb_desc(&desc)) { How about adjusting the _position_ like this: diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 33475b180ea6..f7ee72b4681b 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -1275,6 +1275,12 @@ static int __xsk_generic_xmit(struct sock *sk) if (xp_mb_desc(&desc)) { xs->skb = skb; continue; + } else { + err = xsk_skb_sw_csum(skb, xs->pool); + if (unlikely(err)) { + xsk_drop_skb(skb); + goto out; + } } err = __dev_direct_xmit(skb, xs->queue_id); ? In terms of the error handling, is it correct to drop this skb? And reading through the error cases in skb_checksum_help(), we never have the chance to hit any of them at this point? Please double check that. Another thing is you're adding this to the hot path. I would add unlikely or something similar to remove the impact on generic xmit. > diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c > index 5c31054ad4a4..21a11300076a 100644 > --- a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c > +++ b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c Please separate this test from the patch as a 2/2 patch then. Try to reuse the interfaces that we have and integrate it into the existing selftests instead of reinventing the wheel from scratch. Thanks, Jason > @@ -6,9 +6,12 @@ > #include "xdp_metadata.h" > #include "xsk.h" > > +#include <arpa/inet.h> > #include <bpf/btf.h> > #include <linux/errqueue.h> > +#include <linux/if_ether.h> > #include <linux/if_link.h> > +#include <linux/if_packet.h> > #include <linux/net_tstamp.h> > #include <netinet/udp.h> > #include <sys/mman.h> > @@ -38,6 +41,22 @@ > #define TX_MAC "00:00:00:00:00:01" > #define RX_MAC "00:00:00:00:00:02" > > +#define MB_TX_NAME "mb_tx0" > +#define MB_RX_NAME "mb_rx0" > +#define MB_NETNS_NAME "xdp_mb_csum" > +#define MB_UMEM_NUM 4 > +#define MB_UMEM_FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE > +#define MB_UMEM_SIZE (MB_UMEM_FRAME_SIZE * MB_UMEM_NUM) > +#define MB_METADATA_LEN sizeof(struct xsk_tx_metadata) > +#define MB_PAYLOAD_LEN 128 > +#define MB_SPLIT_LEN 64 > +#define MB_PKT_LEN (sizeof(struct ethhdr) + sizeof(struct iphdr) + \ > + sizeof(struct udphdr) + MB_PAYLOAD_LEN) > +#define MB_TX_ADDR "10.1.0.1" > +#define MB_RX_ADDR "10.1.0.2" > +#define MB_SOURCE_PORT 1234 > +#define MB_DEST_PORT 8080 > + > #define VLAN_ID 59 > #define VLAN_PROTO "802.1Q" > #define VLAN_PID htons(ETH_P_8021Q) > @@ -543,3 +562,283 @@ void test_xdp_metadata(void) > SYS_NOFAIL("ip netns del " RX_NETNS_NAME); > SYS_NOFAIL("ip netns del " TX_NETNS_NAME); > } > + > +static int open_xsk_mb(int ifindex, struct xsk *xsk) > +{ > + const struct xsk_socket_config socket_config = { > + .rx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, > + .tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, > + .bind_flags = XDP_COPY | XDP_USE_SG, > + }; > + const struct xsk_umem_config umem_config = { > + .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, > + .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, > + .frame_size = MB_UMEM_FRAME_SIZE, > + .flags = XDP_UMEM_UNALIGNED_CHUNK_FLAG | XDP_UMEM_TX_SW_CSUM | > + XDP_UMEM_TX_METADATA_LEN, > + .tx_metadata_len = sizeof(struct xsk_tx_metadata), > + }; > + int ret; > + > + xsk->umem_area = mmap(NULL, MB_UMEM_SIZE, PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); > + if (!ASSERT_NEQ(xsk->umem_area, MAP_FAILED, "mmap")) > + return -1; > + > + ret = xsk_umem__create(&xsk->umem, xsk->umem_area, MB_UMEM_SIZE, > + &xsk->fill, &xsk->comp, &umem_config); > + if (!ASSERT_OK(ret, "xsk_umem__create")) > + return ret; > + > + ret = xsk_socket__create(&xsk->socket, ifindex, QUEUE_ID, xsk->umem, > + &xsk->rx, &xsk->tx, &socket_config); > + if (!ASSERT_OK(ret, "xsk_socket__create")) > + return ret; > + > + return 0; > +} > + > +static void close_xsk_mb(struct xsk *xsk) > +{ > + if (xsk->socket) > + xsk_socket__delete(xsk->socket); > + if (xsk->umem) > + xsk_umem__delete(xsk->umem); > + if (xsk->umem_area && xsk->umem_area != MAP_FAILED) > + munmap(xsk->umem_area, MB_UMEM_SIZE); > +} > + > +static __sum16 mb_build_packet(u8 *pkt) > +{ > + struct ethhdr *eth = (void *)pkt; > + struct iphdr *iph = (void *)(eth + 1); > + struct udphdr *udph = (void *)(iph + 1); > + __sum16 expected; > + > + memset(pkt, 0, MB_PKT_LEN); > + memcpy(eth->h_dest, RX_MAC, ETH_ALEN); > + memcpy(eth->h_source, TX_MAC, ETH_ALEN); > + eth->h_proto = htons(ETH_P_IP); > + > + iph->version = 4; > + iph->ihl = 5; > + iph->tot_len = htons((u16)(MB_PKT_LEN - sizeof(*eth))); > + iph->id = htons(1); > + iph->ttl = 64; > + iph->protocol = IPPROTO_UDP; > + iph->saddr = inet_addr(MB_TX_ADDR); > + iph->daddr = inet_addr(MB_RX_ADDR); > + iph->check = build_ip_csum(iph); > + > + udph->source = htons(MB_SOURCE_PORT); > + udph->dest = htons(MB_DEST_PORT); > + udph->len = htons((u16)(sizeof(*udph) + MB_PAYLOAD_LEN)); > + memset(udph + 1, 0xaa, MB_PAYLOAD_LEN); > + > + expected = build_udp_v4_csum(iph, udph); > + udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, > + ntohs(udph->len), IPPROTO_UDP, 0); > + return expected; > +} > + > +static int mb_send_packet(struct xsk *xsk, const u8 *pkt, bool split) > +{ > + struct xsk_tx_metadata *meta; > + struct xdp_desc *tx_desc; > + u32 idx, num = split ? 2 : 1; > + u8 *frame; > + int ret; > + > + ret = xsk_ring_prod__reserve(&xsk->tx, num, &idx); > + if (!ASSERT_EQ(ret, num, "xsk_ring_prod__reserve")) > + return -1; > + > + if (split) { > + frame = xsk_umem__get_data(xsk->umem_area, MB_UMEM_FRAME_SIZE); > + meta = (struct xsk_tx_metadata *)frame; > + memcpy(frame + MB_METADATA_LEN, pkt, MB_SPLIT_LEN); > + > + tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx); > + tx_desc->addr = MB_UMEM_FRAME_SIZE + MB_METADATA_LEN; > + tx_desc->len = MB_SPLIT_LEN; > + tx_desc->options = XDP_TX_METADATA | XDP_PKT_CONTD; > + > + frame = xsk_umem__get_data(xsk->umem_area, > + 2 * MB_UMEM_FRAME_SIZE); > + memcpy(frame + MB_METADATA_LEN, pkt + MB_SPLIT_LEN, > + MB_PKT_LEN - MB_SPLIT_LEN); > + > + tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + 1); > + tx_desc->addr = 2 * MB_UMEM_FRAME_SIZE + MB_METADATA_LEN; > + tx_desc->len = (u32)(MB_PKT_LEN - MB_SPLIT_LEN); > + tx_desc->options = 0; > + } else { > + frame = xsk_umem__get_data(xsk->umem_area, 0); > + meta = (struct xsk_tx_metadata *)frame; > + memcpy(frame + MB_METADATA_LEN, pkt, MB_PKT_LEN); > + > + tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx); > + tx_desc->addr = MB_METADATA_LEN; > + tx_desc->len = (u32)MB_PKT_LEN; > + tx_desc->options = XDP_TX_METADATA; > + } > + > + memset(meta, 0, sizeof(*meta)); > + meta->flags = XDP_TXMD_FLAGS_CHECKSUM; > + meta->request.csum_start = (u16)(sizeof(struct ethhdr) + sizeof(struct iphdr)); > + meta->request.csum_offset = (u16)offsetof(struct udphdr, check); > + > + xsk_ring_prod__submit(&xsk->tx, num); > + > + ret = sendto(xsk_socket__fd(xsk->socket), NULL, 0, MSG_DONTWAIT, > + NULL, 0); > + if (!ASSERT_GE(ret, 0, "sendto")) > + return -1; > + > + return 0; > +} > + > +static int mb_wait_completion(struct xsk *xsk, u32 num) > +{ > + struct pollfd pfd = { > + .fd = xsk_socket__fd(xsk->socket), > + .events = POLLOUT, > + }; > + u32 idx; > + int ret, i; > + > + for (i = 0; i < 100; i++) { > + ret = xsk_ring_cons__peek(&xsk->comp, num, &idx); > + if (ret == num) { > + xsk_ring_cons__release(&xsk->comp, num); > + return 0; > + } > + poll(&pfd, 1, 1); > + } > + > + return -1; > +} > + > +static int mb_recv_packet(int fd, u8 *buf, size_t len) > +{ > + struct sockaddr_ll sll; > + socklen_t sll_len; > + struct pollfd pfd = { > + .fd = fd, > + .events = POLLIN, > + }; > + struct ethhdr *eth; > + struct iphdr *iph; > + struct udphdr *udph; > + int ret; > + > + for (;;) { > + ret = poll(&pfd, 1, 1000); > + if (ret <= 0) > + return -1; > + > + sll_len = sizeof(sll); > + ret = recvfrom(fd, buf, len, 0, (struct sockaddr *)&sll, > + &sll_len); > + if (ret < 0) > + return -1; > + if (ret < (int)(sizeof(*eth) + sizeof(*iph) + sizeof(*udph))) > + continue; > + > + eth = (void *)buf; > + if (eth->h_proto != htons(ETH_P_IP)) > + continue; > + iph = (void *)(eth + 1); > + if (iph->protocol != IPPROTO_UDP) > + continue; > + udph = (void *)(iph + 1); > + if (udph->dest != htons(MB_DEST_PORT)) > + continue; > + > + return ret; > + } > +} > + > +void test_xdp_metadata_mb_csum(void) > +{ > + struct nstoken *tok = NULL; > + struct sockaddr_ll sll = {}; > + struct udphdr *single_udp; > + struct udphdr *multi_udp; > + __sum16 expected_csum; > + u8 pkt[MB_PKT_LEN]; > + u8 single[MB_PKT_LEN]; > + u8 multi[MB_PKT_LEN]; > + struct xsk xsk = {}; > + int tx_ifindex, rx_ifindex; > + int pkt_fd = -1; > + int single_len; > + int multi_len; > + int ret; > + > + SYS_NOFAIL("ip netns del " MB_NETNS_NAME); > + SYS(out, "ip netns add " MB_NETNS_NAME); > + tok = open_netns(MB_NETNS_NAME); > + if (!ASSERT_OK_PTR(tok, "setns")) > + goto out; > + > + SYS(out, "ip link add " MB_TX_NAME " type veth peer name " MB_RX_NAME); > + SYS(out, "ip link set dev " MB_TX_NAME " up"); > + SYS(out, "ip link set dev " MB_RX_NAME " up"); > + > + tx_ifindex = if_nametoindex(MB_TX_NAME); > + rx_ifindex = if_nametoindex(MB_RX_NAME); > + if (!ASSERT_NEQ(tx_ifindex, 0, "if_nametoindex(tx)") || > + !ASSERT_NEQ(rx_ifindex, 0, "if_nametoindex(rx)")) > + goto out; > + > + ret = open_xsk_mb(tx_ifindex, &xsk); > + if (!ASSERT_OK(ret, "open_xsk_mb")) > + goto out; > + > + pkt_fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC, htons(ETH_P_ALL)); > + if (!ASSERT_GE(pkt_fd, 0, "socket(AF_PACKET)")) > + goto out; > + > + sll.sll_family = AF_PACKET; > + sll.sll_protocol = htons(ETH_P_ALL); > + sll.sll_ifindex = rx_ifindex; > + if (!ASSERT_OK(bind(pkt_fd, (struct sockaddr *)&sll, sizeof(sll)), > + "bind(AF_PACKET)")) > + goto out; > + > + expected_csum = mb_build_packet(pkt); > + > + if (!ASSERT_OK(mb_send_packet(&xsk, pkt, false), "send single")) > + goto out; > + single_len = mb_recv_packet(pkt_fd, single, sizeof(single)); > + if (!ASSERT_EQ(single_len, (int)MB_PKT_LEN, "recv single")) > + goto out; > + single_udp = (void *)(single + sizeof(struct ethhdr) + sizeof(struct iphdr)); > + if (!ASSERT_EQ(single_udp->check, expected_csum, "single checksum")) > + goto out; > + if (!ASSERT_OK(mb_wait_completion(&xsk, 1), "complete single")) > + goto out; > + > + if (!ASSERT_OK(mb_send_packet(&xsk, pkt, true), "send multi")) > + goto out; > + multi_len = mb_recv_packet(pkt_fd, multi, sizeof(multi)); > + if (!ASSERT_EQ(multi_len, (int)MB_PKT_LEN, "recv multi")) > + goto out; > + multi_udp = (void *)(multi + sizeof(struct ethhdr) + sizeof(struct iphdr)); > + if (!ASSERT_EQ(multi_udp->check, expected_csum, "multi checksum")) > + goto out; > + if (!ASSERT_OK(mb_wait_completion(&xsk, 2), "complete multi")) > + goto out; > + > + ASSERT_EQ(memcmp(single, multi, MB_PKT_LEN), 0, > + "single and multi-buffer packets differ"); > + > +out: > + if (pkt_fd >= 0) > + close(pkt_fd); > + close_xsk_mb(&xsk); > + if (tok) > + close_netns(tok); > + SYS_NOFAIL("ip netns del " MB_NETNS_NAME); > +} > -- > 2.39.5 (Apple Git-154) > > ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net v2 0/2] xsk: fix multi-buffer TX software checksum 2026-10-24 9:12 [PATCH net] xsk: defer software checksum until the skb is complete Rongzheng Wang 2026-10-24 9:15 ` netdev-bot+sinfo 2026-10-24 12:25 ` Jason Xing @ 2026-10-24 14:11 ` Rongzheng Wang 2026-10-24 14:11 ` [PATCH net v2 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang ` (2 more replies) 2 siblings, 3 replies; 8+ messages in thread From: Rongzheng Wang @ 2026-10-24 14:11 UTC (permalink / raw) To: Magnus Karlsson, Maciej Fijalkowski Cc: Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf, linux-kernel, Rongzheng Wang This is v2 of the AF_XDP multi-buffer TX software checksum fix. v1 was a single patch containing both the kernel fix and a standalone selftest. Review feedback asked to split it into a 2-patch series and to reuse the existing xdp_metadata selftest infrastructure. v2 does that. Patch 1 moves the software checksum calculation from xsk_skb_metadata() to the point where the last descriptor has been added to the skb. The old code calculated the checksum after the first descriptor, so multi-buffer packets only included the first fragment in the checksum. On a checksum failure the packet is now dropped after the descriptors have been consumed. Metadata validation makes this path unreachable in the current copy-mode implementation; keeping the defensive drop avoids a retry path for an skb that is no longer complete. Patch 2 extends test_xdp_metadata instead of adding another test harness. It reuses the existing AF_XDP setup, packet generation, completion, and verification helpers, and sends the same UDP packet both as one descriptor and as two descriptors. Testing: - final exact-else patched kernel: xdp_metadata 1/0 PASSED - same-config unpatched kernel: checksum mismatch, xdp_metadata FAILED - checkpatch --strict: 0 errors / 0 warnings for both patches Changes in v2: - split the kernel fix and selftest into 1/2 and 2/2 - reuse test_xdp_metadata helpers and reduce the selftest patch to 86 insertions - use literal else and unlikely in the checksum error path - document the drop decision and test it on patched and unpatched kernels Rongzheng Wang (2): xsk: defer software checksum until the skb is complete selftests/bpf: cover AF_XDP multi-buffer TX software checksum net/xdp/xsk.c | 22 +-- .../selftests/bpf/prog_tests/xdp_metadata.c | 126 ++++++++++++------ 2 files changed, 100 insertions(+), 48 deletions(-) -- 2.39.5 (Apple Git-154) ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net v2 1/2] xsk: defer software checksum until the skb is complete 2026-10-24 14:11 ` [PATCH net v2 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang @ 2026-10-24 14:11 ` Rongzheng Wang 2026-10-24 14:11 ` [PATCH net v2 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang 2026-10-24 14:15 ` [PATCH net v2 0/2] xsk: fix " netdev-bot+sinfo 2 siblings, 0 replies; 8+ messages in thread From: Rongzheng Wang @ 2026-10-24 14:11 UTC (permalink / raw) To: Magnus Karlsson, Maciej Fijalkowski Cc: Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf, linux-kernel, Rongzheng Wang When XDP_UMEM_TX_SW_CSUM is used with multi-buffer TX, xsk_skb_metadata() calls skb_checksum_help() while only the first descriptor has been added to the skb. The checksum therefore covers just the first fragment, and skb_checksum_help() sets ip_summed to CHECKSUM_NONE, so the remaining fragments are never included in the checksum. Move the software checksum calculation to the point where the last descriptor has been added to the skb. On a checksum failure the packet is now dropped instead of being returned to the application for a retry. This path is not reachable with the existing metadata validation, but the behavior change is intentional. Fixes: 11614723af26 ("xsk: Add option to calculate TX checksum in SW") Cc: stable@vger.kernel.org Signed-off-by: Rongzheng Wang <testwrzwrz@gmail.com> --- net/xdp/xsk.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 33475b180ea6..65be2a368325 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -1011,14 +1011,6 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, skb->csum_start = hr + csum_start; skb->csum_offset = csum_offset; skb->ip_summed = CHECKSUM_PARTIAL; - - if (unlikely(pool->tx_sw_csum)) { - int err; - - err = skb_checksum_help(skb); - if (err) - return err; - } } if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME) @@ -1030,6 +1022,14 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, return 0; } +static int xsk_skb_sw_csum(struct sk_buff *skb, struct xsk_buff_pool *pool) +{ + if (!pool->tx_sw_csum || skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + return skb_checksum_help(skb); +} + static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, struct xdp_desc *desc) { @@ -1275,6 +1275,12 @@ static int __xsk_generic_xmit(struct sock *sk) if (xp_mb_desc(&desc)) { xs->skb = skb; continue; + } else { + err = xsk_skb_sw_csum(skb, xs->pool); + if (unlikely(err)) { + xsk_drop_skb(skb); + goto out; + } } err = __dev_direct_xmit(skb, xs->queue_id); -- 2.39.5 (Apple Git-154) ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net v2 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum 2026-10-24 14:11 ` [PATCH net v2 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang 2026-10-24 14:11 ` [PATCH net v2 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang @ 2026-10-24 14:11 ` Rongzheng Wang 2026-10-24 14:15 ` [PATCH net v2 0/2] xsk: fix " netdev-bot+sinfo 2 siblings, 0 replies; 8+ messages in thread From: Rongzheng Wang @ 2026-10-24 14:11 UTC (permalink / raw) To: Magnus Karlsson, Maciej Fijalkowski Cc: Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf, linux-kernel, Rongzheng Wang Extend test_xdp_metadata to send the same UDP packet both as one TX descriptor and as two descriptors, and to run the existing metadata and checksum verification for both cases. Reuse the existing AF_XDP setup, packet generation, completion, and receive helpers in xdp_metadata.c instead of adding a separate test harness. Signed-off-by: Rongzheng Wang <testwrzwrz@gmail.com> --- .../selftests/bpf/prog_tests/xdp_metadata.c | 126 ++++++++++++------ 1 file changed, 86 insertions(+), 40 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c index 5c31054ad4a4..1cf70fb800fb 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c @@ -19,6 +19,8 @@ #define RX_NAME "veRX" #define UDP_PAYLOAD_BYTES 4 +#define UDP_SPLIT_BYTES (sizeof(struct ethhdr) + sizeof(struct iphdr) + \ + sizeof(struct udphdr)) #define UDP_SOURCE_PORT 1234 #define AF_XDP_CONSUMER_PORT 8080 @@ -56,13 +58,13 @@ struct xsk { struct xsk_socket *socket; }; -static int open_xsk(int ifindex, struct xsk *xsk) +static int open_xsk(int ifindex, struct xsk *xsk, __u32 bind_flags) { int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; const struct xsk_socket_config socket_config = { .rx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, .tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, - .bind_flags = XDP_COPY, + .bind_flags = bind_flags, }; const struct xsk_umem_config umem_config = { .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, @@ -133,36 +135,36 @@ static void close_xsk(struct xsk *xsk) munmap(xsk->umem_area, UMEM_SIZE); } -static int generate_packet(struct xsk *xsk, __u16 dst_port) +static int generate_packet(struct xsk *xsk, __u16 dst_port, u16 split_len) { struct xsk_tx_metadata *meta; struct xdp_desc *tx_desc; struct udphdr *udph; struct ethhdr *eth; struct iphdr *iph; + u16 pkt_len; + u32 num, addr; void *data; __u32 idx; + u8 pkt[256]; int ret; - ret = xsk_ring_prod__reserve(&xsk->tx, 1, &idx); - if (!ASSERT_EQ(ret, 1, "xsk_ring_prod__reserve")) + pkt_len = sizeof(*eth) + sizeof(*iph) + sizeof(*udph) + UDP_PAYLOAD_BYTES; + if (!ASSERT_LT(split_len, pkt_len, "split_len")) return -1; - tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx); - tx_desc->addr = idx % (UMEM_NUM / 2) * UMEM_FRAME_SIZE + sizeof(struct xsk_tx_metadata); - printf("%p: tx_desc[%u]->addr=%llx\n", xsk, idx, tx_desc->addr); - data = xsk_umem__get_data(xsk->umem_area, tx_desc->addr); - - meta = data - sizeof(struct xsk_tx_metadata); - memset(meta, 0, sizeof(*meta)); - meta->flags = XDP_TXMD_FLAGS_TIMESTAMP; + num = split_len ? 2 : 1; + ret = xsk_ring_prod__reserve(&xsk->tx, num, &idx); + if (!ASSERT_EQ(ret, num, "xsk_ring_prod__reserve")) + return -1; - eth = data; + memset(pkt, 0, sizeof(pkt)); + eth = (void *)pkt; iph = (void *)(eth + 1); udph = (void *)(iph + 1); - memcpy(eth->h_dest, "\x00\x00\x00\x00\x00\x02", ETH_ALEN); - memcpy(eth->h_source, "\x00\x00\x00\x00\x00\x01", ETH_ALEN); + memcpy(eth->h_dest, RX_MAC, ETH_ALEN); + memcpy(eth->h_source, TX_MAC, ETH_ALEN); eth->h_proto = htons(ETH_P_IP); iph->version = 0x4; @@ -180,18 +182,38 @@ static int generate_packet(struct xsk *xsk, __u16 dst_port) udph->source = htons(UDP_SOURCE_PORT); udph->dest = htons(dst_port); udph->len = htons(sizeof(*udph) + UDP_PAYLOAD_BYTES); + memset(udph + 1, 0xAA, UDP_PAYLOAD_BYTES); udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, ntohs(udph->len), IPPROTO_UDP, 0); - memset(udph + 1, 0xAA, UDP_PAYLOAD_BYTES); + addr = idx % (UMEM_NUM / 2) * UMEM_FRAME_SIZE; + tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx); + tx_desc->addr = addr + sizeof(*meta); + tx_desc->len = split_len ? split_len : pkt_len; + tx_desc->options = XDP_TX_METADATA; + printf("%p: tx_desc[%u]->addr=%llx\n", xsk, idx, tx_desc->addr); + data = xsk_umem__get_data(xsk->umem_area, tx_desc->addr); + memcpy(data, pkt, tx_desc->len); - meta->flags |= XDP_TXMD_FLAGS_CHECKSUM; + meta = data - sizeof(*meta); + memset(meta, 0, sizeof(*meta)); + meta->flags = XDP_TXMD_FLAGS_TIMESTAMP | XDP_TXMD_FLAGS_CHECKSUM; meta->request.csum_start = sizeof(*eth) + sizeof(*iph); meta->request.csum_offset = offsetof(struct udphdr, check); - tx_desc->len = sizeof(*eth) + sizeof(*iph) + sizeof(*udph) + UDP_PAYLOAD_BYTES; - tx_desc->options |= XDP_TX_METADATA; - xsk_ring_prod__submit(&xsk->tx, 1); + if (split_len) { + u32 addr2 = (idx + 1) % (UMEM_NUM / 2) * UMEM_FRAME_SIZE; + + tx_desc->options |= XDP_PKT_CONTD; + tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + 1); + tx_desc->addr = addr2 + sizeof(*meta); + tx_desc->len = pkt_len - split_len; + tx_desc->options = 0; + data = xsk_umem__get_data(xsk->umem_area, tx_desc->addr); + memcpy(data, pkt + split_len, tx_desc->len); + } + + xsk_ring_prod__submit(&xsk->tx, num); ret = sendto(xsk_socket__fd(xsk->socket), NULL, 0, MSG_DONTWAIT, NULL, 0); if (!ASSERT_GE(ret, 0, "sendto")) @@ -224,24 +246,26 @@ static int generate_packet_inet(void) return err; } -static void complete_tx(struct xsk *xsk) +static void complete_tx(struct xsk *xsk, u32 num) { struct xsk_tx_metadata *meta; __u64 addr; void *data; - __u32 idx; - - if (ASSERT_EQ(xsk_ring_cons__peek(&xsk->comp, 1, &idx), 1, "xsk_ring_cons__peek")) { - addr = *xsk_ring_cons__comp_addr(&xsk->comp, idx); - - printf("%p: complete tx idx=%u addr=%llx\n", xsk, idx, addr); - - data = xsk_umem__get_data(xsk->umem_area, addr); - meta = data - sizeof(struct xsk_tx_metadata); - - ASSERT_NEQ(meta->completion.tx_timestamp, 0, "tx_timestamp"); - - xsk_ring_cons__release(&xsk->comp, 1); + __u32 idx, i; + + if (ASSERT_EQ(xsk_ring_cons__peek(&xsk->comp, num, &idx), num, + "xsk_ring_cons__peek")) { + for (i = 0; i < num; i++) { + addr = *xsk_ring_cons__comp_addr(&xsk->comp, idx + i); + printf("%p: complete tx idx=%u addr=%llx\n", xsk, idx + i, addr); + + if (i == 0) { + data = xsk_umem__get_data(xsk->umem_area, addr); + meta = data - sizeof(*meta); + ASSERT_NEQ(meta->completion.tx_timestamp, 0, "tx_timestamp"); + } + } + xsk_ring_cons__release(&xsk->comp, num); } } @@ -398,7 +422,7 @@ void test_xdp_metadata(void) /* Setup separate AF_XDP for RX interface. */ - ret = open_xsk(rx_ifindex, &rx_xsk); + ret = open_xsk(rx_ifindex, &rx_xsk, XDP_COPY); if (!ASSERT_OK(ret, "open_xsk(RX_NAME)")) goto out; @@ -462,11 +486,11 @@ void test_xdp_metadata(void) /* Setup separate AF_XDP for TX interface nad send packet to the RX socket. */ tx_ifindex = if_nametoindex(TX_NAME); - ret = open_xsk(tx_ifindex, &tx_xsk); + ret = open_xsk(tx_ifindex, &tx_xsk, XDP_COPY | XDP_USE_SG); if (!ASSERT_OK(ret, "open_xsk(TX_NAME)")) goto out; - if (!ASSERT_GE(generate_packet(&tx_xsk, AF_XDP_CONSUMER_PORT), 0, + if (!ASSERT_GE(generate_packet(&tx_xsk, AF_XDP_CONSUMER_PORT, 0), 0, "generate AF_XDP_CONSUMER_PORT")) goto out; @@ -482,7 +506,29 @@ void test_xdp_metadata(void) switch_ns_to_tx(&tok); if (!ASSERT_OK_PTR(tok, "setns tx")) goto out; - complete_tx(&tx_xsk); + complete_tx(&tx_xsk, 1); + + /* Exercise software checksumming of a packet spread over multiple + * TX descriptors. The checksum is only computed once the skb is + * complete, so both fragments must be accounted for. + */ + if (!ASSERT_GE(generate_packet(&tx_xsk, AF_XDP_CONSUMER_PORT, + UDP_SPLIT_BYTES), 0, + "generate multi-buffer AF_XDP packet")) + goto out; + + switch_ns_to_rx(&tok); + if (!ASSERT_OK_PTR(tok, "setns rx")) + goto out; + + if (!ASSERT_GE(verify_xsk_metadata(&rx_xsk, true), 0, + "verify multi-buffer xsk metadata")) + goto out; + + switch_ns_to_tx(&tok); + if (!ASSERT_OK_PTR(tok, "setns tx")) + goto out; + complete_tx(&tx_xsk, 2); /* Now check metadata of packet, generated with network stack */ if (!ASSERT_GE(generate_packet_inet(), 0, "generate UDP packet")) @@ -518,7 +564,7 @@ void test_xdp_metadata(void) goto out; /* Send packet to trigger . */ - if (!ASSERT_GE(generate_packet(&tx_xsk, AF_XDP_CONSUMER_PORT), 0, + if (!ASSERT_GE(generate_packet(&tx_xsk, AF_XDP_CONSUMER_PORT, 0), 0, "generate freplace packet")) goto out; -- 2.39.5 (Apple Git-154) ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 0/2] xsk: fix multi-buffer TX software checksum 2026-10-24 14:11 ` [PATCH net v2 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang 2026-10-24 14:11 ` [PATCH net v2 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang 2026-10-24 14:11 ` [PATCH net v2 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang @ 2026-10-24 14:15 ` netdev-bot+sinfo 2 siblings, 0 replies; 8+ messages in thread From: netdev-bot+sinfo @ 2026-10-24 14:15 UTC (permalink / raw) To: Rongzheng Wang Cc: Magnus Karlsson, Maciej Fijalkowski, Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf, linux-kernel Hi! This is an automated message. This series looks like a fix, but its commit messages seem to be missing some information: - How the issue was discovered, e.g. hit in production, hit during development, syzbot report, manual code inspection, LLM or static analysis tool scan. Please do not repost the series just to address the above. Instead, reply to this email with the missing information, so that reviewers can take it into account. If the series needs another revision for other reasons, please include the information in the commit messages then. The evaluation is done by an LLM so it may be wrong, if you think that is the case please reply and explain. ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-10-24 14:15 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-24 9:12 [PATCH net] xsk: defer software checksum until the skb is complete Rongzheng Wang 2026-10-24 9:15 ` netdev-bot+sinfo 2026-10-24 11:35 ` 王荣峥 2026-10-24 12:25 ` Jason Xing 2026-10-24 14:11 ` [PATCH net v2 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang 2026-10-24 14:11 ` [PATCH net v2 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang 2026-10-24 14:11 ` [PATCH net v2 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang 2026-10-24 14:15 ` [PATCH net v2 0/2] xsk: fix " netdev-bot+sinfo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®