BPF List
 help / color / mirror / Atom feed
* [PATCH net v4 0/2] xsk: fix multi-buffer TX software checksum
@ 2026-10-30  2:32 Rongzheng Wang
  2026-10-30  2:32 ` [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang
  2026-10-30  2:32 ` [PATCH net v4 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang
  0 siblings, 2 replies; 10+ messages in thread
From: Rongzheng Wang @ 2026-10-30  2:32 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 v4 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 sends the packet using every descriptor count from 1 through
MAX_SKB_FRAGS + 1, and runs the existing metadata and checksum verification
for each case.  The last case is the copy-mode frame limit documented in
af_xdp.rst.

Patch 1 is unchanged since v3.  The updated selftest passes in a local QEMU
run.

v4:
- exercise every descriptor count up to MAX_SKB_FRAGS + 1 (Maciej)
- keep the last descriptor within the packet length (Maciej)
- fix the local variable declaration order (Maciej)

v3: https://lore.kernel.org/all/20261028025026.11401-1-testwrzwrz@gmail.com/
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   | 147 +++++++++++-------
 2 files changed, 107 insertions(+), 63 deletions(-)

-- 
2.39.5 (Apple Git-154)


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete
  2026-10-30  2:32 [PATCH net v4 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang
@ 2026-10-30  2:32 ` Rongzheng Wang
  2026-10-30  6:31   ` Jason Xing
  2026-10-30  2:32 ` [PATCH net v4 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang
  1 sibling, 1 reply; 10+ messages in thread
From: Rongzheng Wang @ 2026-10-30  2:32 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 related	[flat|nested] 10+ messages in thread

* [PATCH net v4 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum
  2026-10-30  2:32 [PATCH net v4 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang
  2026-10-30  2:32 ` [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang
@ 2026-10-30  2:32 ` Rongzheng Wang
  2026-10-30 12:02   ` Maciej Fijalkowski
  1 sibling, 1 reply; 10+ messages in thread
From: Rongzheng Wang @ 2026-10-30  2:32 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 using every descriptor
count from 1 through MAX_TX_DESCS (MAX_SKB_FRAGS + 1), and to run the
existing metadata and checksum verification for each case.

Build the packet once and write it into the TX descriptors in a loop
instead of using a flat two-descriptor layout. Keep the first descriptor
large enough to cover the checksum offset; the remaining payload is split
across the continuation descriptors, with the last descriptor taking the
remaining length.

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   | 147 +++++++++++-------
 1 file changed, 92 insertions(+), 55 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..17da0a145666 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c
@@ -18,12 +18,15 @@
 #define TX_NAME "veTX"
 #define RX_NAME "veRX"
 
-#define UDP_PAYLOAD_BYTES 4
+#define UDP_PAYLOAD_BYTES 32
+
+#define MAX_SKB_FRAGS 17 /* minimum CONFIG_MAX_SKB_FRAGS */
+#define MAX_TX_DESCS (MAX_SKB_FRAGS + 1)
 
 #define UDP_SOURCE_PORT 1234
 #define AF_XDP_CONSUMER_PORT 8080
 
-#define UMEM_NUM 16
+#define UMEM_NUM 64
 #define UMEM_FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE
 #define UMEM_SIZE (UMEM_FRAME_SIZE * UMEM_NUM)
 #define XDP_FLAGS XDP_FLAGS_DRV_MODE
@@ -56,13 +59,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 +136,35 @@ 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, u32 num)
 {
 	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;
 
-	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;
+	first_len = sizeof(*eth) + sizeof(*iph) + sizeof(*udph);
+	if (!num || num > MAX_TX_DESCS ||
+	    (num > 1 && pkt_len - first_len < num - 1))
 		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;
+	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 +187,43 @@ 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);
-
-	meta->flags |= XDP_TXMD_FLAGS_CHECKSUM;
-	meta->request.csum_start = sizeof(*eth) + sizeof(*iph);
-	meta->request.csum_offset = offsetof(struct udphdr, check);
+	for (i = 0, offset = 0; i < num; i++) {
+		u32 len;
+
+		if (num == 1)
+			len = pkt_len;
+		else if (!i)
+			len = first_len;
+		else if (i == num - 1)
+			len = pkt_len - offset;
+		else
+			len = (pkt_len - first_len) / (num - 1);
+
+		addr = (idx + i) % (UMEM_NUM / 2) * UMEM_FRAME_SIZE;
+		tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i);
+		tx_desc->addr = addr + sizeof(*meta);
+		tx_desc->len = len;
+		tx_desc->options = i ? 0 : XDP_TX_METADATA;
+		if (i + 1 < num)
+			tx_desc->options |= XDP_PKT_CONTD;
+		printf("%p: tx_desc[%u]->addr=%llx\n", xsk, idx + i, tx_desc->addr);
+		data = xsk_umem__get_data(xsk->umem_area, tx_desc->addr);
+		if (!i) {
+			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);
+		}
+		memcpy(data, pkt + offset, len);
+		offset += len;
+	}
 
-	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);
+	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 +256,27 @@ 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 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);
 	}
 }
 
@@ -325,7 +360,7 @@ static int verify_xsk_metadata(struct xsk *xsk, bool sent_from_af_xdp)
 	ASSERT_EQ(meta->rx_hash_type, 0, "rx_hash_type");
 
 	/* checksum offload */
-	ASSERT_EQ(udph->check, htons(0x721c), "csum");
+	ASSERT_EQ(udph->check, htons(0x1c8f), "csum");
 
 done:
 	xsk_ring_cons__release(&xsk->rx, 1);
@@ -361,6 +396,7 @@ void test_xdp_metadata(void)
 	int retries = 10;
 	int rx_ifindex;
 	int tx_ifindex;
+	unsigned int i;
 	int sock_fd;
 	int ret;
 
@@ -398,7 +434,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,27 +498,28 @@ 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,
-		       "generate AF_XDP_CONSUMER_PORT"))
-		goto out;
+	for (i = 1; i <= MAX_TX_DESCS; i++) {
+		if (!ASSERT_GE(generate_packet(&tx_xsk, AF_XDP_CONSUMER_PORT, i), 0,
+			       "generate AF_XDP packet"))
+			goto out;
 
-	switch_ns_to_rx(&tok);
-	if (!ASSERT_OK_PTR(tok, "setns rx"))
-		goto out;
+		switch_ns_to_rx(&tok);
+		if (!ASSERT_OK_PTR(tok, "setns rx"))
+			goto out;
 
-	/* Verify packet sent from AF_XDP has proper metadata. */
-	if (!ASSERT_GE(verify_xsk_metadata(&rx_xsk, true), 0,
-		       "verify_xsk_metadata"))
-		goto out;
+		if (!ASSERT_GE(verify_xsk_metadata(&rx_xsk, true), 0,
+			       "verify xsk metadata"))
+			goto out;
 
-	switch_ns_to_tx(&tok);
-	if (!ASSERT_OK_PTR(tok, "setns tx"))
-		goto out;
-	complete_tx(&tx_xsk);
+		switch_ns_to_tx(&tok);
+		if (!ASSERT_OK_PTR(tok, "setns tx"))
+			goto out;
+		complete_tx(&tx_xsk, i);
+	}
 
 	/* Now check metadata of packet, generated with network stack */
 	if (!ASSERT_GE(generate_packet_inet(), 0, "generate UDP packet"))
@@ -518,7 +555,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, 1), 0,
 		       "generate freplace packet"))
 		goto out;
 
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete
  2026-10-30  2:32 ` [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang
@ 2026-10-30  6:31   ` Jason Xing
  2026-10-30  8:40     ` Rongzheng Wang
  0 siblings, 1 reply; 10+ messages in thread
From: Jason Xing @ 2026-10-30  6:31 UTC (permalink / raw)
  To: Rongzheng Wang
  Cc: netdev, Maciej Fijalkowski, Magnus Karlsson, Stanislav Fomichev,
	Jesper Dangaard Brouer, stable, bpf, linux-kernel

On Fri, Oct 30, 2026 at 10:33 AM 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.
>
> 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 {

Why are you still using this 'else' as you already posted a new one?
Please take a look at my previous feedback.

And please carry the reviewed-by tags through the version next time.

Thanks,
Jason


> +                       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] 10+ messages in thread

* Re: [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete
  2026-10-30  6:31   ` Jason Xing
@ 2026-10-30  8:40     ` Rongzheng Wang
  2026-10-30  9:15       ` Jason Xing
  0 siblings, 1 reply; 10+ messages in thread
From: Rongzheng Wang @ 2026-10-30  8:40 UTC (permalink / raw)
  To: Jason Xing
  Cc: netdev, Maciej Fijalkowski, Magnus Karlsson, Stanislav Fomichev,
	Jesper Dangaard Brouer, stable, bpf, linux-kernel

Hi Jason,

Thanks for the review. This was a timing race, not intentional: your v3
reply (the Reviewed-by plus the else-branch nit) arrived at 10:30, while
v4 was sent at 10:32 and had already been prepared without it. I should
have checked the thread before sending.

I'll send a v5 as a new thread with the unnecessary else removed, exactly
as you suggested, and your Reviewed-by carried on patch 1. Patch 2 is
unchanged.

Thanks,
Rongzheng

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete
  2026-10-30  8:40     ` Rongzheng Wang
@ 2026-10-30  9:15       ` Jason Xing
  2026-10-30  9:46         ` Rongzheng Wang
  0 siblings, 1 reply; 10+ messages in thread
From: Jason Xing @ 2026-10-30  9:15 UTC (permalink / raw)
  To: Rongzheng Wang
  Cc: netdev, Maciej Fijalkowski, Magnus Karlsson, Stanislav Fomichev,
	Jesper Dangaard Brouer, stable, bpf, linux-kernel

On Fri, Oct 30, 2026 at 4:40 PM Rongzheng Wang <testwrzwrz@gmail.com> wrote:
>
> Hi Jason,
>
> Thanks for the review. This was a timing race, not intentional: your v3
> reply (the Reviewed-by plus the else-branch nit) arrived at 10:30, while
> v4 was sent at 10:32 and had already been prepared without it. I should
> have checked the thread before sending.
>
> I'll send a v5 as a new thread with the unnecessary else removed, exactly
> as you suggested, and your Reviewed-by carried on patch 1. Patch 2 is
> unchanged.

Thanks.

Since I'm not that familiar with the selftest, I would not comment on
that one. I believe Maciej and Bjorn will give their opinions, so I
highly recommend you to wait for their feedback on the selftest patch
:)

Thanks,
Jason

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete
  2026-10-30  9:15       ` Jason Xing
@ 2026-10-30  9:46         ` Rongzheng Wang
  2026-10-30 11:56           ` Maciej Fijalkowski
  0 siblings, 1 reply; 10+ messages in thread
From: Rongzheng Wang @ 2026-10-30  9:46 UTC (permalink / raw)
  To: Jason Xing
  Cc: netdev, Maciej Fijalkowski, Magnus Karlsson, Stanislav Fomichev,
	Jesper Dangaard Brouer, stable, bpf, linux-kernel

Hi Jason,

Makes sense, thanks. I'll wait for their feedback on the selftest patch
before sending v5, and fold it in together with the rest.

Thanks,
Rongzheng

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete
  2026-10-30  9:46         ` Rongzheng Wang
@ 2026-10-30 11:56           ` Maciej Fijalkowski
  2026-10-30 12:24             ` Rongzheng Wang
  0 siblings, 1 reply; 10+ messages in thread
From: Maciej Fijalkowski @ 2026-10-30 11:56 UTC (permalink / raw)
  To: Rongzheng Wang
  Cc: Jason Xing, netdev, Magnus Karlsson, Stanislav Fomichev,
	Jesper Dangaard Brouer, stable, bpf, linux-kernel

On Fri, Oct 30, 2026 at 05:46:33PM +0800, Rongzheng Wang wrote:
> Hi Jason,
> 
> Makes sense, thanks. I'll wait for their feedback on the selftest patch
> before sending v5, and fold it in together with the rest.

Can you stop removing all the previous conversations and rather reply
inline to related paragraphs?

I don't think v5 is necessary. And as Jason said you received 3 review/ack
tags and none of them got included in v4.

> 
> Thanks,
> Rongzheng

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v4 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum
  2026-10-30  2:32 ` [PATCH net v4 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang
@ 2026-10-30 12:02   ` Maciej Fijalkowski
  0 siblings, 0 replies; 10+ messages in thread
From: Maciej Fijalkowski @ 2026-10-30 12:02 UTC (permalink / raw)
  To: Rongzheng Wang
  Cc: netdev, Magnus Karlsson, Jason Xing, Stanislav Fomichev,
	Jesper Dangaard Brouer, stable, bpf, linux-kernel

On Fri, Oct 30, 2026 at 10:32:48AM +0800, Rongzheng Wang wrote:
> Extend test_xdp_metadata to send the same UDP packet using every descriptor
> count from 1 through MAX_TX_DESCS (MAX_SKB_FRAGS + 1), and to run the
> existing metadata and checksum verification for each case.
> 
> Build the packet once and write it into the TX descriptors in a loop
> instead of using a flat two-descriptor layout. Keep the first descriptor
> large enough to cover the checksum offset; the remaining payload is split
> across the continuation descriptors, with the last descriptor taking the
> remaining length.
> 
> 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>

Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>

> ---
>  .../selftests/bpf/prog_tests/xdp_metadata.c   | 147 +++++++++++-------
>  1 file changed, 92 insertions(+), 55 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..17da0a145666 100644
> --- a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c
> +++ b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c
> @@ -18,12 +18,15 @@
>  #define TX_NAME "veTX"
>  #define RX_NAME "veRX"
>  
> -#define UDP_PAYLOAD_BYTES 4
> +#define UDP_PAYLOAD_BYTES 32
> +
> +#define MAX_SKB_FRAGS 17 /* minimum CONFIG_MAX_SKB_FRAGS */
> +#define MAX_TX_DESCS (MAX_SKB_FRAGS + 1)
>  
>  #define UDP_SOURCE_PORT 1234
>  #define AF_XDP_CONSUMER_PORT 8080
>  
> -#define UMEM_NUM 16
> +#define UMEM_NUM 64
>  #define UMEM_FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE
>  #define UMEM_SIZE (UMEM_FRAME_SIZE * UMEM_NUM)
>  #define XDP_FLAGS XDP_FLAGS_DRV_MODE
> @@ -56,13 +59,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 +136,35 @@ 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, u32 num)
>  {
>  	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;
>  
> -	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;
> +	first_len = sizeof(*eth) + sizeof(*iph) + sizeof(*udph);
> +	if (!num || num > MAX_TX_DESCS ||
> +	    (num > 1 && pkt_len - first_len < num - 1))
>  		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;
> +	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 +187,43 @@ 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);
> -
> -	meta->flags |= XDP_TXMD_FLAGS_CHECKSUM;
> -	meta->request.csum_start = sizeof(*eth) + sizeof(*iph);
> -	meta->request.csum_offset = offsetof(struct udphdr, check);
> +	for (i = 0, offset = 0; i < num; i++) {
> +		u32 len;
> +
> +		if (num == 1)
> +			len = pkt_len;
> +		else if (!i)
> +			len = first_len;
> +		else if (i == num - 1)
> +			len = pkt_len - offset;
> +		else
> +			len = (pkt_len - first_len) / (num - 1);
> +
> +		addr = (idx + i) % (UMEM_NUM / 2) * UMEM_FRAME_SIZE;
> +		tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i);
> +		tx_desc->addr = addr + sizeof(*meta);
> +		tx_desc->len = len;
> +		tx_desc->options = i ? 0 : XDP_TX_METADATA;
> +		if (i + 1 < num)
> +			tx_desc->options |= XDP_PKT_CONTD;
> +		printf("%p: tx_desc[%u]->addr=%llx\n", xsk, idx + i, tx_desc->addr);
> +		data = xsk_umem__get_data(xsk->umem_area, tx_desc->addr);
> +		if (!i) {
> +			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);
> +		}
> +		memcpy(data, pkt + offset, len);
> +		offset += len;
> +	}
>  
> -	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);
> +	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 +256,27 @@ 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 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);
>  	}
>  }
>  
> @@ -325,7 +360,7 @@ static int verify_xsk_metadata(struct xsk *xsk, bool sent_from_af_xdp)
>  	ASSERT_EQ(meta->rx_hash_type, 0, "rx_hash_type");
>  
>  	/* checksum offload */
> -	ASSERT_EQ(udph->check, htons(0x721c), "csum");
> +	ASSERT_EQ(udph->check, htons(0x1c8f), "csum");
>  
>  done:
>  	xsk_ring_cons__release(&xsk->rx, 1);
> @@ -361,6 +396,7 @@ void test_xdp_metadata(void)
>  	int retries = 10;
>  	int rx_ifindex;
>  	int tx_ifindex;
> +	unsigned int i;
>  	int sock_fd;
>  	int ret;
>  
> @@ -398,7 +434,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,27 +498,28 @@ 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,
> -		       "generate AF_XDP_CONSUMER_PORT"))
> -		goto out;
> +	for (i = 1; i <= MAX_TX_DESCS; i++) {
> +		if (!ASSERT_GE(generate_packet(&tx_xsk, AF_XDP_CONSUMER_PORT, i), 0,
> +			       "generate AF_XDP packet"))
> +			goto out;
>  
> -	switch_ns_to_rx(&tok);
> -	if (!ASSERT_OK_PTR(tok, "setns rx"))
> -		goto out;
> +		switch_ns_to_rx(&tok);
> +		if (!ASSERT_OK_PTR(tok, "setns rx"))
> +			goto out;
>  
> -	/* Verify packet sent from AF_XDP has proper metadata. */
> -	if (!ASSERT_GE(verify_xsk_metadata(&rx_xsk, true), 0,
> -		       "verify_xsk_metadata"))
> -		goto out;
> +		if (!ASSERT_GE(verify_xsk_metadata(&rx_xsk, true), 0,
> +			       "verify xsk metadata"))
> +			goto out;
>  
> -	switch_ns_to_tx(&tok);
> -	if (!ASSERT_OK_PTR(tok, "setns tx"))
> -		goto out;
> -	complete_tx(&tx_xsk);
> +		switch_ns_to_tx(&tok);
> +		if (!ASSERT_OK_PTR(tok, "setns tx"))
> +			goto out;
> +		complete_tx(&tx_xsk, i);
> +	}
>  
>  	/* Now check metadata of packet, generated with network stack */
>  	if (!ASSERT_GE(generate_packet_inet(), 0, "generate UDP packet"))
> @@ -518,7 +555,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, 1), 0,
>  		       "generate freplace packet"))
>  		goto out;
>  
> -- 
> 2.39.5 (Apple Git-154)
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete
  2026-10-30 11:56           ` Maciej Fijalkowski
@ 2026-10-30 12:24             ` Rongzheng Wang
  0 siblings, 0 replies; 10+ messages in thread
From: Rongzheng Wang @ 2026-10-30 12:24 UTC (permalink / raw)
  To: Maciej Fijalkowski
  Cc: Jason Xing, netdev, Magnus Karlsson, Stanislav Fomichev,
	Jesper Dangaard Brouer, stable, bpf, linux-kernel

Thanks for the review, the suggestions, and the Reviewed-by on 2/2.

> Can you stop removing all the previous conversations and rather reply
> inline to related paragraphs?

Sorry, I'll reply inline from now on.

> I don't think v5 is necessary. And as Jason said you received 3 review/ack
> tags and none of them got included in v4.

You're right. v4 went out before Jason's tag arrived, and I missed the
other two tags from v3. Since you don't think v5 is necessary, I won't
send one; if there is another revision, I'll carry all the tags.

I learned a lot from this patch and from the review feedback. I'll be more
careful with future patches and wait longer for reviewers' feedback before
sending the next version.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-10-30 12:24 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30  2:32 [PATCH net v4 0/2] xsk: fix multi-buffer TX software checksum Rongzheng Wang
2026-10-30  2:32 ` [PATCH net v4 1/2] xsk: defer software checksum until the skb is complete Rongzheng Wang
2026-10-30  6:31   ` Jason Xing
2026-10-30  8:40     ` Rongzheng Wang
2026-10-30  9:15       ` Jason Xing
2026-10-30  9:46         ` Rongzheng Wang
2026-10-30 11:56           ` Maciej Fijalkowski
2026-10-30 12:24             ` Rongzheng Wang
2026-10-30  2:32 ` [PATCH net v4 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum Rongzheng Wang
2026-10-30 12:02   ` Maciej Fijalkowski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox