Commit 43c5026b authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'selftests/bpf: xsk improvements and new stats'



Ciara Loftus says:

====================

This series attempts to improve the xsk selftest framework by:
1. making the default output less verbose
2. adding an optional verbose flag to both the test_xsk.sh script and xdpxceiver app.
3. renaming the debug option in the app to to 'dump-pkts' and add a flag to the test_xsk.sh
script which enables the flag in the app.
4. changing how tests are launched - now they are launched from the xdpxceiver app
instead of the script.

Once the improvements are made, a new set of tests are added which test the xsk
statistics.

The output of the test script now looks like:

./test_xsk.sh
PREREQUISITES: [ PASS ]
1..10
ok 1 PASS: SKB NOPOLL
ok 2 PASS: SKB POLL
ok 3 PASS: SKB NOPOLL Socket Teardown
ok 4 PASS: SKB NOPOLL Bi-directional Sockets
ok 5 PASS: SKB NOPOLL Stats
ok 6 PASS: DRV NOPOLL
ok 7 PASS: DRV POLL
ok 8 PASS: DRV NOPOLL Socket Teardown
ok 9 PASS: DRV NOPOLL Bi-directional Sockets
ok 10 PASS: DRV NOPOLL Stats
# Totals: pass:10 fail:0 xfail:0 xpass:0 skip:0 error:0
XSK KSELFTESTS: [ PASS ]

v2->v3:
* Rename dump-pkts to dump_pkts in test_xsk.sh
* Add examples of flag usage to test_xsk.sh

v1->v2:
* Changed '-d' flag in the shell script to '-D' to be consistent with the xdpxceiver app.
* Renamed debug mode to 'dump-pkts' which better reflects the behaviour.
* Use libpf APIs instead of calls to ss for configuring xdp on the links
* Remove mutex init & destroy for each stats test
* Added a description for each of the new statistics tests
* Distinguish between exiting due to initialisation failure vs test failure

This series applies on commit d310ec03

Ciara Loftus (3):
  selftests/bpf: expose and rename debug argument
  selftests/bpf: restructure xsk selftests
  selftests/bpf: introduce xsk statistics tests
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents e6ac5933 b267e5a4
Loading
Loading
Loading
Loading
+25 −110
Original line number Diff line number Diff line
@@ -71,13 +71,21 @@
#
# Run (full output without color-coding):
#   sudo ./test_xsk.sh
#
# Run with verbose output:
#   sudo ./test_xsk.sh -v
#
# Run and dump packet contents:
#   sudo ./test_xsk.sh -D

. xsk_prereqs.sh

while getopts c flag
while getopts "cvD" flag
do
	case "${flag}" in
		c) colorconsole=1;;
		v) verbose=1;;
		D) dump_pkts=1;;
	esac
done

@@ -95,13 +103,17 @@ NS1=af_xdp${VETH1_POSTFIX}
MTU=1500

setup_vethPairs() {
	if [[ $verbose -eq 1 ]]; then
	        echo "setting up ${VETH0}: namespace: ${NS0}"
	fi
	ip netns add ${NS1}
	ip link add ${VETH0} type veth peer name ${VETH1}
	if [ -f /proc/net/if_inet6 ]; then
		echo 1 > /proc/sys/net/ipv6/conf/${VETH0}/disable_ipv6
	fi
	if [[ $verbose -eq 1 ]]; then
	        echo "setting up ${VETH1}: namespace: ${NS1}"
	fi
	ip link set ${VETH1} netns ${NS1}
	ip netns exec ${NS1} ip link set ${VETH1} mtu ${MTU}
	ip link set ${VETH0} mtu ${MTU}
@@ -125,121 +137,24 @@ echo "${VETH0}:${VETH1},${NS1}" > ${SPECFILE}

validate_veth_spec_file

if [[ $verbose -eq 1 ]]; then
        echo "Spec file created: ${SPECFILE}"

test_status $retval "${TEST_NAME}"

## START TESTS

statusList=()

### TEST 1
TEST_NAME="XSK KSELFTEST FRAMEWORK"

echo "Switching interfaces [${VETH0}, ${VETH1}] to XDP Generic mode"
vethXDPgeneric ${VETH0} ${VETH1} ${NS1}

retval=$?
if [ $retval -eq 0 ]; then
	echo "Switching interfaces [${VETH0}, ${VETH1}] to XDP Native mode"
	vethXDPnative ${VETH0} ${VETH1} ${NS1}
	VERBOSE_ARG="-v"
fi

retval=$?
test_status $retval "${TEST_NAME}"
statusList+=($retval)

### TEST 2
TEST_NAME="SKB NOPOLL"

vethXDPgeneric ${VETH0} ${VETH1} ${NS1}

params=("-S")
execxdpxceiver params

retval=$?
test_status $retval "${TEST_NAME}"
statusList+=($retval)

### TEST 3
TEST_NAME="SKB POLL"

vethXDPgeneric ${VETH0} ${VETH1} ${NS1}

params=("-S" "-p")
execxdpxceiver params

retval=$?
test_status $retval "${TEST_NAME}"
statusList+=($retval)

### TEST 4
TEST_NAME="DRV NOPOLL"

vethXDPnative ${VETH0} ${VETH1} ${NS1}

params=("-N")
execxdpxceiver params

retval=$?
test_status $retval "${TEST_NAME}"
statusList+=($retval)

### TEST 5
TEST_NAME="DRV POLL"

vethXDPnative ${VETH0} ${VETH1} ${NS1}

params=("-N" "-p")
execxdpxceiver params

retval=$?
test_status $retval "${TEST_NAME}"
statusList+=($retval)

### TEST 6
TEST_NAME="SKB SOCKET TEARDOWN"

vethXDPgeneric ${VETH0} ${VETH1} ${NS1}

params=("-S" "-T")
execxdpxceiver params

retval=$?
test_status $retval "${TEST_NAME}"
statusList+=($retval)

### TEST 7
TEST_NAME="DRV SOCKET TEARDOWN"

vethXDPnative ${VETH0} ${VETH1} ${NS1}

params=("-N" "-T")
execxdpxceiver params
if [[ $dump_pkts -eq 1 ]]; then
	DUMP_PKTS_ARG="-D"
fi

retval=$?
test_status $retval "${TEST_NAME}"
statusList+=($retval)

### TEST 8
TEST_NAME="SKB BIDIRECTIONAL SOCKETS"

vethXDPgeneric ${VETH0} ${VETH1} ${NS1}

params=("-S" "-B")
execxdpxceiver params

retval=$?
test_status $retval "${TEST_NAME}"
statusList+=($retval)
## START TESTS

### TEST 9
TEST_NAME="DRV BIDIRECTIONAL SOCKETS"
statusList=()

vethXDPnative ${VETH0} ${VETH1} ${NS1}
TEST_NAME="XSK KSELFTESTS"

params=("-N" "-B")
execxdpxceiver params
execxdpxceiver

retval=$?
test_status $retval "${TEST_NAME}"
+272 −108
Original line number Diff line number Diff line
@@ -18,12 +18,7 @@
 * These selftests test AF_XDP SKB and Native/DRV modes using veth
 * Virtual Ethernet interfaces.
 *
 * The following tests are run:
 *
 * 1. AF_XDP SKB mode
 *    Generic mode XDP is driver independent, used when the driver does
 *    not have support for XDP. Works on any netdevice using sockets and
 *    generic XDP path. XDP hook from netif_receive_skb().
 * For each mode, the following tests are run:
 *    a. nopoll - soft-irq processing
 *    b. poll - using poll() syscall
 *    c. Socket Teardown
@@ -33,19 +28,21 @@
 *       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
 *    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.
 *
 * 2. AF_XDP DRV/Native mode
 *    Works on any netdevice with XDP_REDIRECT support, driver dependent. Processes
 *    packets before SKB allocation. Provides better performance than SKB. Driver
 *    hook available just after DMA of buffer descriptor.
 *    a. nopoll
 *    b. poll
 *    c. Socket Teardown
 *    d. Bi-directional sockets
 *    - Only copy mode is supported because veth does not currently support
 *      zero-copy mode
 *
 * Total tests: 8
 * Total tests: 10
 *
 * Flow:
 * -----
@@ -58,7 +55,7 @@
 * - Rx thread verifies if all 10k packets were received and delivered in-order,
 *   and have the right content
 *
 * Enable/disable debug mode:
 * Enable/disable packet dump mode:
 * --------------------------
 * 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")
@@ -98,17 +95,24 @@ typedef __u16 __sum16;

static void __exit_with_error(int error, const char *file, const char *func, int line)
{
	if (configured_mode == TEST_MODE_UNCONFIGURED) {
		ksft_exit_fail_msg
		("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error, strerror(error));
	} else {
		ksft_test_result_fail
		("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error, strerror(error));
		ksft_exit_xfail();
	}
}

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

#define print_ksft_result(void)\
	(ksft_test_result_pass("PASS: %s %s %s%s\n", uut ? "DRV" : "SKB", opt_poll ? "POLL" :\
			       "NOPOLL", opt_teardown ? "Socket Teardown" : "",\
			       opt_bidi ? "Bi-directional Sockets" : ""))
	(ksft_test_result_pass("PASS: %s %s %s%s%s\n", configured_mode ? "DRV" : "SKB",\
			       test_type == TEST_TYPE_POLL ? "POLL" : "NOPOLL",\
			       test_type == TEST_TYPE_TEARDOWN ? "Socket Teardown" : "",\
			       test_type == TEST_TYPE_BIDI ? "Bi-directional Sockets" : "",\
			       test_type == TEST_TYPE_STATS ? "Stats" : ""))

static void pthread_init_mutex(void)
{
@@ -270,13 +274,20 @@ static void gen_eth_frame(struct xsk_umem_info *umem, u64 addr)
static void xsk_configure_umem(struct ifobject *data, void *buffer, u64 size)
{
	int ret;
	struct xsk_umem_config cfg = {
		.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
		.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
		.frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE,
		.frame_headroom = frame_headroom,
		.flags = XSK_UMEM__DEFAULT_FLAGS
	};

	data->umem = calloc(1, sizeof(struct xsk_umem_info));
	if (!data->umem)
		exit_with_error(errno);

	ret = xsk_umem__create(&data->umem->umem, buffer, size,
			       &data->umem->fq, &data->umem->cq, NULL);
			       &data->umem->fq, &data->umem->cq, &cfg);
	if (ret)
		exit_with_error(ret);

@@ -308,13 +319,13 @@ static int xsk_configure_socket(struct ifobject *ifobject)
		exit_with_error(errno);

	ifobject->xsk->umem = ifobject->umem;
	cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
	cfg.rx_size = rxqsize;
	cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
	cfg.libbpf_flags = 0;
	cfg.xdp_flags = opt_xdp_flags;
	cfg.bind_flags = opt_xdp_bind_flags;
	cfg.xdp_flags = xdp_flags;
	cfg.bind_flags = xdp_bind_flags;

	if (!opt_bidi) {
	if (test_type != TEST_TYPE_BIDI) {
		rxr = (ifobject->fv.vector == rx) ? &ifobject->xsk->rx : NULL;
		txr = (ifobject->fv.vector == tx) ? &ifobject->xsk->tx : NULL;
	} else {
@@ -334,13 +345,8 @@ static int xsk_configure_socket(struct ifobject *ifobject)
static struct option long_options[] = {
	{"interface", required_argument, 0, 'i'},
	{"queue", optional_argument, 0, 'q'},
	{"poll", no_argument, 0, 'p'},
	{"xdp-skb", no_argument, 0, 'S'},
	{"xdp-native", no_argument, 0, 'N'},
	{"copy", no_argument, 0, 'c'},
	{"tear-down", no_argument, 0, 'T'},
	{"bidi", optional_argument, 0, 'B'},
	{"debug", optional_argument, 0, 'D'},
	{"dump-pkts", optional_argument, 0, 'D'},
	{"verbose", no_argument, 0, 'v'},
	{"tx-pkt-count", optional_argument, 0, 'C'},
	{0, 0, 0, 0}
};
@@ -352,13 +358,8 @@ static void usage(const char *prog)
	    "  Options:\n"
	    "  -i, --interface      Use interface\n"
	    "  -q, --queue=n        Use queue n (default 0)\n"
	    "  -p, --poll           Use poll syscall\n"
	    "  -S, --xdp-skb=n      Use XDP SKB mode\n"
	    "  -N, --xdp-native=n   Enforce XDP DRV (native) mode\n"
	    "  -c, --copy           Force copy mode\n"
	    "  -T, --tear-down      Tear down sockets by repeatedly recreating them\n"
	    "  -B, --bidi           Bi-directional sockets test\n"
	    "  -D, --debug          Debug mode - dump packets L2 - L5\n"
	    "  -D, --dump-pkts      Dump packets L2 - L5\n"
	    "  -v, --verbose        Verbose output\n"
	    "  -C, --tx-pkt-count=n Number of packets to send\n";
	ksft_print_msg(str, prog);
}
@@ -392,7 +393,7 @@ static void *nsswitchthread(void *args)
			ksft_test_result_fail("ERROR: [%s] interface \"%s\" does not exist\n",
					      __func__, ifdict[targs->idx]->ifname);
		} else {
			ksft_print_msg("Interface found: %s\n", ifdict[targs->idx]->ifname);
			print_verbose("Interface found: %s\n", ifdict[targs->idx]->ifname);
			targs->retptr = true;
		}
	}
@@ -422,7 +423,7 @@ static int validate_interfaces(void)
			pthread_join(ns_thread, NULL);

			if (targs->retptr)
				ksft_print_msg("NS switched: %s\n", ifdict[i]->nsname);
				print_verbose("NS switched: %s\n", ifdict[i]->nsname);

			free(targs);
		} else {
@@ -432,7 +433,7 @@ static int validate_interfaces(void)
				    ("ERROR: interface \"%s\" does not exist\n", ifdict[i]->ifname);
				ret = false;
			} else {
				ksft_print_msg("Interface found: %s\n", ifdict[i]->ifname);
				print_verbose("Interface found: %s\n", ifdict[i]->ifname);
			}
		}
	}
@@ -446,7 +447,7 @@ static void parse_command_line(int argc, char **argv)
	opterr = 0;

	for (;;) {
		c = getopt_long(argc, argv, "i:q:pSNcTBDC:", long_options, &option_index);
		c = getopt_long(argc, argv, "i:q:DC:v", long_options, &option_index);

		if (c == -1)
			break;
@@ -469,40 +470,26 @@ static void parse_command_line(int argc, char **argv)
		case 'q':
			opt_queue = atoi(optarg);
			break;
		case 'p':
			opt_poll = 1;
			break;
		case 'S':
			opt_xdp_flags |= XDP_FLAGS_SKB_MODE;
			opt_xdp_bind_flags |= XDP_COPY;
			uut = ORDER_CONTENT_VALIDATE_XDP_SKB;
			break;
		case 'N':
			opt_xdp_flags |= XDP_FLAGS_DRV_MODE;
			opt_xdp_bind_flags |= XDP_COPY;
			uut = ORDER_CONTENT_VALIDATE_XDP_DRV;
			break;
		case 'c':
			opt_xdp_bind_flags |= XDP_COPY;
			break;
		case 'T':
			opt_teardown = 1;
			break;
		case 'B':
			opt_bidi = 1;
			break;
		case 'D':
			debug_pkt_dump = 1;
			break;
		case 'C':
			opt_pkt_count = atoi(optarg);
			break;
		case 'v':
			opt_verbose = 1;
			break;
		default:
			usage(basename(argv[0]));
			ksft_exit_xfail();
		}
	}

	if (!opt_pkt_count) {
		print_verbose("No tx-pkt-count specified, using default %u\n", DEFAULT_PKT_CNT);
		opt_pkt_count = DEFAULT_PKT_CNT;
	}

	if (!validate_interfaces()) {
		usage(basename(argv[0]));
		ksft_exit_xfail();
@@ -599,6 +586,8 @@ static void tx_only(struct xsk_socket_info *xsk, u32 *frameptr, int batch_size)
{
	u32 idx;
	unsigned int i;
	bool tx_invalid_test = stat_test_type == STAT_TEST_TX_INVALID;
	u32 len = tx_invalid_test ? XSK_UMEM__DEFAULT_FRAME_SIZE + 1 : PKT_SIZE;

	while (xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx) < batch_size)
		complete_tx_only(xsk, batch_size);
@@ -607,11 +596,16 @@ static void tx_only(struct xsk_socket_info *xsk, u32 *frameptr, int batch_size)
		struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i);

		tx_desc->addr = (*frameptr + i) << XSK_UMEM__DEFAULT_FRAME_SHIFT;
		tx_desc->len = PKT_SIZE;
		tx_desc->len = len;
	}

	xsk_ring_prod__submit(&xsk->tx, batch_size);
	if (!tx_invalid_test) {
		xsk->outstanding_tx += batch_size;
	} else {
		if (!NEED_WAKEUP || xsk_ring_prod__needs_wakeup(&xsk->tx))
			kick_tx(xsk);
	}
	*frameptr += batch_size;
	*frameptr %= num_frames;
	complete_tx_only(xsk, batch_size);
@@ -654,7 +648,7 @@ static void tx_only_all(struct ifobject *ifobject)
	while ((opt_pkt_count && pkt_cnt < opt_pkt_count) || !opt_pkt_count) {
		int batch_size = get_batch_size(pkt_cnt);

		if (opt_poll) {
		if (test_type == TEST_TYPE_POLL) {
			ret = poll(fds, 1, POLL_TMOUT);
			if (ret <= 0)
				continue;
@@ -714,7 +708,7 @@ static void worker_pkt_dump(void)
		int payload = *((uint32_t *)(pkt_buf[iter]->payload + PKT_HDR_SIZE));

		if (payload == EOT) {
			ksft_print_msg("End-of-transmission frame received\n");
			print_verbose("End-of-transmission frame received\n");
			fprintf(stdout, "---------------------------------------\n");
			break;
		}
@@ -723,6 +717,48 @@ static void worker_pkt_dump(void)
	}
}

static void worker_stats_validate(struct ifobject *ifobject)
{
	struct xdp_statistics stats;
	socklen_t optlen;
	int err;
	struct xsk_socket *xsk = stat_test_type == STAT_TEST_TX_INVALID ?
							ifdict[!ifobject->ifdict_index]->xsk->xsk :
							ifobject->xsk->xsk;
	int fd = xsk_socket__fd(xsk);
	unsigned long xsk_stat = 0, expected_stat = opt_pkt_count;

	sigvar = 0;

	optlen = sizeof(stats);
	err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen);
	if (err)
		return;

	if (optlen == sizeof(struct xdp_statistics)) {
		switch (stat_test_type) {
		case STAT_TEST_RX_DROPPED:
			xsk_stat = stats.rx_dropped;
			break;
		case STAT_TEST_TX_INVALID:
			xsk_stat = stats.tx_invalid_descs;
			break;
		case STAT_TEST_RX_FULL:
			xsk_stat = stats.rx_ring_full;
			expected_stat -= RX_FULL_RXQSIZE;
			break;
		case STAT_TEST_RX_FILL_EMPTY:
			xsk_stat = stats.rx_fill_ring_empty_descs;
			break;
		default:
			break;
		}

		if (xsk_stat == expected_stat)
			sigvar = 1;
	}
}

static void worker_pkt_validate(void)
{
	u32 payloadseqnum = -2;
@@ -746,7 +782,7 @@ static void worker_pkt_validate(void)
			}

			if (payloadseqnum == EOT) {
				ksft_print_msg("End-of-transmission frame received: PASS\n");
				print_verbose("End-of-transmission frame received: PASS\n");
				sigvar = 1;
				break;
			}
@@ -836,7 +872,7 @@ static void *worker_testapp_validate(void *arg)
			usleep(USLEEP_MAX);
		}

		ksft_print_msg("Interface [%s] vector [Tx]\n", ifobject->ifname);
		print_verbose("Interface [%s] vector [Tx]\n", ifobject->ifname);
		for (int i = 0; i < num_frames; i++) {
			/*send EOT frame */
			if (i == (num_frames - 1))
@@ -850,7 +886,7 @@ static void *worker_testapp_validate(void *arg)
			gen_eth_frame(ifobject->umem, i * XSK_UMEM__DEFAULT_FRAME_SIZE);
		}

		ksft_print_msg("Sending %d packets on interface %s\n",
		print_verbose("Sending %d packets on interface %s\n",
			       (opt_pkt_count - 1), ifobject->ifname);
		tx_only_all(ifobject);
	} else if (ifobject->fv.vector == rx) {
@@ -860,7 +896,8 @@ static void *worker_testapp_validate(void *arg)
		if (!bidi_pass)
			thread_common_ops(ifobject, bufs, &sync_mutex_tx, &spinning_rx);

		ksft_print_msg("Interface [%s] vector [Rx]\n", ifobject->ifname);
		print_verbose("Interface [%s] vector [Rx]\n", ifobject->ifname);
		if (stat_test_type != STAT_TEST_RX_FILL_EMPTY)
			xsk_populate_fill_ring(ifobject->umem);

		TAILQ_INIT(&head);
@@ -878,26 +915,32 @@ static void *worker_testapp_validate(void *arg)
		pthread_mutex_unlock(&sync_mutex);

		while (1) {
			if (opt_poll) {
			if (test_type == TEST_TYPE_POLL) {
				ret = poll(fds, 1, POLL_TMOUT);
				if (ret <= 0)
					continue;
			}

			if (test_type != TEST_TYPE_STATS) {
				rx_pkt(ifobject->xsk, fds);
				worker_pkt_validate();
			} else {
				worker_stats_validate(ifobject);
			}

			if (sigvar)
				break;
		}

		ksft_print_msg("Received %d packets on interface %s\n",
		if (test_type != TEST_TYPE_STATS)
			print_verbose("Received %d packets on interface %s\n",
				pkt_counter, ifobject->ifname);

		if (opt_teardown)
			ksft_print_msg("Destroying socket\n");
		if (test_type == TEST_TYPE_TEARDOWN)
			print_verbose("Destroying socket\n");
	}

	if (!opt_bidi || bidi_pass) {
	if ((test_type != TEST_TYPE_BIDI) || bidi_pass) {
		xsk_socket__delete(ifobject->xsk->xsk);
		(void)xsk_umem__delete(ifobject->umem->umem);
	}
@@ -907,14 +950,15 @@ static void *worker_testapp_validate(void *arg)
static void testapp_validate(void)
{
	struct timespec max_wait = { 0, 0 };
	bool bidi = test_type == TEST_TYPE_BIDI;

	pthread_attr_init(&attr);
	pthread_attr_setstacksize(&attr, THREAD_STACK);

	if (opt_bidi && bidi_pass) {
	if ((test_type == TEST_TYPE_BIDI) && bidi_pass) {
		pthread_init_mutex();
		if (!switching_notify) {
			ksft_print_msg("Switching Tx/Rx vectors\n");
			print_verbose("Switching Tx/Rx vectors\n");
			switching_notify++;
		}
	}
@@ -922,10 +966,10 @@ static void testapp_validate(void)
	pthread_mutex_lock(&sync_mutex);

	/*Spawn RX thread */
	if (!opt_bidi || !bidi_pass) {
	if (!bidi || !bidi_pass) {
		if (pthread_create(&t0, &attr, worker_testapp_validate, ifdict[1]))
			exit_with_error(errno);
	} else if (opt_bidi && bidi_pass) {
	} else if (bidi && bidi_pass) {
		/*switch Tx/Rx vectors */
		ifdict[0]->fv.vector = rx;
		if (pthread_create(&t0, &attr, worker_testapp_validate, ifdict[0]))
@@ -942,10 +986,10 @@ static void testapp_validate(void)
	pthread_mutex_unlock(&sync_mutex);

	/*Spawn TX thread */
	if (!opt_bidi || !bidi_pass) {
	if (!bidi || !bidi_pass) {
		if (pthread_create(&t1, &attr, worker_testapp_validate, ifdict[0]))
			exit_with_error(errno);
	} else if (opt_bidi && bidi_pass) {
	} else if (bidi && bidi_pass) {
		/*switch Tx/Rx vectors */
		ifdict[1]->fv.vector = tx;
		if (pthread_create(&t1, &attr, worker_testapp_validate, ifdict[1]))
@@ -964,19 +1008,46 @@ static void testapp_validate(void)
		free(pkt_buf);
	}

	if (!opt_teardown && !opt_bidi)
	if (!(test_type == TEST_TYPE_TEARDOWN) && !bidi && !(test_type == TEST_TYPE_STATS))
		print_ksft_result();
}

static void testapp_sockets(void)
{
	for (int i = 0; i < (opt_teardown ? MAX_TEARDOWN_ITER : MAX_BIDI_ITER); i++) {
	for (int i = 0; i < ((test_type == TEST_TYPE_TEARDOWN) ? MAX_TEARDOWN_ITER : MAX_BIDI_ITER);
	     i++) {
		pkt_counter = 0;
		prev_pkt = -1;
		sigvar = 0;
		ksft_print_msg("Creating socket\n");
		print_verbose("Creating socket\n");
		testapp_validate();
		test_type == TEST_TYPE_BIDI ? bidi_pass++ : bidi_pass;
	}

	print_ksft_result();
}

static void testapp_stats(void)
{
	for (int i = 0; i < STAT_TEST_TYPE_MAX; i++) {
		stat_test_type = i;

		/* reset defaults */
		rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
		frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;

		switch (stat_test_type) {
		case STAT_TEST_RX_DROPPED:
			frame_headroom = XSK_UMEM__DEFAULT_FRAME_SIZE -
						XDP_PACKET_HEADROOM - 1;
			break;
		case STAT_TEST_RX_FULL:
			rxqsize = RX_FULL_RXQSIZE;
			break;
		default:
			break;
		}
		testapp_validate();
		opt_bidi ? bidi_pass++ : bidi_pass;
	}

	print_ksft_result();
@@ -1003,6 +1074,104 @@ static void init_iface_config(struct ifaceconfigobj *ifaceconfig)
	ifdict[1]->src_port = ifaceconfig->dst_port;
}

static void *nsdisablemodethread(void *args)
{
	struct targs *targs = args;

	targs->retptr = false;

	if (switch_namespace(targs->idx)) {
		targs->retptr = bpf_set_link_xdp_fd(ifdict[targs->idx]->ifindex, -1, targs->flags);
	} else {
		targs->retptr = errno;
		print_verbose("Failed to switch namespace to %s\n", ifdict[targs->idx]->nsname);
	}

	pthread_exit(NULL);
}

static void disable_xdp_mode(int mode)
{
	int err = 0;
	__u32 flags = XDP_FLAGS_UPDATE_IF_NOEXIST | mode;
	char *mode_str = mode & XDP_FLAGS_SKB_MODE ? "skb" : "drv";

	for (int i = 0; i < MAX_INTERFACES; i++) {
		if (strcmp(ifdict[i]->nsname, "")) {
			struct targs *targs;

			targs = malloc(sizeof(*targs));
			memset(targs, 0, sizeof(*targs));
			if (!targs)
				exit_with_error(errno);

			targs->idx = i;
			targs->flags = flags;
			if (pthread_create(&ns_thread, NULL, nsdisablemodethread, targs))
				exit_with_error(errno);

			pthread_join(ns_thread, NULL);
			err = targs->retptr;
			free(targs);
		} else {
			err = bpf_set_link_xdp_fd(ifdict[i]->ifindex, -1, flags);
		}

		if (err) {
			print_verbose("Failed to disable %s mode on interface %s\n",
						mode_str, ifdict[i]->ifname);
			exit_with_error(err);
		}

		print_verbose("Disabled %s mode for interface: %s\n", mode_str, ifdict[i]->ifname);
		configured_mode = mode & XDP_FLAGS_SKB_MODE ? TEST_MODE_DRV : TEST_MODE_SKB;
	}
}

static void run_pkt_test(int mode, int type)
{
	test_type = type;

	/* reset defaults after potential previous test */
	xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
	pkt_counter = 0;
	switching_notify = 0;
	bidi_pass = 0;
	prev_pkt = -1;
	ifdict[0]->fv.vector = tx;
	ifdict[1]->fv.vector = rx;
	sigvar = 0;
	stat_test_type = -1;
	rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
	frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;

	switch (mode) {
	case (TEST_MODE_SKB):
		if (configured_mode == TEST_MODE_DRV)
			disable_xdp_mode(XDP_FLAGS_DRV_MODE);
		xdp_flags |= XDP_FLAGS_SKB_MODE;
		break;
	case (TEST_MODE_DRV):
		if (configured_mode == TEST_MODE_SKB)
			disable_xdp_mode(XDP_FLAGS_SKB_MODE);
		xdp_flags |= XDP_FLAGS_DRV_MODE;
		break;
	default:
		break;
	}

	pthread_init_mutex();

	if (test_type == TEST_TYPE_STATS)
		testapp_stats();
	else if ((test_type != TEST_TYPE_TEARDOWN) && (test_type != TEST_TYPE_BIDI))
		testapp_validate();
	else
		testapp_sockets();

	pthread_destroy_mutex();
}

int main(int argc, char **argv)
{
	struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY };
@@ -1016,6 +1185,7 @@ int main(int argc, char **argv)
	const char *IP2 = "192.168.100.161";
	u16 UDP_DST_PORT = 2020;
	u16 UDP_SRC_PORT = 2121;
	int i, j;

	ifaceconfig = malloc(sizeof(struct ifaceconfigobj));
	memcpy(ifaceconfig->dst_mac, MAC1, ETH_ALEN);
@@ -1041,24 +1211,18 @@ int main(int argc, char **argv)

	init_iface_config(ifaceconfig);

	pthread_init_mutex();
	disable_xdp_mode(XDP_FLAGS_DRV_MODE);

	ksft_set_plan(1);
	ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);

	if (!opt_teardown && !opt_bidi) {
		testapp_validate();
	} else if (opt_teardown && opt_bidi) {
		ksft_test_result_fail("ERROR: parameters -T and -B cannot be used together\n");
		ksft_exit_xfail();
	} else {
		testapp_sockets();
	for (i = 0; i < TEST_MODE_MAX; i++) {
		for (j = 0; j < TEST_TYPE_MAX; j++)
			run_pkt_test(i, j);
	}

	for (int i = 0; i < MAX_INTERFACES; i++)
		free(ifdict[i]);

	pthread_destroy_mutex();

	ksft_exit_pass();

	return 0;
+42 −15
Original line number Diff line number Diff line
@@ -41,33 +41,59 @@
#define BATCH_SIZE 64
#define POLL_TMOUT 1000
#define NEED_WAKEUP true
#define DEFAULT_PKT_CNT 10000
#define RX_FULL_RXQSIZE 32

#define print_verbose(x...) do { if (opt_verbose) ksft_print_msg(x); } while (0)

typedef __u32 u32;
typedef __u16 u16;
typedef __u8 u8;

enum TESTS {
	ORDER_CONTENT_VALIDATE_XDP_SKB = 0,
	ORDER_CONTENT_VALIDATE_XDP_DRV = 1,
enum TEST_MODES {
	TEST_MODE_UNCONFIGURED = -1,
	TEST_MODE_SKB,
	TEST_MODE_DRV,
	TEST_MODE_MAX
};

enum TEST_TYPES {
	TEST_TYPE_NOPOLL,
	TEST_TYPE_POLL,
	TEST_TYPE_TEARDOWN,
	TEST_TYPE_BIDI,
	TEST_TYPE_STATS,
	TEST_TYPE_MAX
};

u8 uut;
u8 debug_pkt_dump;
u32 num_frames;
u8 switching_notify;
u8 bidi_pass;
enum STAT_TEST_TYPES {
	STAT_TEST_RX_DROPPED,
	STAT_TEST_TX_INVALID,
	STAT_TEST_RX_FULL,
	STAT_TEST_RX_FILL_EMPTY,
	STAT_TEST_TYPE_MAX
};

static int configured_mode = TEST_MODE_UNCONFIGURED;
static u8 debug_pkt_dump;
static u32 num_frames;
static u8 switching_notify;
static u8 bidi_pass;
static int test_type;

static u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
static int opt_queue;
static int opt_pkt_count;
static int opt_poll;
static int opt_teardown;
static int opt_bidi;
static u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP;
static u8 opt_verbose;

static u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
static u32 xdp_bind_flags = XDP_USE_NEED_WAKEUP | XDP_COPY;
static u8 pkt_data[XSK_UMEM__DEFAULT_FRAME_SIZE];
static u32 pkt_counter;
static u32 prev_pkt = -1;
static long prev_pkt = -1;
static int sigvar;
static int stat_test_type;
static u32 rxqsize;
static u32 frame_headroom;

struct xsk_umem_info {
	struct xsk_ring_prod fq;
@@ -137,8 +163,9 @@ pthread_t t0, t1, ns_thread;
pthread_attr_t attr;

struct targs {
	bool retptr;
	u8 retptr;
	int idx;
	u32 flags;
};

TAILQ_HEAD(head_s, pkt) head = TAILQ_HEAD_INITIALIZER(head);
+3 −27
Original line number Diff line number Diff line
@@ -82,24 +82,21 @@ clear_configs()
{
	if [ $(ip netns show | grep $3 &>/dev/null; echo $?;) == 0 ]; then
		[ $(ip netns exec $3 ip link show $2 &>/dev/null; echo $?;) == 0 ] &&
			{ echo "removing link $1:$2"; ip netns exec $3 ip link del $2; }
		echo "removing ns $3"
			{ ip netns exec $3 ip link del $2; }
		ip netns del $3
	fi
	#Once we delete a veth pair node, the entire veth pair is removed,
	#this is just to be cautious just incase the NS does not exist then
	#veth node inside NS won't get removed so we explicitly remove it
	[ $(ip link show $1 &>/dev/null; echo $?;) == 0 ] &&
		{ echo "removing link $1"; ip link del $1; }
		{ ip link del $1; }
	if [ -f ${SPECFILE} ]; then
		echo "removing spec file:" ${SPECFILE}
		rm -f ${SPECFILE}
	fi
}

cleanup_exit()
{
	echo "cleaning up..."
	clear_configs $1 $2 $3
}

@@ -108,28 +105,7 @@ validate_ip_utility()
	[ ! $(type -P ip) ] && { echo "'ip' not found. Skipping tests."; test_exit $ksft_skip 1; }
}

vethXDPgeneric()
{
	ip link set dev $1 xdpdrv off
	ip netns exec $3 ip link set dev $2 xdpdrv off
}

vethXDPnative()
{
	ip link set dev $1 xdpgeneric off
	ip netns exec $3 ip link set dev $2 xdpgeneric off
}

execxdpxceiver()
{
	local -a 'paramkeys=("${!'"$1"'[@]}")' copy
	paramkeysstr=${paramkeys[*]}

	for index in $paramkeysstr;
		do
			current=$1"[$index]"
			copy[$index]=${!current}
		done

	./${XSKOBJ} -i ${VETH0} -i ${VETH1},${NS1} ${copy[*]} -C ${NUMPKTS}
	./${XSKOBJ} -i ${VETH0} -i ${VETH1},${NS1} -C ${NUMPKTS} ${VERBOSE_ARG} ${DUMP_PKTS_ARG}
}