[PATCH net v2 2/2] selftests/bpf: cover AF_XDP multi-buffer TX software checksum
flat view
HOTtoday
From: Rongzheng Wang <hidden>
Date: 2026-10-24 14:12:17
Also in:
bpf, lkml, stable
Subsystem:
bpf [general] (safe dynamic programs and tools), bpf [selftests] (test runners & infrastructure), kernel selftest framework, the rest, xdp (express data path) · Maintainers:
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi, Shuah Khan, Shuah Khan, Linus Torvalds, David S. Miller, Jakub Kicinski, Jesper Dangaard Brouer, John Fastabend
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 <redacted> --- .../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)