xskxceiver.c 43.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2020 Intel Corporation. */

/*
 * Some functions in this program are taken from
 * Linux kernel samples/bpf/xdpsock* and modified
 * for use.
 *
 * See test_xsk.sh for detailed information on test topology
 * and prerequisite network setup.
 *
 * This test program contains two threads, each thread is single socket with
 * a unique UMEM. It validates in-order packet delivery and packet content
 * by sending packets to each other.
 *
 * Tests Information:
 * ------------------
 * These selftests test AF_XDP SKB and Native/DRV modes using veth
 * Virtual Ethernet interfaces.
 *
21
 * For each mode, the following tests are run:
22
 *    a. nopoll - soft-irq processing in run-to-completion mode
23
 *    b. poll - using poll() syscall
24 25 26
 *    c. Socket Teardown
 *       Create a Tx and a Rx socket, Tx from one socket, Rx on another. Destroy
 *       both sockets, then repeat multiple times. Only nopoll mode is used
27 28 29 30
 *    d. Bi-directional sockets
 *       Configure sockets as bi-directional tx/rx sockets, sets up fill and
 *       completion rings on each socket, tx/rx in both directions. Only nopoll
 *       mode is used
31 32 33 34 35 36 37 38 39 40 41 42 43
 *    e. Statistics
 *       Trigger some error conditions and ensure that the appropriate statistics
 *       are incremented. Within this test, the following statistics are tested:
 *       i.   rx dropped
 *            Increase the UMEM frame headroom to a value which results in
 *            insufficient space in the rx buffer for both the packet and the headroom.
 *       ii.  tx invalid
 *            Set the 'len' field of tx descriptors to an invalid value (umem frame
 *            size + 1).
 *       iii. rx ring full
 *            Reduce the size of the RX ring to a fraction of the fill ring size.
 *       iv.  fill queue empty
 *            Do not populate the fill queue and then try to receive pkts.
44 45 46 47
 *    f. bpf_link resource persistence
 *       Configure sockets at indexes 0 and 1, run a traffic on queue ids 0,
 *       then remove xsk sockets from queue 0 on both veth interfaces and
 *       finally run a traffic on queues ids 1
48
 *    g. unaligned mode
49 50
 *    h. tests for invalid and corner case Tx descriptors so that the correct ones
 *       are discarded and let through, respectively.
51
 *    i. 2K frame size tests
52
 *
53
 * Total tests: 12
54 55 56 57 58 59 60 61 62 63 64 65
 *
 * Flow:
 * -----
 * - Single process spawns two threads: Tx and Rx
 * - Each of these two threads attach to a veth interface within their assigned
 *   namespaces
 * - Each thread Creates one AF_XDP socket connected to a unique umem for each
 *   veth interface
 * - Tx thread Transmits 10k packets from veth<xxxx> to veth<yyyy>
 * - Rx thread verifies if all 10k packets were received and delivered in-order,
 *   and have the right content
 *
66
 * Enable/disable packet dump mode:
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
 * --------------------------
 * To enable L2 - L4 headers and payload dump of each packet on STDOUT, add
 * parameter -D to params array in test_xsk.sh, i.e. params=("-S" "-D")
 */

#define _GNU_SOURCE
#include <fcntl.h>
#include <errno.h>
#include <getopt.h>
#include <asm/barrier.h>
#include <linux/if_link.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <locale.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <sys/mman.h>
93
#include <sys/socket.h>
94
#include <sys/time.h>
95 96 97 98 99
#include <sys/types.h>
#include <sys/queue.h>
#include <time.h>
#include <unistd.h>
#include <stdatomic.h>
100
#include "xsk.h"
101
#include "xskxceiver.h"
102 103
#include "../kselftest.h"

104
/* AF_XDP APIs were moved into libxdp and marked as deprecated in libbpf.
105
 * Until xskxceiver is either moved or re-writed into libxdp, suppress
106 107 108 109
 * deprecation warnings in this file
 */
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

110 111 112 113 114 115 116
static const char *MAC1 = "\x00\x0A\x56\x9E\xEE\x62";
static const char *MAC2 = "\x00\x0A\x56\x9E\xEE\x61";
static const char *IP1 = "192.168.100.162";
static const char *IP2 = "192.168.100.161";
static const u16 UDP_PORT1 = 2020;
static const u16 UDP_PORT2 = 2121;

117 118
static void __exit_with_error(int error, const char *file, const char *func, int line)
{
119 120 121
	ksft_test_result_fail("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error,
			      strerror(error));
	ksft_exit_xfail();
122 123 124 125
}

#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)

126
#define mode_string(test) (test)->ifobj_tx->xdp_flags & XDP_FLAGS_SKB_MODE ? "SKB" : "DRV"
127
#define busy_poll_string(test) (test)->ifobj_tx->busy_poll ? "BUSY-POLL " : ""
128

129 130 131 132 133 134 135 136 137
static void report_failure(struct test_spec *test)
{
	if (test->fail)
		return;

	ksft_test_result_fail("FAIL: %s %s%s\n", mode_string(test), busy_poll_string(test),
			      test->name);
	test->fail = true;
}
138

139
static void memset32_htonl(void *dest, u32 val, u32 size)
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
{
	u32 *ptr = (u32 *)dest;
	int i;

	val = htonl(val);

	for (i = 0; i < (size & (~0x3)); i += 4)
		ptr[i >> 2] = val;
}

/*
 * Fold a partial checksum
 * This function code has been taken from
 * Linux kernel include/asm-generic/checksum.h
 */
155
static __u16 csum_fold(__u32 csum)
156 157 158 159 160 161 162 163 164 165 166 167
{
	u32 sum = (__force u32)csum;

	sum = (sum & 0xffff) + (sum >> 16);
	sum = (sum & 0xffff) + (sum >> 16);
	return (__force __u16)~sum;
}

/*
 * This function code has been taken from
 * Linux kernel lib/checksum.c
 */
168
static u32 from64to32(u64 x)
169 170 171 172 173 174 175 176 177 178 179 180
{
	/* add up 32-bit and 32-bit for 32+c bit */
	x = (x & 0xffffffff) + (x >> 32);
	/* add up carry.. */
	x = (x & 0xffffffff) + (x >> 32);
	return (u32)x;
}

/*
 * This function code has been taken from
 * Linux kernel lib/checksum.c
 */
181
static __u32 csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u32 sum)
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
{
	unsigned long long s = (__force u32)sum;

	s += (__force u32)saddr;
	s += (__force u32)daddr;
#ifdef __BIG_ENDIAN__
	s += proto + len;
#else
	s += (proto + len) << 8;
#endif
	return (__force __u32)from64to32(s);
}

/*
 * This function has been taken from
 * Linux kernel include/asm-generic/checksum.h
 */
199
static __u16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u32 sum)
200 201 202 203
{
	return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}

204
static u16 udp_csum(u32 saddr, u32 daddr, u32 len, u8 proto, u16 *udp_pkt)
205 206 207 208 209 210 211 212 213 214 215
{
	u32 csum = 0;
	u32 cnt = 0;

	/* udp hdr and data */
	for (; cnt < len; cnt += 2)
		csum += udp_pkt[cnt >> 1];

	return csum_tcpudp_magic(saddr, daddr, len, proto, csum);
}

216
static void gen_eth_hdr(struct ifobject *ifobject, struct ethhdr *eth_hdr)
217
{
218 219
	memcpy(eth_hdr->h_dest, ifobject->dst_mac, ETH_ALEN);
	memcpy(eth_hdr->h_source, ifobject->src_mac, ETH_ALEN);
220 221 222
	eth_hdr->h_proto = htons(ETH_P_IP);
}

223
static void gen_ip_hdr(struct ifobject *ifobject, struct iphdr *ip_hdr)
224 225 226 227 228 229 230 231 232
{
	ip_hdr->version = IP_PKT_VER;
	ip_hdr->ihl = 0x5;
	ip_hdr->tos = IP_PKT_TOS;
	ip_hdr->tot_len = htons(IP_PKT_SIZE);
	ip_hdr->id = 0;
	ip_hdr->frag_off = 0;
	ip_hdr->ttl = IPDEFTTL;
	ip_hdr->protocol = IPPROTO_UDP;
233 234
	ip_hdr->saddr = ifobject->src_ip;
	ip_hdr->daddr = ifobject->dst_ip;
235 236 237
	ip_hdr->check = 0;
}

238
static void gen_udp_hdr(u32 payload, void *pkt, struct ifobject *ifobject,
239
			struct udphdr *udp_hdr)
240
{
241 242
	udp_hdr->source = htons(ifobject->src_port);
	udp_hdr->dest = htons(ifobject->dst_port);
243
	udp_hdr->len = htons(UDP_PKT_SIZE);
244
	memset32_htonl(pkt + PKT_HDR_SIZE, payload, UDP_PKT_DATA_SIZE);
245 246 247 248 249 250 251 252 253
}

static void gen_udp_csum(struct udphdr *udp_hdr, struct iphdr *ip_hdr)
{
	udp_hdr->check = 0;
	udp_hdr->check =
	    udp_csum(ip_hdr->saddr, ip_hdr->daddr, UDP_PKT_SIZE, IPPROTO_UDP, (u16 *)udp_hdr);
}

254
static int xsk_configure_umem(struct xsk_umem_info *umem, void *buffer, u64 size)
255
{
256 257 258
	struct xsk_umem_config cfg = {
		.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
		.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
259
		.frame_size = umem->frame_size,
260
		.frame_headroom = umem->frame_headroom,
261 262
		.flags = XSK_UMEM__DEFAULT_FLAGS
	};
263
	int ret;
264

265 266 267
	if (umem->unaligned_mode)
		cfg.flags |= XDP_UMEM_UNALIGNED_CHUNK_FLAG;

268 269
	ret = xsk_umem__create(&umem->umem, buffer, size,
			       &umem->fq, &umem->cq, &cfg);
270
	if (ret)
271
		return ret;
272

273
	umem->buffer = buffer;
274
	return 0;
275 276
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
static void enable_busy_poll(struct xsk_socket_info *xsk)
{
	int sock_opt;

	sock_opt = 1;
	if (setsockopt(xsk_socket__fd(xsk->xsk), SOL_SOCKET, SO_PREFER_BUSY_POLL,
		       (void *)&sock_opt, sizeof(sock_opt)) < 0)
		exit_with_error(errno);

	sock_opt = 20;
	if (setsockopt(xsk_socket__fd(xsk->xsk), SOL_SOCKET, SO_BUSY_POLL,
		       (void *)&sock_opt, sizeof(sock_opt)) < 0)
		exit_with_error(errno);

	sock_opt = BATCH_SIZE;
	if (setsockopt(xsk_socket__fd(xsk->xsk), SOL_SOCKET, SO_BUSY_POLL_BUDGET,
		       (void *)&sock_opt, sizeof(sock_opt)) < 0)
		exit_with_error(errno);
}

297
static int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem,
298
				struct ifobject *ifobject, bool shared)
299
{
300
	struct xsk_socket_config cfg = {};
301 302 303
	struct xsk_ring_cons *rxr;
	struct xsk_ring_prod *txr;

304
	xsk->umem = umem;
305
	cfg.rx_size = xsk->rxqsize;
306
	cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
307
	cfg.libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD;
308 309
	cfg.xdp_flags = ifobject->xdp_flags;
	cfg.bind_flags = ifobject->bind_flags;
310 311
	if (shared)
		cfg.bind_flags |= XDP_SHARED_UMEM;
312

313 314
	txr = ifobject->tx_on ? &xsk->tx : NULL;
	rxr = ifobject->rx_on ? &xsk->rx : NULL;
315
	return xsk_socket__create(&xsk->xsk, ifobject->ifname, 0, umem->umem, rxr, txr, &cfg);
316 317 318 319
}

static struct option long_options[] = {
	{"interface", required_argument, 0, 'i'},
320 321
	{"busy-poll", no_argument, 0, 'b'},
	{"dump-pkts", no_argument, 0, 'D'},
322
	{"verbose", no_argument, 0, 'v'},
323 324 325 326 327 328
	{0, 0, 0, 0}
};

static void usage(const char *prog)
{
	const char *str =
329 330 331 332
		"  Usage: %s [OPTIONS]\n"
		"  Options:\n"
		"  -i, --interface      Use interface\n"
		"  -D, --dump-pkts      Dump packets L2 - L5\n"
333 334
		"  -v, --verbose        Verbose output\n"
		"  -b, --busy-poll      Enable busy poll\n";
335

336 337 338
	ksft_print_msg(str, prog);
}

339
static int switch_namespace(const char *nsname)
340 341 342 343
{
	char fqns[26] = "/var/run/netns/";
	int nsfd;

344 345 346 347
	if (!nsname || strlen(nsname) == 0)
		return -1;

	strncat(fqns, nsname, sizeof(fqns) - strlen(fqns) - 1);
348 349 350 351 352 353 354 355
	nsfd = open(fqns, O_RDONLY);

	if (nsfd == -1)
		exit_with_error(errno);

	if (setns(nsfd, 0) == -1)
		exit_with_error(errno);

356
	print_verbose("NS switched: %s\n", nsname);
357

358
	return nsfd;
359 360
}

361
static bool validate_interface(struct ifobject *ifobj)
362
{
363 364 365
	if (!strcmp(ifobj->ifname, ""))
		return false;
	return true;
366 367
}

368 369
static void parse_command_line(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx, int argc,
			       char **argv)
370
{
371 372 373
	struct ifobject *ifobj;
	u32 interface_nb = 0;
	int option_index, c;
374 375 376 377

	opterr = 0;

	for (;;) {
378
		char *sptr, *token;
379

380
		c = getopt_long(argc, argv, "i:Dvb", long_options, &option_index);
381 382 383 384 385
		if (c == -1)
			break;

		switch (c) {
		case 'i':
386
			if (interface_nb == 0)
387
				ifobj = ifobj_tx;
388
			else if (interface_nb == 1)
389
				ifobj = ifobj_rx;
390
			else
391 392 393
				break;

			sptr = strndupa(optarg, strlen(optarg));
394
			memcpy(ifobj->ifname, strsep(&sptr, ","), MAX_INTERFACE_NAME_CHARS);
395 396
			token = strsep(&sptr, ",");
			if (token)
397 398
				memcpy(ifobj->nsname, token, MAX_INTERFACES_NAMESPACE_CHARS);
			interface_nb++;
399 400
			break;
		case 'D':
401
			opt_pkt_dump = true;
402
			break;
403
		case 'v':
404
			opt_verbose = true;
405
			break;
406 407 408 409
		case 'b':
			ifobj_tx->busy_poll = true;
			ifobj_rx->busy_poll = true;
			break;
410 411 412 413 414
		default:
			usage(basename(argv[0]));
			ksft_exit_xfail();
		}
	}
415
}
416

417 418 419 420 421 422 423 424 425
static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
			     struct ifobject *ifobj_rx)
{
	u32 i, j;

	for (i = 0; i < MAX_INTERFACES; i++) {
		struct ifobject *ifobj = i ? ifobj_rx : ifobj_tx;

		ifobj->xsk = &ifobj->xsk_arr[0];
426
		ifobj->use_poll = false;
427 428
		ifobj->use_fill_ring = true;
		ifobj->release_rx = true;
429
		ifobj->pkt_stream = test->pkt_stream_default;
430
		ifobj->validation_func = NULL;
431

432 433 434 435 436 437 438
		if (i == 0) {
			ifobj->rx_on = false;
			ifobj->tx_on = true;
		} else {
			ifobj->rx_on = true;
			ifobj->tx_on = false;
		}
439

440 441 442 443
		memset(ifobj->umem, 0, sizeof(*ifobj->umem));
		ifobj->umem->num_frames = DEFAULT_UMEM_BUFFERS;
		ifobj->umem->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;

444 445
		for (j = 0; j < MAX_SOCKETS; j++) {
			memset(&ifobj->xsk_arr[j], 0, sizeof(ifobj->xsk_arr[j]));
446
			ifobj->xsk_arr[j].rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
447
		}
448
	}
449 450 451

	test->ifobj_tx = ifobj_tx;
	test->ifobj_rx = ifobj_rx;
452 453
	test->current_step = 0;
	test->total_steps = 1;
454
	test->nb_sockets = 1;
455
	test->fail = false;
456 457 458
}

static void test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
459
			   struct ifobject *ifobj_rx, enum test_mode mode)
460
{
461
	struct pkt_stream *pkt_stream;
462 463
	u32 i;

464
	pkt_stream = test->pkt_stream_default;
465
	memset(test, 0, sizeof(*test));
466
	test->pkt_stream_default = pkt_stream;
467 468 469 470 471 472 473 474 475 476 477 478 479

	for (i = 0; i < MAX_INTERFACES; i++) {
		struct ifobject *ifobj = i ? ifobj_rx : ifobj_tx;

		ifobj->xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
		if (mode == TEST_MODE_SKB)
			ifobj->xdp_flags |= XDP_FLAGS_SKB_MODE;
		else
			ifobj->xdp_flags |= XDP_FLAGS_DRV_MODE;

		ifobj->bind_flags = XDP_USE_NEED_WAKEUP | XDP_COPY;
	}

480 481 482 483 484 485
	__test_spec_init(test, ifobj_tx, ifobj_rx);
}

static void test_spec_reset(struct test_spec *test)
{
	__test_spec_init(test, test->ifobj_tx, test->ifobj_rx);
486 487
}

488 489 490 491 492
static void test_spec_set_name(struct test_spec *test, const char *name)
{
	strncpy(test->name, name, MAX_TEST_NAME_SIZE);
}

493 494 495 496 497 498
static void pkt_stream_reset(struct pkt_stream *pkt_stream)
{
	if (pkt_stream)
		pkt_stream->rx_pkt_nb = 0;
}

499
static struct pkt *pkt_stream_get_pkt(struct pkt_stream *pkt_stream, u32 pkt_nb)
500
{
501 502 503 504 505 506
	if (pkt_nb >= pkt_stream->nb_pkts)
		return NULL;

	return &pkt_stream->pkts[pkt_nb];
}

507
static struct pkt *pkt_stream_get_next_rx_pkt(struct pkt_stream *pkt_stream, u32 *pkts_sent)
508 509
{
	while (pkt_stream->rx_pkt_nb < pkt_stream->nb_pkts) {
510
		(*pkts_sent)++;
511 512 513 514 515 516 517
		if (pkt_stream->pkts[pkt_stream->rx_pkt_nb].valid)
			return &pkt_stream->pkts[pkt_stream->rx_pkt_nb++];
		pkt_stream->rx_pkt_nb++;
	}
	return NULL;
}

518 519 520 521 522 523 524 525
static void pkt_stream_delete(struct pkt_stream *pkt_stream)
{
	free(pkt_stream->pkts);
	free(pkt_stream);
}

static void pkt_stream_restore_default(struct test_spec *test)
{
526 527 528
	struct pkt_stream *tx_pkt_stream = test->ifobj_tx->pkt_stream;

	if (tx_pkt_stream != test->pkt_stream_default) {
529 530 531
		pkt_stream_delete(test->ifobj_tx->pkt_stream);
		test->ifobj_tx->pkt_stream = test->pkt_stream_default;
	}
532 533 534 535

	if (test->ifobj_rx->pkt_stream != test->pkt_stream_default &&
	    test->ifobj_rx->pkt_stream != tx_pkt_stream)
		pkt_stream_delete(test->ifobj_rx->pkt_stream);
536 537 538
	test->ifobj_rx->pkt_stream = test->pkt_stream_default;
}

539
static struct pkt_stream *__pkt_stream_alloc(u32 nb_pkts)
540 541 542
{
	struct pkt_stream *pkt_stream;

543
	pkt_stream = calloc(1, sizeof(*pkt_stream));
544
	if (!pkt_stream)
545
		return NULL;
546 547

	pkt_stream->pkts = calloc(nb_pkts, sizeof(*pkt_stream->pkts));
548 549 550 551 552 553 554 555 556
	if (!pkt_stream->pkts) {
		free(pkt_stream);
		return NULL;
	}

	pkt_stream->nb_pkts = nb_pkts;
	return pkt_stream;
}

557 558 559 560 561 562 563 564 565 566
static void pkt_set(struct xsk_umem_info *umem, struct pkt *pkt, u64 addr, u32 len)
{
	pkt->addr = addr;
	pkt->len = len;
	if (len > umem->frame_size - XDP_PACKET_HEADROOM - MIN_PKT_SIZE * 2 - umem->frame_headroom)
		pkt->valid = false;
	else
		pkt->valid = true;
}

567 568 569 570 571 572 573
static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb_pkts, u32 pkt_len)
{
	struct pkt_stream *pkt_stream;
	u32 i;

	pkt_stream = __pkt_stream_alloc(nb_pkts);
	if (!pkt_stream)
574 575 576 577
		exit_with_error(ENOMEM);

	pkt_stream->nb_pkts = nb_pkts;
	for (i = 0; i < nb_pkts; i++) {
578 579
		pkt_set(umem, &pkt_stream->pkts[i], (i % umem->num_frames) * umem->frame_size,
			pkt_len);
580 581 582 583 584 585
		pkt_stream->pkts[i].payload = i;
	}

	return pkt_stream;
}

586 587 588 589 590 591
static struct pkt_stream *pkt_stream_clone(struct xsk_umem_info *umem,
					   struct pkt_stream *pkt_stream)
{
	return pkt_stream_generate(umem, pkt_stream->nb_pkts, pkt_stream->pkts[0].len);
}

592 593 594 595 596 597 598 599 600
static void pkt_stream_replace(struct test_spec *test, u32 nb_pkts, u32 pkt_len)
{
	struct pkt_stream *pkt_stream;

	pkt_stream = pkt_stream_generate(test->ifobj_tx->umem, nb_pkts, pkt_len);
	test->ifobj_tx->pkt_stream = pkt_stream;
	test->ifobj_rx->pkt_stream = pkt_stream;
}

601
static void pkt_stream_replace_half(struct test_spec *test, u32 pkt_len, int offset)
602 603 604 605 606 607
{
	struct xsk_umem_info *umem = test->ifobj_tx->umem;
	struct pkt_stream *pkt_stream;
	u32 i;

	pkt_stream = pkt_stream_clone(umem, test->pkt_stream_default);
608 609 610
	for (i = 1; i < test->pkt_stream_default->nb_pkts; i += 2)
		pkt_set(umem, &pkt_stream->pkts[i],
			(i % umem->num_frames) * umem->frame_size + offset, pkt_len);
611 612 613 614 615

	test->ifobj_tx->pkt_stream = pkt_stream;
	test->ifobj_rx->pkt_stream = pkt_stream;
}

616 617 618 619 620 621 622 623 624 625 626 627 628
static void pkt_stream_receive_half(struct test_spec *test)
{
	struct xsk_umem_info *umem = test->ifobj_rx->umem;
	struct pkt_stream *pkt_stream = test->ifobj_tx->pkt_stream;
	u32 i;

	test->ifobj_rx->pkt_stream = pkt_stream_generate(umem, pkt_stream->nb_pkts,
							 pkt_stream->pkts[0].len);
	pkt_stream = test->ifobj_rx->pkt_stream;
	for (i = 1; i < pkt_stream->nb_pkts; i += 2)
		pkt_stream->pkts[i].valid = false;
}

629 630 631 632 633 634 635 636 637 638
static struct pkt *pkt_generate(struct ifobject *ifobject, u32 pkt_nb)
{
	struct pkt *pkt = pkt_stream_get_pkt(ifobject->pkt_stream, pkt_nb);
	struct udphdr *udp_hdr;
	struct ethhdr *eth_hdr;
	struct iphdr *ip_hdr;
	void *data;

	if (!pkt)
		return NULL;
639
	if (!pkt->valid || pkt->len < MIN_PKT_SIZE)
640
		return pkt;
641 642 643 644 645

	data = xsk_umem__get_data(ifobject->umem->buffer, pkt->addr);
	udp_hdr = (struct udphdr *)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
	ip_hdr = (struct iphdr *)(data + sizeof(struct ethhdr));
	eth_hdr = (struct ethhdr *)data;
646 647 648 649 650

	gen_udp_hdr(pkt_nb, data, ifobject, udp_hdr);
	gen_ip_hdr(ifobject, ip_hdr);
	gen_udp_csum(udp_hdr, ip_hdr);
	gen_eth_hdr(ifobject, eth_hdr);
651 652

	return pkt;
653 654
}

655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
static void pkt_stream_generate_custom(struct test_spec *test, struct pkt *pkts, u32 nb_pkts)
{
	struct pkt_stream *pkt_stream;
	u32 i;

	pkt_stream = __pkt_stream_alloc(nb_pkts);
	if (!pkt_stream)
		exit_with_error(ENOMEM);

	test->ifobj_tx->pkt_stream = pkt_stream;
	test->ifobj_rx->pkt_stream = pkt_stream;

	for (i = 0; i < nb_pkts; i++) {
		pkt_stream->pkts[i].addr = pkts[i].addr;
		pkt_stream->pkts[i].len = pkts[i].len;
		pkt_stream->pkts[i].payload = i;
		pkt_stream->pkts[i].valid = pkts[i].valid;
	}
}

675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
static void pkt_dump(void *pkt, u32 len)
{
	char s[INET_ADDRSTRLEN];
	struct ethhdr *ethhdr;
	struct udphdr *udphdr;
	struct iphdr *iphdr;
	int payload, i;

	ethhdr = pkt;
	iphdr = pkt + sizeof(*ethhdr);
	udphdr = pkt + sizeof(*ethhdr) + sizeof(*iphdr);

	/*extract L2 frame */
	fprintf(stdout, "DEBUG>> L2: dst mac: ");
	for (i = 0; i < ETH_ALEN; i++)
		fprintf(stdout, "%02X", ethhdr->h_dest[i]);

	fprintf(stdout, "\nDEBUG>> L2: src mac: ");
	for (i = 0; i < ETH_ALEN; i++)
		fprintf(stdout, "%02X", ethhdr->h_source[i]);

	/*extract L3 frame */
	fprintf(stdout, "\nDEBUG>> L3: ip_hdr->ihl: %02X\n", iphdr->ihl);
	fprintf(stdout, "DEBUG>> L3: ip_hdr->saddr: %s\n",
		inet_ntop(AF_INET, &iphdr->saddr, s, sizeof(s)));
	fprintf(stdout, "DEBUG>> L3: ip_hdr->daddr: %s\n",
		inet_ntop(AF_INET, &iphdr->daddr, s, sizeof(s)));
	/*extract L4 frame */
	fprintf(stdout, "DEBUG>> L4: udp_hdr->src: %d\n", ntohs(udphdr->source));
	fprintf(stdout, "DEBUG>> L4: udp_hdr->dst: %d\n", ntohs(udphdr->dest));
	/*extract L5 frame */
	payload = *((uint32_t *)(pkt + PKT_HDR_SIZE));

	fprintf(stdout, "DEBUG>> L5: payload: %d\n", payload);
	fprintf(stdout, "---------------------------------------\n");
}

712 713 714 715 716 717 718 719 720 721 722 723 724 725
static bool is_offset_correct(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream, u64 addr,
			      u64 pkt_stream_addr)
{
	u32 headroom = umem->unaligned_mode ? 0 : umem->frame_headroom;
	u32 offset = addr % umem->frame_size, expected_offset = 0;

	if (!pkt_stream->use_addr_for_fill)
		pkt_stream_addr = 0;

	expected_offset += (pkt_stream_addr + headroom + XDP_PACKET_HEADROOM) % umem->frame_size;

	if (offset == expected_offset)
		return true;

726
	ksft_print_msg("[%s] expected [%u], got [%u]\n", __func__, expected_offset, offset);
727 728 729
	return false;
}

730
static bool is_pkt_valid(struct pkt *pkt, void *buffer, u64 addr, u32 len)
731
{
732
	void *data = xsk_umem__get_data(buffer, addr);
733
	struct iphdr *iphdr = (struct iphdr *)(data + sizeof(struct ethhdr));
734

735
	if (!pkt) {
736
		ksft_print_msg("[%s] too many packets received\n", __func__);
737 738 739
		return false;
	}

740 741
	if (len < MIN_PKT_SIZE || pkt->len < MIN_PKT_SIZE) {
		/* Do not try to verify packets that are smaller than minimum size. */
742 743 744 745
		return true;
	}

	if (pkt->len != len) {
746 747
		ksft_print_msg("[%s] expected length [%d], got length [%d]\n",
			       __func__, pkt->len, len);
748 749 750
		return false;
	}

751
	if (iphdr->version == IP_PKT_VER && iphdr->tos == IP_PKT_TOS) {
752
		u32 seqnum = ntohl(*((u32 *)(data + PKT_HDR_SIZE)));
753

754
		if (opt_pkt_dump)
755
			pkt_dump(data, PKT_SIZE);
756

757
		if (pkt->payload != seqnum) {
758 759
			ksft_print_msg("[%s] expected seqnum [%d], got seqnum [%d]\n",
				       __func__, pkt->payload, seqnum);
760 761
			return false;
		}
762 763 764 765
	} else {
		ksft_print_msg("Invalid frame received: ");
		ksft_print_msg("[IP_PKT_VER: %02X], [IP_PKT_TOS: %02X]\n", iphdr->version,
			       iphdr->tos);
766
		return false;
767
	}
768 769

	return true;
770 771
}

772 773 774 775 776
static void kick_tx(struct xsk_socket_info *xsk)
{
	int ret;

	ret = sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0);
777 778 779 780
	if (ret >= 0)
		return;
	if (errno == ENOBUFS || errno == EAGAIN || errno == EBUSY || errno == ENETDOWN) {
		usleep(100);
781
		return;
782
	}
783 784 785
	exit_with_error(errno);
}

786 787 788 789 790 791 792 793 794
static void kick_rx(struct xsk_socket_info *xsk)
{
	int ret;

	ret = recvfrom(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, NULL);
	if (ret < 0)
		exit_with_error(errno);
}

795
static int complete_pkts(struct xsk_socket_info *xsk, int batch_size)
796 797 798 799
{
	unsigned int rcvd;
	u32 idx;

800
	if (xsk_ring_prod__needs_wakeup(&xsk->tx))
801 802 803 804
		kick_tx(xsk);

	rcvd = xsk_ring_cons__peek(&xsk->umem->cq, batch_size, &idx);
	if (rcvd) {
805 806 807
		if (rcvd > xsk->outstanding_tx) {
			u64 addr = *xsk_ring_cons__comp_addr(&xsk->umem->cq, idx + rcvd - 1);

808
			ksft_print_msg("[%s] Too many packets completed\n", __func__);
809
			ksft_print_msg("Last completion address: %llx\n", addr);
810
			return TEST_FAILURE;
811 812
		}

813 814 815
		xsk_ring_cons__release(&xsk->umem->cq, rcvd);
		xsk->outstanding_tx -= rcvd;
	}
816 817

	return TEST_PASS;
818 819
}

820
static int receive_pkts(struct ifobject *ifobj, struct pollfd *fds)
821
{
822
	struct timeval tv_end, tv_now, tv_timeout = {RECV_TMOUT, 0};
823
	u32 idx_rx = 0, idx_fq = 0, rcvd, i, pkts_sent = 0;
824 825
	struct pkt_stream *pkt_stream = ifobj->pkt_stream;
	struct xsk_socket_info *xsk = ifobj->xsk;
826
	struct xsk_umem_info *umem = xsk->umem;
827
	struct pkt *pkt;
828 829
	int ret;

830 831 832 833 834
	ret = gettimeofday(&tv_now, NULL);
	if (ret)
		exit_with_error(errno);
	timeradd(&tv_now, &tv_timeout, &tv_end);

835
	pkt = pkt_stream_get_next_rx_pkt(pkt_stream, &pkts_sent);
836
	while (pkt) {
837 838 839 840 841 842 843 844
		ret = gettimeofday(&tv_now, NULL);
		if (ret)
			exit_with_error(errno);
		if (timercmp(&tv_now, &tv_end, >)) {
			ksft_print_msg("ERROR: [%s] Receive loop timed out\n", __func__);
			return TEST_FAILURE;
		}

845 846
		kick_rx(xsk);

847 848
		rcvd = xsk_ring_cons__peek(&xsk->rx, BATCH_SIZE, &idx_rx);
		if (!rcvd) {
849
			if (xsk_ring_prod__needs_wakeup(&umem->fq)) {
850 851 852 853 854
				ret = poll(fds, 1, POLL_TMOUT);
				if (ret < 0)
					exit_with_error(-ret);
			}
			continue;
855 856
		}

857 858 859
		if (ifobj->use_fill_ring) {
			ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq);
			while (ret != rcvd) {
860 861
				if (ret < 0)
					exit_with_error(-ret);
862 863 864 865 866 867
				if (xsk_ring_prod__needs_wakeup(&umem->fq)) {
					ret = poll(fds, 1, POLL_TMOUT);
					if (ret < 0)
						exit_with_error(-ret);
				}
				ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq);
868
			}
869 870
		}

871 872 873
		for (i = 0; i < rcvd; i++) {
			const struct xdp_desc *desc = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++);
			u64 addr = desc->addr, orig;
874

875 876
			orig = xsk_umem__extract_addr(addr);
			addr = xsk_umem__add_offset_to_addr(addr);
877

878 879 880
			if (!is_pkt_valid(pkt, umem->buffer, addr, desc->len) ||
			    !is_offset_correct(umem, pkt_stream, addr, pkt->addr))
				return TEST_FAILURE;
881

882 883 884
			if (ifobj->use_fill_ring)
				*xsk_ring_prod__fill_addr(&umem->fq, idx_fq++) = orig;
			pkt = pkt_stream_get_next_rx_pkt(pkt_stream, &pkts_sent);
885
		}
886

887 888 889 890
		if (ifobj->use_fill_ring)
			xsk_ring_prod__submit(&umem->fq, rcvd);
		if (ifobj->release_rx)
			xsk_ring_cons__release(&xsk->rx, rcvd);
891 892

		pthread_mutex_lock(&pacing_mutex);
893
		pkts_in_flight -= pkts_sent;
894 895 896
		if (pkts_in_flight < umem->num_frames)
			pthread_cond_signal(&pacing_cond);
		pthread_mutex_unlock(&pacing_mutex);
897
		pkts_sent = 0;
898
	}
899 900

	return TEST_PASS;
901 902
}

903
static int __send_pkts(struct ifobject *ifobject, u32 *pkt_nb)
904
{
905
	struct xsk_socket_info *xsk = ifobject->xsk;
906
	u32 i, idx, valid_pkts = 0;
907

908 909
	while (xsk_ring_prod__reserve(&xsk->tx, BATCH_SIZE, &idx) < BATCH_SIZE)
		complete_pkts(xsk, BATCH_SIZE);
910

911
	for (i = 0; i < BATCH_SIZE; i++) {
912
		struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i);
913
		struct pkt *pkt = pkt_generate(ifobject, *pkt_nb);
914

915 916
		if (!pkt)
			break;
917

918 919
		tx_desc->addr = pkt->addr;
		tx_desc->len = pkt->len;
920
		(*pkt_nb)++;
921 922
		if (pkt->valid)
			valid_pkts++;
923
	}
924

925 926
	pthread_mutex_lock(&pacing_mutex);
	pkts_in_flight += valid_pkts;
927 928
	/* pkts_in_flight might be negative if many invalid packets are sent */
	if (pkts_in_flight >= (int)(ifobject->umem->num_frames - BATCH_SIZE)) {
929 930 931 932 933
		kick_tx(xsk);
		pthread_cond_wait(&pacing_cond, &pacing_mutex);
	}
	pthread_mutex_unlock(&pacing_mutex);

934
	xsk_ring_prod__submit(&xsk->tx, i);
935
	xsk->outstanding_tx += valid_pkts;
936 937
	if (complete_pkts(xsk, i))
		return TEST_FAILURE;
938

939
	usleep(10);
940
	return TEST_PASS;
941 942
}

943
static void wait_for_tx_completion(struct xsk_socket_info *xsk)
944
{
945 946
	while (xsk->outstanding_tx)
		complete_pkts(xsk, BATCH_SIZE);
947 948
}

949
static int send_pkts(struct test_spec *test, struct ifobject *ifobject)
950
{
951
	struct pollfd fds = { };
952
	u32 pkt_cnt = 0;
953

954 955
	fds.fd = xsk_socket__fd(ifobject->xsk->xsk);
	fds.events = POLLOUT;
956

957
	while (pkt_cnt < ifobject->pkt_stream->nb_pkts) {
958 959
		int err;

960
		if (ifobject->use_poll) {
961 962
			int ret;

963
			ret = poll(&fds, 1, POLL_TMOUT);
964 965 966
			if (ret <= 0)
				continue;

967
			if (!(fds.revents & POLLOUT))
968 969 970
				continue;
		}

971 972 973
		err = __send_pkts(ifobject, &pkt_cnt);
		if (err || test->fail)
			return TEST_FAILURE;
974 975
	}

976
	wait_for_tx_completion(ifobject->xsk);
977
	return TEST_PASS;
978 979
}

980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
static int get_xsk_stats(struct xsk_socket *xsk, struct xdp_statistics *stats)
{
	int fd = xsk_socket__fd(xsk), err;
	socklen_t optlen, expected_len;

	optlen = sizeof(*stats);
	err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, stats, &optlen);
	if (err) {
		ksft_print_msg("[%s] getsockopt(XDP_STATISTICS) error %u %s\n",
			       __func__, -err, strerror(-err));
		return TEST_FAILURE;
	}

	expected_len = sizeof(struct xdp_statistics);
	if (optlen != expected_len) {
		ksft_print_msg("[%s] getsockopt optlen error. Expected: %u got: %u\n",
			       __func__, expected_len, optlen);
		return TEST_FAILURE;
	}

	return TEST_PASS;
}

static int validate_rx_dropped(struct ifobject *ifobject)
1004
{
1005
	struct xsk_socket *xsk = ifobject->xsk->xsk;
1006 1007 1008
	struct xdp_statistics stats;
	int err;

1009 1010
	kick_rx(ifobject->xsk);

1011 1012
	err = get_xsk_stats(xsk, &stats);
	if (err)
1013
		return TEST_FAILURE;
1014

1015
	if (stats.rx_dropped == ifobject->pkt_stream->nb_pkts / 2)
1016
		return TEST_PASS;
1017

1018
	return TEST_FAILURE;
1019 1020 1021 1022 1023 1024 1025 1026
}

static int validate_rx_full(struct ifobject *ifobject)
{
	struct xsk_socket *xsk = ifobject->xsk->xsk;
	struct xdp_statistics stats;
	int err;

1027
	usleep(1000);
1028 1029 1030 1031 1032 1033
	kick_rx(ifobject->xsk);

	err = get_xsk_stats(xsk, &stats);
	if (err)
		return TEST_FAILURE;

1034
	if (stats.rx_ring_full)
1035 1036
		return TEST_PASS;

1037
	return TEST_FAILURE;
1038 1039 1040 1041 1042 1043 1044 1045
}

static int validate_fill_empty(struct ifobject *ifobject)
{
	struct xsk_socket *xsk = ifobject->xsk->xsk;
	struct xdp_statistics stats;
	int err;

1046
	usleep(1000);
1047 1048 1049 1050 1051 1052
	kick_rx(ifobject->xsk);

	err = get_xsk_stats(xsk, &stats);
	if (err)
		return TEST_FAILURE;

1053
	if (stats.rx_fill_ring_empty_descs)
1054
		return TEST_PASS;
1055

1056
	return TEST_FAILURE;
1057 1058
}

1059
static int validate_tx_invalid_descs(struct ifobject *ifobject)
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
{
	struct xsk_socket *xsk = ifobject->xsk->xsk;
	int fd = xsk_socket__fd(xsk);
	struct xdp_statistics stats;
	socklen_t optlen;
	int err;

	optlen = sizeof(stats);
	err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen);
	if (err) {
1070 1071 1072
		ksft_print_msg("[%s] getsockopt(XDP_STATISTICS) error %u %s\n",
			       __func__, -err, strerror(-err));
		return TEST_FAILURE;
1073 1074
	}

1075
	if (stats.tx_invalid_descs != ifobject->pkt_stream->nb_pkts / 2) {
1076 1077 1078 1079
		ksft_print_msg("[%s] tx_invalid_descs incorrect. Got [%u] expected [%u]\n",
			       __func__, stats.tx_invalid_descs, ifobject->pkt_stream->nb_pkts);
		return TEST_FAILURE;
	}
1080

1081
	return TEST_PASS;
1082 1083
}

1084
static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
1085
{
1086
	u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size;
1087
	int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
1088
	LIBBPF_OPTS(bpf_xdp_query_opts, opts);
1089 1090
	int ret, ifindex;
	void *bufs;
1091
	u32 i;
1092

1093 1094
	ifobject->ns_fd = switch_namespace(ifobject->nsname);

1095 1096 1097
	if (ifobject->umem->unaligned_mode)
		mmap_flags |= MAP_HUGETLB;

1098 1099 1100
	bufs = mmap(NULL, umem_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
	if (bufs == MAP_FAILED)
		exit_with_error(errno);
1101

1102 1103 1104
	ret = xsk_configure_umem(ifobject->umem, bufs, umem_sz);
	if (ret)
		exit_with_error(-ret);
1105

1106 1107
	for (i = 0; i < test->nb_sockets; i++) {
		u32 ctr = 0;
1108

1109
		while (ctr++ < SOCK_RECONF_CTR) {
1110 1111
			ret = xsk_configure_socket(&ifobject->xsk_arr[i], ifobject->umem,
						   ifobject, !!i);
1112 1113
			if (!ret)
				break;
1114

1115 1116 1117 1118 1119
			/* Retry if it fails as xsk_socket__create() is asynchronous */
			if (ctr >= SOCK_RECONF_CTR)
				exit_with_error(-ret);
			usleep(USLEEP_MAX);
		}
1120 1121 1122

		if (ifobject->busy_poll)
			enable_busy_poll(&ifobject->xsk_arr[i]);
1123 1124
	}

1125
	ifobject->xsk = &ifobject->xsk_arr[0];
1126 1127 1128 1129 1130 1131 1132 1133

	if (!ifobject->rx_on)
		return;

	ifindex = if_nametoindex(ifobject->ifname);
	if (!ifindex)
		exit_with_error(errno);

1134
	ret = xsk_setup_xdp_prog_xsk(ifobject->xsk->xsk, &ifobject->xsk_map_fd);
1135 1136 1137
	if (ret)
		exit_with_error(-ret);

1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
	ret = bpf_xdp_query(ifindex, ifobject->xdp_flags, &opts);
	if (ret)
		exit_with_error(-ret);

	if (ifobject->xdp_flags & XDP_FLAGS_SKB_MODE) {
		if (opts.attach_mode != XDP_ATTACHED_SKB) {
			ksft_print_msg("ERROR: [%s] XDP prog not in SKB mode\n");
			exit_with_error(-EINVAL);
		}
	} else if (ifobject->xdp_flags & XDP_FLAGS_DRV_MODE) {
		if (opts.attach_mode != XDP_ATTACHED_DRV) {
			ksft_print_msg("ERROR: [%s] XDP prog not in DRV mode\n");
			exit_with_error(-EINVAL);
		}
	}

1154 1155 1156
	ret = xsk_socket__update_xskmap(ifobject->xsk->xsk, ifobject->xsk_map_fd);
	if (ret)
		exit_with_error(-ret);
1157 1158
}

1159 1160
static void testapp_cleanup_xsk_res(struct ifobject *ifobj)
{
1161
	print_verbose("Destroying socket\n");
1162
	xsk_socket__delete(ifobj->xsk->xsk);
1163
	munmap(ifobj->umem->buffer, ifobj->umem->num_frames * ifobj->umem->frame_size);
1164
	xsk_umem__delete(ifobj->umem->umem);
1165 1166
}

1167
static void *worker_testapp_validate_tx(void *arg)
1168
{
1169 1170
	struct test_spec *test = (struct test_spec *)arg;
	struct ifobject *ifobject = test->ifobj_tx;
1171
	int err;
1172

1173 1174
	if (test->current_step == 1)
		thread_common_ops(test, ifobject);
1175

1176 1177
	print_verbose("Sending %d packets on interface %s\n", ifobject->pkt_stream->nb_pkts,
		      ifobject->ifname);
1178
	err = send_pkts(test, ifobject);
1179

1180
	if (!err && ifobject->validation_func)
1181
		err = ifobject->validation_func(ifobject);
1182
	if (err)
1183
		report_failure(test);
1184

1185
	if (test->total_steps == test->current_step || err)
1186
		testapp_cleanup_xsk_res(ifobject);
1187 1188
	pthread_exit(NULL);
}
1189

1190 1191
static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream)
{
1192
	u32 idx = 0, i, buffers_to_fill;
1193 1194
	int ret;

1195 1196 1197 1198 1199 1200 1201
	if (umem->num_frames < XSK_RING_PROD__DEFAULT_NUM_DESCS)
		buffers_to_fill = umem->num_frames;
	else
		buffers_to_fill = XSK_RING_PROD__DEFAULT_NUM_DESCS;

	ret = xsk_ring_prod__reserve(&umem->fq, buffers_to_fill, &idx);
	if (ret != buffers_to_fill)
1202
		exit_with_error(ENOSPC);
1203
	for (i = 0; i < buffers_to_fill; i++) {
1204 1205 1206 1207 1208 1209 1210 1211 1212
		u64 addr;

		if (pkt_stream->use_addr_for_fill) {
			struct pkt *pkt = pkt_stream_get_pkt(pkt_stream, i);

			if (!pkt)
				break;
			addr = pkt->addr;
		} else {
1213
			addr = i * umem->frame_size;
1214 1215 1216 1217
		}

		*xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr;
	}
1218
	xsk_ring_prod__submit(&umem->fq, buffers_to_fill);
1219 1220
}

1221 1222
static void *worker_testapp_validate_rx(void *arg)
{
1223 1224
	struct test_spec *test = (struct test_spec *)arg;
	struct ifobject *ifobject = test->ifobj_rx;
1225
	struct pollfd fds = { };
1226
	int err;
1227

1228 1229
	if (test->current_step == 1)
		thread_common_ops(test, ifobject);
1230

1231
	xsk_populate_fill_ring(ifobject->umem, ifobject->pkt_stream);
1232

1233 1234
	fds.fd = xsk_socket__fd(ifobject->xsk->xsk);
	fds.events = POLLIN;
1235

1236
	pthread_barrier_wait(&barr);
1237

1238
	err = receive_pkts(ifobject, &fds);
1239

1240 1241
	if (!err && ifobject->validation_func)
		err = ifobject->validation_func(ifobject);
1242 1243 1244 1245 1246 1247
	if (err) {
		report_failure(test);
		pthread_mutex_lock(&pacing_mutex);
		pthread_cond_signal(&pacing_cond);
		pthread_mutex_unlock(&pacing_mutex);
	}
1248

1249
	if (test->total_steps == test->current_step || err)
1250
		testapp_cleanup_xsk_res(ifobject);
1251 1252 1253
	pthread_exit(NULL);
}

1254
static int testapp_validate_traffic(struct test_spec *test)
1255
{
1256 1257
	struct ifobject *ifobj_tx = test->ifobj_tx;
	struct ifobject *ifobj_rx = test->ifobj_rx;
1258
	pthread_t t0, t1;
1259

1260 1261
	if (pthread_barrier_init(&barr, NULL, 2))
		exit_with_error(errno);
1262

1263
	test->current_step++;
1264
	pkt_stream_reset(ifobj_rx->pkt_stream);
1265
	pkts_in_flight = 0;
1266

1267
	/*Spawn RX thread */
1268
	pthread_create(&t0, NULL, ifobj_rx->func_ptr, test);
1269

1270 1271
	pthread_barrier_wait(&barr);
	if (pthread_barrier_destroy(&barr))
1272 1273 1274
		exit_with_error(errno);

	/*Spawn TX thread */
1275
	pthread_create(&t1, NULL, ifobj_tx->func_ptr, test);
1276 1277 1278

	pthread_join(t1, NULL);
	pthread_join(t0, NULL);
1279 1280

	return !!test->fail;
1281 1282
}

1283
static void testapp_teardown(struct test_spec *test)
1284 1285 1286
{
	int i;

1287
	test_spec_set_name(test, "TEARDOWN");
1288
	for (i = 0; i < MAX_TEARDOWN_ITER; i++) {
1289 1290
		if (testapp_validate_traffic(test))
			return;
1291
		test_spec_reset(test);
1292 1293 1294
	}
}

1295
static void swap_directions(struct ifobject **ifobj1, struct ifobject **ifobj2)
1296
{
1297 1298
	thread_func_t tmp_func_ptr = (*ifobj1)->func_ptr;
	struct ifobject *tmp_ifobj = (*ifobj1);
1299

1300 1301
	(*ifobj1)->func_ptr = (*ifobj2)->func_ptr;
	(*ifobj2)->func_ptr = tmp_func_ptr;
1302

1303 1304
	*ifobj1 = *ifobj2;
	*ifobj2 = tmp_ifobj;
1305 1306
}

1307
static void testapp_bidi(struct test_spec *test)
1308
{
1309
	test_spec_set_name(test, "BIDIRECTIONAL");
1310 1311
	test->ifobj_tx->rx_on = true;
	test->ifobj_rx->tx_on = true;
1312
	test->total_steps = 2;
1313 1314
	if (testapp_validate_traffic(test))
		return;
1315 1316 1317 1318

	print_verbose("Switching Tx/Rx vectors\n");
	swap_directions(&test->ifobj_rx, &test->ifobj_tx);
	testapp_validate_traffic(test);
1319

1320
	swap_directions(&test->ifobj_rx, &test->ifobj_tx);
1321 1322
}

1323
static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx)
1324
{
1325 1326
	int ret;

1327 1328 1329 1330
	xsk_socket__delete(ifobj_tx->xsk->xsk);
	xsk_socket__delete(ifobj_rx->xsk->xsk);
	ifobj_tx->xsk = &ifobj_tx->xsk_arr[1];
	ifobj_rx->xsk = &ifobj_rx->xsk_arr[1];
1331 1332 1333 1334

	ret = xsk_socket__update_xskmap(ifobj_rx->xsk->xsk, ifobj_rx->xsk_map_fd);
	if (ret)
		exit_with_error(-ret);
1335 1336
}

1337
static void testapp_bpf_res(struct test_spec *test)
1338
{
1339
	test_spec_set_name(test, "BPF_RES");
1340
	test->total_steps = 2;
1341
	test->nb_sockets = 2;
1342 1343
	if (testapp_validate_traffic(test))
		return;
1344 1345 1346

	swap_xsk_resources(test->ifobj_tx, test->ifobj_rx);
	testapp_validate_traffic(test);
1347 1348
}

1349 1350 1351 1352 1353 1354 1355
static void testapp_headroom(struct test_spec *test)
{
	test_spec_set_name(test, "UMEM_HEADROOM");
	test->ifobj_rx->umem->frame_headroom = UMEM_HEADROOM_TEST_SIZE;
	testapp_validate_traffic(test);
}

1356
static void testapp_stats_rx_dropped(struct test_spec *test)
1357
{
1358 1359
	test_spec_set_name(test, "STAT_RX_DROPPED");
	test->ifobj_rx->umem->frame_headroom = test->ifobj_rx->umem->frame_size -
1360 1361 1362
		XDP_PACKET_HEADROOM - MIN_PKT_SIZE * 3;
	pkt_stream_replace_half(test, MIN_PKT_SIZE * 4, 0);
	pkt_stream_receive_half(test);
1363 1364 1365
	test->ifobj_rx->validation_func = validate_rx_dropped;
	testapp_validate_traffic(test);
}
1366

1367 1368 1369
static void testapp_stats_tx_invalid_descs(struct test_spec *test)
{
	test_spec_set_name(test, "STAT_TX_INVALID");
1370
	pkt_stream_replace_half(test, XSK_UMEM__INVALID_FRAME_SIZE, 0);
1371 1372
	test->ifobj_tx->validation_func = validate_tx_invalid_descs;
	testapp_validate_traffic(test);
1373

1374 1375
	pkt_stream_restore_default(test);
}
1376

1377 1378 1379
static void testapp_stats_rx_full(struct test_spec *test)
{
	test_spec_set_name(test, "STAT_RX_FULL");
1380 1381 1382 1383 1384 1385 1386 1387
	pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, PKT_SIZE);
	test->ifobj_rx->pkt_stream = pkt_stream_generate(test->ifobj_rx->umem,
							 DEFAULT_UMEM_BUFFERS, PKT_SIZE);
	if (!test->ifobj_rx->pkt_stream)
		exit_with_error(ENOMEM);

	test->ifobj_rx->xsk->rxqsize = DEFAULT_UMEM_BUFFERS;
	test->ifobj_rx->release_rx = false;
1388 1389
	test->ifobj_rx->validation_func = validate_rx_full;
	testapp_validate_traffic(test);
1390 1391

	pkt_stream_restore_default(test);
1392
}
1393

1394 1395 1396
static void testapp_stats_fill_empty(struct test_spec *test)
{
	test_spec_set_name(test, "STAT_RX_FILL_EMPTY");
1397 1398 1399
	pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, PKT_SIZE);
	test->ifobj_rx->pkt_stream = pkt_stream_generate(test->ifobj_rx->umem,
							 DEFAULT_UMEM_BUFFERS, PKT_SIZE);
1400 1401
	if (!test->ifobj_rx->pkt_stream)
		exit_with_error(ENOMEM);
1402 1403

	test->ifobj_rx->use_fill_ring = false;
1404 1405 1406 1407
	test->ifobj_rx->validation_func = validate_fill_empty;
	testapp_validate_traffic(test);

	pkt_stream_restore_default(test);
1408 1409
}

1410 1411 1412 1413 1414 1415 1416
/* Simple test */
static bool hugepages_present(struct ifobject *ifobject)
{
	const size_t mmap_sz = 2 * ifobject->umem->num_frames * ifobject->umem->frame_size;
	void *bufs;

	bufs = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
1417
		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435
	if (bufs == MAP_FAILED)
		return false;

	munmap(bufs, mmap_sz);
	return true;
}

static bool testapp_unaligned(struct test_spec *test)
{
	if (!hugepages_present(test->ifobj_tx)) {
		ksft_test_result_skip("No 2M huge pages present.\n");
		return false;
	}

	test_spec_set_name(test, "UNALIGNED_MODE");
	test->ifobj_tx->umem->unaligned_mode = true;
	test->ifobj_rx->umem->unaligned_mode = true;
	/* Let half of the packets straddle a buffer boundrary */
1436
	pkt_stream_replace_half(test, PKT_SIZE, -PKT_SIZE / 2);
1437 1438 1439 1440 1441 1442 1443
	test->ifobj_rx->pkt_stream->use_addr_for_fill = true;
	testapp_validate_traffic(test);

	pkt_stream_restore_default(test);
	return true;
}

1444 1445 1446 1447 1448 1449 1450 1451 1452
static void testapp_single_pkt(struct test_spec *test)
{
	struct pkt pkts[] = {{0x1000, PKT_SIZE, 0, true}};

	pkt_stream_generate_custom(test, pkts, ARRAY_SIZE(pkts));
	testapp_validate_traffic(test);
	pkt_stream_restore_default(test);
}

1453 1454 1455
static void testapp_invalid_desc(struct test_spec *test)
{
	struct pkt pkts[] = {
1456 1457 1458 1459
		/* Zero packet address allowed */
		{0, PKT_SIZE, 0, true},
		/* Allowed packet */
		{0x1000, PKT_SIZE, 0, true},
1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
		/* Straddling the start of umem */
		{-2, PKT_SIZE, 0, false},
		/* Packet too large */
		{0x2000, XSK_UMEM__INVALID_FRAME_SIZE, 0, false},
		/* After umem ends */
		{UMEM_SIZE, PKT_SIZE, 0, false},
		/* Straddle the end of umem */
		{UMEM_SIZE - PKT_SIZE / 2, PKT_SIZE, 0, false},
		/* Straddle a page boundrary */
		{0x3000 - PKT_SIZE / 2, PKT_SIZE, 0, false},
1470 1471
		/* Straddle a 2K boundrary */
		{0x3800 - PKT_SIZE / 2, PKT_SIZE, 0, true},
1472 1473 1474 1475 1476 1477 1478
		/* Valid packet for synch so that something is received */
		{0x4000, PKT_SIZE, 0, true}};

	if (test->ifobj_tx->umem->unaligned_mode) {
		/* Crossing a page boundrary allowed */
		pkts[6].valid = true;
	}
1479 1480 1481 1482 1483
	if (test->ifobj_tx->umem->frame_size == XSK_UMEM__DEFAULT_FRAME_SIZE / 2) {
		/* Crossing a 2K frame size boundrary not allowed */
		pkts[7].valid = false;
	}

1484 1485 1486 1487 1488
	pkt_stream_generate_custom(test, pkts, ARRAY_SIZE(pkts));
	testapp_validate_traffic(test);
	pkt_stream_restore_default(test);
}

1489 1490
static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *src_mac,
		       const char *dst_ip, const char *src_ip, const u16 dst_port,
1491
		       const u16 src_port, thread_func_t func_ptr)
1492
{
1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
	struct in_addr ip;

	memcpy(ifobj->dst_mac, dst_mac, ETH_ALEN);
	memcpy(ifobj->src_mac, src_mac, ETH_ALEN);

	inet_aton(dst_ip, &ip);
	ifobj->dst_ip = ip.s_addr;

	inet_aton(src_ip, &ip);
	ifobj->src_ip = ip.s_addr;

	ifobj->dst_port = dst_port;
	ifobj->src_port = src_port;
1506

1507
	ifobj->func_ptr = func_ptr;
1508 1509
}

1510
static void run_pkt_test(struct test_spec *test, enum test_mode mode, enum test_type type)
1511
{
1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523
	switch (type) {
	case TEST_TYPE_STATS_RX_DROPPED:
		testapp_stats_rx_dropped(test);
		break;
	case TEST_TYPE_STATS_TX_INVALID_DESCS:
		testapp_stats_tx_invalid_descs(test);
		break;
	case TEST_TYPE_STATS_RX_FULL:
		testapp_stats_rx_full(test);
		break;
	case TEST_TYPE_STATS_FILL_EMPTY:
		testapp_stats_fill_empty(test);
1524 1525
		break;
	case TEST_TYPE_TEARDOWN:
1526
		testapp_teardown(test);
1527 1528
		break;
	case TEST_TYPE_BIDI:
1529
		testapp_bidi(test);
1530
		break;
1531
	case TEST_TYPE_BPF_RES:
1532
		testapp_bpf_res(test);
1533
		break;
1534
	case TEST_TYPE_RUN_TO_COMPLETION:
1535 1536 1537
		test_spec_set_name(test, "RUN_TO_COMPLETION");
		testapp_validate_traffic(test);
		break;
1538 1539 1540 1541
	case TEST_TYPE_RUN_TO_COMPLETION_SINGLE_PKT:
		test_spec_set_name(test, "RUN_TO_COMPLETION_SINGLE_PKT");
		testapp_single_pkt(test);
		break;
1542 1543 1544 1545
	case TEST_TYPE_RUN_TO_COMPLETION_2K_FRAME:
		test_spec_set_name(test, "RUN_TO_COMPLETION_2K_FRAME_SIZE");
		test->ifobj_tx->umem->frame_size = 2048;
		test->ifobj_rx->umem->frame_size = 2048;
1546
		pkt_stream_replace(test, DEFAULT_PKT_CNT, PKT_SIZE);
1547 1548 1549 1550
		testapp_validate_traffic(test);

		pkt_stream_restore_default(test);
		break;
1551
	case TEST_TYPE_POLL:
1552 1553
		test->ifobj_tx->use_poll = true;
		test->ifobj_rx->use_poll = true;
1554
		test_spec_set_name(test, "POLL");
1555
		testapp_validate_traffic(test);
1556
		break;
1557 1558 1559 1560
	case TEST_TYPE_ALIGNED_INV_DESC:
		test_spec_set_name(test, "ALIGNED_INV_DESC");
		testapp_invalid_desc(test);
		break;
1561 1562 1563 1564 1565 1566
	case TEST_TYPE_ALIGNED_INV_DESC_2K_FRAME:
		test_spec_set_name(test, "ALIGNED_INV_DESC_2K_FRAME_SIZE");
		test->ifobj_tx->umem->frame_size = 2048;
		test->ifobj_rx->umem->frame_size = 2048;
		testapp_invalid_desc(test);
		break;
1567
	case TEST_TYPE_UNALIGNED_INV_DESC:
1568 1569 1570 1571
		if (!hugepages_present(test->ifobj_tx)) {
			ksft_test_result_skip("No 2M huge pages present.\n");
			return;
		}
1572 1573 1574 1575 1576
		test_spec_set_name(test, "UNALIGNED_INV_DESC");
		test->ifobj_tx->umem->unaligned_mode = true;
		test->ifobj_rx->umem->unaligned_mode = true;
		testapp_invalid_desc(test);
		break;
1577 1578 1579 1580
	case TEST_TYPE_UNALIGNED:
		if (!testapp_unaligned(test))
			return;
		break;
1581 1582 1583
	case TEST_TYPE_HEADROOM:
		testapp_headroom(test);
		break;
1584 1585
	default:
		break;
1586
	}
1587

1588 1589 1590
	if (!test->fail)
		ksft_test_result_pass("PASS: %s %s%s\n", mode_string(test), busy_poll_string(test),
				      test->name);
1591 1592
}

1593 1594 1595 1596 1597 1598 1599 1600
static struct ifobject *ifobject_create(void)
{
	struct ifobject *ifobj;

	ifobj = calloc(1, sizeof(struct ifobject));
	if (!ifobj)
		return NULL;

1601
	ifobj->xsk_arr = calloc(MAX_SOCKETS, sizeof(*ifobj->xsk_arr));
1602 1603 1604
	if (!ifobj->xsk_arr)
		goto out_xsk_arr;

1605 1606 1607
	ifobj->umem = calloc(1, sizeof(*ifobj->umem));
	if (!ifobj->umem)
		goto out_umem;
1608

1609 1610
	ifobj->ns_fd = -1;

1611 1612
	return ifobj;

1613
out_umem:
1614 1615 1616 1617 1618 1619 1620 1621
	free(ifobj->xsk_arr);
out_xsk_arr:
	free(ifobj);
	return NULL;
}

static void ifobject_delete(struct ifobject *ifobj)
{
1622 1623
	if (ifobj->ns_fd != -1)
		close(ifobj->ns_fd);
1624
	free(ifobj->umem);
1625 1626 1627 1628
	free(ifobj->xsk_arr);
	free(ifobj);
}

1629 1630
int main(int argc, char **argv)
{
1631
	struct pkt_stream *pkt_stream_default;
1632
	struct ifobject *ifobj_tx, *ifobj_rx;
1633
	u32 i, j, failed_tests = 0;
1634
	struct test_spec test;
1635

1636 1637
	/* Use libbpf 1.0 API mode */
	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
1638

1639 1640 1641 1642 1643 1644 1645
	ifobj_tx = ifobject_create();
	if (!ifobj_tx)
		exit_with_error(ENOMEM);
	ifobj_rx = ifobject_create();
	if (!ifobj_rx)
		exit_with_error(ENOMEM);

1646 1647
	setlocale(LC_ALL, "");

1648
	parse_command_line(ifobj_tx, ifobj_rx, argc, argv);
1649 1650 1651 1652 1653

	if (!validate_interface(ifobj_tx) || !validate_interface(ifobj_rx)) {
		usage(basename(argv[0]));
		ksft_exit_xfail();
	}
1654

1655
	init_iface(ifobj_tx, MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2,
1656
		   worker_testapp_validate_tx);
1657
	init_iface(ifobj_rx, MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1,
1658
		   worker_testapp_validate_rx);
1659

1660 1661 1662 1663 1664 1665
	test_spec_init(&test, ifobj_tx, ifobj_rx, 0);
	pkt_stream_default = pkt_stream_generate(ifobj_tx->umem, DEFAULT_PKT_CNT, PKT_SIZE);
	if (!pkt_stream_default)
		exit_with_error(ENOMEM);
	test.pkt_stream_default = pkt_stream_default;

1666
	ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
1667

1668
	for (i = 0; i < TEST_MODE_MAX; i++)
1669
		for (j = 0; j < TEST_TYPE_MAX; j++) {
1670
			test_spec_init(&test, ifobj_tx, ifobj_rx, i);
1671
			run_pkt_test(&test, i, j);
1672
			usleep(USLEEP_MAX);
1673 1674 1675

			if (test.fail)
				failed_tests++;
1676
		}
1677

1678
	pkt_stream_delete(pkt_stream_default);
1679 1680
	ifobject_delete(ifobj_tx);
	ifobject_delete(ifobj_rx);
1681

1682 1683 1684 1685
	if (failed_tests)
		ksft_exit_fail();
	else
		ksft_exit_pass();
1686
}