* [PATCH net v3 0/2] xsk: fix multi-buffer TX software checksum @ 2026-10-28 2:50 Rongzheng Wang 2026-10-28 2:50 ` [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang 2026-10-28 2:50 ` [PATCH net v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang 0 siblings, 2 replies; 8+ messages in thread From: Rongzheng Wang @ 2026-10-28 2:50 UTC (permalink / raw) To: netdev Cc: Maciej Fijalkowski, Magnus Karlsson, Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, bpf, linux-kernel, Rongzheng Wang This is v3 of the AF_XDP multi-buffer TX software checksum fix. I found this while testing multi-buffer TX with XDP_UMEM_TX_SW_CSUM: a two-descriptor packet had a bad wire checksum, while a single descriptor was correct. There was no production or syzbot report. 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. Patch 2 extends test_xdp_metadata to cover the single-descriptor and multi-buffer TX cases. The updated selftest passes on the v3 kernel in a local QEMU run. v3: - preserve unlikely() around pool->tx_sw_csum (Maciej) - restore the binary MAC literals in the selftest (Sashiko) - keep a distinct malformed-packet counter out of this stable fix; if it is useful, it can be added separately for net-next later (Magnus) v2: https://lore.kernel.org/all/20261024141157.59301-1-testwrzwrz@gmail.com/ v1: https://lore.kernel.org/all/20261024091256.17918-1-testwrzwrz@gmail.com/ 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 | 23 ++-- .../selftests/bpf/prog_tests/xdp_metadata.c | 122 ++++++++++++------ 2 files changed, 99 insertions(+), 46 deletions(-) -- 2.39.5 (Apple Git-154) ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete 2026-10-28 2:50 [PATCH net v3 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang @ 2026-10-28 2:50 ` Rongzheng Wang 2026-10-28 13:21 ` Maciej Fijalkowski 2026-10-28 15:57 ` [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete Stanislav Fomichev 2026-10-28 2:50 ` [PATCH net v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang 1 sibling, 2 replies; 8+ messages in thread From: Rongzheng Wang @ 2026-10-28 2:50 UTC (permalink / raw) To: netdev Cc: Maciej Fijalkowski, Magnus Karlsson, Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, 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 | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 33475b180ea6..aa6b010192ad 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,15 @@ 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 (unlikely(pool->tx_sw_csum && + skb->ip_summed == CHECKSUM_PARTIAL)) + return skb_checksum_help(skb); + + return 0; +} + static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, struct xdp_desc *desc) { @@ -1275,6 +1276,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
* Re: [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete 2026-10-28 2:50 ` [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang @ 2026-10-28 13:21 ` Maciej Fijalkowski 2026-10-28 15:55 ` [PATCH net v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang 2026-10-28 15:57 ` [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete Stanislav Fomichev 1 sibling, 1 reply; 8+ messages in thread From: Maciej Fijalkowski @ 2026-10-28 13:21 UTC (permalink / raw) To: Rongzheng Wang Cc: netdev, Magnus Karlsson, Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, bpf, linux-kernel On Wed, Oct 28, 2026 at 10:50:25AM +0800, Rongzheng Wang 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. > > Fixes: 11614723af26 ("xsk: Add option to calculate TX checksum in SW") > Cc: stable@vger.kernel.org > Signed-off-by: Rongzheng Wang <testwrzwrz@gmail.com> Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com> > --- > net/xdp/xsk.c | 23 +++++++++++++++-------- > 1 file changed, 15 insertions(+), 8 deletions(-) > > diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c > index 33475b180ea6..aa6b010192ad 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,15 @@ 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 (unlikely(pool->tx_sw_csum && > + skb->ip_summed == CHECKSUM_PARTIAL)) > + return skb_checksum_help(skb); > + > + return 0; > +} > + > static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, > struct xdp_desc *desc) > { > @@ -1275,6 +1276,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
* Re: [PATCH net v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum 2026-10-28 13:21 ` Maciej Fijalkowski @ 2026-10-28 15:55 ` Rongzheng Wang 0 siblings, 0 replies; 8+ messages in thread From: Rongzheng Wang @ 2026-10-28 15:55 UTC (permalink / raw) To: Maciej Fijalkowski Cc: Magnus Karlsson, Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf, linux-kernel Hi Maciej, Thanks for the review and for the Reviewed-by on 1/2. > nit: breaking RCT - I know it's test but Stan payed attention to keeping > the order... Fixed in v4. The declaration block now follows the width order used in this file: struct xsk_tx_metadata *meta; struct xdp_desc *tx_desc; struct udphdr *udph; struct ethhdr *eth; struct iphdr *iph; u16 first_len; u16 pkt_len; u8 pkt[256]; u32 offset; void *data; __u32 idx; u32 addr; int ret; u32 i; > would it be worth exercising this up to MAX_SKB_FRAGS ? > then instead of having this flat layout you would loop over the desc > count. Good point. I changed it to loop over the descriptor count and exercise 1, 2, and MAX_SKB_FRAGS + 1 (18) frames. The extra frame is the linear head; the remaining MAX_SKB_FRAGS frames are skb frags, matching the copy-mode limit documented in af_xdp.rst. The last descriptor takes the remaining length so the fragments cannot overrun pkt_len. The updated selftest passes. I'll send v4 as a new thread. Thanks, Rongzheng ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete 2026-10-28 2:50 ` [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang 2026-10-28 13:21 ` Maciej Fijalkowski @ 2026-10-28 15:57 ` Stanislav Fomichev 1 sibling, 0 replies; 8+ messages in thread From: Stanislav Fomichev @ 2026-10-28 15:57 UTC (permalink / raw) To: Rongzheng Wang Cc: netdev, Maciej Fijalkowski, Magnus Karlsson, Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, bpf, linux-kernel On 10/28, Rongzheng Wang 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. > > Fixes: 11614723af26 ("xsk: Add option to calculate TX checksum in SW") > Cc: stable@vger.kernel.org > Signed-off-by: Rongzheng Wang <testwrzwrz@gmail.com> Acked-by: Stanislav Fomichev <sdf@fomichev.me> ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum 2026-10-28 2:50 [PATCH net v3 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang 2026-10-28 2:50 ` [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang @ 2026-10-28 2:50 ` Rongzheng Wang 2026-10-28 13:20 ` Maciej Fijalkowski 1 sibling, 1 reply; 8+ messages in thread From: Rongzheng Wang @ 2026-10-28 2:50 UTC (permalink / raw) To: netdev Cc: Maciej Fijalkowski, Magnus Karlsson, Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, 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 | 122 ++++++++++++------ 1 file changed, 84 insertions(+), 38 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..13de524c7fef 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,31 +135,31 @@ 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); @@ -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 v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum 2026-10-28 2:50 ` [PATCH net v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang @ 2026-10-28 13:20 ` Maciej Fijalkowski 2026-10-28 16:38 ` Rongzheng Wang 0 siblings, 1 reply; 8+ messages in thread From: Maciej Fijalkowski @ 2026-10-28 13:20 UTC (permalink / raw) To: Rongzheng Wang Cc: netdev, Magnus Karlsson, Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, bpf, linux-kernel On Wed, Oct 28, 2026 at 10:50:26AM +0800, Rongzheng Wang wrote: > 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 | 122 ++++++++++++------ > 1 file changed, 84 insertions(+), 38 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..13de524c7fef 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,31 +135,31 @@ 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]; nit: breaking RCT - I know it's test but Stan payed attention to keeping the order... > 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); > > @@ -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); would it be worth exercising this up to MAX_SKB_FRAGS ? then instead of having this flat layout you would loop over the desc count. > + } > + > + 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 v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum 2026-10-28 13:20 ` Maciej Fijalkowski @ 2026-10-28 16:38 ` Rongzheng Wang 0 siblings, 0 replies; 8+ messages in thread From: Rongzheng Wang @ 2026-10-28 16:38 UTC (permalink / raw) To: Maciej Fijalkowski Cc: Magnus Karlsson, Jason Xing, Stanislav Fomichev, Jesper Dangaard Brouer, stable, netdev, bpf, linux-kernel Following up: I extended the selftest to run every descriptor count from 1 through MAX_SKB_FRAGS + 1, not just 1, 2, and the maximum. This also covers uneven fragment splits. All cases pass. Thanks, Rongzheng ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-10-28 16:38 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-28 2:50 [PATCH net v3 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang 2026-10-28 2:50 ` [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang 2026-10-28 13:21 ` Maciej Fijalkowski 2026-10-28 15:55 ` [PATCH net v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang 2026-10-28 15:57 ` [PATCH net v3 1/2] xsk: defer software checksum until the skb is complete Stanislav Fomichev 2026-10-28 2:50 ` [PATCH net v3 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang 2026-10-28 13:20 ` Maciej Fijalkowski 2026-10-28 16:38 ` Rongzheng Wang
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®