2018-05-02 19:01:36 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2018-05-18 20:00:21 +08:00
|
|
|
/* Copyright(c) 2017 - 2018 Intel Corporation. */
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
#include <asm/barrier.h>
|
2018-05-02 19:01:36 +08:00
|
|
|
#include <errno.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <linux/bpf.h>
|
2019-02-21 17:21:27 +08:00
|
|
|
#include <linux/compiler.h>
|
2018-05-02 19:01:36 +08:00
|
|
|
#include <linux/if_link.h>
|
|
|
|
#include <linux/if_xdp.h>
|
|
|
|
#include <linux/if_ether.h>
|
2019-02-21 17:21:27 +08:00
|
|
|
#include <locale.h>
|
|
|
|
#include <net/ethernet.h>
|
2018-05-02 19:01:36 +08:00
|
|
|
#include <net/if.h>
|
2019-02-21 17:21:27 +08:00
|
|
|
#include <poll.h>
|
|
|
|
#include <pthread.h>
|
2018-05-02 19:01:36 +08:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2019-02-21 17:21:27 +08:00
|
|
|
#include <sys/mman.h>
|
2018-05-02 19:01:36 +08:00
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <sys/socket.h>
|
2019-02-21 17:21:27 +08:00
|
|
|
#include <sys/types.h>
|
2018-05-02 19:01:36 +08:00
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2019-06-15 23:14:47 +08:00
|
|
|
#include "libbpf.h"
|
|
|
|
#include "xsk.h"
|
2019-11-08 01:47:37 +08:00
|
|
|
#include "xdpsock.h"
|
2018-05-15 13:35:02 +08:00
|
|
|
#include <bpf/bpf.h>
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
#ifndef SOL_XDP
|
|
|
|
#define SOL_XDP 283
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef AF_XDP
|
|
|
|
#define AF_XDP 44
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef PF_XDP
|
|
|
|
#define PF_XDP AF_XDP
|
|
|
|
#endif
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
#define NUM_FRAMES (4 * 1024)
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
#define DEBUG_HEXDUMP 0
|
|
|
|
|
2018-06-04 19:57:14 +08:00
|
|
|
typedef __u64 u64;
|
2018-05-02 19:01:36 +08:00
|
|
|
typedef __u32 u32;
|
|
|
|
|
|
|
|
static unsigned long prev_time;
|
|
|
|
|
|
|
|
enum benchmark_type {
|
|
|
|
BENCH_RXDROP = 0,
|
|
|
|
BENCH_TXONLY = 1,
|
|
|
|
BENCH_L2FWD = 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum benchmark_type opt_bench = BENCH_RXDROP;
|
2019-02-02 05:42:28 +08:00
|
|
|
static u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
2018-05-02 19:01:36 +08:00
|
|
|
static const char *opt_if = "";
|
|
|
|
static int opt_ifindex;
|
|
|
|
static int opt_queue;
|
2019-12-20 16:55:25 +08:00
|
|
|
static unsigned long opt_duration;
|
|
|
|
static unsigned long start_time;
|
|
|
|
static bool benchmark_done;
|
2019-12-20 16:55:27 +08:00
|
|
|
static u32 opt_batch_size = 64;
|
2018-05-02 19:01:36 +08:00
|
|
|
static int opt_poll;
|
|
|
|
static int opt_interval = 1;
|
2019-08-14 15:27:21 +08:00
|
|
|
static u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP;
|
2019-08-27 10:25:28 +08:00
|
|
|
static u32 opt_umem_flags;
|
|
|
|
static int opt_unaligned_chunks;
|
2019-08-27 10:25:30 +08:00
|
|
|
static int opt_mmap_flags;
|
2019-08-27 10:25:28 +08:00
|
|
|
static u32 opt_xdp_bind_flags;
|
2019-06-26 22:35:27 +08:00
|
|
|
static int opt_xsk_frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
|
2019-08-14 15:27:21 +08:00
|
|
|
static int opt_timeout = 1000;
|
|
|
|
static bool opt_need_wakeup = true;
|
2019-11-08 01:47:37 +08:00
|
|
|
static u32 opt_num_xsks = 1;
|
|
|
|
static u32 prog_id;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
struct xsk_umem_info {
|
|
|
|
struct xsk_ring_prod fq;
|
|
|
|
struct xsk_ring_cons cq;
|
|
|
|
struct xsk_umem *umem;
|
|
|
|
void *buffer;
|
2018-05-02 19:01:36 +08:00
|
|
|
};
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
struct xsk_socket_info {
|
|
|
|
struct xsk_ring_cons rx;
|
|
|
|
struct xsk_ring_prod tx;
|
|
|
|
struct xsk_umem_info *umem;
|
|
|
|
struct xsk_socket *xsk;
|
2018-05-02 19:01:36 +08:00
|
|
|
unsigned long rx_npkts;
|
|
|
|
unsigned long tx_npkts;
|
|
|
|
unsigned long prev_rx_npkts;
|
|
|
|
unsigned long prev_tx_npkts;
|
2019-02-21 17:21:27 +08:00
|
|
|
u32 outstanding_tx;
|
2018-05-02 19:01:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int num_socks;
|
2019-02-21 17:21:27 +08:00
|
|
|
struct xsk_socket_info *xsks[MAX_SOCKS];
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
static unsigned long get_nsecs(void)
|
|
|
|
{
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
|
|
return ts.tv_sec * 1000000000UL + ts.tv_nsec;
|
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
static void print_benchmark(bool running)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-02-21 17:21:27 +08:00
|
|
|
const char *bench_str = "INVALID";
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
if (opt_bench == BENCH_RXDROP)
|
|
|
|
bench_str = "rxdrop";
|
|
|
|
else if (opt_bench == BENCH_TXONLY)
|
|
|
|
bench_str = "txonly";
|
|
|
|
else if (opt_bench == BENCH_L2FWD)
|
|
|
|
bench_str = "l2fwd";
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
printf("%s:%d %s ", opt_if, opt_queue, bench_str);
|
|
|
|
if (opt_xdp_flags & XDP_FLAGS_SKB_MODE)
|
|
|
|
printf("xdp-skb ");
|
|
|
|
else if (opt_xdp_flags & XDP_FLAGS_DRV_MODE)
|
|
|
|
printf("xdp-drv ");
|
|
|
|
else
|
|
|
|
printf(" ");
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
if (opt_poll)
|
|
|
|
printf("poll() ");
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
if (running) {
|
|
|
|
printf("running...");
|
|
|
|
fflush(stdout);
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
static void dump_stats(void)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-02-21 17:21:27 +08:00
|
|
|
unsigned long now = get_nsecs();
|
|
|
|
long dt = now - prev_time;
|
|
|
|
int i;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
prev_time = now;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
for (i = 0; i < num_socks && xsks[i]; i++) {
|
|
|
|
char *fmt = "%-15s %'-11.0f %'-11lu\n";
|
|
|
|
double rx_pps, tx_pps;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
rx_pps = (xsks[i]->rx_npkts - xsks[i]->prev_rx_npkts) *
|
|
|
|
1000000000. / dt;
|
|
|
|
tx_pps = (xsks[i]->tx_npkts - xsks[i]->prev_tx_npkts) *
|
|
|
|
1000000000. / dt;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
printf("\n sock%d@", i);
|
|
|
|
print_benchmark(false);
|
|
|
|
printf("\n");
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
printf("%-15s %-11s %-11s %-11.2f\n", "", "pps", "pkts",
|
|
|
|
dt / 1000000000.);
|
|
|
|
printf(fmt, "rx", rx_pps, xsks[i]->rx_npkts);
|
|
|
|
printf(fmt, "tx", tx_pps, xsks[i]->tx_npkts);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
xsks[i]->prev_rx_npkts = xsks[i]->rx_npkts;
|
|
|
|
xsks[i]->prev_tx_npkts = xsks[i]->tx_npkts;
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-20 16:55:25 +08:00
|
|
|
static bool is_benchmark_done(void)
|
|
|
|
{
|
|
|
|
if (opt_duration > 0) {
|
|
|
|
unsigned long dt = (get_nsecs() - start_time);
|
|
|
|
|
|
|
|
if (dt >= opt_duration)
|
|
|
|
benchmark_done = true;
|
|
|
|
}
|
|
|
|
return benchmark_done;
|
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
static void *poller(void *arg)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-02-21 17:21:27 +08:00
|
|
|
(void)arg;
|
2019-12-20 16:55:25 +08:00
|
|
|
while (!is_benchmark_done()) {
|
2019-02-21 17:21:27 +08:00
|
|
|
sleep(opt_interval);
|
|
|
|
dump_stats();
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
return NULL;
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
static void remove_xdp_program(void)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-11-08 01:47:37 +08:00
|
|
|
u32 curr_prog_id = 0;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
if (bpf_get_link_xdp_id(opt_ifindex, &curr_prog_id, opt_xdp_flags)) {
|
|
|
|
printf("bpf_get_link_xdp_id failed\n");
|
|
|
|
exit(EXIT_FAILURE);
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
2019-02-21 17:21:27 +08:00
|
|
|
if (prog_id == curr_prog_id)
|
|
|
|
bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags);
|
|
|
|
else if (!curr_prog_id)
|
|
|
|
printf("couldn't find a prog id on a given interface\n");
|
|
|
|
else
|
|
|
|
printf("program on interface changed, not removing\n");
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
static void int_exit(int sig)
|
2019-12-20 16:55:26 +08:00
|
|
|
{
|
|
|
|
benchmark_done = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xdpsock_cleanup(void)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-02-21 17:21:27 +08:00
|
|
|
struct xsk_umem *umem = xsks[0]->umem->umem;
|
2019-11-08 01:47:37 +08:00
|
|
|
int i;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
dump_stats();
|
2019-11-08 01:47:37 +08:00
|
|
|
for (i = 0; i < num_socks; i++)
|
|
|
|
xsk_socket__delete(xsks[i]->xsk);
|
2019-02-21 17:21:27 +08:00
|
|
|
(void)xsk_umem__delete(umem);
|
|
|
|
remove_xdp_program();
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
static void __exit_with_error(int error, const char *file, const char *func,
|
|
|
|
int line)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-02-21 17:21:27 +08:00
|
|
|
fprintf(stderr, "%s:%s:%i: errno: %d/\"%s\"\n", file, func,
|
|
|
|
line, error, strerror(error));
|
|
|
|
dump_stats();
|
|
|
|
remove_xdp_program();
|
|
|
|
exit(EXIT_FAILURE);
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, \
|
|
|
|
__LINE__)
|
|
|
|
static const char pkt_data[] =
|
|
|
|
"\x3c\xfd\xfe\x9e\x7f\x71\xec\xb1\xd7\x98\x3a\xc0\x08\x00\x45\x00"
|
|
|
|
"\x00\x2e\x00\x00\x00\x00\x40\x11\x88\x97\x05\x08\x07\x08\xc8\x14"
|
|
|
|
"\x1e\x04\x10\x92\x10\x92\x00\x1a\x6d\xa3\x34\x33\x1f\x69\x40\x6b"
|
|
|
|
"\x54\x59\xb6\x14\x2d\x11\x44\xbf\xaf\xd9\xbe\xaa";
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
static void swap_mac_addresses(void *data)
|
|
|
|
{
|
|
|
|
struct ether_header *eth = (struct ether_header *)data;
|
|
|
|
struct ether_addr *src_addr = (struct ether_addr *)ð->ether_shost;
|
|
|
|
struct ether_addr *dst_addr = (struct ether_addr *)ð->ether_dhost;
|
|
|
|
struct ether_addr tmp;
|
|
|
|
|
|
|
|
tmp = *src_addr;
|
|
|
|
*src_addr = *dst_addr;
|
|
|
|
*dst_addr = tmp;
|
|
|
|
}
|
|
|
|
|
2018-06-04 19:57:14 +08:00
|
|
|
static void hex_dump(void *pkt, size_t length, u64 addr)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
|
|
|
const unsigned char *address = (unsigned char *)pkt;
|
|
|
|
const unsigned char *line = address;
|
|
|
|
size_t line_size = 32;
|
|
|
|
unsigned char c;
|
2018-06-04 19:57:14 +08:00
|
|
|
char buf[32];
|
|
|
|
int i = 0;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2018-06-04 19:57:14 +08:00
|
|
|
if (!DEBUG_HEXDUMP)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sprintf(buf, "addr=%llu", addr);
|
2018-05-02 19:01:36 +08:00
|
|
|
printf("length = %zu\n", length);
|
2018-06-04 19:57:14 +08:00
|
|
|
printf("%s | ", buf);
|
2018-05-02 19:01:36 +08:00
|
|
|
while (length-- > 0) {
|
|
|
|
printf("%02X ", *address++);
|
|
|
|
if (!(++i % line_size) || (length == 0 && i % line_size)) {
|
|
|
|
if (length == 0) {
|
|
|
|
while (i++ % line_size)
|
|
|
|
printf("__ ");
|
|
|
|
}
|
|
|
|
printf(" | "); /* right close */
|
|
|
|
while (line < address) {
|
|
|
|
c = *line++;
|
|
|
|
printf("%c", (c < 33 || c == 255) ? 0x2E : c);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
if (length > 0)
|
2018-06-04 19:57:14 +08:00
|
|
|
printf("%s | ", buf);
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2019-12-20 16:55:27 +08:00
|
|
|
static void gen_eth_frame(struct xsk_umem_info *umem, u64 addr)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-02-21 17:21:27 +08:00
|
|
|
memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data,
|
|
|
|
sizeof(pkt_data) - 1);
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
static struct xsk_umem_info *xsk_configure_umem(void *buffer, u64 size)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-02-21 17:21:27 +08:00
|
|
|
struct xsk_umem_info *umem;
|
2019-06-26 22:35:27 +08:00
|
|
|
struct xsk_umem_config cfg = {
|
|
|
|
.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
|
|
|
|
.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
|
|
|
|
.frame_size = opt_xsk_frame_size,
|
|
|
|
.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM,
|
2019-08-27 10:25:28 +08:00
|
|
|
.flags = opt_umem_flags
|
2019-06-26 22:35:27 +08:00
|
|
|
};
|
2019-11-08 01:47:39 +08:00
|
|
|
int ret;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
umem = calloc(1, sizeof(*umem));
|
2019-02-21 17:21:27 +08:00
|
|
|
if (!umem)
|
|
|
|
exit_with_error(errno);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
ret = xsk_umem__create(&umem->umem, buffer, size, &umem->fq, &umem->cq,
|
2019-06-26 22:35:27 +08:00
|
|
|
&cfg);
|
2019-02-21 17:21:27 +08:00
|
|
|
if (ret)
|
|
|
|
exit_with_error(-ret);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-11-08 01:47:39 +08:00
|
|
|
umem->buffer = buffer;
|
|
|
|
return umem;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xsk_populate_fill_ring(struct xsk_umem_info *umem)
|
|
|
|
{
|
|
|
|
int ret, i;
|
|
|
|
u32 idx;
|
|
|
|
|
2019-11-08 01:47:37 +08:00
|
|
|
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);
|
|
|
|
for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++)
|
|
|
|
*xsk_ring_prod__fill_addr(&umem->fq, idx++) =
|
|
|
|
i * opt_xsk_frame_size;
|
|
|
|
xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS);
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-11-08 01:47:39 +08:00
|
|
|
static struct xsk_socket_info *xsk_configure_socket(struct xsk_umem_info *umem,
|
|
|
|
bool rx, bool tx)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-02-21 17:21:27 +08:00
|
|
|
struct xsk_socket_config cfg;
|
|
|
|
struct xsk_socket_info *xsk;
|
2019-11-08 01:47:39 +08:00
|
|
|
struct xsk_ring_cons *rxr;
|
|
|
|
struct xsk_ring_prod *txr;
|
2019-02-21 17:21:27 +08:00
|
|
|
int ret;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
xsk = calloc(1, sizeof(*xsk));
|
2019-02-21 17:21:27 +08:00
|
|
|
if (!xsk)
|
|
|
|
exit_with_error(errno);
|
|
|
|
|
|
|
|
xsk->umem = umem;
|
|
|
|
cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
|
|
|
|
cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
|
2019-11-08 01:47:37 +08:00
|
|
|
if (opt_num_xsks > 1)
|
|
|
|
cfg.libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD;
|
|
|
|
else
|
|
|
|
cfg.libbpf_flags = 0;
|
2019-02-21 17:21:27 +08:00
|
|
|
cfg.xdp_flags = opt_xdp_flags;
|
|
|
|
cfg.bind_flags = opt_xdp_bind_flags;
|
2019-11-08 01:47:37 +08:00
|
|
|
|
2019-11-08 01:47:39 +08:00
|
|
|
rxr = rx ? &xsk->rx : NULL;
|
|
|
|
txr = tx ? &xsk->tx : NULL;
|
|
|
|
ret = xsk_socket__create(&xsk->xsk, opt_if, opt_queue, umem->umem,
|
|
|
|
rxr, txr, &cfg);
|
2019-02-21 17:21:27 +08:00
|
|
|
if (ret)
|
|
|
|
exit_with_error(-ret);
|
|
|
|
|
|
|
|
ret = bpf_get_link_xdp_id(opt_ifindex, &prog_id, opt_xdp_flags);
|
|
|
|
if (ret)
|
|
|
|
exit_with_error(-ret);
|
|
|
|
|
2018-05-02 19:01:36 +08:00
|
|
|
return xsk;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct option long_options[] = {
|
|
|
|
{"rxdrop", no_argument, 0, 'r'},
|
|
|
|
{"txonly", no_argument, 0, 't'},
|
|
|
|
{"l2fwd", no_argument, 0, 'l'},
|
|
|
|
{"interface", required_argument, 0, 'i'},
|
|
|
|
{"queue", required_argument, 0, 'q'},
|
|
|
|
{"poll", no_argument, 0, 'p'},
|
|
|
|
{"xdp-skb", no_argument, 0, 'S'},
|
|
|
|
{"xdp-native", no_argument, 0, 'N'},
|
|
|
|
{"interval", required_argument, 0, 'n'},
|
2018-08-28 20:44:35 +08:00
|
|
|
{"zero-copy", no_argument, 0, 'z'},
|
|
|
|
{"copy", no_argument, 0, 'c'},
|
2019-06-26 22:35:27 +08:00
|
|
|
{"frame-size", required_argument, 0, 'f'},
|
2019-08-14 15:27:21 +08:00
|
|
|
{"no-need-wakeup", no_argument, 0, 'm'},
|
2019-08-27 10:25:28 +08:00
|
|
|
{"unaligned", no_argument, 0, 'u'},
|
2019-11-08 01:47:37 +08:00
|
|
|
{"shared-umem", no_argument, 0, 'M'},
|
2019-11-15 00:28:47 +08:00
|
|
|
{"force", no_argument, 0, 'F'},
|
2019-12-20 16:55:25 +08:00
|
|
|
{"duration", required_argument, 0, 'd'},
|
2019-12-20 16:55:27 +08:00
|
|
|
{"batch-size", required_argument, 0, 'b'},
|
2018-05-02 19:01:36 +08:00
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
static void usage(const char *prog)
|
|
|
|
{
|
|
|
|
const char *str =
|
|
|
|
" Usage: %s [OPTIONS]\n"
|
|
|
|
" Options:\n"
|
|
|
|
" -r, --rxdrop Discard all incoming packets (default)\n"
|
|
|
|
" -t, --txonly Only send packets\n"
|
|
|
|
" -l, --l2fwd MAC swap L2 forwarding\n"
|
|
|
|
" -i, --interface=n Run on interface n\n"
|
|
|
|
" -q, --queue=n Use queue n (default 0)\n"
|
|
|
|
" -p, --poll Use poll syscall\n"
|
|
|
|
" -S, --xdp-skb=n Use XDP skb-mod\n"
|
2019-10-07 16:26:36 +08:00
|
|
|
" -N, --xdp-native=n Enforce XDP native mode\n"
|
2018-05-02 19:01:36 +08:00
|
|
|
" -n, --interval=n Specify statistics update interval (default 1 sec).\n"
|
2018-08-28 20:44:35 +08:00
|
|
|
" -z, --zero-copy Force zero-copy mode.\n"
|
|
|
|
" -c, --copy Force copy mode.\n"
|
2019-08-14 15:27:21 +08:00
|
|
|
" -m, --no-need-wakeup Turn off use of driver need wakeup flag.\n"
|
2019-08-27 10:25:28 +08:00
|
|
|
" -f, --frame-size=n Set the frame size (must be a power of two in aligned mode, default is %d).\n"
|
|
|
|
" -u, --unaligned Enable unaligned chunk placement\n"
|
2019-11-08 01:47:37 +08:00
|
|
|
" -M, --shared-umem Enable XDP_SHARED_UMEM\n"
|
2019-11-15 00:28:47 +08:00
|
|
|
" -F, --force Force loading the XDP prog\n"
|
2019-12-20 16:55:25 +08:00
|
|
|
" -d, --duration=n Duration in secs to run command.\n"
|
|
|
|
" Default: forever.\n"
|
2019-12-20 16:55:27 +08:00
|
|
|
" -b, --batch-size=n Batch size for sending or receiving\n"
|
|
|
|
" packets. Default: %d\n"
|
2018-05-02 19:01:36 +08:00
|
|
|
"\n";
|
2019-12-20 16:55:27 +08:00
|
|
|
fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE,
|
|
|
|
opt_batch_size);
|
2018-05-02 19:01:36 +08:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_command_line(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int option_index, c;
|
|
|
|
|
|
|
|
opterr = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
2019-12-20 16:55:27 +08:00
|
|
|
c = getopt_long(argc, argv, "Frtli:q:pSNn:czf:muMd:b:",
|
2019-08-14 15:27:21 +08:00
|
|
|
long_options, &option_index);
|
2018-05-02 19:01:36 +08:00
|
|
|
if (c == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case 'r':
|
|
|
|
opt_bench = BENCH_RXDROP;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
opt_bench = BENCH_TXONLY;
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
opt_bench = BENCH_L2FWD;
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
opt_if = optarg;
|
|
|
|
break;
|
|
|
|
case 'q':
|
|
|
|
opt_queue = atoi(optarg);
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
opt_poll = 1;
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
opt_xdp_flags |= XDP_FLAGS_SKB_MODE;
|
2018-06-04 20:06:01 +08:00
|
|
|
opt_xdp_bind_flags |= XDP_COPY;
|
2018-05-02 19:01:36 +08:00
|
|
|
break;
|
|
|
|
case 'N':
|
samples/bpf: Attach XDP programs in driver mode by default
When attaching XDP programs, userspace can set flags to request the attach
mode (generic/SKB mode, driver mode or hw offloaded mode). If no such flags
are requested, the kernel will attempt to attach in driver mode, and then
silently fall back to SKB mode if this fails.
The silent fallback is a major source of user confusion, as users will try
to load a program on a device without XDP support, and instead of an error
they will get the silent fallback behaviour, not notice, and then wonder
why performance is not what they were expecting.
In an attempt to combat this, let's switch all the samples to default to
explicitly requesting driver-mode attach. As part of this, ensure that all
the userspace utilities have a switch to enable SKB mode. For those that
have a switch to request driver mode, keep it but turn it into a no-op.
Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
Acked-by: David Ahern <dsahern@gmail.com>
Link: https://lore.kernel.org/bpf/20191216110742.364456-1-toke@redhat.com
2019-12-16 19:07:42 +08:00
|
|
|
/* default, set below */
|
2018-05-02 19:01:36 +08:00
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
opt_interval = atoi(optarg);
|
|
|
|
break;
|
2018-08-28 20:44:35 +08:00
|
|
|
case 'z':
|
|
|
|
opt_xdp_bind_flags |= XDP_ZEROCOPY;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
opt_xdp_bind_flags |= XDP_COPY;
|
|
|
|
break;
|
2019-08-27 10:25:28 +08:00
|
|
|
case 'u':
|
|
|
|
opt_umem_flags |= XDP_UMEM_UNALIGNED_CHUNK_FLAG;
|
|
|
|
opt_unaligned_chunks = 1;
|
2019-08-27 10:25:30 +08:00
|
|
|
opt_mmap_flags = MAP_HUGETLB;
|
2019-08-27 10:25:28 +08:00
|
|
|
break;
|
2019-02-02 05:42:28 +08:00
|
|
|
case 'F':
|
|
|
|
opt_xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
|
|
|
|
break;
|
2019-06-26 22:35:27 +08:00
|
|
|
case 'f':
|
|
|
|
opt_xsk_frame_size = atoi(optarg);
|
2019-11-08 01:47:37 +08:00
|
|
|
break;
|
2019-08-14 15:27:21 +08:00
|
|
|
case 'm':
|
|
|
|
opt_need_wakeup = false;
|
|
|
|
opt_xdp_bind_flags &= ~XDP_USE_NEED_WAKEUP;
|
2019-06-26 22:35:27 +08:00
|
|
|
break;
|
2019-11-08 01:47:37 +08:00
|
|
|
case 'M':
|
|
|
|
opt_num_xsks = MAX_SOCKS;
|
|
|
|
break;
|
2019-12-20 16:55:25 +08:00
|
|
|
case 'd':
|
|
|
|
opt_duration = atoi(optarg);
|
|
|
|
opt_duration *= 1000000000;
|
|
|
|
break;
|
2019-12-20 16:55:27 +08:00
|
|
|
case 'b':
|
|
|
|
opt_batch_size = atoi(optarg);
|
|
|
|
break;
|
2018-05-02 19:01:36 +08:00
|
|
|
default:
|
|
|
|
usage(basename(argv[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
samples/bpf: Attach XDP programs in driver mode by default
When attaching XDP programs, userspace can set flags to request the attach
mode (generic/SKB mode, driver mode or hw offloaded mode). If no such flags
are requested, the kernel will attempt to attach in driver mode, and then
silently fall back to SKB mode if this fails.
The silent fallback is a major source of user confusion, as users will try
to load a program on a device without XDP support, and instead of an error
they will get the silent fallback behaviour, not notice, and then wonder
why performance is not what they were expecting.
In an attempt to combat this, let's switch all the samples to default to
explicitly requesting driver-mode attach. As part of this, ensure that all
the userspace utilities have a switch to enable SKB mode. For those that
have a switch to request driver mode, keep it but turn it into a no-op.
Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
Acked-by: David Ahern <dsahern@gmail.com>
Link: https://lore.kernel.org/bpf/20191216110742.364456-1-toke@redhat.com
2019-12-16 19:07:42 +08:00
|
|
|
if (!(opt_xdp_flags & XDP_FLAGS_SKB_MODE))
|
|
|
|
opt_xdp_flags |= XDP_FLAGS_DRV_MODE;
|
|
|
|
|
2018-05-02 19:01:36 +08:00
|
|
|
opt_ifindex = if_nametoindex(opt_if);
|
|
|
|
if (!opt_ifindex) {
|
|
|
|
fprintf(stderr, "ERROR: interface \"%s\" does not exist\n",
|
|
|
|
opt_if);
|
|
|
|
usage(basename(argv[0]));
|
|
|
|
}
|
2019-02-21 17:21:27 +08:00
|
|
|
|
2019-08-27 10:25:28 +08:00
|
|
|
if ((opt_xsk_frame_size & (opt_xsk_frame_size - 1)) &&
|
|
|
|
!opt_unaligned_chunks) {
|
2019-06-26 22:35:27 +08:00
|
|
|
fprintf(stderr, "--frame-size=%d is not a power of two\n",
|
|
|
|
opt_xsk_frame_size);
|
|
|
|
usage(basename(argv[0]));
|
|
|
|
}
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
static void kick_tx(struct xsk_socket_info *xsk)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
ret = sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0);
|
2018-06-29 15:48:19 +08:00
|
|
|
if (ret >= 0 || errno == ENOBUFS || errno == EAGAIN || errno == EBUSY)
|
2018-05-02 19:01:36 +08:00
|
|
|
return;
|
2019-02-21 17:21:27 +08:00
|
|
|
exit_with_error(errno);
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
static inline void complete_tx_l2fwd(struct xsk_socket_info *xsk,
|
|
|
|
struct pollfd *fds)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-08-27 10:25:29 +08:00
|
|
|
struct xsk_umem_info *umem = xsk->umem;
|
2019-03-01 14:19:41 +08:00
|
|
|
u32 idx_cq = 0, idx_fq = 0;
|
2018-05-02 19:01:36 +08:00
|
|
|
unsigned int rcvd;
|
|
|
|
size_t ndescs;
|
|
|
|
|
|
|
|
if (!xsk->outstanding_tx)
|
|
|
|
return;
|
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
if (!opt_need_wakeup || xsk_ring_prod__needs_wakeup(&xsk->tx))
|
|
|
|
kick_tx(xsk);
|
|
|
|
|
2019-12-20 16:55:27 +08:00
|
|
|
ndescs = (xsk->outstanding_tx > opt_batch_size) ? opt_batch_size :
|
2019-02-21 17:21:27 +08:00
|
|
|
xsk->outstanding_tx;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
/* re-add completed Tx buffers */
|
2019-08-27 10:25:29 +08:00
|
|
|
rcvd = xsk_ring_cons__peek(&umem->cq, ndescs, &idx_cq);
|
2018-05-02 19:01:36 +08:00
|
|
|
if (rcvd > 0) {
|
2019-02-21 17:21:27 +08:00
|
|
|
unsigned int i;
|
|
|
|
int ret;
|
|
|
|
|
2019-08-27 10:25:29 +08:00
|
|
|
ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq);
|
2019-02-21 17:21:27 +08:00
|
|
|
while (ret != rcvd) {
|
|
|
|
if (ret < 0)
|
|
|
|
exit_with_error(-ret);
|
2019-08-27 10:25:29 +08:00
|
|
|
if (xsk_ring_prod__needs_wakeup(&umem->fq))
|
2019-08-14 15:27:21 +08:00
|
|
|
ret = poll(fds, num_socks, opt_timeout);
|
2019-08-27 10:25:29 +08:00
|
|
|
ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq);
|
2019-02-21 17:21:27 +08:00
|
|
|
}
|
2019-08-27 10:25:29 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
for (i = 0; i < rcvd; i++)
|
2019-08-27 10:25:29 +08:00
|
|
|
*xsk_ring_prod__fill_addr(&umem->fq, idx_fq++) =
|
|
|
|
*xsk_ring_cons__comp_addr(&umem->cq, idx_cq++);
|
2019-02-21 17:21:27 +08:00
|
|
|
|
|
|
|
xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
|
|
|
|
xsk_ring_cons__release(&xsk->umem->cq, rcvd);
|
2018-05-02 19:01:36 +08:00
|
|
|
xsk->outstanding_tx -= rcvd;
|
|
|
|
xsk->tx_npkts += rcvd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
static inline void complete_tx_only(struct xsk_socket_info *xsk)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
|
|
|
unsigned int rcvd;
|
2019-02-21 17:21:27 +08:00
|
|
|
u32 idx;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
if (!xsk->outstanding_tx)
|
|
|
|
return;
|
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
if (!opt_need_wakeup || xsk_ring_prod__needs_wakeup(&xsk->tx))
|
|
|
|
kick_tx(xsk);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-12-20 16:55:27 +08:00
|
|
|
rcvd = xsk_ring_cons__peek(&xsk->umem->cq, opt_batch_size, &idx);
|
2018-05-02 19:01:36 +08:00
|
|
|
if (rcvd > 0) {
|
2019-02-21 17:21:27 +08:00
|
|
|
xsk_ring_cons__release(&xsk->umem->cq, rcvd);
|
2018-05-02 19:01:36 +08:00
|
|
|
xsk->outstanding_tx -= rcvd;
|
|
|
|
xsk->tx_npkts += rcvd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
static void rx_drop(struct xsk_socket_info *xsk, struct pollfd *fds)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
|
|
|
unsigned int rcvd, i;
|
2019-03-01 14:19:41 +08:00
|
|
|
u32 idx_rx = 0, idx_fq = 0;
|
2019-02-21 17:21:27 +08:00
|
|
|
int ret;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-12-20 16:55:27 +08:00
|
|
|
rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx);
|
2019-08-14 15:27:21 +08:00
|
|
|
if (!rcvd) {
|
|
|
|
if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq))
|
|
|
|
ret = poll(fds, num_socks, opt_timeout);
|
2018-05-02 19:01:36 +08:00
|
|
|
return;
|
2019-08-14 15:27:21 +08:00
|
|
|
}
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
|
|
|
|
while (ret != rcvd) {
|
|
|
|
if (ret < 0)
|
|
|
|
exit_with_error(-ret);
|
2019-08-14 15:27:21 +08:00
|
|
|
if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq))
|
|
|
|
ret = poll(fds, num_socks, opt_timeout);
|
2019-02-21 17:21:27 +08:00
|
|
|
ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
|
|
|
|
}
|
|
|
|
|
2018-05-02 19:01:36 +08:00
|
|
|
for (i = 0; i < rcvd; i++) {
|
2019-02-21 17:21:27 +08:00
|
|
|
u64 addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr;
|
|
|
|
u32 len = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++)->len;
|
2019-08-27 10:25:29 +08:00
|
|
|
u64 orig = xsk_umem__extract_addr(addr);
|
|
|
|
|
|
|
|
addr = xsk_umem__add_offset_to_addr(addr);
|
2019-02-21 17:21:27 +08:00
|
|
|
char *pkt = xsk_umem__get_data(xsk->umem->buffer, addr);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
hex_dump(pkt, len, addr);
|
2019-08-27 10:25:29 +08:00
|
|
|
*xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = orig;
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
|
|
|
|
xsk_ring_cons__release(&xsk->rx, rcvd);
|
2018-05-02 19:01:36 +08:00
|
|
|
xsk->rx_npkts += rcvd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rx_drop_all(void)
|
|
|
|
{
|
2019-11-08 01:47:37 +08:00
|
|
|
struct pollfd fds[MAX_SOCKS] = {};
|
2019-08-14 15:27:21 +08:00
|
|
|
int i, ret;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
for (i = 0; i < num_socks; i++) {
|
2019-02-21 17:21:27 +08:00
|
|
|
fds[i].fd = xsk_socket__fd(xsks[i]->xsk);
|
2018-05-02 19:01:36 +08:00
|
|
|
fds[i].events = POLLIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (opt_poll) {
|
2019-08-14 15:27:21 +08:00
|
|
|
ret = poll(fds, num_socks, opt_timeout);
|
2018-05-02 19:01:36 +08:00
|
|
|
if (ret <= 0)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < num_socks; i++)
|
2019-08-14 15:27:21 +08:00
|
|
|
rx_drop(xsks[i], fds);
|
2019-12-20 16:55:25 +08:00
|
|
|
|
|
|
|
if (benchmark_done)
|
|
|
|
break;
|
2019-08-14 15:27:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tx_only(struct xsk_socket_info *xsk, u32 frame_nb)
|
|
|
|
{
|
|
|
|
u32 idx;
|
2019-12-20 16:55:27 +08:00
|
|
|
unsigned int i;
|
2019-08-14 15:27:21 +08:00
|
|
|
|
2019-12-20 16:55:27 +08:00
|
|
|
while (xsk_ring_prod__reserve(&xsk->tx, opt_batch_size, &idx) <
|
|
|
|
opt_batch_size) {
|
|
|
|
complete_tx_only(xsk);
|
|
|
|
}
|
2019-08-14 15:27:21 +08:00
|
|
|
|
2019-12-20 16:55:27 +08:00
|
|
|
for (i = 0; i < opt_batch_size; i++) {
|
|
|
|
struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx,
|
|
|
|
idx + i);
|
|
|
|
tx_desc->addr = (frame_nb + i) << XSK_UMEM__DEFAULT_FRAME_SHIFT;
|
|
|
|
tx_desc->len = sizeof(pkt_data) - 1;
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
2019-08-14 15:27:21 +08:00
|
|
|
|
2019-12-20 16:55:27 +08:00
|
|
|
xsk_ring_prod__submit(&xsk->tx, opt_batch_size);
|
|
|
|
xsk->outstanding_tx += opt_batch_size;
|
|
|
|
frame_nb += opt_batch_size;
|
|
|
|
frame_nb %= NUM_FRAMES;
|
2019-08-14 15:27:21 +08:00
|
|
|
complete_tx_only(xsk);
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
static void tx_only_all(void)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-11-08 01:47:37 +08:00
|
|
|
struct pollfd fds[MAX_SOCKS] = {};
|
2019-08-14 15:27:21 +08:00
|
|
|
u32 frame_nb[MAX_SOCKS] = {};
|
|
|
|
int i, ret;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
for (i = 0; i < num_socks; i++) {
|
|
|
|
fds[0].fd = xsk_socket__fd(xsks[i]->xsk);
|
|
|
|
fds[0].events = POLLOUT;
|
|
|
|
}
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (opt_poll) {
|
2019-08-14 15:27:21 +08:00
|
|
|
ret = poll(fds, num_socks, opt_timeout);
|
2018-05-02 19:01:36 +08:00
|
|
|
if (ret <= 0)
|
|
|
|
continue;
|
|
|
|
|
2019-02-21 17:21:27 +08:00
|
|
|
if (!(fds[0].revents & POLLOUT))
|
2018-05-02 19:01:36 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
for (i = 0; i < num_socks; i++)
|
|
|
|
tx_only(xsks[i], frame_nb[i]);
|
2019-12-20 16:55:25 +08:00
|
|
|
|
|
|
|
if (benchmark_done)
|
|
|
|
break;
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
static void l2fwd(struct xsk_socket_info *xsk, struct pollfd *fds)
|
2018-05-02 19:01:36 +08:00
|
|
|
{
|
2019-08-14 15:27:21 +08:00
|
|
|
unsigned int rcvd, i;
|
|
|
|
u32 idx_rx = 0, idx_tx = 0;
|
|
|
|
int ret;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
complete_tx_l2fwd(xsk, fds);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-12-20 16:55:27 +08:00
|
|
|
rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx);
|
2019-08-14 15:27:21 +08:00
|
|
|
if (!rcvd) {
|
|
|
|
if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq))
|
|
|
|
ret = poll(fds, num_socks, opt_timeout);
|
|
|
|
return;
|
|
|
|
}
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
ret = xsk_ring_prod__reserve(&xsk->tx, rcvd, &idx_tx);
|
|
|
|
while (ret != rcvd) {
|
|
|
|
if (ret < 0)
|
|
|
|
exit_with_error(-ret);
|
|
|
|
if (xsk_ring_prod__needs_wakeup(&xsk->tx))
|
|
|
|
kick_tx(xsk);
|
2019-02-21 17:21:27 +08:00
|
|
|
ret = xsk_ring_prod__reserve(&xsk->tx, rcvd, &idx_tx);
|
2019-08-14 15:27:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < rcvd; i++) {
|
|
|
|
u64 addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr;
|
|
|
|
u32 len = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++)->len;
|
2019-09-13 18:39:48 +08:00
|
|
|
u64 orig = addr;
|
2019-08-27 10:25:29 +08:00
|
|
|
|
|
|
|
addr = xsk_umem__add_offset_to_addr(addr);
|
2019-08-14 15:27:21 +08:00
|
|
|
char *pkt = xsk_umem__get_data(xsk->umem->buffer, addr);
|
|
|
|
|
|
|
|
swap_mac_addresses(pkt);
|
2019-02-21 17:21:27 +08:00
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
hex_dump(pkt, len, addr);
|
2019-08-27 10:25:29 +08:00
|
|
|
xsk_ring_prod__tx_desc(&xsk->tx, idx_tx)->addr = orig;
|
2019-08-14 15:27:21 +08:00
|
|
|
xsk_ring_prod__tx_desc(&xsk->tx, idx_tx++)->len = len;
|
|
|
|
}
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
xsk_ring_prod__submit(&xsk->tx, rcvd);
|
|
|
|
xsk_ring_cons__release(&xsk->rx, rcvd);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
xsk->rx_npkts += rcvd;
|
|
|
|
xsk->outstanding_tx += rcvd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2fwd_all(void)
|
|
|
|
{
|
2019-11-08 01:47:37 +08:00
|
|
|
struct pollfd fds[MAX_SOCKS] = {};
|
2019-08-14 15:27:21 +08:00
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
for (i = 0; i < num_socks; i++) {
|
|
|
|
fds[i].fd = xsk_socket__fd(xsks[i]->xsk);
|
|
|
|
fds[i].events = POLLOUT | POLLIN;
|
|
|
|
}
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
for (;;) {
|
|
|
|
if (opt_poll) {
|
|
|
|
ret = poll(fds, num_socks, opt_timeout);
|
|
|
|
if (ret <= 0)
|
|
|
|
continue;
|
|
|
|
}
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-08-14 15:27:21 +08:00
|
|
|
for (i = 0; i < num_socks; i++)
|
|
|
|
l2fwd(xsks[i], fds);
|
2019-12-20 16:55:25 +08:00
|
|
|
|
|
|
|
if (benchmark_done)
|
|
|
|
break;
|
2018-05-02 19:01:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-08 01:47:37 +08:00
|
|
|
static void load_xdp_program(char **argv, struct bpf_object **obj)
|
|
|
|
{
|
|
|
|
struct bpf_prog_load_attr prog_load_attr = {
|
|
|
|
.prog_type = BPF_PROG_TYPE_XDP,
|
|
|
|
};
|
|
|
|
char xdp_filename[256];
|
|
|
|
int prog_fd;
|
|
|
|
|
|
|
|
snprintf(xdp_filename, sizeof(xdp_filename), "%s_kern.o", argv[0]);
|
|
|
|
prog_load_attr.file = xdp_filename;
|
|
|
|
|
|
|
|
if (bpf_prog_load_xattr(&prog_load_attr, obj, &prog_fd))
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
if (prog_fd < 0) {
|
|
|
|
fprintf(stderr, "ERROR: no program found: %s\n",
|
|
|
|
strerror(prog_fd));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bpf_set_link_xdp_fd(opt_ifindex, prog_fd, opt_xdp_flags) < 0) {
|
|
|
|
fprintf(stderr, "ERROR: link set xdp fd failed\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void enter_xsks_into_map(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
struct bpf_map *map;
|
|
|
|
int i, xsks_map;
|
|
|
|
|
|
|
|
map = bpf_object__find_map_by_name(obj, "xsks_map");
|
|
|
|
xsks_map = bpf_map__fd(map);
|
|
|
|
if (xsks_map < 0) {
|
|
|
|
fprintf(stderr, "ERROR: no xsks map found: %s\n",
|
|
|
|
strerror(xsks_map));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < num_socks; i++) {
|
|
|
|
int fd = xsk_socket__fd(xsks[i]->xsk);
|
|
|
|
int key, ret;
|
|
|
|
|
|
|
|
key = i;
|
|
|
|
ret = bpf_map_update_elem(xsks_map, &key, &fd, 0);
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "ERROR: bpf_map_update_elem %d\n", i);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-02 19:01:36 +08:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
|
2019-11-08 01:47:39 +08:00
|
|
|
bool rx = false, tx = false;
|
2019-02-21 17:21:27 +08:00
|
|
|
struct xsk_umem_info *umem;
|
2019-11-08 01:47:37 +08:00
|
|
|
struct bpf_object *obj;
|
2018-05-02 19:01:36 +08:00
|
|
|
pthread_t pt;
|
2019-11-08 01:47:37 +08:00
|
|
|
int i, ret;
|
2019-02-21 17:21:27 +08:00
|
|
|
void *bufs;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
parse_command_line(argc, argv);
|
|
|
|
|
|
|
|
if (setrlimit(RLIMIT_MEMLOCK, &r)) {
|
|
|
|
fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n",
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2019-11-08 01:47:37 +08:00
|
|
|
if (opt_num_xsks > 1)
|
|
|
|
load_xdp_program(argv, &obj);
|
|
|
|
|
2019-08-27 10:25:30 +08:00
|
|
|
/* Reserve memory for the umem. Use hugepages if unaligned chunk mode */
|
|
|
|
bufs = mmap(NULL, NUM_FRAMES * opt_xsk_frame_size,
|
|
|
|
PROT_READ | PROT_WRITE,
|
|
|
|
MAP_PRIVATE | MAP_ANONYMOUS | opt_mmap_flags, -1, 0);
|
|
|
|
if (bufs == MAP_FAILED) {
|
|
|
|
printf("ERROR: mmap failed\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2019-11-08 01:47:37 +08:00
|
|
|
|
|
|
|
/* Create sockets... */
|
2019-06-26 22:35:27 +08:00
|
|
|
umem = xsk_configure_umem(bufs, NUM_FRAMES * opt_xsk_frame_size);
|
2019-11-08 01:47:39 +08:00
|
|
|
if (opt_bench == BENCH_RXDROP || opt_bench == BENCH_L2FWD) {
|
|
|
|
rx = true;
|
|
|
|
xsk_populate_fill_ring(umem);
|
|
|
|
}
|
|
|
|
if (opt_bench == BENCH_L2FWD || opt_bench == BENCH_TXONLY)
|
|
|
|
tx = true;
|
2019-11-08 01:47:37 +08:00
|
|
|
for (i = 0; i < opt_num_xsks; i++)
|
2019-11-08 01:47:39 +08:00
|
|
|
xsks[num_socks++] = xsk_configure_socket(umem, rx, tx);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-11-08 01:47:39 +08:00
|
|
|
if (opt_bench == BENCH_TXONLY)
|
|
|
|
for (i = 0; i < NUM_FRAMES; i++)
|
|
|
|
gen_eth_frame(umem, i * opt_xsk_frame_size);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-11-08 01:47:37 +08:00
|
|
|
if (opt_num_xsks > 1 && opt_bench != BENCH_TXONLY)
|
|
|
|
enter_xsks_into_map(obj);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
signal(SIGINT, int_exit);
|
|
|
|
signal(SIGTERM, int_exit);
|
|
|
|
signal(SIGABRT, int_exit);
|
|
|
|
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
|
|
|
|
ret = pthread_create(&pt, NULL, poller, NULL);
|
2019-02-21 17:21:27 +08:00
|
|
|
if (ret)
|
|
|
|
exit_with_error(ret);
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
prev_time = get_nsecs();
|
2019-12-20 16:55:25 +08:00
|
|
|
start_time = prev_time;
|
2018-05-02 19:01:36 +08:00
|
|
|
|
|
|
|
if (opt_bench == BENCH_RXDROP)
|
|
|
|
rx_drop_all();
|
|
|
|
else if (opt_bench == BENCH_TXONLY)
|
2019-08-14 15:27:21 +08:00
|
|
|
tx_only_all();
|
2018-05-02 19:01:36 +08:00
|
|
|
else
|
2019-08-14 15:27:21 +08:00
|
|
|
l2fwd_all();
|
2018-05-02 19:01:36 +08:00
|
|
|
|
2019-12-20 16:55:25 +08:00
|
|
|
pthread_join(pt, NULL);
|
|
|
|
|
2019-12-20 16:55:26 +08:00
|
|
|
xdpsock_cleanup();
|
|
|
|
|
2018-05-02 19:01:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|