diff --git a/tools/testing/selftests/net/lib/xdp_helper.c b/tools/testing/selftests/net/lib/xdp_helper.c
index eb025a9f35b1..5f4dec4408d9 100644
--- a/tools/testing/selftests/net/lib/xdp_helper.c
+++ b/tools/testing/selftests/net/lib/xdp_helper.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#define _FILE_OFFSET_BITS 64
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -19,15 +20,91 @@
static void print_usage(const char *bin)
{
- fprintf(stderr, "Usage: %s ifindex queue_id [-z]\n\n"
- "where:\n\t-z: force zerocopy mode", bin);
+ fprintf(stderr, "Usage: %s ifindex queue_id [-z] [-t frame_hex]\n\n"
+ "where:\n\t-z: force zerocopy mode\n"
+ "\t-t: send one frame, given in hex, and exit\n", bin);
+}
+
+static int parse_frame(unsigned char *buf, const char *hex)
+{
+ size_t len = strlen(hex) / 2;
+ size_t i;
+
+ if (!len || len > 2048 || strlen(hex) % 2 ||
+ strspn(hex, "0123456789abcdefABCDEF") != strlen(hex))
+ return -1;
+ for (i = 0; i < len; i++)
+ if (sscanf(hex + 2 * i, "%2hhx", &buf[i]) != 1)
+ return -1;
+ return len;
+}
+
+/* Send the frame at UMEM address 0 and wait until its descriptor shows up
+ * on the completion ring. In copy mode this happens when the skb is freed.
+ */
+static int send_frame(int sock_fd, __u32 len)
+{
+ struct xdp_mmap_offsets off;
+ socklen_t optlen = sizeof(off);
+ struct xdp_desc *desc;
+ size_t tx_sz, cq_sz;
+ char *tx, *cq;
+ int ret = 1;
+ int i;
+
+ if (getsockopt(sock_fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &optlen)) {
+ perror("getsockopt failed");
+ return 1;
+ }
+
+ tx_sz = off.tx.desc + NUM_DESC * sizeof(*desc);
+ tx = mmap(NULL, tx_sz, PROT_READ | PROT_WRITE, MAP_SHARED, sock_fd,
+ XDP_PGOFF_TX_RING);
+ if (tx == MAP_FAILED) {
+ perror("TX ring mmap failed");
+ return 1;
+ }
+
+ cq_sz = off.cr.desc + NUM_DESC * sizeof(__u64);
+ cq = mmap(NULL, cq_sz, PROT_READ | PROT_WRITE, MAP_SHARED, sock_fd,
+ XDP_UMEM_PGOFF_COMPLETION_RING);
+ if (cq == MAP_FAILED) {
+ perror("completion ring mmap failed");
+ goto out_tx;
+ }
+
+ desc = (struct xdp_desc *)(tx + off.tx.desc);
+ desc->addr = 0;
+ desc->len = len;
+ __atomic_store_n((__u32 *)(tx + off.tx.producer), 1, __ATOMIC_RELEASE);
+
+ if (sendto(sock_fd, NULL, 0, MSG_DONTWAIT, NULL, 0) < 0) {
+ perror("sendto failed");
+ goto out;
+ }
+
+ for (i = 0; i < 100; i++) {
+ usleep(10000);
+ if (__atomic_load_n((__u32 *)(cq + off.cr.producer),
+ __ATOMIC_ACQUIRE)) {
+ ret = 0;
+ goto out;
+ }
+ }
+
+ fprintf(stderr, "no TX completion\n");
+out:
+ munmap(cq, cq_sz);
+out_tx:
+ munmap(tx, tx_sz);
+ return ret;
}
/* this is a simple helper program that creates an XDP socket and does the
* minimum necessary to get bind() to succeed.
*
- * this test program is not intended to actually process packets, but could be
- * extended in the future if that is actually needed.
+ * without -t it does not process packets. with -t it sends one frame
+ * through the TX ring and exits once the frame completes.
*
* it is used by queues.py to ensure the xsk netlinux attribute is set
* correctly.@@ -37,13 +114,16 @@ int main(int argc, char **argv)
struct xdp_umem_reg umem_reg = { 0 };
struct sockaddr_xdp sxdp = { 0 };
int num_desc = NUM_DESC;
+ int frame_len = 0;
void *umem_area;
int retry = 0;
int ifindex;
int sock_fd;
int queue;
+ int ret;
+ int i;
- if (argc != 3 && argc != 4) {
+ if (argc < 3) {
print_usage(argv[0]);
return 1;
}@@ -94,9 +174,21 @@ int main(int argc, char **argv)
sxdp.sxdp_queue_id = queue;
sxdp.sxdp_flags = 0;
- if (argc > 3) {
- if (!strcmp(argv[3], "-z")) {
+ for (i = 3; i < argc; i++) {
+ if (!strcmp(argv[i], "-z")) {
sxdp.sxdp_flags = XDP_ZEROCOPY;
+ } else if (!strcmp(argv[i], "-t") && i + 1 < argc &&
+ !frame_len) {
+ frame_len = parse_frame(umem_area, argv[++i]);
+ if (frame_len < 0) {
+ print_usage(argv[0]);
+ return 1;
+ }
+ if (setsockopt(sock_fd, SOL_XDP, XDP_TX_RING, &num_desc,
+ sizeof(num_desc))) {
+ perror("setsockopt XDP_TX_RING failed");
+ return 1;
+ }
} else {
print_usage(argv[0]);
return 1;@@ -119,6 +211,13 @@ int main(int argc, char **argv)
}
}
+ if (frame_len) {
+ ret = send_frame(sock_fd, frame_len);
+ munmap(umem_area, UMEM_SZ);
+ close(sock_fd);
+ return ret;
+ }
+
ksft_ready();
ksft_wait();
--
2.56.0