Commit 34e78bab authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'seltests/xsk: prepare for AF_XDP multi-buffer testing'

Magnus Karlsson says:

====================
Prepare the AF_XDP selftests test framework code for the upcoming
multi-buffer support in AF_XDP. This so that the multi-buffer patch
set does not become way too large. In that upcoming patch set, we are
only including the multi-buffer tests together with any framework
code that depends on the new options bit introduced in the AF_XDP
multi-buffer implementation itself.

Currently, the test framework is based on the premise that a packet
consists of a single fragment and thus occupies a single buffer and a
single descriptor. Multi-buffer breaks this assumption, as that is the
whole purpose of it. Now, a packet can consist of multiple buffers and
therefore consume multiple descriptors.

The patch set starts with some clean-ups and simplifications followed
by patches that make sure that the current code works even when a
packet occupies multiple buffers. The actual code for sending and
receiving multi-buffer packets will be included in the AF_XDP
multi-buffer patch set as it depends on a new bit being used in the
options field of the descriptor.

Patch set anatomy:
1: The XDP program was unnecessarily changed many times. Fixes this.

2: There is no reason to generate a full UDP/IPv4 packet as it is
   never used. Simplify the code by just generating a valid Ethernet
   frame.

3: Introduce a more complicated payload pattern that can detect
   fragments out of bounds in a multi-buffer packet and other errors
   found in single-fragment packets.

4: As a convenience, dump the content of the faulty packet at error.

5: To simplify the code, make the usage of the packet stream for Tx
   and Rx more similar.

6: Store the offset of the packet in the buffer in the struct pkt
   definition instead of the address in the umem itself and introduce
   a simple buffer allocator. The address only made sense when all
   packets consumed a single buffer. Now, we do not know beforehand
   how many buffers a packet will consume, so we instead just allocate
   a buffer from the allocator and specify the offset within that
   buffer.

7: Test for huge pages only once instead of before each test that needs it.

8: Populate the fill ring based on how many frags are needed for each
   packet.

9: Change the data generation code so it can generate data for
   multi-buffer packets too.

10: Adjust the packet pacing algorithm so that it can cope with
    multi-buffer packets. The pacing algorithm is present so that Tx
    does not send too many packets/frames to Rx that it starts to drop
    packets. That would ruin the tests.

v1 -> v2:
* Fixed spelling error in patch #6 [Simon]
* Fixed compilation error with llvm in patch #7 [Daniel]

Thanks: Magnus
====================
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 0697e439 7cd6df4f
......@@ -68,9 +68,6 @@
# Run with verbose output:
# sudo ./test_xsk.sh -v
#
# Run and dump packet contents:
# sudo ./test_xsk.sh -D
#
# Set up veth interfaces and leave them up so xskxceiver can be launched in a debugger:
# sudo ./test_xsk.sh -d
#
......@@ -81,11 +78,10 @@
ETH=""
while getopts "vDi:d" flag
while getopts "vi:d" flag
do
case "${flag}" in
v) verbose=1;;
D) dump_pkts=1;;
d) debug=1;;
i) ETH=${OPTARG};;
esac
......@@ -157,10 +153,6 @@ if [[ $verbose -eq 1 ]]; then
ARGS+="-v "
fi
if [[ $dump_pkts -eq 1 ]]; then
ARGS="-D "
fi
retval=$?
test_status $retval "${TEST_NAME}"
......
......@@ -134,6 +134,11 @@ static inline void xsk_ring_prod__submit(struct xsk_ring_prod *prod, __u32 nb)
__atomic_store_n(prod->producer, *prod->producer + nb, __ATOMIC_RELEASE);
}
static inline void xsk_ring_prod__cancel(struct xsk_ring_prod *prod, __u32 nb)
{
prod->cached_prod -= nb;
}
static inline __u32 xsk_ring_cons__peek(struct xsk_ring_cons *cons, __u32 nb, __u32 *idx)
{
__u32 entries = xsk_cons_nb_avail(cons, nb);
......
......@@ -76,16 +76,13 @@
#include <asm/barrier.h>
#include <linux/if_link.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/mman.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>
......@@ -94,10 +91,8 @@
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <time.h>
#include <unistd.h>
#include <stdatomic.h>
#include "xsk_xdp_progs.skel.h"
#include "xsk.h"
......@@ -109,10 +104,6 @@
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;
static void __exit_with_error(int error, const char *file, const char *func, int line)
{
......@@ -147,112 +138,25 @@ static void report_failure(struct test_spec *test)
test->fail = true;
}
static void memset32_htonl(void *dest, u32 val, u32 size)
{
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
*/
static __u16 csum_fold(__u32 csum)
{
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
*/
static u32 from64to32(u64 x)
{
/* 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
*/
static __u32 csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u32 sum)
{
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
/* The payload is a word consisting of a packet sequence number in the upper
* 16-bits and a intra packet data sequence number in the lower 16 bits. So the 3rd packet's
* 5th word of data will contain the number (2<<16) | 4 as they are numbered from 0.
*/
static __u16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u32 sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
static u16 udp_csum(u32 saddr, u32 daddr, u32 len, u8 proto, u16 *udp_pkt)
static void write_payload(void *dest, u32 pkt_nb, u32 start, u32 size)
{
u32 csum = 0;
u32 cnt = 0;
/* udp hdr and data */
for (; cnt < len; cnt += 2)
csum += udp_pkt[cnt >> 1];
u32 *ptr = (u32 *)dest, i;
return csum_tcpudp_magic(saddr, daddr, len, proto, csum);
start /= sizeof(*ptr);
size /= sizeof(*ptr);
for (i = 0; i < size; i++)
ptr[i] = htonl(pkt_nb << 16 | (i + start));
}
static void gen_eth_hdr(struct ifobject *ifobject, struct ethhdr *eth_hdr)
{
memcpy(eth_hdr->h_dest, ifobject->dst_mac, ETH_ALEN);
memcpy(eth_hdr->h_source, ifobject->src_mac, ETH_ALEN);
eth_hdr->h_proto = htons(ETH_P_IP);
}
static void gen_ip_hdr(struct ifobject *ifobject, struct iphdr *ip_hdr)
{
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;
ip_hdr->saddr = ifobject->src_ip;
ip_hdr->daddr = ifobject->dst_ip;
ip_hdr->check = 0;
}
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 + PKT_HDR_SIZE, payload, UDP_PKT_DATA_SIZE);
eth_hdr->h_proto = htons(ETH_P_LOOPBACK);
}
static bool is_umem_valid(struct ifobject *ifobj)
......@@ -260,19 +164,18 @@ static bool is_umem_valid(struct ifobject *ifobj)
return !!ifobj->umem->umem;
}
static void gen_udp_csum(struct udphdr *udp_hdr, struct iphdr *ip_hdr)
static u32 mode_to_xdp_flags(enum test_mode mode)
{
udp_hdr->check = 0;
udp_hdr->check =
udp_csum(ip_hdr->saddr, ip_hdr->daddr, UDP_PKT_SIZE, IPPROTO_UDP, (u16 *)udp_hdr);
return (mode == TEST_MODE_SKB) ? XDP_FLAGS_SKB_MODE : XDP_FLAGS_DRV_MODE;
}
static u32 mode_to_xdp_flags(enum test_mode mode)
static u64 umem_size(struct xsk_umem_info *umem)
{
return (mode == TEST_MODE_SKB) ? XDP_FLAGS_SKB_MODE : XDP_FLAGS_DRV_MODE;
return umem->num_frames * umem->frame_size;
}
static int xsk_configure_umem(struct xsk_umem_info *umem, void *buffer, u64 size)
static int xsk_configure_umem(struct ifobject *ifobj, struct xsk_umem_info *umem, void *buffer,
u64 size)
{
struct xsk_umem_config cfg = {
.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
......@@ -292,9 +195,31 @@ static int xsk_configure_umem(struct xsk_umem_info *umem, void *buffer, u64 size
return ret;
umem->buffer = buffer;
if (ifobj->shared_umem && ifobj->rx_on) {
umem->base_addr = umem_size(umem);
umem->next_buffer = umem_size(umem);
}
return 0;
}
static u64 umem_alloc_buffer(struct xsk_umem_info *umem)
{
u64 addr;
addr = umem->next_buffer;
umem->next_buffer += umem->frame_size;
if (umem->next_buffer >= umem->base_addr + umem_size(umem))
umem->next_buffer = umem->base_addr;
return addr;
}
static void umem_reset_alloc(struct xsk_umem_info *umem)
{
umem->next_buffer = 0;
}
static void enable_busy_poll(struct xsk_socket_info *xsk)
{
int sock_opt;
......@@ -354,7 +279,7 @@ static bool ifobj_zc_avail(struct ifobject *ifobject)
exit_with_error(ENOMEM);
}
umem->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
ret = xsk_configure_umem(umem, bufs, umem_sz);
ret = xsk_configure_umem(ifobject, umem, bufs, umem_sz);
if (ret)
exit_with_error(-ret);
......@@ -380,7 +305,6 @@ static bool ifobj_zc_avail(struct ifobject *ifobject)
static struct option long_options[] = {
{"interface", required_argument, 0, 'i'},
{"busy-poll", no_argument, 0, 'b'},
{"dump-pkts", no_argument, 0, 'D'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
......@@ -391,7 +315,6 @@ static void usage(const char *prog)
" Usage: %s [OPTIONS]\n"
" Options:\n"
" -i, --interface Use interface\n"
" -D, --dump-pkts Dump packets L2 - L5\n"
" -v, --verbose Verbose output\n"
" -b, --busy-poll Enable busy poll\n";
......@@ -415,7 +338,7 @@ static void parse_command_line(struct ifobject *ifobj_tx, struct ifobject *ifobj
opterr = 0;
for (;;) {
c = getopt_long(argc, argv, "i:Dvb", long_options, &option_index);
c = getopt_long(argc, argv, "i:vb", long_options, &option_index);
if (c == -1)
break;
......@@ -437,9 +360,6 @@ static void parse_command_line(struct ifobject *ifobj_tx, struct ifobject *ifobj
interface_nb++;
break;
case 'D':
opt_pkt_dump = true;
break;
case 'v':
opt_verbose = true;
break;
......@@ -482,9 +402,6 @@ static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
memset(ifobj->umem, 0, sizeof(*ifobj->umem));
ifobj->umem->num_frames = DEFAULT_UMEM_BUFFERS;
ifobj->umem->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
if (ifobj->shared_umem && ifobj->rx_on)
ifobj->umem->base_addr = DEFAULT_UMEM_BUFFERS *
XSK_UMEM__DEFAULT_FRAME_SIZE;
for (j = 0; j < MAX_SOCKETS; j++) {
memset(&ifobj->xsk_arr[j], 0, sizeof(ifobj->xsk_arr[j]));
......@@ -554,24 +471,24 @@ static void test_spec_set_xdp_prog(struct test_spec *test, struct bpf_program *x
static void pkt_stream_reset(struct pkt_stream *pkt_stream)
{
if (pkt_stream)
pkt_stream->rx_pkt_nb = 0;
pkt_stream->current_pkt_nb = 0;
}
static struct pkt *pkt_stream_get_pkt(struct pkt_stream *pkt_stream, u32 pkt_nb)
static struct pkt *pkt_stream_get_next_tx_pkt(struct pkt_stream *pkt_stream)
{
if (pkt_nb >= pkt_stream->nb_pkts)
if (pkt_stream->current_pkt_nb >= pkt_stream->nb_pkts)
return NULL;
return &pkt_stream->pkts[pkt_nb];
return &pkt_stream->pkts[pkt_stream->current_pkt_nb++];
}
static struct pkt *pkt_stream_get_next_rx_pkt(struct pkt_stream *pkt_stream, u32 *pkts_sent)
{
while (pkt_stream->rx_pkt_nb < pkt_stream->nb_pkts) {
while (pkt_stream->current_pkt_nb < pkt_stream->nb_pkts) {
(*pkts_sent)++;
if (pkt_stream->pkts[pkt_stream->rx_pkt_nb].valid)
return &pkt_stream->pkts[pkt_stream->rx_pkt_nb++];
pkt_stream->rx_pkt_nb++;
if (pkt_stream->pkts[pkt_stream->current_pkt_nb].valid)
return &pkt_stream->pkts[pkt_stream->current_pkt_nb++];
pkt_stream->current_pkt_nb++;
}
return NULL;
}
......@@ -616,9 +533,21 @@ static struct pkt_stream *__pkt_stream_alloc(u32 nb_pkts)
return pkt_stream;
}
static void pkt_set(struct xsk_umem_info *umem, struct pkt *pkt, u64 addr, u32 len)
static u32 ceil_u32(u32 a, u32 b)
{
return (a + b - 1) / b;
}
static u32 pkt_nb_frags(u32 frame_size, struct pkt *pkt)
{
if (!pkt || !pkt->valid)
return 1;
return ceil_u32(pkt->len, frame_size);
}
static void pkt_set(struct xsk_umem_info *umem, struct pkt *pkt, int offset, u32 len)
{
pkt->addr = addr + umem->base_addr;
pkt->offset = offset;
pkt->len = len;
if (len > umem->frame_size - XDP_PACKET_HEADROOM - MIN_PKT_SIZE * 2 - umem->frame_headroom)
pkt->valid = false;
......@@ -626,6 +555,11 @@ static void pkt_set(struct xsk_umem_info *umem, struct pkt *pkt, u64 addr, u32 l
pkt->valid = true;
}
static u32 pkt_get_buffer_len(struct xsk_umem_info *umem, u32 len)
{
return ceil_u32(len, umem->frame_size) * umem->frame_size;
}
static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb_pkts, u32 pkt_len)
{
struct pkt_stream *pkt_stream;
......@@ -635,10 +569,13 @@ static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb
if (!pkt_stream)
exit_with_error(ENOMEM);
pkt_stream->nb_pkts = nb_pkts;
pkt_stream->max_pkt_len = pkt_len;
for (i = 0; i < nb_pkts; i++) {
pkt_set(umem, &pkt_stream->pkts[i], (i % umem->num_frames) * umem->frame_size,
pkt_len);
pkt_stream->pkts[i].payload = i;
struct pkt *pkt = &pkt_stream->pkts[i];
pkt_set(umem, pkt, 0, pkt_len);
pkt->pkt_nb = i;
}
return pkt_stream;
......@@ -669,8 +606,7 @@ static void __pkt_stream_replace_half(struct ifobject *ifobj, u32 pkt_len,
pkt_stream = pkt_stream_clone(umem, ifobj->pkt_stream);
for (i = 1; i < ifobj->pkt_stream->nb_pkts; i += 2)
pkt_set(umem, &pkt_stream->pkts[i],
(i % umem->num_frames) * umem->frame_size + offset, pkt_len);
pkt_set(umem, &pkt_stream->pkts[i], offset, pkt_len);
ifobj->pkt_stream = pkt_stream;
}
......@@ -694,30 +630,31 @@ static void pkt_stream_receive_half(struct test_spec *test)
pkt_stream->pkts[i].valid = false;
}
static struct pkt *pkt_generate(struct ifobject *ifobject, u32 pkt_nb)
static u64 pkt_get_addr(struct pkt *pkt, struct xsk_umem_info *umem)
{
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->valid)
return pkt->offset;
return pkt->offset + umem_alloc_buffer(umem);
}
if (!pkt)
return NULL;
if (!pkt->valid || pkt->len < MIN_PKT_SIZE)
return pkt;
static void pkt_generate(struct ifobject *ifobject, u64 addr, u32 len, u32 pkt_nb,
u32 bytes_written)
{
void *data = xsk_umem__get_data(ifobject->umem->buffer, addr);
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;
if (len < MIN_PKT_SIZE)
return;
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);
if (!bytes_written) {
gen_eth_hdr(ifobject, data);
return pkt;
len -= PKT_HDR_SIZE;
data += PKT_HDR_SIZE;
} else {
bytes_written -= PKT_HDR_SIZE;
}
write_payload(data, pkt_nb, bytes_written, len);
}
static void __pkt_stream_generate_custom(struct ifobject *ifobj,
......@@ -731,10 +668,14 @@ static void __pkt_stream_generate_custom(struct ifobject *ifobj,
exit_with_error(ENOMEM);
for (i = 0; i < nb_pkts; i++) {
pkt_stream->pkts[i].addr = pkts[i].addr + ifobj->umem->base_addr;
pkt_stream->pkts[i].len = pkts[i].len;
pkt_stream->pkts[i].payload = i;
pkt_stream->pkts[i].valid = pkts[i].valid;
struct pkt *pkt = &pkt_stream->pkts[i];
pkt->offset = pkts[i].offset;
pkt->len = pkts[i].len;
pkt->pkt_nb = i;
pkt->valid = pkts[i].valid;
if (pkt->len > pkt_stream->max_pkt_len)
pkt_stream->max_pkt_len = pkt->len;
}
ifobj->pkt_stream = pkt_stream;
......@@ -746,53 +687,62 @@ static void pkt_stream_generate_custom(struct test_spec *test, struct pkt *pkts,
__pkt_stream_generate_custom(test->ifobj_rx, pkts, nb_pkts);
}
static void pkt_dump(void *pkt, u32 len)
{
char s[INET_ADDRSTRLEN];
struct ethhdr *ethhdr;
struct udphdr *udphdr;
struct iphdr *iphdr;
u32 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 = ntohl(*((u32 *)(pkt + PKT_HDR_SIZE)));
static void pkt_print_data(u32 *data, u32 cnt)
{
u32 i;
for (i = 0; i < cnt; i++) {
u32 seqnum, pkt_nb;
fprintf(stdout, "DEBUG>> L5: payload: %d\n", payload);
fprintf(stdout, "---------------------------------------\n");
seqnum = ntohl(*data) & 0xffff;
pkt_nb = ntohl(*data) >> 16;
fprintf(stdout, "%u:%u ", pkt_nb, seqnum);
data++;
}
}
static bool is_offset_correct(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream, u64 addr,
u64 pkt_stream_addr)
static void pkt_dump(void *pkt, u32 len, bool eth_header)
{
struct ethhdr *ethhdr = pkt;
u32 i, *data;
if (eth_header) {
/*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]);
data = pkt + PKT_HDR_SIZE;
} else {
data = pkt;
}
/*extract L5 frame */
fprintf(stdout, "\nDEBUG>> L5: seqnum: ");
pkt_print_data(data, PKT_DUMP_NB_TO_PRINT);
fprintf(stdout, "....");
if (len > PKT_DUMP_NB_TO_PRINT * sizeof(u32)) {
fprintf(stdout, "\n.... ");
pkt_print_data(data + len / sizeof(u32) - PKT_DUMP_NB_TO_PRINT,
PKT_DUMP_NB_TO_PRINT);
}
fprintf(stdout, "\n---------------------------------------\n");
}
static bool is_offset_correct(struct xsk_umem_info *umem, struct pkt *pkt, u64 addr)
{
u32 headroom = umem->unaligned_mode ? 0 : umem->frame_headroom;
u32 offset = addr % umem->frame_size, expected_offset = 0;
u32 offset = addr % umem->frame_size, expected_offset;
int pkt_offset = pkt->valid ? pkt->offset : 0;
if (!pkt_stream->use_addr_for_fill)
pkt_stream_addr = 0;
if (!umem->unaligned_mode)
pkt_offset = 0;
expected_offset += (pkt_stream_addr + headroom + XDP_PACKET_HEADROOM) % umem->frame_size;
expected_offset = (pkt_offset + headroom + XDP_PACKET_HEADROOM) % umem->frame_size;
if (offset == expected_offset)
return true;
......@@ -806,9 +756,9 @@ static bool is_metadata_correct(struct pkt *pkt, void *buffer, u64 addr)
void *data = xsk_umem__get_data(buffer, addr);
struct xdp_info *meta = data - sizeof(struct xdp_info);
if (meta->count != pkt->payload) {
if (meta->count != pkt->pkt_nb) {
ksft_print_msg("[%s] expected meta_count [%d], got meta_count [%d]\n",
__func__, pkt->payload, meta->count);
__func__, pkt->pkt_nb, meta->count);
return false;
}
......@@ -818,11 +768,11 @@ static bool is_metadata_correct(struct pkt *pkt, void *buffer, u64 addr)
static bool is_pkt_valid(struct pkt *pkt, void *buffer, u64 addr, u32 len)
{
void *data = xsk_umem__get_data(buffer, addr);
struct iphdr *iphdr = (struct iphdr *)(data + sizeof(struct ethhdr));
u32 seqnum, pkt_data;
if (!pkt) {
ksft_print_msg("[%s] too many packets received\n", __func__);
return false;
goto error;
}
if (len < MIN_PKT_SIZE || pkt->len < MIN_PKT_SIZE) {
......@@ -833,28 +783,23 @@ static bool is_pkt_valid(struct pkt *pkt, void *buffer, u64 addr, u32 len)
if (pkt->len != len) {
ksft_print_msg("[%s] expected length [%d], got length [%d]\n",
__func__, pkt->len, len);
return false;
goto error;
}
if (iphdr->version == IP_PKT_VER && iphdr->tos == IP_PKT_TOS) {
u32 seqnum = ntohl(*((u32 *)(data + PKT_HDR_SIZE)));
if (opt_pkt_dump)
pkt_dump(data, PKT_SIZE);
pkt_data = ntohl(*((u32 *)(data + PKT_HDR_SIZE)));
seqnum = pkt_data >> 16;
if (pkt->payload != seqnum) {
ksft_print_msg("[%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;
if (pkt->pkt_nb != seqnum) {
ksft_print_msg("[%s] expected seqnum [%d], got seqnum [%d]\n",
__func__, pkt->pkt_nb, seqnum);
goto error;
}
return true;
error:
pkt_dump(data, len, true);
return false;
}
static void kick_tx(struct xsk_socket_info *xsk)
......@@ -976,7 +921,7 @@ static int receive_pkts(struct test_spec *test, struct pollfd *fds)
addr = xsk_umem__add_offset_to_addr(addr);
if (!is_pkt_valid(pkt, umem->buffer, addr, desc->len) ||
!is_offset_correct(umem, pkt_stream, addr, pkt->addr) ||
!is_offset_correct(umem, pkt, addr) ||
(ifobj->use_metadata && !is_metadata_correct(pkt, umem->buffer, addr)))
return TEST_FAILURE;
......@@ -992,8 +937,6 @@ static int receive_pkts(struct test_spec *test, struct pollfd *fds)
pthread_mutex_lock(&pacing_mutex);
pkts_in_flight -= pkts_sent;
if (pkts_in_flight < umem->num_frames)
pthread_cond_signal(&pacing_cond);
pthread_mutex_unlock(&pacing_mutex);
pkts_sent = 0;
}
......@@ -1001,14 +944,21 @@ static int receive_pkts(struct test_spec *test, struct pollfd *fds)
return TEST_PASS;
}
static int __send_pkts(struct ifobject *ifobject, u32 *pkt_nb, struct pollfd *fds,
bool timeout)
static int __send_pkts(struct ifobject *ifobject, struct pollfd *fds, bool timeout)
{
struct xsk_socket_info *xsk = ifobject->xsk;
struct xsk_umem_info *umem = ifobject->umem;
u32 i, idx = 0, valid_pkts = 0, buffer_len;
bool use_poll = ifobject->use_poll;
u32 i, idx = 0, valid_pkts = 0;
int ret;
buffer_len = pkt_get_buffer_len(umem, ifobject->pkt_stream->max_pkt_len);
/* pkts_in_flight might be negative if many invalid packets are sent */
if (pkts_in_flight >= (int)((umem_size(umem) - BATCH_SIZE * buffer_len) / buffer_len)) {
kick_tx(xsk);
return TEST_CONTINUE;
}
while (xsk_ring_prod__reserve(&xsk->tx, BATCH_SIZE, &idx) < BATCH_SIZE) {
if (use_poll) {
ret = poll(fds, 1, POLL_TMOUT);
......@@ -1034,25 +984,21 @@ static int __send_pkts(struct ifobject *ifobject, u32 *pkt_nb, struct pollfd *fd
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);
struct pkt *pkt = pkt_stream_get_next_tx_pkt(ifobject->pkt_stream);
if (!pkt)
break;
tx_desc->addr = pkt->addr;
tx_desc->addr = pkt_get_addr(pkt, umem);
tx_desc->len = pkt->len;
(*pkt_nb)++;
if (pkt->valid)
if (pkt->valid) {
valid_pkts++;
pkt_generate(ifobject, tx_desc->addr, tx_desc->len, pkt->pkt_nb, 0);
}
}
pthread_mutex_lock(&pacing_mutex);
pkts_in_flight += valid_pkts;
/* pkts_in_flight might be negative if many invalid packets are sent */
if (pkts_in_flight >= (int)(ifobject->umem->num_frames - BATCH_SIZE)) {
kick_tx(xsk);
pthread_cond_wait(&pacing_cond, &pacing_mutex);
}
pthread_mutex_unlock(&pacing_mutex);
xsk_ring_prod__submit(&xsk->tx, i);
......@@ -1088,18 +1034,21 @@ static void wait_for_tx_completion(struct xsk_socket_info *xsk)
static int send_pkts(struct test_spec *test, struct ifobject *ifobject)
{
struct pkt_stream *pkt_stream = ifobject->pkt_stream;
bool timeout = !is_umem_valid(test->ifobj_rx);
struct pollfd fds = { };
u32 pkt_cnt = 0, ret;
u32 ret;
fds.fd = xsk_socket__fd(ifobject->xsk->xsk);
fds.events = POLLOUT;
while (pkt_cnt < ifobject->pkt_stream->nb_pkts) {
ret = __send_pkts(ifobject, &pkt_cnt, &fds, timeout);
while (pkt_stream->current_pkt_nb < pkt_stream->nb_pkts) {
ret = __send_pkts(ifobject, &fds, timeout);
if (ret == TEST_CONTINUE && !test->fail)
continue;
if ((ret || test->fail) && !timeout)
return TEST_FAILURE;
else if (ret == TEST_PASS && timeout)
if (ret == TEST_PASS && timeout)
return ret;
}
......@@ -1249,11 +1198,14 @@ static void thread_common_ops_tx(struct test_spec *test, struct ifobject *ifobje
ifobject->xsk = &ifobject->xsk_arr[0];
ifobject->xskmap = test->ifobj_rx->xskmap;
memcpy(ifobject->umem, test->ifobj_rx->umem, sizeof(struct xsk_umem_info));
ifobject->umem->base_addr = 0;
}
static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream)
static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream,
bool fill_up)
{
u32 idx = 0, i, buffers_to_fill;
u32 rx_frame_size = umem->frame_size - XDP_PACKET_HEADROOM;
u32 idx = 0, filled = 0, buffers_to_fill, nb_pkts;
int ret;
if (umem->num_frames < XSK_RING_PROD__DEFAULT_NUM_DESCS)
......@@ -1264,22 +1216,33 @@ static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream
ret = xsk_ring_prod__reserve(&umem->fq, buffers_to_fill, &idx);
if (ret != buffers_to_fill)
exit_with_error(ENOSPC);
for (i = 0; i < buffers_to_fill; i++) {
u64 addr;
if (pkt_stream->use_addr_for_fill) {
struct pkt *pkt = pkt_stream_get_pkt(pkt_stream, i);
while (filled < buffers_to_fill) {
struct pkt *pkt = pkt_stream_get_next_rx_pkt(pkt_stream, &nb_pkts);
u64 addr;
u32 i;
for (i = 0; i < pkt_nb_frags(rx_frame_size, pkt); i++) {
if (!pkt) {
if (!fill_up)
break;
addr = filled * umem->frame_size + umem->base_addr;
} else if (pkt->offset >= 0) {
addr = pkt->offset % umem->frame_size + umem_alloc_buffer(umem);
} else {
addr = pkt->offset + umem_alloc_buffer(umem);
}
if (!pkt)
*xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr;
if (++filled >= buffers_to_fill)
break;
addr = pkt->addr;
} else {
addr = i * umem->frame_size;
}
*xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr;
}
xsk_ring_prod__submit(&umem->fq, i);
xsk_ring_prod__submit(&umem->fq, filled);
xsk_ring_prod__cancel(&umem->fq, buffers_to_fill - filled);
pkt_stream_reset(pkt_stream);
umem_reset_alloc(umem);
}
static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
......@@ -1300,12 +1263,10 @@ static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
if (bufs == MAP_FAILED)
exit_with_error(errno);
ret = xsk_configure_umem(ifobject->umem, bufs, umem_sz);
ret = xsk_configure_umem(ifobject, ifobject->umem, bufs, umem_sz);
if (ret)
exit_with_error(-ret);
xsk_populate_fill_ring(ifobject->umem, ifobject->pkt_stream);
xsk_configure_socket(test, ifobject, ifobject->umem, false);
ifobject->xsk = &ifobject->xsk_arr[0];
......@@ -1313,6 +1274,8 @@ static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
if (!ifobject->rx_on)
return;
xsk_populate_fill_ring(ifobject->umem, ifobject->pkt_stream, ifobject->use_fill_ring);
ret = xsk_update_xskmap(ifobject->xskmap, ifobject->xsk->xsk);
if (ret)
exit_with_error(errno);
......@@ -1370,12 +1333,8 @@ static void *worker_testapp_validate_rx(void *arg)
if (!err && ifobject->validation_func)
err = ifobject->validation_func(ifobject);
if (err) {
if (err)
report_failure(test);
pthread_mutex_lock(&pacing_mutex);
pthread_cond_signal(&pacing_cond);
pthread_mutex_unlock(&pacing_mutex);
}
pthread_exit(NULL);
}
......@@ -1402,11 +1361,20 @@ static void handler(int signum)
pthread_exit(NULL);
}
static bool xdp_prog_changed(struct test_spec *test, struct ifobject *ifobj)
static bool xdp_prog_changed_rx(struct test_spec *test)
{
struct ifobject *ifobj = test->ifobj_rx;
return ifobj->xdp_prog != test->xdp_prog_rx || ifobj->mode != test->mode;
}
static bool xdp_prog_changed_tx(struct test_spec *test)
{
struct ifobject *ifobj = test->ifobj_tx;
return ifobj->xdp_prog != test->xdp_prog_tx || ifobj->mode != test->mode;
}
static void xsk_reattach_xdp(struct ifobject *ifobj, struct bpf_program *xdp_prog,
struct bpf_map *xskmap, enum test_mode mode)
{
......@@ -1433,13 +1401,13 @@ static void xsk_reattach_xdp(struct ifobject *ifobj, struct bpf_program *xdp_pro
static void xsk_attach_xdp_progs(struct test_spec *test, struct ifobject *ifobj_rx,
struct ifobject *ifobj_tx)
{
if (xdp_prog_changed(test, ifobj_rx))
if (xdp_prog_changed_rx(test))
xsk_reattach_xdp(ifobj_rx, test->xdp_prog_rx, test->xskmap_rx, test->mode);
if (!ifobj_tx || ifobj_tx->shared_umem)
return;
if (xdp_prog_changed(test, ifobj_tx))
if (xdp_prog_changed_tx(test))
xsk_reattach_xdp(ifobj_tx, test->xdp_prog_tx, test->xskmap_tx, test->mode);
}
......@@ -1448,9 +1416,11 @@ static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *i
{
pthread_t t0, t1;
if (ifobj2)
if (ifobj2) {
if (pthread_barrier_init(&barr, NULL, 2))
exit_with_error(errno);
pkt_stream_reset(ifobj2->pkt_stream);
}
test->current_step++;
pkt_stream_reset(ifobj1->pkt_stream);
......@@ -1493,6 +1463,12 @@ static int testapp_validate_traffic(struct test_spec *test)
struct ifobject *ifobj_rx = test->ifobj_rx;
struct ifobject *ifobj_tx = test->ifobj_tx;
if ((ifobj_rx->umem->unaligned_mode && !ifobj_rx->unaligned_supp) ||
(ifobj_tx->umem->unaligned_mode && !ifobj_tx->unaligned_supp)) {
ksft_test_result_skip("No huge pages present.\n");
return TEST_SKIP;
}
xsk_attach_xdp_progs(test, ifobj_rx, ifobj_tx);
return __testapp_validate_traffic(test, ifobj_rx, ifobj_tx);
}
......@@ -1502,16 +1478,18 @@ static int testapp_validate_traffic_single_thread(struct test_spec *test, struct
return __testapp_validate_traffic(test, ifobj, NULL);
}
static void testapp_teardown(struct test_spec *test)
static int testapp_teardown(struct test_spec *test)
{
int i;
test_spec_set_name(test, "TEARDOWN");
for (i = 0; i < MAX_TEARDOWN_ITER; i++) {
if (testapp_validate_traffic(test))
return;
return TEST_FAILURE;
test_spec_reset(test);
}
return TEST_PASS;
}
static void swap_directions(struct ifobject **ifobj1, struct ifobject **ifobj2)
......@@ -1526,20 +1504,23 @@ static void swap_directions(struct ifobject **ifobj1, struct ifobject **ifobj2)
*ifobj2 = tmp_ifobj;
}
static void testapp_bidi(struct test_spec *test)
static int testapp_bidi(struct test_spec *test)
{
int res;
test_spec_set_name(test, "BIDIRECTIONAL");
test->ifobj_tx->rx_on = true;
test->ifobj_rx->tx_on = true;
test->total_steps = 2;
if (testapp_validate_traffic(test))
return;
return TEST_FAILURE;
print_verbose("Switching Tx/Rx vectors\n");
swap_directions(&test->ifobj_rx, &test->ifobj_tx);
__testapp_validate_traffic(test, test->ifobj_rx, test->ifobj_tx);
res = __testapp_validate_traffic(test, test->ifobj_rx, test->ifobj_tx);
swap_directions(&test->ifobj_rx, &test->ifobj_tx);
return res;
}
static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx)
......@@ -1556,160 +1537,139 @@ static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj
exit_with_error(errno);
}
static void testapp_bpf_res(struct test_spec *test)
static int testapp_bpf_res(struct test_spec *test)
{
test_spec_set_name(test, "BPF_RES");
test->total_steps = 2;
test->nb_sockets = 2;
if (testapp_validate_traffic(test))
return;
return TEST_FAILURE;
swap_xsk_resources(test->ifobj_tx, test->ifobj_rx);
testapp_validate_traffic(test);
return testapp_validate_traffic(test);
}
static void testapp_headroom(struct test_spec *test)
static int 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);
return testapp_validate_traffic(test);
}
static void testapp_stats_rx_dropped(struct test_spec *test)
static int testapp_stats_rx_dropped(struct test_spec *test)
{
test_spec_set_name(test, "STAT_RX_DROPPED");
if (test->mode == TEST_MODE_ZC) {
ksft_test_result_skip("Can not run RX_DROPPED test for ZC mode\n");
return TEST_SKIP;
}
pkt_stream_replace_half(test, MIN_PKT_SIZE * 4, 0);
test->ifobj_rx->umem->frame_headroom = test->ifobj_rx->umem->frame_size -
XDP_PACKET_HEADROOM - MIN_PKT_SIZE * 3;
pkt_stream_receive_half(test);
test->ifobj_rx->validation_func = validate_rx_dropped;
testapp_validate_traffic(test);
return testapp_validate_traffic(test);
}
static void testapp_stats_tx_invalid_descs(struct test_spec *test)
static int testapp_stats_tx_invalid_descs(struct test_spec *test)
{
test_spec_set_name(test, "STAT_TX_INVALID");
pkt_stream_replace_half(test, XSK_UMEM__INVALID_FRAME_SIZE, 0);
test->ifobj_tx->validation_func = validate_tx_invalid_descs;
testapp_validate_traffic(test);
return testapp_validate_traffic(test);
}
static void testapp_stats_rx_full(struct test_spec *test)
static int testapp_stats_rx_full(struct test_spec *test)
{
test_spec_set_name(test, "STAT_RX_FULL");
pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, PKT_SIZE);
pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, MIN_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);
DEFAULT_UMEM_BUFFERS, MIN_PKT_SIZE);
test->ifobj_rx->xsk->rxqsize = DEFAULT_UMEM_BUFFERS;
test->ifobj_rx->release_rx = false;
test->ifobj_rx->validation_func = validate_rx_full;
testapp_validate_traffic(test);
return testapp_validate_traffic(test);
}
static void testapp_stats_fill_empty(struct test_spec *test)
static int testapp_stats_fill_empty(struct test_spec *test)
{
test_spec_set_name(test, "STAT_RX_FILL_EMPTY");
pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, PKT_SIZE);
pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, MIN_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);
DEFAULT_UMEM_BUFFERS, MIN_PKT_SIZE);
test->ifobj_rx->use_fill_ring = false;
test->ifobj_rx->validation_func = validate_fill_empty;
testapp_validate_traffic(test);
}
/* Simple test */
static bool hugepages_present(struct ifobject *ifobject)
{
size_t mmap_sz = 2 * ifobject->umem->num_frames * ifobject->umem->frame_size;
void *bufs;
bufs = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_HUGE_2MB, -1, 0);
if (bufs == MAP_FAILED)
return false;
mmap_sz = ceil_u64(mmap_sz, HUGEPAGE_SIZE) * HUGEPAGE_SIZE;
munmap(bufs, mmap_sz);
return true;
return testapp_validate_traffic(test);
}
static bool testapp_unaligned(struct test_spec *test)
static int 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 */
pkt_stream_replace_half(test, PKT_SIZE, -PKT_SIZE / 2);
test->ifobj_rx->pkt_stream->use_addr_for_fill = true;
testapp_validate_traffic(test);
/* Let half of the packets straddle a 4K buffer boundary */
pkt_stream_replace_half(test, MIN_PKT_SIZE, -MIN_PKT_SIZE / 2);
return true;
return testapp_validate_traffic(test);
}
static void testapp_single_pkt(struct test_spec *test)
static int testapp_single_pkt(struct test_spec *test)
{
struct pkt pkts[] = {{0x1000, PKT_SIZE, 0, true}};
struct pkt pkts[] = {{0, MIN_PKT_SIZE, 0, true}};
pkt_stream_generate_custom(test, pkts, ARRAY_SIZE(pkts));
testapp_validate_traffic(test);
return testapp_validate_traffic(test);
}
static void testapp_invalid_desc(struct test_spec *test)
static int testapp_invalid_desc(struct test_spec *test)
{
u64 umem_size = test->ifobj_tx->umem->num_frames * test->ifobj_tx->umem->frame_size;
struct xsk_umem_info *umem = test->ifobj_tx->umem;
u64 umem_size = umem->num_frames * umem->frame_size;
struct pkt pkts[] = {
/* Zero packet address allowed */
{0, PKT_SIZE, 0, true},
{0, MIN_PKT_SIZE, 0, true},
/* Allowed packet */
{0x1000, PKT_SIZE, 0, true},
{0, MIN_PKT_SIZE, 0, true},
/* Straddling the start of umem */
{-2, PKT_SIZE, 0, false},
{-2, MIN_PKT_SIZE, 0, false},
/* Packet too large */
{0x2000, XSK_UMEM__INVALID_FRAME_SIZE, 0, false},
{0, XSK_UMEM__INVALID_FRAME_SIZE, 0, false},
/* Up to end of umem allowed */
{umem_size - PKT_SIZE, PKT_SIZE, 0, true},
{umem_size - MIN_PKT_SIZE - 2 * umem->frame_size, MIN_PKT_SIZE, 0, true},
/* After umem ends */
{umem_size, PKT_SIZE, 0, false},
{umem_size, MIN_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},
/* Straddle a 2K boundrary */
{0x3800 - PKT_SIZE / 2, PKT_SIZE, 0, true},
{umem_size - MIN_PKT_SIZE / 2, MIN_PKT_SIZE, 0, false},
/* Straddle a 4K boundary */
{0x1000 - MIN_PKT_SIZE / 2, MIN_PKT_SIZE, 0, false},
/* Straddle a 2K boundary */
{0x800 - MIN_PKT_SIZE / 2, MIN_PKT_SIZE, 0, true},
/* Valid packet for synch so that something is received */
{0x4000, PKT_SIZE, 0, true}};
{0, MIN_PKT_SIZE, 0, true}};
if (test->ifobj_tx->umem->unaligned_mode) {
/* Crossing a page boundrary allowed */
if (umem->unaligned_mode) {
/* Crossing a page boundary allowed */
pkts[7].valid = true;
}
if (test->ifobj_tx->umem->frame_size == XSK_UMEM__DEFAULT_FRAME_SIZE / 2) {
/* Crossing a 2K frame size boundrary not allowed */
if (umem->frame_size == XSK_UMEM__DEFAULT_FRAME_SIZE / 2) {
/* Crossing a 2K frame size boundary not allowed */
pkts[8].valid = false;
}
if (test->ifobj_tx->shared_umem) {
pkts[4].addr += umem_size;
pkts[5].addr += umem_size;
pkts[6].addr += umem_size;
pkts[4].offset += umem_size;
pkts[5].offset += umem_size;
pkts[6].offset += umem_size;
}
pkt_stream_generate_custom(test, pkts, ARRAY_SIZE(pkts));
testapp_validate_traffic(test);
return testapp_validate_traffic(test);
}
static void testapp_xdp_drop(struct test_spec *test)
static int testapp_xdp_drop(struct test_spec *test)
{
struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs;
struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs;
......@@ -1719,10 +1679,10 @@ static void testapp_xdp_drop(struct test_spec *test)
skel_rx->maps.xsk, skel_tx->maps.xsk);
pkt_stream_receive_half(test);
testapp_validate_traffic(test);
return testapp_validate_traffic(test);
}
static void testapp_xdp_metadata_count(struct test_spec *test)
static int testapp_xdp_metadata_count(struct test_spec *test)
{
struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs;
struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs;
......@@ -1743,10 +1703,10 @@ static void testapp_xdp_metadata_count(struct test_spec *test)
if (bpf_map_update_elem(bpf_map__fd(data_map), &key, &count, BPF_ANY))
exit_with_error(errno);
testapp_validate_traffic(test);
return testapp_validate_traffic(test);
}
static void testapp_poll_txq_tmout(struct test_spec *test)
static int testapp_poll_txq_tmout(struct test_spec *test)
{
test_spec_set_name(test, "POLL_TXQ_FULL");
......@@ -1754,14 +1714,14 @@ static void testapp_poll_txq_tmout(struct test_spec *test)
/* create invalid frame by set umem frame_size and pkt length equal to 2048 */
test->ifobj_tx->umem->frame_size = 2048;
pkt_stream_replace(test, 2 * DEFAULT_PKT_CNT, 2048);
testapp_validate_traffic_single_thread(test, test->ifobj_tx);
return testapp_validate_traffic_single_thread(test, test->ifobj_tx);
}
static void testapp_poll_rxq_tmout(struct test_spec *test)
static int testapp_poll_rxq_tmout(struct test_spec *test)
{
test_spec_set_name(test, "POLL_RXQ_EMPTY");
test->ifobj_rx->use_poll = true;
testapp_validate_traffic_single_thread(test, test->ifobj_rx);
return testapp_validate_traffic_single_thread(test, test->ifobj_rx);
}
static int xsk_load_xdp_programs(struct ifobject *ifobj)
......@@ -1778,25 +1738,30 @@ static void xsk_unload_xdp_programs(struct ifobject *ifobj)
xsk_xdp_progs__destroy(ifobj->xdp_progs);
}
/* Simple test */
static bool hugepages_present(void)
{
size_t mmap_sz = 2 * DEFAULT_UMEM_BUFFERS * XSK_UMEM__DEFAULT_FRAME_SIZE;
void *bufs;
bufs = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, MAP_HUGE_2MB);
if (bufs == MAP_FAILED)
return false;
mmap_sz = ceil_u64(mmap_sz, HUGEPAGE_SIZE) * HUGEPAGE_SIZE;
munmap(bufs, mmap_sz);
return true;
}
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,
const u16 src_port, thread_func_t func_ptr)
thread_func_t func_ptr)
{
struct in_addr ip;
int err;
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;
ifobj->func_ptr = func_ptr;
err = xsk_load_xdp_programs(ifobj);
......@@ -1804,94 +1769,87 @@ static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *
printf("Error loading XDP program\n");
exit_with_error(err);
}
if (hugepages_present())
ifobj->unaligned_supp = true;
}
static void run_pkt_test(struct test_spec *test, enum test_mode mode, enum test_type type)
{
int ret = TEST_SKIP;
switch (type) {
case TEST_TYPE_STATS_RX_DROPPED:
if (mode == TEST_MODE_ZC) {
ksft_test_result_skip("Can not run RX_DROPPED test for ZC mode\n");
return;
}
testapp_stats_rx_dropped(test);
ret = testapp_stats_rx_dropped(test);
break;
case TEST_TYPE_STATS_TX_INVALID_DESCS:
testapp_stats_tx_invalid_descs(test);
ret = testapp_stats_tx_invalid_descs(test);
break;
case TEST_TYPE_STATS_RX_FULL:
testapp_stats_rx_full(test);
ret = testapp_stats_rx_full(test);
break;
case TEST_TYPE_STATS_FILL_EMPTY:
testapp_stats_fill_empty(test);
ret = testapp_stats_fill_empty(test);
break;
case TEST_TYPE_TEARDOWN:
testapp_teardown(test);
ret = testapp_teardown(test);
break;
case TEST_TYPE_BIDI:
testapp_bidi(test);
ret = testapp_bidi(test);
break;
case TEST_TYPE_BPF_RES:
testapp_bpf_res(test);
ret = testapp_bpf_res(test);
break;
case TEST_TYPE_RUN_TO_COMPLETION:
test_spec_set_name(test, "RUN_TO_COMPLETION");
testapp_validate_traffic(test);
ret = testapp_validate_traffic(test);
break;
case TEST_TYPE_RUN_TO_COMPLETION_SINGLE_PKT:
test_spec_set_name(test, "RUN_TO_COMPLETION_SINGLE_PKT");
testapp_single_pkt(test);
ret = testapp_single_pkt(test);
break;
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;
pkt_stream_replace(test, DEFAULT_PKT_CNT, PKT_SIZE);
testapp_validate_traffic(test);
pkt_stream_replace(test, DEFAULT_PKT_CNT, MIN_PKT_SIZE);
ret = testapp_validate_traffic(test);
break;
case TEST_TYPE_RX_POLL:
test->ifobj_rx->use_poll = true;
test_spec_set_name(test, "POLL_RX");
testapp_validate_traffic(test);
ret = testapp_validate_traffic(test);
break;
case TEST_TYPE_TX_POLL:
test->ifobj_tx->use_poll = true;
test_spec_set_name(test, "POLL_TX");
testapp_validate_traffic(test);
ret = testapp_validate_traffic(test);
break;
case TEST_TYPE_POLL_TXQ_TMOUT:
testapp_poll_txq_tmout(test);
ret = testapp_poll_txq_tmout(test);
break;
case TEST_TYPE_POLL_RXQ_TMOUT:
testapp_poll_rxq_tmout(test);
ret = testapp_poll_rxq_tmout(test);
break;
case TEST_TYPE_ALIGNED_INV_DESC:
test_spec_set_name(test, "ALIGNED_INV_DESC");
testapp_invalid_desc(test);
ret = testapp_invalid_desc(test);
break;
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);
ret = testapp_invalid_desc(test);
break;
case TEST_TYPE_UNALIGNED_INV_DESC:
if (!hugepages_present(test->ifobj_tx)) {
ksft_test_result_skip("No 2M huge pages present.\n");
return;
}
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);
ret = testapp_invalid_desc(test);
break;
case TEST_TYPE_UNALIGNED_INV_DESC_4K1_FRAME: {
u64 page_size, umem_size;
if (!hugepages_present(test->ifobj_tx)) {
ksft_test_result_skip("No 2M huge pages present.\n");
return;
}
test_spec_set_name(test, "UNALIGNED_INV_DESC_4K1_FRAME_SIZE");
/* Odd frame size so the UMEM doesn't end near a page boundary. */
test->ifobj_tx->umem->frame_size = 4001;
......@@ -1903,29 +1861,28 @@ static void run_pkt_test(struct test_spec *test, enum test_mode mode, enum test_
*/
page_size = sysconf(_SC_PAGESIZE);
umem_size = test->ifobj_tx->umem->num_frames * test->ifobj_tx->umem->frame_size;
assert(umem_size % page_size > PKT_SIZE);
assert(umem_size % page_size < page_size - PKT_SIZE);
testapp_invalid_desc(test);
assert(umem_size % page_size > MIN_PKT_SIZE);
assert(umem_size % page_size < page_size - MIN_PKT_SIZE);
ret = testapp_invalid_desc(test);
break;
}
case TEST_TYPE_UNALIGNED:
if (!testapp_unaligned(test))
return;
ret = testapp_unaligned(test);
break;
case TEST_TYPE_HEADROOM:
testapp_headroom(test);
ret = testapp_headroom(test);
break;
case TEST_TYPE_XDP_DROP_HALF:
testapp_xdp_drop(test);
ret = testapp_xdp_drop(test);
break;
case TEST_TYPE_XDP_METADATA_COUNT:
testapp_xdp_metadata_count(test);
ret = testapp_xdp_metadata_count(test);
break;
default:
break;
}
if (!test->fail)
if (ret == TEST_PASS)
ksft_test_result_pass("PASS: %s %s%s\n", mode_string(test), busy_poll_string(test),
test->name);
pkt_stream_restore_default(test);
......@@ -2030,14 +1987,12 @@ int main(int argc, char **argv)
modes++;
}
init_iface(ifobj_rx, MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2,
worker_testapp_validate_rx);
init_iface(ifobj_tx, MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1,
worker_testapp_validate_tx);
init_iface(ifobj_rx, MAC1, MAC2, worker_testapp_validate_rx);
init_iface(ifobj_tx, MAC2, MAC1, worker_testapp_validate_tx);
test_spec_init(&test, ifobj_tx, ifobj_rx, 0);
tx_pkt_stream_default = pkt_stream_generate(ifobj_tx->umem, DEFAULT_PKT_CNT, PKT_SIZE);
rx_pkt_stream_default = pkt_stream_generate(ifobj_rx->umem, DEFAULT_PKT_CNT, PKT_SIZE);
tx_pkt_stream_default = pkt_stream_generate(ifobj_tx->umem, DEFAULT_PKT_CNT, MIN_PKT_SIZE);
rx_pkt_stream_default = pkt_stream_generate(ifobj_rx->umem, DEFAULT_PKT_CNT, MIN_PKT_SIZE);
if (!tx_pkt_stream_default || !rx_pkt_stream_default)
exit_with_error(ENOMEM);
test.tx_pkt_stream_default = tx_pkt_stream_default;
......
......@@ -30,22 +30,14 @@
#define TEST_PASS 0
#define TEST_FAILURE -1
#define TEST_CONTINUE 1
#define TEST_SKIP 2
#define MAX_INTERFACES 2
#define MAX_INTERFACE_NAME_CHARS 16
#define MAX_SOCKETS 2
#define MAX_TEST_NAME_SIZE 32
#define MAX_TEARDOWN_ITER 10
#define PKT_HDR_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + \
sizeof(struct udphdr))
#define MIN_ETH_PKT_SIZE 64
#define ETH_FCS_SIZE 4
#define MIN_PKT_SIZE (MIN_ETH_PKT_SIZE - ETH_FCS_SIZE)
#define PKT_SIZE (MIN_PKT_SIZE)
#define IP_PKT_SIZE (PKT_SIZE - sizeof(struct ethhdr))
#define IP_PKT_VER 0x4
#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 PKT_HDR_SIZE (sizeof(struct ethhdr) + 2) /* Just to align the data in the packet */
#define MIN_PKT_SIZE 64
#define USLEEP_MAX 10000
#define SOCK_RECONF_CTR 10
#define BATCH_SIZE 64
......@@ -57,6 +49,7 @@
#define UMEM_HEADROOM_TEST_SIZE 128
#define XSK_UMEM__INVALID_FRAME_SIZE (XSK_UMEM__DEFAULT_FRAME_SIZE + 1)
#define HUGEPAGE_SIZE (2 * 1024 * 1024)
#define PKT_DUMP_NB_TO_PRINT 16
#define print_verbose(x...) do { if (opt_verbose) ksft_print_msg(x); } while (0)
......@@ -93,13 +86,13 @@ enum test_type {
TEST_TYPE_MAX
};
static bool opt_pkt_dump;
static bool opt_verbose;
struct xsk_umem_info {
struct xsk_ring_prod fq;
struct xsk_ring_cons cq;
struct xsk_umem *umem;
u64 next_buffer;
u32 num_frames;
u32 frame_headroom;
void *buffer;
......@@ -118,17 +111,17 @@ struct xsk_socket_info {
};
struct pkt {
u64 addr;
int offset;
u32 len;
u32 payload;
u32 pkt_nb;
bool valid;
};
struct pkt_stream {
u32 nb_pkts;
u32 rx_pkt_nb;
u32 current_pkt_nb;
struct pkt *pkts;
bool use_addr_for_fill;
u32 max_pkt_len;
};
struct ifobject;
......@@ -148,11 +141,7 @@ struct ifobject {
struct bpf_program *xdp_prog;
enum test_mode mode;
int ifindex;
u32 dst_ip;
u32 src_ip;
u32 bind_flags;
u16 src_port;
u16 dst_port;
bool tx_on;
bool rx_on;
bool use_poll;
......@@ -161,6 +150,7 @@ struct ifobject {
bool release_rx;
bool shared_umem;
bool use_metadata;
bool unaligned_supp;
u8 dst_mac[ETH_ALEN];
u8 src_mac[ETH_ALEN];
};
......@@ -184,7 +174,6 @@ struct test_spec {
pthread_barrier_t barr;
pthread_mutex_t pacing_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t pacing_cond = PTHREAD_COND_INITIALIZER;
int pkts_in_flight;
......
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