Commit 7d789bd0 authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'selftests: xsk: various simplifications'

Magnus Karlsson says:

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

This patch set mainly contains various simplifications to the xsk
selftests. The only exception is the introduction of packet streams
that describes what the Tx process should send and what the Rx process
should receive. If it receives anything else, the test fails. This
mechanism can be used to produce tests were all packets are not
received by the Rx thread or modified in some way. An example of this
is if an XDP program does XDP_PASS on some of the packets.

This patch set will be followed by another patch set that implements a
new structure that will facilitate adding new tests. A couple of new
tests will also be included in that patch set.

v2 -> v3:

* Reworked patch 12 so that it now has functions for creating and
  destroying ifobjects. Simplifies the code. [Maciej]
* The packet stream now allocates the supplied buffer array length,
  instead of the default one. [Maciej]
* pkt_stream_get_pkt() now returns NULL when indexing a non-existing
  packet. [Maciej]
* pkt_validate() is now is_pkt_valid(). [Maciej]
* Slowed down packet sending speed even more in patch 11 so that slow
  systems do not silenty drop packets in skb mode.

v1 -> v2:

* Dropped the patch with per process limit changes as it is not needed
  [Yonghong]
* Improved the commit message of patch 1 [Yonghong]
* Fixed a spelling error in patch 9

Thanks: Magnus
====================
Acked-by: default avatarMaciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 35cba298 33a6bef8
......@@ -63,14 +63,11 @@
# ----------------
# Must run with CAP_NET_ADMIN capability.
#
# Run (full color-coded output):
# sudo ./test_xsk.sh -c
# Run:
# sudo ./test_xsk.sh
#
# If running from kselftests:
# sudo make colorconsole=1 run_tests
#
# Run (full output without color-coding):
# sudo ./test_xsk.sh
# sudo make run_tests
#
# Run with verbose output:
# sudo ./test_xsk.sh -v
......@@ -83,7 +80,6 @@
while getopts "cvD" flag
do
case "${flag}" in
c) colorconsole=1;;
v) verbose=1;;
D) dump_pkts=1;;
esac
......
......@@ -70,7 +70,6 @@
#include <errno.h>
#include <getopt.h>
#include <asm/barrier.h>
typedef __u16 __sum16;
#include <linux/if_link.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
......@@ -106,14 +105,9 @@ static const u16 UDP_PORT2 = 2121;
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();
}
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__)
......@@ -126,7 +120,7 @@ static void __exit_with_error(int error, const char *file, const char *func, int
test_type == TEST_TYPE_STATS ? "Stats" : "",\
test_type == TEST_TYPE_BPF_RES ? "BPF RES" : ""))
static void *memset32_htonl(void *dest, u32 val, u32 size)
static void memset32_htonl(void *dest, u32 val, u32 size)
{
u32 *ptr = (u32 *)dest;
int i;
......@@ -135,11 +129,6 @@ static void *memset32_htonl(void *dest, u32 val, u32 size)
for (i = 0; i < (size & (~0x3)); i += 4)
ptr[i >> 2] = val;
for (; i < size; i++)
((char *)dest)[i] = ((char *)&val)[i & 3];
return dest;
}
/*
......@@ -230,13 +219,13 @@ static void gen_ip_hdr(struct ifobject *ifobject, struct iphdr *ip_hdr)
ip_hdr->check = 0;
}
static void gen_udp_hdr(struct generic_data *data, struct ifobject *ifobject,
static void gen_udp_hdr(u32 payload, void *pkt, struct ifobject *ifobject,
struct udphdr *udp_hdr)
{
udp_hdr->source = htons(ifobject->src_port);
udp_hdr->dest = htons(ifobject->dst_port);
udp_hdr->len = htons(UDP_PKT_SIZE);
memset32_htonl(pkt_data + PKT_HDR_SIZE, htonl(data->seqnum), UDP_PKT_DATA_SIZE);
memset32_htonl(pkt + PKT_HDR_SIZE, payload, UDP_PKT_DATA_SIZE);
}
static void gen_udp_csum(struct udphdr *udp_hdr, struct iphdr *ip_hdr)
......@@ -246,12 +235,7 @@ static void gen_udp_csum(struct udphdr *udp_hdr, struct iphdr *ip_hdr)
udp_csum(ip_hdr->saddr, ip_hdr->daddr, UDP_PKT_SIZE, IPPROTO_UDP, (u16 *)udp_hdr);
}
static void gen_eth_frame(struct xsk_umem_info *umem, u64 addr)
{
memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data, PKT_SIZE);
}
static void xsk_configure_umem(struct ifobject *data, void *buffer, int idx)
static void xsk_configure_umem(struct ifobject *data, void *buffer, u64 size, int idx)
{
struct xsk_umem_config cfg = {
.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
......@@ -260,7 +244,6 @@ static void xsk_configure_umem(struct ifobject *data, void *buffer, int idx)
.frame_headroom = frame_headroom,
.flags = XSK_UMEM__DEFAULT_FLAGS
};
int size = num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE;
struct xsk_umem_info *umem;
int ret;
......@@ -271,7 +254,7 @@ static void xsk_configure_umem(struct ifobject *data, void *buffer, int idx)
ret = xsk_umem__create(&umem->umem, buffer, size,
&umem->fq, &umem->cq, &cfg);
if (ret)
exit_with_error(ret);
exit_with_error(-ret);
umem->buffer = buffer;
......@@ -285,7 +268,7 @@ static void xsk_populate_fill_ring(struct xsk_umem_info *umem)
ret = xsk_ring_prod__reserve(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx);
if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS)
exit_with_error(ret);
exit_with_error(-ret);
for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++)
*xsk_ring_prod__fill_addr(&umem->fq, idx++) = i * XSK_UMEM__DEFAULT_FRAME_SIZE;
xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS);
......@@ -333,20 +316,19 @@ static struct option long_options[] = {
{"queue", optional_argument, 0, 'q'},
{"dump-pkts", optional_argument, 0, 'D'},
{"verbose", no_argument, 0, 'v'},
{"tx-pkt-count", optional_argument, 0, 'C'},
{0, 0, 0, 0}
};
static void usage(const char *prog)
{
const char *str =
" Usage: %s [OPTIONS]\n"
" Options:\n"
" -i, --interface Use interface\n"
" -q, --queue=n Use queue n (default 0)\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";
" Usage: %s [OPTIONS]\n"
" Options:\n"
" -i, --interface Use interface\n"
" -q, --queue=n Use queue n (default 0)\n"
" -D, --dump-pkts Dump packets L2 - L5\n"
" -v, --verbose Verbose output\n";
ksft_print_msg(str, prog);
}
......@@ -392,7 +374,7 @@ static void parse_command_line(int argc, char **argv)
opterr = 0;
for (;;) {
c = getopt_long(argc, argv, "i:DC:v", long_options, &option_index);
c = getopt_long(argc, argv, "i:Dv", long_options, &option_index);
if (c == -1)
break;
......@@ -413,13 +395,10 @@ static void parse_command_line(int argc, char **argv)
interface_index++;
break;
case 'D':
debug_pkt_dump = 1;
break;
case 'C':
opt_pkt_count = atoi(optarg);
opt_pkt_dump = true;
break;
case 'v':
opt_verbose = 1;
opt_verbose = true;
break;
default:
usage(basename(argv[0]));
......@@ -427,17 +406,143 @@ static void parse_command_line(int argc, char **argv)
}
}
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();
}
}
static struct pkt *pkt_stream_get_pkt(struct pkt_stream *pkt_stream, u32 pkt_nb)
{
if (pkt_nb >= pkt_stream->nb_pkts)
return NULL;
return &pkt_stream->pkts[pkt_nb];
}
static struct pkt_stream *pkt_stream_generate(u32 nb_pkts, u32 pkt_len)
{
struct pkt_stream *pkt_stream;
u32 i;
pkt_stream = malloc(sizeof(*pkt_stream));
if (!pkt_stream)
exit_with_error(ENOMEM);
pkt_stream->pkts = calloc(nb_pkts, sizeof(*pkt_stream->pkts));
if (!pkt_stream->pkts)
exit_with_error(ENOMEM);
pkt_stream->nb_pkts = nb_pkts;
for (i = 0; i < nb_pkts; i++) {
pkt_stream->pkts[i].addr = (i % num_frames) * XSK_UMEM__DEFAULT_FRAME_SIZE;
pkt_stream->pkts[i].len = pkt_len;
pkt_stream->pkts[i].payload = i;
}
return pkt_stream;
}
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;
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;
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);
return pkt;
}
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");
}
static bool is_pkt_valid(struct pkt *pkt, void *buffer, const struct xdp_desc *desc)
{
void *data = xsk_umem__get_data(buffer, desc->addr);
struct iphdr *iphdr = (struct iphdr *)(data + sizeof(struct ethhdr));
if (!pkt) {
ksft_test_result_fail("ERROR: [%s] too many packets received\n", __func__);
return false;
}
if (iphdr->version == IP_PKT_VER && iphdr->tos == IP_PKT_TOS) {
u32 seqnum = ntohl(*((u32 *)(data + PKT_HDR_SIZE)));
if (opt_pkt_dump && test_type != TEST_TYPE_STATS)
pkt_dump(data, PKT_SIZE);
if (pkt->len != desc->len) {
ksft_test_result_fail
("ERROR: [%s] expected length [%d], got length [%d]\n",
__func__, pkt->len, desc->len);
return false;
}
if (pkt->payload != seqnum) {
ksft_test_result_fail
("ERROR: [%s] expected seqnum [%d], got seqnum [%d]\n",
__func__, pkt->payload, seqnum);
return false;
}
} else {
ksft_print_msg("Invalid frame received: ");
ksft_print_msg("[IP_PKT_VER: %02X], [IP_PKT_TOS: %02X]\n", iphdr->version,
iphdr->tos);
return false;
}
return true;
}
static void kick_tx(struct xsk_socket_info *xsk)
{
int ret;
......@@ -448,7 +553,7 @@ static void kick_tx(struct xsk_socket_info *xsk)
exit_with_error(errno);
}
static void complete_tx_only(struct xsk_socket_info *xsk, int batch_size)
static void complete_pkts(struct xsk_socket_info *xsk, int batch_size)
{
unsigned int rcvd;
u32 idx;
......@@ -463,133 +568,108 @@ static void complete_tx_only(struct xsk_socket_info *xsk, int batch_size)
if (rcvd) {
xsk_ring_cons__release(&xsk->umem->cq, rcvd);
xsk->outstanding_tx -= rcvd;
xsk->tx_npkts += rcvd;
}
}
static void rx_pkt(struct xsk_socket_info *xsk, struct pollfd *fds)
static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info *xsk,
struct pollfd *fds)
{
unsigned int rcvd, i;
u32 idx_rx = 0, idx_fq = 0;
u32 idx_rx = 0, idx_fq = 0, rcvd, i, pkt_count = 0;
struct pkt *pkt;
int ret;
rcvd = xsk_ring_cons__peek(&xsk->rx, BATCH_SIZE, &idx_rx);
if (!rcvd) {
if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) {
ret = poll(fds, 1, POLL_TMOUT);
if (ret < 0)
exit_with_error(ret);
pkt = pkt_stream_get_pkt(pkt_stream, pkt_count++);
while (pkt) {
rcvd = xsk_ring_cons__peek(&xsk->rx, BATCH_SIZE, &idx_rx);
if (!rcvd) {
if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) {
ret = poll(fds, 1, POLL_TMOUT);
if (ret < 0)
exit_with_error(-ret);
}
continue;
}
return;
}
ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
while (ret != rcvd) {
if (ret < 0)
exit_with_error(ret);
if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) {
ret = poll(fds, 1, POLL_TMOUT);
ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
while (ret != rcvd) {
if (ret < 0)
exit_with_error(ret);
exit_with_error(-ret);
if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) {
ret = poll(fds, 1, POLL_TMOUT);
if (ret < 0)
exit_with_error(-ret);
}
ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
}
ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
}
for (i = 0; i < rcvd; i++) {
u64 addr, orig;
addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr;
xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++);
orig = xsk_umem__extract_addr(addr);
addr = xsk_umem__add_offset_to_addr(addr);
pkt_node_rx = malloc(sizeof(struct pkt) + PKT_SIZE);
if (!pkt_node_rx)
exit_with_error(errno);
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;
pkt_node_rx->pkt_frame = malloc(PKT_SIZE);
if (!pkt_node_rx->pkt_frame)
exit_with_error(errno);
orig = xsk_umem__extract_addr(addr);
addr = xsk_umem__add_offset_to_addr(addr);
if (!is_pkt_valid(pkt, xsk->umem->buffer, desc))
return;
memcpy(pkt_node_rx->pkt_frame, xsk_umem__get_data(xsk->umem->buffer, addr),
PKT_SIZE);
TAILQ_INSERT_HEAD(&head, pkt_node_rx, pkt_nodes);
*xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = orig;
pkt = pkt_stream_get_pkt(pkt_stream, pkt_count++);
}
*xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = orig;
xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
xsk_ring_cons__release(&xsk->rx, rcvd);
}
xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
xsk_ring_cons__release(&xsk->rx, rcvd);
xsk->rx_npkts += rcvd;
}
static void tx_only(struct xsk_socket_info *xsk, u32 *frameptr, int batch_size)
static u32 __send_pkts(struct ifobject *ifobject, u32 pkt_nb)
{
u32 idx = 0;
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;
struct xsk_socket_info *xsk = ifobject->xsk;
u32 i, idx;
while (xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx) < batch_size)
complete_tx_only(xsk, batch_size);
while (xsk_ring_prod__reserve(&xsk->tx, BATCH_SIZE, &idx) < BATCH_SIZE)
complete_pkts(xsk, BATCH_SIZE);
for (i = 0; i < batch_size; i++) {
for (i = 0; i < BATCH_SIZE; i++) {
struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i);
struct pkt *pkt = pkt_generate(ifobject, pkt_nb);
tx_desc->addr = (*frameptr + i) << XSK_UMEM__DEFAULT_FRAME_SHIFT;
tx_desc->len = len;
}
if (!pkt)
break;
xsk_ring_prod__submit(&xsk->tx, batch_size);
if (!tx_invalid_test) {
xsk->outstanding_tx += batch_size;
} else if (xsk_ring_prod__needs_wakeup(&xsk->tx)) {
kick_tx(xsk);
tx_desc->addr = pkt->addr;
tx_desc->len = pkt->len;
pkt_nb++;
}
*frameptr += batch_size;
*frameptr %= num_frames;
complete_tx_only(xsk, batch_size);
}
static int get_batch_size(int pkt_cnt)
{
if (!opt_pkt_count)
return BATCH_SIZE;
if (pkt_cnt + BATCH_SIZE <= opt_pkt_count)
return BATCH_SIZE;
xsk_ring_prod__submit(&xsk->tx, i);
if (stat_test_type != STAT_TEST_TX_INVALID)
xsk->outstanding_tx += i;
else if (xsk_ring_prod__needs_wakeup(&xsk->tx))
kick_tx(xsk);
complete_pkts(xsk, i);
return opt_pkt_count - pkt_cnt;
return i;
}
static void complete_tx_only_all(struct ifobject *ifobject)
static void wait_for_tx_completion(struct xsk_socket_info *xsk)
{
bool pending;
do {
pending = false;
if (ifobject->xsk->outstanding_tx) {
complete_tx_only(ifobject->xsk, BATCH_SIZE);
pending = !!ifobject->xsk->outstanding_tx;
}
} while (pending);
while (xsk->outstanding_tx)
complete_pkts(xsk, BATCH_SIZE);
}
static void tx_only_all(struct ifobject *ifobject)
static void send_pkts(struct ifobject *ifobject)
{
struct pollfd fds[MAX_SOCKS] = { };
u32 frame_nb = 0;
int pkt_cnt = 0;
int ret;
u32 pkt_cnt = 0;
fds[0].fd = xsk_socket__fd(ifobject->xsk->xsk);
fds[0].events = POLLOUT;
while ((opt_pkt_count && pkt_cnt < opt_pkt_count) || !opt_pkt_count) {
int batch_size = get_batch_size(pkt_cnt);
while (pkt_cnt < ifobject->pkt_stream->nb_pkts) {
u32 sent;
if (test_type == TEST_TYPE_POLL) {
int ret;
ret = poll(fds, 1, POLL_TMOUT);
if (ret <= 0)
continue;
......@@ -598,78 +678,30 @@ static void tx_only_all(struct ifobject *ifobject)
continue;
}
tx_only(ifobject->xsk, &frame_nb, batch_size);
pkt_cnt += batch_size;
sent = __send_pkts(ifobject, pkt_cnt);
pkt_cnt += sent;
usleep(10);
}
if (opt_pkt_count)
complete_tx_only_all(ifobject);
wait_for_tx_completion(ifobject->xsk);
}
static void worker_pkt_dump(void)
{
struct ethhdr *ethhdr;
struct iphdr *iphdr;
struct udphdr *udphdr;
char s[128];
int payload;
void *ptr;
fprintf(stdout, "---------------------------------------\n");
for (int iter = 0; iter < num_frames - 1; iter++) {
ptr = pkt_buf[iter]->payload;
ethhdr = ptr;
iphdr = ptr + sizeof(*ethhdr);
udphdr = ptr + sizeof(*ethhdr) + sizeof(*iphdr);
/*extract L2 frame */
fprintf(stdout, "DEBUG>> L2: dst mac: ");
for (int i = 0; i < ETH_ALEN; i++)
fprintf(stdout, "%02X", ethhdr->h_dest[i]);
fprintf(stdout, "\nDEBUG>> L2: src mac: ");
for (int 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 *)(ptr + PKT_HDR_SIZE));
if (payload == EOT) {
print_verbose("End-of-transmission frame received\n");
fprintf(stdout, "---------------------------------------\n");
break;
}
fprintf(stdout, "DEBUG>> L5: payload: %d\n", payload);
fprintf(stdout, "---------------------------------------\n");
}
}
static void worker_stats_validate(struct ifobject *ifobject)
static bool rx_stats_are_valid(struct ifobject *ifobject)
{
u32 xsk_stat = 0, expected_stat = ifobject->pkt_stream->nb_pkts;
struct xsk_socket *xsk = ifobject->xsk->xsk;
int fd = xsk_socket__fd(xsk);
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 (err) {
ksft_test_result_fail("ERROR: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
__func__, -err, strerror(-err));
return true;
}
if (optlen == sizeof(struct xdp_statistics)) {
switch (stat_test_type) {
......@@ -677,8 +709,7 @@ static void worker_stats_validate(struct ifobject *ifobject)
xsk_stat = stats.rx_dropped;
break;
case STAT_TEST_TX_INVALID:
xsk_stat = stats.tx_invalid_descs;
break;
return true;
case STAT_TEST_RX_FULL:
xsk_stat = stats.rx_ring_full;
expected_stat -= RX_FULL_RXQSIZE;
......@@ -691,99 +722,70 @@ static void worker_stats_validate(struct ifobject *ifobject)
}
if (xsk_stat == expected_stat)
sigvar = 1;
return true;
}
return false;
}
static void worker_pkt_validate(void)
static void tx_stats_validate(struct ifobject *ifobject)
{
u32 payloadseqnum = -2;
struct iphdr *iphdr;
while (1) {
pkt_node_rx_q = TAILQ_LAST(&head, head_s);
if (!pkt_node_rx_q)
break;
iphdr = (struct iphdr *)(pkt_node_rx_q->pkt_frame + sizeof(struct ethhdr));
/*do not increment pktcounter if !(tos=0x9 and ipv4) */
if (iphdr->version == IP_PKT_VER && iphdr->tos == IP_PKT_TOS) {
payloadseqnum = *((uint32_t *)(pkt_node_rx_q->pkt_frame + PKT_HDR_SIZE));
if (debug_pkt_dump && payloadseqnum != EOT) {
pkt_obj = malloc(sizeof(*pkt_obj));
pkt_obj->payload = malloc(PKT_SIZE);
memcpy(pkt_obj->payload, pkt_node_rx_q->pkt_frame, PKT_SIZE);
pkt_buf[payloadseqnum] = pkt_obj;
}
if (payloadseqnum == EOT) {
print_verbose("End-of-transmission frame received: PASS\n");
sigvar = 1;
break;
}
struct xsk_socket *xsk = ifobject->xsk->xsk;
int fd = xsk_socket__fd(xsk);
struct xdp_statistics stats;
socklen_t optlen;
int err;
if (prev_pkt + 1 != payloadseqnum) {
ksft_test_result_fail
("ERROR: [%s] prev_pkt [%d], payloadseqnum [%d]\n",
__func__, prev_pkt, payloadseqnum);
ksft_exit_xfail();
}
optlen = sizeof(stats);
err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen);
if (err) {
ksft_test_result_fail("ERROR: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
__func__, -err, strerror(-err));
return;
}
prev_pkt = payloadseqnum;
pkt_counter++;
} else {
ksft_print_msg("Invalid frame received: ");
ksft_print_msg("[IP_PKT_VER: %02X], [IP_PKT_TOS: %02X]\n", iphdr->version,
iphdr->tos);
}
if (stats.tx_invalid_descs == ifobject->pkt_stream->nb_pkts)
return;
TAILQ_REMOVE(&head, pkt_node_rx_q, pkt_nodes);
free(pkt_node_rx_q->pkt_frame);
free(pkt_node_rx_q);
pkt_node_rx_q = NULL;
}
ksft_test_result_fail("ERROR: [%s] tx_invalid_descs incorrect. Got [%u] expected [%u]\n",
__func__, stats.tx_invalid_descs, ifobject->pkt_stream->nb_pkts);
}
static void thread_common_ops(struct ifobject *ifobject, void *bufs)
{
int umem_sz = num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE;
u64 umem_sz = num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE;
int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
size_t mmap_sz = umem_sz;
int ctr = 0;
int ret;
ifobject->ns_fd = switch_namespace(ifobject->nsname);
if (test_type == TEST_TYPE_BPF_RES)
umem_sz *= 2;
mmap_sz *= 2;
bufs = mmap(NULL, umem_sz,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
bufs = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
if (bufs == MAP_FAILED)
exit_with_error(errno);
xsk_configure_umem(ifobject, bufs, 0);
ifobject->umem = ifobject->umem_arr[0];
ret = xsk_configure_socket(ifobject, 0);
/* Retry Create Socket if it fails as xsk_socket__create()
* is asynchronous
*/
while (ret && ctr < SOCK_RECONF_CTR) {
xsk_configure_umem(ifobject, bufs, 0);
while (ctr++ < SOCK_RECONF_CTR) {
xsk_configure_umem(ifobject, bufs, umem_sz, 0);
ifobject->umem = ifobject->umem_arr[0];
ret = xsk_configure_socket(ifobject, 0);
if (!ret)
break;
/* Retry Create Socket if it fails as xsk_socket__create() is asynchronous */
usleep(USLEEP_MAX);
ctr++;
if (ctr >= SOCK_RECONF_CTR)
exit_with_error(-ret);
}
if (ctr >= SOCK_RECONF_CTR)
exit_with_error(ret);
ifobject->umem = ifobject->umem_arr[0];
ifobject->xsk = ifobject->xsk_arr[0];
if (test_type == TEST_TYPE_BPF_RES) {
xsk_configure_umem(ifobject, (u8 *)bufs + (umem_sz / 2), 1);
xsk_configure_umem(ifobject, (u8 *)bufs + umem_sz, umem_sz, 1);
ifobject->umem = ifobject->umem_arr[1];
ret = xsk_configure_socket(ifobject, 1);
}
......@@ -809,33 +811,18 @@ static void testapp_cleanup_xsk_res(struct ifobject *ifobj)
static void *worker_testapp_validate_tx(void *arg)
{
struct udphdr *udp_hdr =
(struct udphdr *)(pkt_data + sizeof(struct ethhdr) + sizeof(struct iphdr));
struct iphdr *ip_hdr = (struct iphdr *)(pkt_data + sizeof(struct ethhdr));
struct ethhdr *eth_hdr = (struct ethhdr *)pkt_data;
struct ifobject *ifobject = (struct ifobject *)arg;
struct generic_data data;
void *bufs = NULL;
if (!second_step)
thread_common_ops(ifobject, bufs);
for (int i = 0; i < num_frames; i++) {
/*send EOT frame */
if (i == (num_frames - 1))
data.seqnum = -1;
else
data.seqnum = i;
gen_udp_hdr(&data, ifobject, udp_hdr);
gen_ip_hdr(ifobject, ip_hdr);
gen_udp_csum(udp_hdr, ip_hdr);
gen_eth_hdr(ifobject, eth_hdr);
gen_eth_frame(ifobject->umem, i * XSK_UMEM__DEFAULT_FRAME_SIZE);
}
print_verbose("Sending %d packets on interface %s\n", ifobject->pkt_stream->nb_pkts,
ifobject->ifname);
send_pkts(ifobject);
print_verbose("Sending %d packets on interface %s\n",
(opt_pkt_count - 1), ifobject->ifname);
tx_only_all(ifobject);
if (stat_test_type == STAT_TEST_TX_INVALID)
tx_stats_validate(ifobject);
testapp_cleanup_xsk_res(ifobject);
pthread_exit(NULL);
......@@ -853,31 +840,16 @@ static void *worker_testapp_validate_rx(void *arg)
if (stat_test_type != STAT_TEST_RX_FILL_EMPTY)
xsk_populate_fill_ring(ifobject->umem);
TAILQ_INIT(&head);
if (debug_pkt_dump) {
pkt_buf = calloc(num_frames, sizeof(*pkt_buf));
if (!pkt_buf)
exit_with_error(errno);
}
fds[0].fd = xsk_socket__fd(ifobject->xsk->xsk);
fds[0].events = POLLIN;
pthread_barrier_wait(&barr);
while (1) {
if (test_type != TEST_TYPE_STATS) {
rx_pkt(ifobject->xsk, fds);
worker_pkt_validate();
} else {
worker_stats_validate(ifobject);
}
if (sigvar)
break;
}
print_verbose("Received %d packets on interface %s\n",
pkt_counter, ifobject->ifname);
if (test_type == TEST_TYPE_STATS)
while (!rx_stats_are_valid(ifobject))
continue;
else
receive_pkts(ifobject->pkt_stream, ifobject->xsk, fds);
if (test_type == TEST_TYPE_TEARDOWN)
print_verbose("Destroying socket\n");
......@@ -890,10 +862,18 @@ static void testapp_validate(void)
{
bool bidi = test_type == TEST_TYPE_BIDI;
bool bpf = test_type == TEST_TYPE_BPF_RES;
struct pkt_stream *pkt_stream;
if (pthread_barrier_init(&barr, NULL, 2))
exit_with_error(errno);
if (stat_test_type == STAT_TEST_TX_INVALID)
pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, XSK_UMEM__INVALID_FRAME_SIZE);
else
pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, PKT_SIZE);
ifdict_tx->pkt_stream = pkt_stream;
ifdict_rx->pkt_stream = pkt_stream;
/*Spawn RX thread */
pthread_create(&t0, NULL, ifdict_rx->func_ptr, ifdict_rx);
......@@ -907,15 +887,6 @@ static void testapp_validate(void)
pthread_join(t1, NULL);
pthread_join(t0, NULL);
if (debug_pkt_dump && test_type != TEST_TYPE_STATS) {
worker_pkt_dump();
for (int iter = 0; iter < num_frames - 1; iter++) {
free(pkt_buf[iter]->payload);
free(pkt_buf[iter]);
}
free(pkt_buf);
}
if (!(test_type == TEST_TYPE_TEARDOWN) && !bidi && !bpf && !(test_type == TEST_TYPE_STATS))
print_ksft_result();
}
......@@ -925,9 +896,6 @@ static void testapp_teardown(void)
int i;
for (i = 0; i < MAX_TEARDOWN_ITER; i++) {
pkt_counter = 0;
prev_pkt = -1;
sigvar = 0;
print_verbose("Creating socket\n");
testapp_validate();
}
......@@ -953,9 +921,6 @@ static void swap_vectors(struct ifobject *ifobj1, struct ifobject *ifobj2)
static void testapp_bidi(void)
{
for (int i = 0; i < MAX_BIDI_ITER; i++) {
pkt_counter = 0;
prev_pkt = -1;
sigvar = 0;
print_verbose("Creating socket\n");
testapp_validate();
if (!second_step) {
......@@ -987,9 +952,6 @@ static void testapp_bpf_res(void)
int i;
for (i = 0; i < MAX_BPF_ITER; i++) {
pkt_counter = 0;
prev_pkt = -1;
sigvar = 0;
print_verbose("Creating socket\n");
testapp_validate();
if (!second_step)
......@@ -1017,6 +979,8 @@ static void testapp_stats(void)
case STAT_TEST_RX_FULL:
rxqsize = RX_FULL_RXQSIZE;
break;
case STAT_TEST_TX_INVALID:
continue;
default:
break;
}
......@@ -1062,10 +1026,7 @@ static void run_pkt_test(int mode, int type)
/* reset defaults after potential previous test */
xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
pkt_counter = 0;
second_step = 0;
prev_pkt = -1;
sigvar = 0;
stat_test_type = -1;
rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
......@@ -1102,62 +1063,70 @@ static void run_pkt_test(int mode, int type)
}
}
static struct ifobject *ifobject_create(void)
{
struct ifobject *ifobj;
ifobj = calloc(1, sizeof(struct ifobject));
if (!ifobj)
return NULL;
ifobj->xsk_arr = calloc(2, sizeof(struct xsk_socket_info *));
if (!ifobj->xsk_arr)
goto out_xsk_arr;
ifobj->umem_arr = calloc(2, sizeof(struct xsk_umem_info *));
if (!ifobj->umem_arr)
goto out_umem_arr;
return ifobj;
out_umem_arr:
free(ifobj->xsk_arr);
out_xsk_arr:
free(ifobj);
return NULL;
}
static void ifobject_delete(struct ifobject *ifobj)
{
free(ifobj->umem_arr);
free(ifobj->xsk_arr);
free(ifobj);
}
int main(int argc, char **argv)
{
struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY };
bool failure = false;
int i, j;
if (setrlimit(RLIMIT_MEMLOCK, &_rlim))
exit_with_error(errno);
for (int i = 0; i < MAX_INTERFACES; i++) {
ifdict[i] = malloc(sizeof(struct ifobject));
for (i = 0; i < MAX_INTERFACES; i++) {
ifdict[i] = ifobject_create();
if (!ifdict[i])
exit_with_error(errno);
ifdict[i]->ifdict_index = i;
ifdict[i]->xsk_arr = calloc(2, sizeof(struct xsk_socket_info *));
if (!ifdict[i]->xsk_arr) {
failure = true;
goto cleanup;
}
ifdict[i]->umem_arr = calloc(2, sizeof(struct xsk_umem_info *));
if (!ifdict[i]->umem_arr) {
failure = true;
goto cleanup;
}
exit_with_error(ENOMEM);
}
setlocale(LC_ALL, "");
parse_command_line(argc, argv);
num_frames = ++opt_pkt_count;
init_iface(ifdict[0], MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2, tx);
init_iface(ifdict[1], MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1, rx);
init_iface(ifdict[tx], MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2, tx);
init_iface(ifdict[rx], MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1, rx);
ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
for (i = 0; i < TEST_MODE_MAX; i++) {
for (j = 0; j < TEST_TYPE_MAX; j++)
for (i = 0; i < TEST_MODE_MAX; i++)
for (j = 0; j < TEST_TYPE_MAX; j++) {
run_pkt_test(i, j);
}
cleanup:
for (int i = 0; i < MAX_INTERFACES; i++) {
if (ifdict[i]->ns_fd != -1)
close(ifdict[i]->ns_fd);
free(ifdict[i]->xsk_arr);
free(ifdict[i]->umem_arr);
free(ifdict[i]);
}
usleep(USLEEP_MAX);
}
if (failure)
exit_with_error(errno);
for (i = 0; i < MAX_INTERFACES; i++)
ifobject_delete(ifdict[i]);
ksft_exit_pass();
return 0;
}
......@@ -34,28 +34,23 @@
#define IP_PKT_TOS 0x9
#define UDP_PKT_SIZE (IP_PKT_SIZE - sizeof(struct iphdr))
#define UDP_PKT_DATA_SIZE (UDP_PKT_SIZE - sizeof(struct udphdr))
#define EOT (-1)
#define USLEEP_MAX 200000
#define USLEEP_MAX 10000
#define SOCK_RECONF_CTR 10
#define BATCH_SIZE 64
#define BATCH_SIZE 8
#define POLL_TMOUT 1000
#define DEFAULT_PKT_CNT 10000
#define DEFAULT_PKT_CNT (4 * 1024)
#define RX_FULL_RXQSIZE 32
#define XSK_UMEM__INVALID_FRAME_SIZE (XSK_UMEM__DEFAULT_FRAME_SIZE + 1)
#define print_verbose(x...) do { if (opt_verbose) ksft_print_msg(x); } while (0)
typedef __u32 u32;
typedef __u16 u16;
typedef __u8 u8;
enum TEST_MODES {
TEST_MODE_UNCONFIGURED = -1,
enum test_mode {
TEST_MODE_SKB,
TEST_MODE_DRV,
TEST_MODE_MAX
};
enum TEST_TYPES {
enum test_type {
TEST_TYPE_NOPOLL,
TEST_TYPE_POLL,
TEST_TYPE_TEARDOWN,
......@@ -65,7 +60,7 @@ enum TEST_TYPES {
TEST_TYPE_MAX
};
enum STAT_TEST_TYPES {
enum stat_test_type {
STAT_TEST_RX_DROPPED,
STAT_TEST_TX_INVALID,
STAT_TEST_RX_FULL,
......@@ -73,21 +68,16 @@ enum STAT_TEST_TYPES {
STAT_TEST_TYPE_MAX
};
static int configured_mode = TEST_MODE_UNCONFIGURED;
static u8 debug_pkt_dump;
static u32 num_frames;
static int configured_mode;
static bool opt_pkt_dump;
static u32 num_frames = DEFAULT_PKT_CNT / 4;
static bool second_step;
static int test_type;
static int opt_pkt_count;
static u8 opt_verbose;
static bool 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 long prev_pkt = -1;
static int sigvar;
static int stat_test_type;
static u32 rxqsize;
static u32 frame_headroom;
......@@ -104,10 +94,6 @@ struct xsk_socket_info {
struct xsk_ring_prod tx;
struct xsk_umem_info *umem;
struct xsk_socket *xsk;
unsigned long rx_npkts;
unsigned long tx_npkts;
unsigned long prev_rx_npkts;
unsigned long prev_tx_npkts;
u32 outstanding_tx;
};
......@@ -118,8 +104,15 @@ struct flow_vector {
} vector;
};
struct generic_data {
u32 seqnum;
struct pkt {
u64 addr;
u32 len;
u32 payload;
};
struct pkt_stream {
u32 nb_pkts;
struct pkt *pkts;
};
struct ifobject {
......@@ -131,8 +124,8 @@ struct ifobject {
struct xsk_umem_info *umem;
void *(*func_ptr)(void *arg);
struct flow_vector fv;
struct pkt_stream *pkt_stream;
int ns_fd;
int ifdict_index;
u32 dst_ip;
u32 src_ip;
u16 src_port;
......@@ -149,18 +142,4 @@ static struct ifobject *ifdict_tx;
pthread_barrier_t barr;
pthread_t t0, t1;
TAILQ_HEAD(head_s, pkt) head = TAILQ_HEAD_INITIALIZER(head);
struct head_s *head_p;
struct pkt {
char *pkt_frame;
TAILQ_ENTRY(pkt) pkt_nodes;
} *pkt_node_rx, *pkt_node_rx_q;
struct pkt_frame {
char *payload;
} *pkt_obj;
struct pkt_frame **pkt_buf;
#endif /* XDPXCEIVER_H */
......@@ -8,14 +8,8 @@ ksft_xfail=2
ksft_xpass=3
ksft_skip=4
GREEN='\033[0;92m'
YELLOW='\033[0;93m'
RED='\033[0;31m'
NC='\033[0m'
STACK_LIM=131072
SPECFILE=veth.spec
XSKOBJ=xdpxceiver
NUMPKTS=10000
validate_root_exec()
{
......@@ -50,22 +44,12 @@ validate_veth_spec_file()
test_status()
{
statusval=$1
if [ -n "${colorconsole+set}" ]; then
if [ $statusval -eq 2 ]; then
echo -e "${YELLOW}$2${NC}: [ ${RED}FAIL${NC} ]"
elif [ $statusval -eq 1 ]; then
echo -e "${YELLOW}$2${NC}: [ ${RED}SKIPPED${NC} ]"
elif [ $statusval -eq 0 ]; then
echo -e "${YELLOW}$2${NC}: [ ${GREEN}PASS${NC} ]"
fi
else
if [ $statusval -eq 2 ]; then
echo -e "$2: [ FAIL ]"
elif [ $statusval -eq 1 ]; then
echo -e "$2: [ SKIPPED ]"
elif [ $statusval -eq 0 ]; then
echo -e "$2: [ PASS ]"
fi
if [ $statusval -eq 2 ]; then
echo -e "$2: [ FAIL ]"
elif [ $statusval -eq 1 ]; then
echo -e "$2: [ SKIPPED ]"
elif [ $statusval -eq 0 ]; then
echo -e "$2: [ PASS ]"
fi
}
......@@ -107,5 +91,5 @@ validate_ip_utility()
execxdpxceiver()
{
./${XSKOBJ} -i ${VETH0} -i ${VETH1},${NS1} -C ${NUMPKTS} ${VERBOSE_ARG} ${DUMP_PKTS_ARG}
./${XSKOBJ} -i ${VETH0} -i ${VETH1},${NS1} ${VERBOSE_ARG} ${DUMP_PKTS_ARG}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment