Merge branch 'AF_XDP selftests improvements & bpf_link'
Maciej Fijalkowski says: ==================== Changes since v4 (all in patch 6): - do not close potentially invalid bpf_link fd (Toke) - fix misspelling in label (Toke) - mask out XDP_FLAGS_UPDATE_IF_NOEXIST and XDP_FLAGS_REPLACE explicitly when creating bpf_link (Toke) Changes since v3: - do not unload netlink-based XDP prog when updating map elem failed and current socket was not the creator of XDP resources (Toke) - pull out code paths based on prog_id value within __xsk_setup_xdp_prog so that teardown in case of error at any point is more clear Changes since v2: - fix c&p failure in veth's get_channels implementation (Magnus) - provide a backward compatibilty if bpf_link is not supported (Andrii) - check for a link type while looking up existing bpf_links (Andrii) Changes since v1: - selftests improvements and test case for bpf_link persistence itself - do not unload netlink-based prog when --force flag is set (John) - simplify return semantics in xsk_link_lookup (John) v4: https://lore.kernel.org/bpf/20210326230938.49998-1-maciej.fijalkowski@intel.com/ v3: https://lore.kernel.org/bpf/20210322205816.65159-1-maciej.fijalkowski@intel.com/ v2: https://lore.kernel.org/bpf/20210311152910.56760-1-maciej.fijalkowski@intel.com/ v1: https://lore.kernel.org/bpf/20210215154638.4627-1-maciej.fijalkowski@intel.com/ -------------------------------------------------- This set is another approach towards addressing the below issue: // load xdp prog and xskmap and add entry to xskmap at idx 10 $ sudo ./xdpsock -i ens801f0 -t -q 10 // add entry to xskmap at idx 11 $ sudo ./xdpsock -i ens801f0 -t -q 11 terminate one of the processes and another one is unable to work due to the fact that the XDP prog was unloaded from interface. Previous attempt was, to put it mildly, a bit broken, as there was no synchronization between updates to additional map, as Bjorn pointed out. See https://lore.kernel.org/netdev/20190603131907.13395-5-maciej.fijalkowski@intel.com/ In the meantime bpf_link was introduced and it seems that it can address the issue of refcounting the XDP prog on interface. Although the bpf_link is the meat of the set, selftests improvements are a bigger part of it. Overall, we've been able to reduce the complexity of xsk selftests by removing a bunch of synchronization resources and simplifying logic and structs. Last but not least, for multiqueue veth working with AF-XDP, ethtool's get_channels API needs to be implemented, so it's also included in that set. Note also that in order to make it work, a commit from bpf tree: veth: store queue_mapping independently of XDP prog presence https://lore.kernel.org/bpf/20210303152903.11172-1-maciej.fijalkowski@intel.com/ is needed. Thanks, Maciej Björn Töpel (3): selftests: xsk: remove thread attribute selftests: xsk: Remove mutex and condition variable selftests: xsk: Remove unused defines ==================== Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
2976706f34
|
@ -218,6 +218,17 @@ static void veth_get_ethtool_stats(struct net_device *dev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void veth_get_channels(struct net_device *dev,
|
||||||
|
struct ethtool_channels *channels)
|
||||||
|
{
|
||||||
|
channels->tx_count = dev->real_num_tx_queues;
|
||||||
|
channels->rx_count = dev->real_num_rx_queues;
|
||||||
|
channels->max_tx = dev->real_num_tx_queues;
|
||||||
|
channels->max_rx = dev->real_num_rx_queues;
|
||||||
|
channels->combined_count = min(dev->real_num_rx_queues, dev->real_num_tx_queues);
|
||||||
|
channels->max_combined = min(dev->real_num_rx_queues, dev->real_num_tx_queues);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct ethtool_ops veth_ethtool_ops = {
|
static const struct ethtool_ops veth_ethtool_ops = {
|
||||||
.get_drvinfo = veth_get_drvinfo,
|
.get_drvinfo = veth_get_drvinfo,
|
||||||
.get_link = ethtool_op_get_link,
|
.get_link = ethtool_op_get_link,
|
||||||
|
@ -226,6 +237,7 @@ static const struct ethtool_ops veth_ethtool_ops = {
|
||||||
.get_ethtool_stats = veth_get_ethtool_stats,
|
.get_ethtool_stats = veth_get_ethtool_stats,
|
||||||
.get_link_ksettings = veth_get_link_ksettings,
|
.get_link_ksettings = veth_get_link_ksettings,
|
||||||
.get_ts_info = ethtool_op_get_ts_info,
|
.get_ts_info = ethtool_op_get_ts_info,
|
||||||
|
.get_channels = veth_get_channels,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* general routines */
|
/* general routines */
|
||||||
|
|
|
@ -96,7 +96,6 @@ static int opt_xsk_frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
|
||||||
static int opt_timeout = 1000;
|
static int opt_timeout = 1000;
|
||||||
static bool opt_need_wakeup = true;
|
static bool opt_need_wakeup = true;
|
||||||
static u32 opt_num_xsks = 1;
|
static u32 opt_num_xsks = 1;
|
||||||
static u32 prog_id;
|
|
||||||
static bool opt_busy_poll;
|
static bool opt_busy_poll;
|
||||||
static bool opt_reduced_cap;
|
static bool opt_reduced_cap;
|
||||||
|
|
||||||
|
@ -462,59 +461,37 @@ static void *poller(void *arg)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_xdp_program(void)
|
|
||||||
{
|
|
||||||
u32 curr_prog_id = 0;
|
|
||||||
int cmd = CLOSE_CONN;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
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");
|
|
||||||
|
|
||||||
if (opt_reduced_cap) {
|
|
||||||
if (write(sock, &cmd, sizeof(int)) < 0) {
|
|
||||||
fprintf(stderr, "Error writing into stream socket: %s", strerror(errno));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void int_exit(int sig)
|
static void int_exit(int sig)
|
||||||
{
|
{
|
||||||
benchmark_done = true;
|
benchmark_done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xdpsock_cleanup(void)
|
|
||||||
{
|
|
||||||
struct xsk_umem *umem = xsks[0]->umem->umem;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
dump_stats();
|
|
||||||
for (i = 0; i < num_socks; i++)
|
|
||||||
xsk_socket__delete(xsks[i]->xsk);
|
|
||||||
(void)xsk_umem__delete(umem);
|
|
||||||
remove_xdp_program();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit_with_error(int error, const char *file, const char *func,
|
static void __exit_with_error(int error, const char *file, const char *func,
|
||||||
int line)
|
int line)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s:%s:%i: errno: %d/\"%s\"\n", file, func,
|
fprintf(stderr, "%s:%s:%i: errno: %d/\"%s\"\n", file, func,
|
||||||
line, error, strerror(error));
|
line, error, strerror(error));
|
||||||
dump_stats();
|
|
||||||
remove_xdp_program();
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, \
|
#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)
|
||||||
__LINE__)
|
|
||||||
|
static void xdpsock_cleanup(void)
|
||||||
|
{
|
||||||
|
struct xsk_umem *umem = xsks[0]->umem->umem;
|
||||||
|
int i, cmd = CLOSE_CONN;
|
||||||
|
|
||||||
|
dump_stats();
|
||||||
|
for (i = 0; i < num_socks; i++)
|
||||||
|
xsk_socket__delete(xsks[i]->xsk);
|
||||||
|
(void)xsk_umem__delete(umem);
|
||||||
|
|
||||||
|
if (opt_reduced_cap) {
|
||||||
|
if (write(sock, &cmd, sizeof(int)) < 0)
|
||||||
|
exit_with_error(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void swap_mac_addresses(void *data)
|
static void swap_mac_addresses(void *data)
|
||||||
{
|
{
|
||||||
struct ether_header *eth = (struct ether_header *)data;
|
struct ether_header *eth = (struct ether_header *)data;
|
||||||
|
@ -880,10 +857,6 @@ static struct xsk_socket_info *xsk_configure_socket(struct xsk_umem_info *umem,
|
||||||
if (ret)
|
if (ret)
|
||||||
exit_with_error(-ret);
|
exit_with_error(-ret);
|
||||||
|
|
||||||
ret = bpf_get_link_xdp_id(opt_ifindex, &prog_id, opt_xdp_flags);
|
|
||||||
if (ret)
|
|
||||||
exit_with_error(-ret);
|
|
||||||
|
|
||||||
xsk->app_stats.rx_empty_polls = 0;
|
xsk->app_stats.rx_empty_polls = 0;
|
||||||
xsk->app_stats.fill_fail_polls = 0;
|
xsk->app_stats.fill_fail_polls = 0;
|
||||||
xsk->app_stats.copy_tx_sendtos = 0;
|
xsk->app_stats.copy_tx_sendtos = 0;
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <linux/if_link.h>
|
||||||
|
|
||||||
#include "bpf.h"
|
#include "bpf.h"
|
||||||
#include "libbpf.h"
|
#include "libbpf.h"
|
||||||
|
@ -70,8 +71,10 @@ struct xsk_ctx {
|
||||||
int ifindex;
|
int ifindex;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
int prog_fd;
|
int prog_fd;
|
||||||
|
int link_fd;
|
||||||
int xsks_map_fd;
|
int xsks_map_fd;
|
||||||
char ifname[IFNAMSIZ];
|
char ifname[IFNAMSIZ];
|
||||||
|
bool has_bpf_link;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct xsk_socket {
|
struct xsk_socket {
|
||||||
|
@ -409,7 +412,7 @@ static int xsk_load_xdp_prog(struct xsk_socket *xsk)
|
||||||
static const int log_buf_size = 16 * 1024;
|
static const int log_buf_size = 16 * 1024;
|
||||||
struct xsk_ctx *ctx = xsk->ctx;
|
struct xsk_ctx *ctx = xsk->ctx;
|
||||||
char log_buf[log_buf_size];
|
char log_buf[log_buf_size];
|
||||||
int err, prog_fd;
|
int prog_fd;
|
||||||
|
|
||||||
/* This is the fallback C-program:
|
/* This is the fallback C-program:
|
||||||
* SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
|
* SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
|
||||||
|
@ -499,14 +502,41 @@ static int xsk_load_xdp_prog(struct xsk_socket *xsk)
|
||||||
return prog_fd;
|
return prog_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bpf_set_link_xdp_fd(xsk->ctx->ifindex, prog_fd,
|
ctx->prog_fd = prog_fd;
|
||||||
xsk->config.xdp_flags);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xsk_create_bpf_link(struct xsk_socket *xsk)
|
||||||
|
{
|
||||||
|
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);
|
||||||
|
struct xsk_ctx *ctx = xsk->ctx;
|
||||||
|
__u32 prog_id = 0;
|
||||||
|
int link_fd;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = bpf_get_link_xdp_id(ctx->ifindex, &prog_id, xsk->config.xdp_flags);
|
||||||
if (err) {
|
if (err) {
|
||||||
close(prog_fd);
|
pr_warn("getting XDP prog id failed\n");
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->prog_fd = prog_fd;
|
/* if there's a netlink-based XDP prog loaded on interface, bail out
|
||||||
|
* and ask user to do the removal by himself
|
||||||
|
*/
|
||||||
|
if (prog_id) {
|
||||||
|
pr_warn("Netlink-based XDP prog detected, please unload it in order to launch AF_XDP prog\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.flags = xsk->config.xdp_flags & ~(XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_REPLACE);
|
||||||
|
|
||||||
|
link_fd = bpf_link_create(ctx->prog_fd, ctx->ifindex, BPF_XDP, &opts);
|
||||||
|
if (link_fd < 0) {
|
||||||
|
pr_warn("bpf_link_create failed: %s\n", strerror(errno));
|
||||||
|
return link_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->link_fd = link_fd;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,7 +655,6 @@ static int xsk_lookup_bpf_maps(struct xsk_socket *xsk)
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = 0;
|
|
||||||
if (ctx->xsks_map_fd == -1)
|
if (ctx->xsks_map_fd == -1)
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
|
|
||||||
|
@ -642,6 +671,98 @@ static int xsk_set_bpf_maps(struct xsk_socket *xsk)
|
||||||
&xsk->fd, 0);
|
&xsk->fd, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int xsk_link_lookup(int ifindex, __u32 *prog_id, int *link_fd)
|
||||||
|
{
|
||||||
|
struct bpf_link_info link_info;
|
||||||
|
__u32 link_len;
|
||||||
|
__u32 id = 0;
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
err = bpf_link_get_next_id(id, &id);
|
||||||
|
if (err) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
err = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pr_warn("can't get next link: %s\n", strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = bpf_link_get_fd_by_id(id);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (errno == ENOENT)
|
||||||
|
continue;
|
||||||
|
pr_warn("can't get link by id (%u): %s\n", id, strerror(errno));
|
||||||
|
err = -errno;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
link_len = sizeof(struct bpf_link_info);
|
||||||
|
memset(&link_info, 0, link_len);
|
||||||
|
err = bpf_obj_get_info_by_fd(fd, &link_info, &link_len);
|
||||||
|
if (err) {
|
||||||
|
pr_warn("can't get link info: %s\n", strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (link_info.type == BPF_LINK_TYPE_XDP) {
|
||||||
|
if (link_info.xdp.ifindex == ifindex) {
|
||||||
|
*link_fd = fd;
|
||||||
|
if (prog_id)
|
||||||
|
*prog_id = link_info.prog_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xsk_probe_bpf_link(void)
|
||||||
|
{
|
||||||
|
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts,
|
||||||
|
.flags = XDP_FLAGS_SKB_MODE);
|
||||||
|
struct bpf_load_program_attr prog_attr;
|
||||||
|
struct bpf_insn insns[2] = {
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, XDP_PASS),
|
||||||
|
BPF_EXIT_INSN()
|
||||||
|
};
|
||||||
|
int prog_fd, link_fd = -1;
|
||||||
|
int ifindex_lo = 1;
|
||||||
|
bool ret = false;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = xsk_link_lookup(ifindex_lo, NULL, &link_fd);
|
||||||
|
if (err)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (link_fd >= 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
memset(&prog_attr, 0, sizeof(prog_attr));
|
||||||
|
prog_attr.prog_type = BPF_PROG_TYPE_XDP;
|
||||||
|
prog_attr.insns = insns;
|
||||||
|
prog_attr.insns_cnt = ARRAY_SIZE(insns);
|
||||||
|
prog_attr.license = "GPL";
|
||||||
|
|
||||||
|
prog_fd = bpf_load_program_xattr(&prog_attr, NULL, 0);
|
||||||
|
if (prog_fd < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
link_fd = bpf_link_create(prog_fd, ifindex_lo, BPF_XDP, &opts);
|
||||||
|
close(prog_fd);
|
||||||
|
|
||||||
|
if (link_fd >= 0) {
|
||||||
|
ret = true;
|
||||||
|
close(link_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int xsk_create_xsk_struct(int ifindex, struct xsk_socket *xsk)
|
static int xsk_create_xsk_struct(int ifindex, struct xsk_socket *xsk)
|
||||||
{
|
{
|
||||||
char ifname[IFNAMSIZ];
|
char ifname[IFNAMSIZ];
|
||||||
|
@ -663,65 +784,109 @@ static int xsk_create_xsk_struct(int ifindex, struct xsk_socket *xsk)
|
||||||
ctx->ifname[IFNAMSIZ - 1] = 0;
|
ctx->ifname[IFNAMSIZ - 1] = 0;
|
||||||
|
|
||||||
xsk->ctx = ctx;
|
xsk->ctx = ctx;
|
||||||
|
xsk->ctx->has_bpf_link = xsk_probe_bpf_link();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __xsk_setup_xdp_prog(struct xsk_socket *_xdp,
|
static int xsk_init_xdp_res(struct xsk_socket *xsk,
|
||||||
int *xsks_map_fd)
|
int *xsks_map_fd)
|
||||||
|
{
|
||||||
|
struct xsk_ctx *ctx = xsk->ctx;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = xsk_create_bpf_maps(xsk);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = xsk_load_xdp_prog(xsk);
|
||||||
|
if (err)
|
||||||
|
goto err_load_xdp_prog;
|
||||||
|
|
||||||
|
if (ctx->has_bpf_link)
|
||||||
|
err = xsk_create_bpf_link(xsk);
|
||||||
|
else
|
||||||
|
err = bpf_set_link_xdp_fd(xsk->ctx->ifindex, ctx->prog_fd,
|
||||||
|
xsk->config.xdp_flags);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
goto err_attach_xdp_prog;
|
||||||
|
|
||||||
|
if (!xsk->rx)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = xsk_set_bpf_maps(xsk);
|
||||||
|
if (err)
|
||||||
|
goto err_set_bpf_maps;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err_set_bpf_maps:
|
||||||
|
if (ctx->has_bpf_link)
|
||||||
|
close(ctx->link_fd);
|
||||||
|
else
|
||||||
|
bpf_set_link_xdp_fd(ctx->ifindex, -1, 0);
|
||||||
|
err_attach_xdp_prog:
|
||||||
|
close(ctx->prog_fd);
|
||||||
|
err_load_xdp_prog:
|
||||||
|
xsk_delete_bpf_maps(xsk);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xsk_lookup_xdp_res(struct xsk_socket *xsk, int *xsks_map_fd, int prog_id)
|
||||||
|
{
|
||||||
|
struct xsk_ctx *ctx = xsk->ctx;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ctx->prog_fd = bpf_prog_get_fd_by_id(prog_id);
|
||||||
|
if (ctx->prog_fd < 0) {
|
||||||
|
err = -errno;
|
||||||
|
goto err_prog_fd;
|
||||||
|
}
|
||||||
|
err = xsk_lookup_bpf_maps(xsk);
|
||||||
|
if (err)
|
||||||
|
goto err_lookup_maps;
|
||||||
|
|
||||||
|
if (!xsk->rx)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = xsk_set_bpf_maps(xsk);
|
||||||
|
if (err)
|
||||||
|
goto err_set_maps;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err_set_maps:
|
||||||
|
close(ctx->xsks_map_fd);
|
||||||
|
err_lookup_maps:
|
||||||
|
close(ctx->prog_fd);
|
||||||
|
err_prog_fd:
|
||||||
|
if (ctx->has_bpf_link)
|
||||||
|
close(ctx->link_fd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __xsk_setup_xdp_prog(struct xsk_socket *_xdp, int *xsks_map_fd)
|
||||||
{
|
{
|
||||||
struct xsk_socket *xsk = _xdp;
|
struct xsk_socket *xsk = _xdp;
|
||||||
struct xsk_ctx *ctx = xsk->ctx;
|
struct xsk_ctx *ctx = xsk->ctx;
|
||||||
__u32 prog_id = 0;
|
__u32 prog_id = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = bpf_get_link_xdp_id(ctx->ifindex, &prog_id,
|
if (ctx->has_bpf_link)
|
||||||
xsk->config.xdp_flags);
|
err = xsk_link_lookup(ctx->ifindex, &prog_id, &ctx->link_fd);
|
||||||
|
else
|
||||||
|
err = bpf_get_link_xdp_id(ctx->ifindex, &prog_id, xsk->config.xdp_flags);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (!prog_id) {
|
err = !prog_id ? xsk_init_xdp_res(xsk, xsks_map_fd) :
|
||||||
err = xsk_create_bpf_maps(xsk);
|
xsk_lookup_xdp_res(xsk, xsks_map_fd, prog_id);
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
err = xsk_load_xdp_prog(xsk);
|
if (!err && xsks_map_fd)
|
||||||
if (err) {
|
|
||||||
goto err_load_xdp_prog;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx->prog_fd = bpf_prog_get_fd_by_id(prog_id);
|
|
||||||
if (ctx->prog_fd < 0)
|
|
||||||
return -errno;
|
|
||||||
err = xsk_lookup_bpf_maps(xsk);
|
|
||||||
if (err) {
|
|
||||||
close(ctx->prog_fd);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xsk->rx) {
|
|
||||||
err = xsk_set_bpf_maps(xsk);
|
|
||||||
if (err) {
|
|
||||||
if (!prog_id) {
|
|
||||||
goto err_set_bpf_maps;
|
|
||||||
} else {
|
|
||||||
close(ctx->prog_fd);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (xsks_map_fd)
|
|
||||||
*xsks_map_fd = ctx->xsks_map_fd;
|
*xsks_map_fd = ctx->xsks_map_fd;
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_set_bpf_maps:
|
|
||||||
close(ctx->prog_fd);
|
|
||||||
bpf_set_link_xdp_fd(ctx->ifindex, -1, 0);
|
|
||||||
err_load_xdp_prog:
|
|
||||||
xsk_delete_bpf_maps(xsk);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -898,6 +1063,7 @@ int xsk_socket__create_shared(struct xsk_socket **xsk_ptr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xsk->ctx = ctx;
|
xsk->ctx = ctx;
|
||||||
|
xsk->ctx->has_bpf_link = xsk_probe_bpf_link();
|
||||||
|
|
||||||
if (rx) {
|
if (rx) {
|
||||||
err = setsockopt(xsk->fd, SOL_XDP, XDP_RX_RING,
|
err = setsockopt(xsk->fd, SOL_XDP, XDP_RX_RING,
|
||||||
|
@ -1054,6 +1220,8 @@ void xsk_socket__delete(struct xsk_socket *xsk)
|
||||||
if (ctx->prog_fd != -1) {
|
if (ctx->prog_fd != -1) {
|
||||||
xsk_delete_bpf_maps(xsk);
|
xsk_delete_bpf_maps(xsk);
|
||||||
close(ctx->prog_fd);
|
close(ctx->prog_fd);
|
||||||
|
if (ctx->has_bpf_link)
|
||||||
|
close(ctx->link_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = xsk_get_mmap_offsets(xsk->fd, &off);
|
err = xsk_get_mmap_offsets(xsk->fd, &off);
|
||||||
|
|
|
@ -107,7 +107,7 @@ setup_vethPairs() {
|
||||||
echo "setting up ${VETH0}: namespace: ${NS0}"
|
echo "setting up ${VETH0}: namespace: ${NS0}"
|
||||||
fi
|
fi
|
||||||
ip netns add ${NS1}
|
ip netns add ${NS1}
|
||||||
ip link add ${VETH0} type veth peer name ${VETH1}
|
ip link add ${VETH0} numtxqueues 4 numrxqueues 4 type veth peer name ${VETH1} numtxqueues 4 numrxqueues 4
|
||||||
if [ -f /proc/net/if_inet6 ]; then
|
if [ -f /proc/net/if_inet6 ]; then
|
||||||
echo 1 > /proc/sys/net/ipv6/conf/${VETH0}/disable_ipv6
|
echo 1 > /proc/sys/net/ipv6/conf/${VETH0}/disable_ipv6
|
||||||
fi
|
fi
|
||||||
|
@ -118,6 +118,7 @@ setup_vethPairs() {
|
||||||
ip netns exec ${NS1} ip link set ${VETH1} mtu ${MTU}
|
ip netns exec ${NS1} ip link set ${VETH1} mtu ${MTU}
|
||||||
ip link set ${VETH0} mtu ${MTU}
|
ip link set ${VETH0} mtu ${MTU}
|
||||||
ip netns exec ${NS1} ip link set ${VETH1} up
|
ip netns exec ${NS1} ip link set ${VETH1} up
|
||||||
|
ip netns exec ${NS1} ip link set dev lo up
|
||||||
ip link set ${VETH0} up
|
ip link set ${VETH0} up
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,12 @@
|
||||||
* Reduce the size of the RX ring to a fraction of the fill ring size.
|
* Reduce the size of the RX ring to a fraction of the fill ring size.
|
||||||
* iv. fill queue empty
|
* iv. fill queue empty
|
||||||
* Do not populate the fill queue and then try to receive pkts.
|
* Do not populate the fill queue and then try to receive pkts.
|
||||||
|
* f. bpf_link resource persistence
|
||||||
|
* Configure sockets at indexes 0 and 1, run a traffic on queue ids 0,
|
||||||
|
* then remove xsk sockets from queue 0 on both veth interfaces and
|
||||||
|
* finally run a traffic on queues ids 1
|
||||||
*
|
*
|
||||||
* Total tests: 10
|
* Total tests: 12
|
||||||
*
|
*
|
||||||
* Flow:
|
* Flow:
|
||||||
* -----
|
* -----
|
||||||
|
@ -93,6 +97,13 @@ typedef __u16 __sum16;
|
||||||
#include "xdpxceiver.h"
|
#include "xdpxceiver.h"
|
||||||
#include "../kselftest.h"
|
#include "../kselftest.h"
|
||||||
|
|
||||||
|
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)
|
static void __exit_with_error(int error, const char *file, const char *func, int line)
|
||||||
{
|
{
|
||||||
if (configured_mode == TEST_MODE_UNCONFIGURED) {
|
if (configured_mode == TEST_MODE_UNCONFIGURED) {
|
||||||
|
@ -108,27 +119,12 @@ static void __exit_with_error(int error, const char *file, const char *func, int
|
||||||
#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)
|
#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)
|
||||||
|
|
||||||
#define print_ksft_result(void)\
|
#define print_ksft_result(void)\
|
||||||
(ksft_test_result_pass("PASS: %s %s %s%s%s\n", configured_mode ? "DRV" : "SKB",\
|
(ksft_test_result_pass("PASS: %s %s %s%s%s%s\n", configured_mode ? "DRV" : "SKB",\
|
||||||
test_type == TEST_TYPE_POLL ? "POLL" : "NOPOLL",\
|
test_type == TEST_TYPE_POLL ? "POLL" : "NOPOLL",\
|
||||||
test_type == TEST_TYPE_TEARDOWN ? "Socket Teardown" : "",\
|
test_type == TEST_TYPE_TEARDOWN ? "Socket Teardown" : "",\
|
||||||
test_type == TEST_TYPE_BIDI ? "Bi-directional Sockets" : "",\
|
test_type == TEST_TYPE_BIDI ? "Bi-directional Sockets" : "",\
|
||||||
test_type == TEST_TYPE_STATS ? "Stats" : ""))
|
test_type == TEST_TYPE_STATS ? "Stats" : "",\
|
||||||
|
test_type == TEST_TYPE_BPF_RES ? "BPF RES" : ""))
|
||||||
static void pthread_init_mutex(void)
|
|
||||||
{
|
|
||||||
pthread_mutex_init(&sync_mutex, NULL);
|
|
||||||
pthread_mutex_init(&sync_mutex_tx, NULL);
|
|
||||||
pthread_cond_init(&signal_rx_condition, NULL);
|
|
||||||
pthread_cond_init(&signal_tx_condition, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pthread_destroy_mutex(void)
|
|
||||||
{
|
|
||||||
pthread_mutex_destroy(&sync_mutex);
|
|
||||||
pthread_mutex_destroy(&sync_mutex_tx);
|
|
||||||
pthread_cond_destroy(&signal_rx_condition);
|
|
||||||
pthread_cond_destroy(&signal_tx_condition);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *memset32_htonl(void *dest, u32 val, u32 size)
|
static void *memset32_htonl(void *dest, u32 val, u32 size)
|
||||||
{
|
{
|
||||||
|
@ -146,25 +142,12 @@ static void *memset32_htonl(void *dest, u32 val, u32 size)
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This function code has been taken from
|
|
||||||
* Linux kernel lib/checksum.c
|
|
||||||
*/
|
|
||||||
static inline unsigned short from32to16(unsigned int x)
|
|
||||||
{
|
|
||||||
/* add up 16-bit and 16-bit for 16+c bit */
|
|
||||||
x = (x & 0xffff) + (x >> 16);
|
|
||||||
/* add up carry.. */
|
|
||||||
x = (x & 0xffff) + (x >> 16);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fold a partial checksum
|
* Fold a partial checksum
|
||||||
* This function code has been taken from
|
* This function code has been taken from
|
||||||
* Linux kernel include/asm-generic/checksum.h
|
* Linux kernel include/asm-generic/checksum.h
|
||||||
*/
|
*/
|
||||||
static inline __u16 csum_fold(__u32 csum)
|
static __u16 csum_fold(__u32 csum)
|
||||||
{
|
{
|
||||||
u32 sum = (__force u32)csum;
|
u32 sum = (__force u32)csum;
|
||||||
|
|
||||||
|
@ -177,7 +160,7 @@ static inline __u16 csum_fold(__u32 csum)
|
||||||
* This function code has been taken from
|
* This function code has been taken from
|
||||||
* Linux kernel lib/checksum.c
|
* Linux kernel lib/checksum.c
|
||||||
*/
|
*/
|
||||||
static inline u32 from64to32(u64 x)
|
static u32 from64to32(u64 x)
|
||||||
{
|
{
|
||||||
/* add up 32-bit and 32-bit for 32+c bit */
|
/* add up 32-bit and 32-bit for 32+c bit */
|
||||||
x = (x & 0xffffffff) + (x >> 32);
|
x = (x & 0xffffffff) + (x >> 32);
|
||||||
|
@ -186,13 +169,11 @@ static inline u32 from64to32(u64 x)
|
||||||
return (u32)x;
|
return (u32)x;
|
||||||
}
|
}
|
||||||
|
|
||||||
__u32 csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u32 sum);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function code has been taken from
|
* This function code has been taken from
|
||||||
* Linux kernel lib/checksum.c
|
* Linux kernel lib/checksum.c
|
||||||
*/
|
*/
|
||||||
__u32 csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u32 sum)
|
static __u32 csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u32 sum)
|
||||||
{
|
{
|
||||||
unsigned long long s = (__force u32)sum;
|
unsigned long long s = (__force u32)sum;
|
||||||
|
|
||||||
|
@ -210,13 +191,12 @@ __u32 csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u3
|
||||||
* This function has been taken from
|
* This function has been taken from
|
||||||
* Linux kernel include/asm-generic/checksum.h
|
* Linux kernel include/asm-generic/checksum.h
|
||||||
*/
|
*/
|
||||||
static inline __u16
|
static __u16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u32 sum)
|
||||||
csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __u32 sum)
|
|
||||||
{
|
{
|
||||||
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
|
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u16 udp_csum(u32 saddr, u32 daddr, u32 len, u8 proto, u16 *udp_pkt)
|
static u16 udp_csum(u32 saddr, u32 daddr, u32 len, u8 proto, u16 *udp_pkt)
|
||||||
{
|
{
|
||||||
u32 csum = 0;
|
u32 csum = 0;
|
||||||
u32 cnt = 0;
|
u32 cnt = 0;
|
||||||
|
@ -271,9 +251,8 @@ static void gen_eth_frame(struct xsk_umem_info *umem, u64 addr)
|
||||||
memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data, PKT_SIZE);
|
memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data, PKT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xsk_configure_umem(struct ifobject *data, void *buffer, u64 size)
|
static void xsk_configure_umem(struct ifobject *data, void *buffer, int idx)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct xsk_umem_config cfg = {
|
struct xsk_umem_config cfg = {
|
||||||
.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
|
.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
|
||||||
.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
|
.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
|
||||||
|
@ -281,17 +260,22 @@ static void xsk_configure_umem(struct ifobject *data, void *buffer, u64 size)
|
||||||
.frame_headroom = frame_headroom,
|
.frame_headroom = frame_headroom,
|
||||||
.flags = XSK_UMEM__DEFAULT_FLAGS
|
.flags = XSK_UMEM__DEFAULT_FLAGS
|
||||||
};
|
};
|
||||||
|
int size = num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE;
|
||||||
|
struct xsk_umem_info *umem;
|
||||||
|
int ret;
|
||||||
|
|
||||||
data->umem = calloc(1, sizeof(struct xsk_umem_info));
|
umem = calloc(1, sizeof(struct xsk_umem_info));
|
||||||
if (!data->umem)
|
if (!umem)
|
||||||
exit_with_error(errno);
|
exit_with_error(errno);
|
||||||
|
|
||||||
ret = xsk_umem__create(&data->umem->umem, buffer, size,
|
ret = xsk_umem__create(&umem->umem, buffer, size,
|
||||||
&data->umem->fq, &data->umem->cq, &cfg);
|
&umem->fq, &umem->cq, &cfg);
|
||||||
if (ret)
|
if (ret)
|
||||||
exit_with_error(ret);
|
exit_with_error(ret);
|
||||||
|
|
||||||
data->umem->buffer = buffer;
|
umem->buffer = buffer;
|
||||||
|
|
||||||
|
data->umem_arr[idx] = umem;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xsk_populate_fill_ring(struct xsk_umem_info *umem)
|
static void xsk_populate_fill_ring(struct xsk_umem_info *umem)
|
||||||
|
@ -307,18 +291,19 @@ static void xsk_populate_fill_ring(struct xsk_umem_info *umem)
|
||||||
xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS);
|
xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xsk_configure_socket(struct ifobject *ifobject)
|
static int xsk_configure_socket(struct ifobject *ifobject, int idx)
|
||||||
{
|
{
|
||||||
struct xsk_socket_config cfg;
|
struct xsk_socket_config cfg;
|
||||||
|
struct xsk_socket_info *xsk;
|
||||||
struct xsk_ring_cons *rxr;
|
struct xsk_ring_cons *rxr;
|
||||||
struct xsk_ring_prod *txr;
|
struct xsk_ring_prod *txr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ifobject->xsk = calloc(1, sizeof(struct xsk_socket_info));
|
xsk = calloc(1, sizeof(struct xsk_socket_info));
|
||||||
if (!ifobject->xsk)
|
if (!xsk)
|
||||||
exit_with_error(errno);
|
exit_with_error(errno);
|
||||||
|
|
||||||
ifobject->xsk->umem = ifobject->umem;
|
xsk->umem = ifobject->umem;
|
||||||
cfg.rx_size = rxqsize;
|
cfg.rx_size = rxqsize;
|
||||||
cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
|
cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
|
||||||
cfg.libbpf_flags = 0;
|
cfg.libbpf_flags = 0;
|
||||||
|
@ -326,19 +311,20 @@ static int xsk_configure_socket(struct ifobject *ifobject)
|
||||||
cfg.bind_flags = xdp_bind_flags;
|
cfg.bind_flags = xdp_bind_flags;
|
||||||
|
|
||||||
if (test_type != TEST_TYPE_BIDI) {
|
if (test_type != TEST_TYPE_BIDI) {
|
||||||
rxr = (ifobject->fv.vector == rx) ? &ifobject->xsk->rx : NULL;
|
rxr = (ifobject->fv.vector == rx) ? &xsk->rx : NULL;
|
||||||
txr = (ifobject->fv.vector == tx) ? &ifobject->xsk->tx : NULL;
|
txr = (ifobject->fv.vector == tx) ? &xsk->tx : NULL;
|
||||||
} else {
|
} else {
|
||||||
rxr = &ifobject->xsk->rx;
|
rxr = &xsk->rx;
|
||||||
txr = &ifobject->xsk->tx;
|
txr = &xsk->tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = xsk_socket__create(&ifobject->xsk->xsk, ifobject->ifname,
|
ret = xsk_socket__create(&xsk->xsk, ifobject->ifname, idx,
|
||||||
opt_queue, ifobject->umem->umem, rxr, txr, &cfg);
|
ifobject->umem->umem, rxr, txr, &cfg);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
ifobject->xsk_arr[idx] = xsk;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,12 +350,15 @@ static void usage(const char *prog)
|
||||||
ksft_print_msg(str, prog);
|
ksft_print_msg(str, prog);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool switch_namespace(int idx)
|
static int switch_namespace(const char *nsname)
|
||||||
{
|
{
|
||||||
char fqns[26] = "/var/run/netns/";
|
char fqns[26] = "/var/run/netns/";
|
||||||
int nsfd;
|
int nsfd;
|
||||||
|
|
||||||
strncat(fqns, ifdict[idx]->nsname, sizeof(fqns) - strlen(fqns) - 1);
|
if (!nsname || strlen(nsname) == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
strncat(fqns, nsname, sizeof(fqns) - strlen(fqns) - 1);
|
||||||
nsfd = open(fqns, O_RDONLY);
|
nsfd = open(fqns, O_RDONLY);
|
||||||
|
|
||||||
if (nsfd == -1)
|
if (nsfd == -1)
|
||||||
|
@ -378,26 +367,9 @@ static bool switch_namespace(int idx)
|
||||||
if (setns(nsfd, 0) == -1)
|
if (setns(nsfd, 0) == -1)
|
||||||
exit_with_error(errno);
|
exit_with_error(errno);
|
||||||
|
|
||||||
return true;
|
print_verbose("NS switched: %s\n", nsname);
|
||||||
}
|
|
||||||
|
|
||||||
static void *nsswitchthread(void *args)
|
return nsfd;
|
||||||
{
|
|
||||||
struct targs *targs = args;
|
|
||||||
|
|
||||||
targs->retptr = false;
|
|
||||||
|
|
||||||
if (switch_namespace(targs->idx)) {
|
|
||||||
ifdict[targs->idx]->ifindex = if_nametoindex(ifdict[targs->idx]->ifname);
|
|
||||||
if (!ifdict[targs->idx]->ifindex) {
|
|
||||||
ksft_test_result_fail("ERROR: [%s] interface \"%s\" does not exist\n",
|
|
||||||
__func__, ifdict[targs->idx]->ifname);
|
|
||||||
} else {
|
|
||||||
print_verbose("Interface found: %s\n", ifdict[targs->idx]->ifname);
|
|
||||||
targs->retptr = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int validate_interfaces(void)
|
static int validate_interfaces(void)
|
||||||
|
@ -409,33 +381,6 @@ static int validate_interfaces(void)
|
||||||
ret = false;
|
ret = false;
|
||||||
ksft_test_result_fail("ERROR: interfaces: -i <int>,<ns> -i <int>,<ns>.");
|
ksft_test_result_fail("ERROR: interfaces: -i <int>,<ns> -i <int>,<ns>.");
|
||||||
}
|
}
|
||||||
if (strcmp(ifdict[i]->nsname, "")) {
|
|
||||||
struct targs *targs;
|
|
||||||
|
|
||||||
targs = malloc(sizeof(*targs));
|
|
||||||
if (!targs)
|
|
||||||
exit_with_error(errno);
|
|
||||||
|
|
||||||
targs->idx = i;
|
|
||||||
if (pthread_create(&ns_thread, NULL, nsswitchthread, targs))
|
|
||||||
exit_with_error(errno);
|
|
||||||
|
|
||||||
pthread_join(ns_thread, NULL);
|
|
||||||
|
|
||||||
if (targs->retptr)
|
|
||||||
print_verbose("NS switched: %s\n", ifdict[i]->nsname);
|
|
||||||
|
|
||||||
free(targs);
|
|
||||||
} else {
|
|
||||||
ifdict[i]->ifindex = if_nametoindex(ifdict[i]->ifname);
|
|
||||||
if (!ifdict[i]->ifindex) {
|
|
||||||
ksft_test_result_fail
|
|
||||||
("ERROR: interface \"%s\" does not exist\n", ifdict[i]->ifname);
|
|
||||||
ret = false;
|
|
||||||
} else {
|
|
||||||
print_verbose("Interface found: %s\n", ifdict[i]->ifname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -447,7 +392,7 @@ static void parse_command_line(int argc, char **argv)
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = getopt_long(argc, argv, "i:q:DC:v", long_options, &option_index);
|
c = getopt_long(argc, argv, "i:DC:v", long_options, &option_index);
|
||||||
|
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -467,9 +412,6 @@ static void parse_command_line(int argc, char **argv)
|
||||||
MAX_INTERFACES_NAMESPACE_CHARS);
|
MAX_INTERFACES_NAMESPACE_CHARS);
|
||||||
interface_index++;
|
interface_index++;
|
||||||
break;
|
break;
|
||||||
case 'q':
|
|
||||||
opt_queue = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'D':
|
case 'D':
|
||||||
debug_pkt_dump = 1;
|
debug_pkt_dump = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -506,7 +448,7 @@ static void kick_tx(struct xsk_socket_info *xsk)
|
||||||
exit_with_error(errno);
|
exit_with_error(errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void complete_tx_only(struct xsk_socket_info *xsk, int batch_size)
|
static void complete_tx_only(struct xsk_socket_info *xsk, int batch_size)
|
||||||
{
|
{
|
||||||
unsigned int rcvd;
|
unsigned int rcvd;
|
||||||
u32 idx;
|
u32 idx;
|
||||||
|
@ -514,7 +456,7 @@ static inline void complete_tx_only(struct xsk_socket_info *xsk, int batch_size)
|
||||||
if (!xsk->outstanding_tx)
|
if (!xsk->outstanding_tx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!NEED_WAKEUP || xsk_ring_prod__needs_wakeup(&xsk->tx))
|
if (xsk_ring_prod__needs_wakeup(&xsk->tx))
|
||||||
kick_tx(xsk);
|
kick_tx(xsk);
|
||||||
|
|
||||||
rcvd = xsk_ring_cons__peek(&xsk->umem->cq, batch_size, &idx);
|
rcvd = xsk_ring_cons__peek(&xsk->umem->cq, batch_size, &idx);
|
||||||
|
@ -602,8 +544,7 @@ static void tx_only(struct xsk_socket_info *xsk, u32 *frameptr, int batch_size)
|
||||||
xsk_ring_prod__submit(&xsk->tx, batch_size);
|
xsk_ring_prod__submit(&xsk->tx, batch_size);
|
||||||
if (!tx_invalid_test) {
|
if (!tx_invalid_test) {
|
||||||
xsk->outstanding_tx += batch_size;
|
xsk->outstanding_tx += batch_size;
|
||||||
} else {
|
} else if (xsk_ring_prod__needs_wakeup(&xsk->tx)) {
|
||||||
if (!NEED_WAKEUP || xsk_ring_prod__needs_wakeup(&xsk->tx))
|
|
||||||
kick_tx(xsk);
|
kick_tx(xsk);
|
||||||
}
|
}
|
||||||
*frameptr += batch_size;
|
*frameptr += batch_size;
|
||||||
|
@ -611,7 +552,7 @@ static void tx_only(struct xsk_socket_info *xsk, u32 *frameptr, int batch_size)
|
||||||
complete_tx_only(xsk, batch_size);
|
complete_tx_only(xsk, batch_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int get_batch_size(int pkt_cnt)
|
static int get_batch_size(int pkt_cnt)
|
||||||
{
|
{
|
||||||
if (!opt_pkt_count)
|
if (!opt_pkt_count)
|
||||||
return BATCH_SIZE;
|
return BATCH_SIZE;
|
||||||
|
@ -667,45 +608,40 @@ static void tx_only_all(struct ifobject *ifobject)
|
||||||
|
|
||||||
static void worker_pkt_dump(void)
|
static void worker_pkt_dump(void)
|
||||||
{
|
{
|
||||||
struct in_addr ipaddr;
|
struct ethhdr *ethhdr;
|
||||||
|
struct iphdr *iphdr;
|
||||||
|
struct udphdr *udphdr;
|
||||||
|
char s[128];
|
||||||
|
int payload;
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
fprintf(stdout, "---------------------------------------\n");
|
fprintf(stdout, "---------------------------------------\n");
|
||||||
for (int iter = 0; iter < num_frames - 1; iter++) {
|
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 */
|
/*extract L2 frame */
|
||||||
fprintf(stdout, "DEBUG>> L2: dst mac: ");
|
fprintf(stdout, "DEBUG>> L2: dst mac: ");
|
||||||
for (int i = 0; i < ETH_ALEN; i++)
|
for (int i = 0; i < ETH_ALEN; i++)
|
||||||
fprintf(stdout, "%02X", ((struct ethhdr *)
|
fprintf(stdout, "%02X", ethhdr->h_dest[i]);
|
||||||
pkt_buf[iter]->payload)->h_dest[i]);
|
|
||||||
|
|
||||||
fprintf(stdout, "\nDEBUG>> L2: src mac: ");
|
fprintf(stdout, "\nDEBUG>> L2: src mac: ");
|
||||||
for (int i = 0; i < ETH_ALEN; i++)
|
for (int i = 0; i < ETH_ALEN; i++)
|
||||||
fprintf(stdout, "%02X", ((struct ethhdr *)
|
fprintf(stdout, "%02X", ethhdr->h_source[i]);
|
||||||
pkt_buf[iter]->payload)->h_source[i]);
|
|
||||||
|
|
||||||
/*extract L3 frame */
|
/*extract L3 frame */
|
||||||
fprintf(stdout, "\nDEBUG>> L3: ip_hdr->ihl: %02X\n",
|
fprintf(stdout, "\nDEBUG>> L3: ip_hdr->ihl: %02X\n", iphdr->ihl);
|
||||||
((struct iphdr *)(pkt_buf[iter]->payload + sizeof(struct ethhdr)))->ihl);
|
fprintf(stdout, "DEBUG>> L3: ip_hdr->saddr: %s\n",
|
||||||
|
inet_ntop(AF_INET, &iphdr->saddr, s, sizeof(s)));
|
||||||
ipaddr.s_addr =
|
fprintf(stdout, "DEBUG>> L3: ip_hdr->daddr: %s\n",
|
||||||
((struct iphdr *)(pkt_buf[iter]->payload + sizeof(struct ethhdr)))->saddr;
|
inet_ntop(AF_INET, &iphdr->daddr, s, sizeof(s)));
|
||||||
fprintf(stdout, "DEBUG>> L3: ip_hdr->saddr: %s\n", inet_ntoa(ipaddr));
|
|
||||||
|
|
||||||
ipaddr.s_addr =
|
|
||||||
((struct iphdr *)(pkt_buf[iter]->payload + sizeof(struct ethhdr)))->daddr;
|
|
||||||
fprintf(stdout, "DEBUG>> L3: ip_hdr->daddr: %s\n", inet_ntoa(ipaddr));
|
|
||||||
|
|
||||||
/*extract L4 frame */
|
/*extract L4 frame */
|
||||||
fprintf(stdout, "DEBUG>> L4: udp_hdr->src: %d\n",
|
fprintf(stdout, "DEBUG>> L4: udp_hdr->src: %d\n", ntohs(udphdr->source));
|
||||||
ntohs(((struct udphdr *)(pkt_buf[iter]->payload +
|
fprintf(stdout, "DEBUG>> L4: udp_hdr->dst: %d\n", ntohs(udphdr->dest));
|
||||||
sizeof(struct ethhdr) +
|
|
||||||
sizeof(struct iphdr)))->source));
|
|
||||||
|
|
||||||
fprintf(stdout, "DEBUG>> L4: udp_hdr->dst: %d\n",
|
|
||||||
ntohs(((struct udphdr *)(pkt_buf[iter]->payload +
|
|
||||||
sizeof(struct ethhdr) +
|
|
||||||
sizeof(struct iphdr)))->dest));
|
|
||||||
/*extract L5 frame */
|
/*extract L5 frame */
|
||||||
int payload = *((uint32_t *)(pkt_buf[iter]->payload + PKT_HDR_SIZE));
|
payload = *((uint32_t *)(ptr + PKT_HDR_SIZE));
|
||||||
|
|
||||||
if (payload == EOT) {
|
if (payload == EOT) {
|
||||||
print_verbose("End-of-transmission frame received\n");
|
print_verbose("End-of-transmission frame received\n");
|
||||||
|
@ -809,37 +745,69 @@ static void worker_pkt_validate(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void thread_common_ops(struct ifobject *ifobject, void *bufs, pthread_mutex_t *mutexptr,
|
static void thread_common_ops(struct ifobject *ifobject, void *bufs)
|
||||||
atomic_int *spinningptr)
|
|
||||||
{
|
{
|
||||||
|
int umem_sz = num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE;
|
||||||
int ctr = 0;
|
int ctr = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
xsk_configure_umem(ifobject, bufs, num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE);
|
ifobject->ns_fd = switch_namespace(ifobject->nsname);
|
||||||
ret = xsk_configure_socket(ifobject);
|
|
||||||
|
if (test_type == TEST_TYPE_BPF_RES)
|
||||||
|
umem_sz *= 2;
|
||||||
|
|
||||||
|
bufs = mmap(NULL, umem_sz,
|
||||||
|
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -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()
|
/* Retry Create Socket if it fails as xsk_socket__create()
|
||||||
* is asynchronous
|
* is asynchronous
|
||||||
*
|
|
||||||
* Essential to lock Mutex here to prevent Tx thread from
|
|
||||||
* entering before Rx and causing a deadlock
|
|
||||||
*/
|
*/
|
||||||
pthread_mutex_lock(mutexptr);
|
|
||||||
while (ret && ctr < SOCK_RECONF_CTR) {
|
while (ret && ctr < SOCK_RECONF_CTR) {
|
||||||
atomic_store(spinningptr, 1);
|
xsk_configure_umem(ifobject, bufs, 0);
|
||||||
xsk_configure_umem(ifobject, bufs, num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE);
|
ifobject->umem = ifobject->umem_arr[0];
|
||||||
ret = xsk_configure_socket(ifobject);
|
ret = xsk_configure_socket(ifobject, 0);
|
||||||
usleep(USLEEP_MAX);
|
usleep(USLEEP_MAX);
|
||||||
ctr++;
|
ctr++;
|
||||||
}
|
}
|
||||||
atomic_store(spinningptr, 0);
|
|
||||||
pthread_mutex_unlock(mutexptr);
|
|
||||||
|
|
||||||
if (ctr >= SOCK_RECONF_CTR)
|
if (ctr >= SOCK_RECONF_CTR)
|
||||||
exit_with_error(ret);
|
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);
|
||||||
|
ifobject->umem = ifobject->umem_arr[1];
|
||||||
|
ret = xsk_configure_socket(ifobject, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *worker_testapp_validate(void *arg)
|
ifobject->umem = ifobject->umem_arr[0];
|
||||||
|
ifobject->xsk = ifobject->xsk_arr[0];
|
||||||
|
print_verbose("Interface [%s] vector [%s]\n",
|
||||||
|
ifobject->ifname, ifobject->fv.vector == tx ? "Tx" : "Rx");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool testapp_is_test_two_stepped(void)
|
||||||
|
{
|
||||||
|
return (test_type != TEST_TYPE_BIDI && test_type != TEST_TYPE_BPF_RES) || second_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testapp_cleanup_xsk_res(struct ifobject *ifobj)
|
||||||
|
{
|
||||||
|
if (testapp_is_test_two_stepped()) {
|
||||||
|
xsk_socket__delete(ifobj->xsk->xsk);
|
||||||
|
(void)xsk_umem__delete(ifobj->umem->umem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *worker_testapp_validate_tx(void *arg)
|
||||||
{
|
{
|
||||||
struct udphdr *udp_hdr =
|
struct udphdr *udp_hdr =
|
||||||
(struct udphdr *)(pkt_data + sizeof(struct ethhdr) + sizeof(struct iphdr));
|
(struct udphdr *)(pkt_data + sizeof(struct ethhdr) + sizeof(struct iphdr));
|
||||||
|
@ -849,30 +817,9 @@ static void *worker_testapp_validate(void *arg)
|
||||||
struct generic_data data;
|
struct generic_data data;
|
||||||
void *bufs = NULL;
|
void *bufs = NULL;
|
||||||
|
|
||||||
pthread_attr_setstacksize(&attr, THREAD_STACK);
|
if (!second_step)
|
||||||
|
thread_common_ops(ifobject, bufs);
|
||||||
|
|
||||||
if (!bidi_pass) {
|
|
||||||
bufs = mmap(NULL, num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE,
|
|
||||||
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
||||||
if (bufs == MAP_FAILED)
|
|
||||||
exit_with_error(errno);
|
|
||||||
|
|
||||||
if (strcmp(ifobject->nsname, ""))
|
|
||||||
switch_namespace(ifobject->ifdict_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifobject->fv.vector == tx) {
|
|
||||||
int spinningrxctr = 0;
|
|
||||||
|
|
||||||
if (!bidi_pass)
|
|
||||||
thread_common_ops(ifobject, bufs, &sync_mutex_tx, &spinning_tx);
|
|
||||||
|
|
||||||
while (atomic_load(&spinning_rx) && spinningrxctr < SOCK_RECONF_CTR) {
|
|
||||||
spinningrxctr++;
|
|
||||||
usleep(USLEEP_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_verbose("Interface [%s] vector [Tx]\n", ifobject->ifname);
|
|
||||||
for (int i = 0; i < num_frames; i++) {
|
for (int i = 0; i < num_frames; i++) {
|
||||||
/*send EOT frame */
|
/*send EOT frame */
|
||||||
if (i == (num_frames - 1))
|
if (i == (num_frames - 1))
|
||||||
|
@ -889,14 +836,20 @@ static void *worker_testapp_validate(void *arg)
|
||||||
print_verbose("Sending %d packets on interface %s\n",
|
print_verbose("Sending %d packets on interface %s\n",
|
||||||
(opt_pkt_count - 1), ifobject->ifname);
|
(opt_pkt_count - 1), ifobject->ifname);
|
||||||
tx_only_all(ifobject);
|
tx_only_all(ifobject);
|
||||||
} else if (ifobject->fv.vector == rx) {
|
|
||||||
|
testapp_cleanup_xsk_res(ifobject);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *worker_testapp_validate_rx(void *arg)
|
||||||
|
{
|
||||||
|
struct ifobject *ifobject = (struct ifobject *)arg;
|
||||||
struct pollfd fds[MAX_SOCKS] = { };
|
struct pollfd fds[MAX_SOCKS] = { };
|
||||||
int ret;
|
void *bufs = NULL;
|
||||||
|
|
||||||
if (!bidi_pass)
|
if (!second_step)
|
||||||
thread_common_ops(ifobject, bufs, &sync_mutex_tx, &spinning_rx);
|
thread_common_ops(ifobject, bufs);
|
||||||
|
|
||||||
print_verbose("Interface [%s] vector [Rx]\n", ifobject->ifname);
|
|
||||||
if (stat_test_type != STAT_TEST_RX_FILL_EMPTY)
|
if (stat_test_type != STAT_TEST_RX_FILL_EMPTY)
|
||||||
xsk_populate_fill_ring(ifobject->umem);
|
xsk_populate_fill_ring(ifobject->umem);
|
||||||
|
|
||||||
|
@ -910,96 +863,51 @@ static void *worker_testapp_validate(void *arg)
|
||||||
fds[0].fd = xsk_socket__fd(ifobject->xsk->xsk);
|
fds[0].fd = xsk_socket__fd(ifobject->xsk->xsk);
|
||||||
fds[0].events = POLLIN;
|
fds[0].events = POLLIN;
|
||||||
|
|
||||||
pthread_mutex_lock(&sync_mutex);
|
pthread_barrier_wait(&barr);
|
||||||
pthread_cond_signal(&signal_rx_condition);
|
|
||||||
pthread_mutex_unlock(&sync_mutex);
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (test_type == TEST_TYPE_POLL) {
|
|
||||||
ret = poll(fds, 1, POLL_TMOUT);
|
|
||||||
if (ret <= 0)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test_type != TEST_TYPE_STATS) {
|
if (test_type != TEST_TYPE_STATS) {
|
||||||
rx_pkt(ifobject->xsk, fds);
|
rx_pkt(ifobject->xsk, fds);
|
||||||
worker_pkt_validate();
|
worker_pkt_validate();
|
||||||
} else {
|
} else {
|
||||||
worker_stats_validate(ifobject);
|
worker_stats_validate(ifobject);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sigvar)
|
if (sigvar)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_type != TEST_TYPE_STATS)
|
|
||||||
print_verbose("Received %d packets on interface %s\n",
|
print_verbose("Received %d packets on interface %s\n",
|
||||||
pkt_counter, ifobject->ifname);
|
pkt_counter, ifobject->ifname);
|
||||||
|
|
||||||
if (test_type == TEST_TYPE_TEARDOWN)
|
if (test_type == TEST_TYPE_TEARDOWN)
|
||||||
print_verbose("Destroying socket\n");
|
print_verbose("Destroying socket\n");
|
||||||
}
|
|
||||||
|
|
||||||
if ((test_type != TEST_TYPE_BIDI) || bidi_pass) {
|
testapp_cleanup_xsk_res(ifobject);
|
||||||
xsk_socket__delete(ifobject->xsk->xsk);
|
|
||||||
(void)xsk_umem__delete(ifobject->umem->umem);
|
|
||||||
}
|
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testapp_validate(void)
|
static void testapp_validate(void)
|
||||||
{
|
{
|
||||||
struct timespec max_wait = { 0, 0 };
|
|
||||||
bool bidi = test_type == TEST_TYPE_BIDI;
|
bool bidi = test_type == TEST_TYPE_BIDI;
|
||||||
|
bool bpf = test_type == TEST_TYPE_BPF_RES;
|
||||||
|
|
||||||
pthread_attr_init(&attr);
|
if (pthread_barrier_init(&barr, NULL, 2))
|
||||||
pthread_attr_setstacksize(&attr, THREAD_STACK);
|
exit_with_error(errno);
|
||||||
|
|
||||||
if ((test_type == TEST_TYPE_BIDI) && bidi_pass) {
|
|
||||||
pthread_init_mutex();
|
|
||||||
if (!switching_notify) {
|
|
||||||
print_verbose("Switching Tx/Rx vectors\n");
|
|
||||||
switching_notify++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&sync_mutex);
|
|
||||||
|
|
||||||
/*Spawn RX thread */
|
/*Spawn RX thread */
|
||||||
if (!bidi || !bidi_pass) {
|
pthread_create(&t0, NULL, ifdict_rx->func_ptr, ifdict_rx);
|
||||||
if (pthread_create(&t0, &attr, worker_testapp_validate, ifdict[1]))
|
|
||||||
exit_with_error(errno);
|
|
||||||
} else if (bidi && bidi_pass) {
|
|
||||||
/*switch Tx/Rx vectors */
|
|
||||||
ifdict[0]->fv.vector = rx;
|
|
||||||
if (pthread_create(&t0, &attr, worker_testapp_validate, ifdict[0]))
|
|
||||||
exit_with_error(errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clock_gettime(CLOCK_REALTIME, &max_wait))
|
pthread_barrier_wait(&barr);
|
||||||
|
if (pthread_barrier_destroy(&barr))
|
||||||
exit_with_error(errno);
|
exit_with_error(errno);
|
||||||
max_wait.tv_sec += TMOUT_SEC;
|
|
||||||
|
|
||||||
if (pthread_cond_timedwait(&signal_rx_condition, &sync_mutex, &max_wait) == ETIMEDOUT)
|
|
||||||
exit_with_error(errno);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&sync_mutex);
|
|
||||||
|
|
||||||
/*Spawn TX thread */
|
/*Spawn TX thread */
|
||||||
if (!bidi || !bidi_pass) {
|
pthread_create(&t1, NULL, ifdict_tx->func_ptr, ifdict_tx);
|
||||||
if (pthread_create(&t1, &attr, worker_testapp_validate, ifdict[0]))
|
|
||||||
exit_with_error(errno);
|
|
||||||
} else if (bidi && bidi_pass) {
|
|
||||||
/*switch Tx/Rx vectors */
|
|
||||||
ifdict[1]->fv.vector = tx;
|
|
||||||
if (pthread_create(&t1, &attr, worker_testapp_validate, ifdict[1]))
|
|
||||||
exit_with_error(errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_join(t1, NULL);
|
pthread_join(t1, NULL);
|
||||||
pthread_join(t0, NULL);
|
pthread_join(t0, NULL);
|
||||||
|
|
||||||
if (debug_pkt_dump) {
|
if (debug_pkt_dump && test_type != TEST_TYPE_STATS) {
|
||||||
worker_pkt_dump();
|
worker_pkt_dump();
|
||||||
for (int iter = 0; iter < num_frames - 1; iter++) {
|
for (int iter = 0; iter < num_frames - 1; iter++) {
|
||||||
free(pkt_buf[iter]->payload);
|
free(pkt_buf[iter]->payload);
|
||||||
|
@ -1008,20 +916,85 @@ static void testapp_validate(void)
|
||||||
free(pkt_buf);
|
free(pkt_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(test_type == TEST_TYPE_TEARDOWN) && !bidi && !(test_type == TEST_TYPE_STATS))
|
if (!(test_type == TEST_TYPE_TEARDOWN) && !bidi && !bpf && !(test_type == TEST_TYPE_STATS))
|
||||||
print_ksft_result();
|
print_ksft_result();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testapp_sockets(void)
|
static void testapp_teardown(void)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ((test_type == TEST_TYPE_TEARDOWN) ? MAX_TEARDOWN_ITER : MAX_BIDI_ITER);
|
int i;
|
||||||
i++) {
|
|
||||||
|
for (i = 0; i < MAX_TEARDOWN_ITER; i++) {
|
||||||
pkt_counter = 0;
|
pkt_counter = 0;
|
||||||
prev_pkt = -1;
|
prev_pkt = -1;
|
||||||
sigvar = 0;
|
sigvar = 0;
|
||||||
print_verbose("Creating socket\n");
|
print_verbose("Creating socket\n");
|
||||||
testapp_validate();
|
testapp_validate();
|
||||||
test_type == TEST_TYPE_BIDI ? bidi_pass++ : bidi_pass;
|
}
|
||||||
|
|
||||||
|
print_ksft_result();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swap_vectors(struct ifobject *ifobj1, struct ifobject *ifobj2)
|
||||||
|
{
|
||||||
|
void *(*tmp_func_ptr)(void *) = ifobj1->func_ptr;
|
||||||
|
enum fvector tmp_vector = ifobj1->fv.vector;
|
||||||
|
|
||||||
|
ifobj1->func_ptr = ifobj2->func_ptr;
|
||||||
|
ifobj1->fv.vector = ifobj2->fv.vector;
|
||||||
|
|
||||||
|
ifobj2->func_ptr = tmp_func_ptr;
|
||||||
|
ifobj2->fv.vector = tmp_vector;
|
||||||
|
|
||||||
|
ifdict_tx = ifobj1;
|
||||||
|
ifdict_rx = 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) {
|
||||||
|
print_verbose("Switching Tx/Rx vectors\n");
|
||||||
|
swap_vectors(ifdict[1], ifdict[0]);
|
||||||
|
}
|
||||||
|
second_step = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
swap_vectors(ifdict[0], ifdict[1]);
|
||||||
|
|
||||||
|
print_ksft_result();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swap_xsk_res(void)
|
||||||
|
{
|
||||||
|
xsk_socket__delete(ifdict_tx->xsk->xsk);
|
||||||
|
xsk_umem__delete(ifdict_tx->umem->umem);
|
||||||
|
xsk_socket__delete(ifdict_rx->xsk->xsk);
|
||||||
|
xsk_umem__delete(ifdict_rx->umem->umem);
|
||||||
|
ifdict_tx->umem = ifdict_tx->umem_arr[1];
|
||||||
|
ifdict_tx->xsk = ifdict_tx->xsk_arr[1];
|
||||||
|
ifdict_rx->umem = ifdict_rx->umem_arr[1];
|
||||||
|
ifdict_rx->xsk = ifdict_rx->xsk_arr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
swap_xsk_res();
|
||||||
|
second_step = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_ksft_result();
|
print_ksft_result();
|
||||||
|
@ -1053,78 +1026,33 @@ static void testapp_stats(void)
|
||||||
print_ksft_result();
|
print_ksft_result();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_iface_config(struct ifaceconfigobj *ifaceconfig)
|
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, enum fvector vector)
|
||||||
{
|
{
|
||||||
/*Init interface0 */
|
struct in_addr ip;
|
||||||
ifdict[0]->fv.vector = tx;
|
|
||||||
memcpy(ifdict[0]->dst_mac, ifaceconfig->dst_mac, ETH_ALEN);
|
|
||||||
memcpy(ifdict[0]->src_mac, ifaceconfig->src_mac, ETH_ALEN);
|
|
||||||
ifdict[0]->dst_ip = ifaceconfig->dst_ip.s_addr;
|
|
||||||
ifdict[0]->src_ip = ifaceconfig->src_ip.s_addr;
|
|
||||||
ifdict[0]->dst_port = ifaceconfig->dst_port;
|
|
||||||
ifdict[0]->src_port = ifaceconfig->src_port;
|
|
||||||
|
|
||||||
/*Init interface1 */
|
memcpy(ifobj->dst_mac, dst_mac, ETH_ALEN);
|
||||||
ifdict[1]->fv.vector = rx;
|
memcpy(ifobj->src_mac, src_mac, ETH_ALEN);
|
||||||
memcpy(ifdict[1]->dst_mac, ifaceconfig->src_mac, ETH_ALEN);
|
|
||||||
memcpy(ifdict[1]->src_mac, ifaceconfig->dst_mac, ETH_ALEN);
|
|
||||||
ifdict[1]->dst_ip = ifaceconfig->src_ip.s_addr;
|
|
||||||
ifdict[1]->src_ip = ifaceconfig->dst_ip.s_addr;
|
|
||||||
ifdict[1]->dst_port = ifaceconfig->src_port;
|
|
||||||
ifdict[1]->src_port = ifaceconfig->dst_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *nsdisablemodethread(void *args)
|
inet_aton(dst_ip, &ip);
|
||||||
{
|
ifobj->dst_ip = ip.s_addr;
|
||||||
struct targs *targs = args;
|
|
||||||
|
|
||||||
targs->retptr = false;
|
inet_aton(src_ip, &ip);
|
||||||
|
ifobj->src_ip = ip.s_addr;
|
||||||
|
|
||||||
if (switch_namespace(targs->idx)) {
|
ifobj->dst_port = dst_port;
|
||||||
targs->retptr = bpf_set_link_xdp_fd(ifdict[targs->idx]->ifindex, -1, targs->flags);
|
ifobj->src_port = src_port;
|
||||||
|
|
||||||
|
if (vector == tx) {
|
||||||
|
ifobj->fv.vector = tx;
|
||||||
|
ifobj->func_ptr = worker_testapp_validate_tx;
|
||||||
|
ifdict_tx = ifobj;
|
||||||
} else {
|
} else {
|
||||||
targs->retptr = errno;
|
ifobj->fv.vector = rx;
|
||||||
print_verbose("Failed to switch namespace to %s\n", ifdict[targs->idx]->nsname);
|
ifobj->func_ptr = worker_testapp_validate_rx;
|
||||||
}
|
ifdict_rx = ifobj;
|
||||||
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void disable_xdp_mode(int mode)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
__u32 flags = XDP_FLAGS_UPDATE_IF_NOEXIST | mode;
|
|
||||||
char *mode_str = mode & XDP_FLAGS_SKB_MODE ? "skb" : "drv";
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_INTERFACES; i++) {
|
|
||||||
if (strcmp(ifdict[i]->nsname, "")) {
|
|
||||||
struct targs *targs;
|
|
||||||
|
|
||||||
targs = malloc(sizeof(*targs));
|
|
||||||
memset(targs, 0, sizeof(*targs));
|
|
||||||
if (!targs)
|
|
||||||
exit_with_error(errno);
|
|
||||||
|
|
||||||
targs->idx = i;
|
|
||||||
targs->flags = flags;
|
|
||||||
if (pthread_create(&ns_thread, NULL, nsdisablemodethread, targs))
|
|
||||||
exit_with_error(errno);
|
|
||||||
|
|
||||||
pthread_join(ns_thread, NULL);
|
|
||||||
err = targs->retptr;
|
|
||||||
free(targs);
|
|
||||||
} else {
|
|
||||||
err = bpf_set_link_xdp_fd(ifdict[i]->ifindex, -1, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
print_verbose("Failed to disable %s mode on interface %s\n",
|
|
||||||
mode_str, ifdict[i]->ifname);
|
|
||||||
exit_with_error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_verbose("Disabled %s mode for interface: %s\n", mode_str, ifdict[i]->ifname);
|
|
||||||
configured_mode = mode & XDP_FLAGS_SKB_MODE ? TEST_MODE_DRV : TEST_MODE_SKB;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1135,72 +1063,70 @@ static void run_pkt_test(int mode, int type)
|
||||||
/* reset defaults after potential previous test */
|
/* reset defaults after potential previous test */
|
||||||
xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
||||||
pkt_counter = 0;
|
pkt_counter = 0;
|
||||||
switching_notify = 0;
|
second_step = 0;
|
||||||
bidi_pass = 0;
|
|
||||||
prev_pkt = -1;
|
prev_pkt = -1;
|
||||||
ifdict[0]->fv.vector = tx;
|
|
||||||
ifdict[1]->fv.vector = rx;
|
|
||||||
sigvar = 0;
|
sigvar = 0;
|
||||||
stat_test_type = -1;
|
stat_test_type = -1;
|
||||||
rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
|
rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
|
||||||
frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
|
frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
|
||||||
|
|
||||||
|
configured_mode = mode;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case (TEST_MODE_SKB):
|
case (TEST_MODE_SKB):
|
||||||
if (configured_mode == TEST_MODE_DRV)
|
|
||||||
disable_xdp_mode(XDP_FLAGS_DRV_MODE);
|
|
||||||
xdp_flags |= XDP_FLAGS_SKB_MODE;
|
xdp_flags |= XDP_FLAGS_SKB_MODE;
|
||||||
break;
|
break;
|
||||||
case (TEST_MODE_DRV):
|
case (TEST_MODE_DRV):
|
||||||
if (configured_mode == TEST_MODE_SKB)
|
|
||||||
disable_xdp_mode(XDP_FLAGS_SKB_MODE);
|
|
||||||
xdp_flags |= XDP_FLAGS_DRV_MODE;
|
xdp_flags |= XDP_FLAGS_DRV_MODE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_init_mutex();
|
switch (test_type) {
|
||||||
|
case TEST_TYPE_STATS:
|
||||||
if (test_type == TEST_TYPE_STATS)
|
|
||||||
testapp_stats();
|
testapp_stats();
|
||||||
else if ((test_type != TEST_TYPE_TEARDOWN) && (test_type != TEST_TYPE_BIDI))
|
break;
|
||||||
|
case TEST_TYPE_TEARDOWN:
|
||||||
|
testapp_teardown();
|
||||||
|
break;
|
||||||
|
case TEST_TYPE_BIDI:
|
||||||
|
testapp_bidi();
|
||||||
|
break;
|
||||||
|
case TEST_TYPE_BPF_RES:
|
||||||
|
testapp_bpf_res();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
testapp_validate();
|
testapp_validate();
|
||||||
else
|
break;
|
||||||
testapp_sockets();
|
}
|
||||||
|
|
||||||
pthread_destroy_mutex();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY };
|
struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY };
|
||||||
|
bool failure = false;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
if (setrlimit(RLIMIT_MEMLOCK, &_rlim))
|
if (setrlimit(RLIMIT_MEMLOCK, &_rlim))
|
||||||
exit_with_error(errno);
|
exit_with_error(errno);
|
||||||
|
|
||||||
const char *MAC1 = "\x00\x0A\x56\x9E\xEE\x62";
|
|
||||||
const char *MAC2 = "\x00\x0A\x56\x9E\xEE\x61";
|
|
||||||
const char *IP1 = "192.168.100.162";
|
|
||||||
const char *IP2 = "192.168.100.161";
|
|
||||||
u16 UDP_DST_PORT = 2020;
|
|
||||||
u16 UDP_SRC_PORT = 2121;
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
ifaceconfig = malloc(sizeof(struct ifaceconfigobj));
|
|
||||||
memcpy(ifaceconfig->dst_mac, MAC1, ETH_ALEN);
|
|
||||||
memcpy(ifaceconfig->src_mac, MAC2, ETH_ALEN);
|
|
||||||
inet_aton(IP1, &ifaceconfig->dst_ip);
|
|
||||||
inet_aton(IP2, &ifaceconfig->src_ip);
|
|
||||||
ifaceconfig->dst_port = UDP_DST_PORT;
|
|
||||||
ifaceconfig->src_port = UDP_SRC_PORT;
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_INTERFACES; i++) {
|
for (int i = 0; i < MAX_INTERFACES; i++) {
|
||||||
ifdict[i] = malloc(sizeof(struct ifobject));
|
ifdict[i] = malloc(sizeof(struct ifobject));
|
||||||
if (!ifdict[i])
|
if (!ifdict[i])
|
||||||
exit_with_error(errno);
|
exit_with_error(errno);
|
||||||
|
|
||||||
ifdict[i]->ifdict_index = i;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
@ -1209,9 +1135,8 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
num_frames = ++opt_pkt_count;
|
num_frames = ++opt_pkt_count;
|
||||||
|
|
||||||
init_iface_config(ifaceconfig);
|
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);
|
||||||
disable_xdp_mode(XDP_FLAGS_DRV_MODE);
|
|
||||||
|
|
||||||
ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
|
ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
|
||||||
|
|
||||||
|
@ -1220,8 +1145,17 @@ int main(int argc, char **argv)
|
||||||
run_pkt_test(i, j);
|
run_pkt_test(i, j);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < MAX_INTERFACES; i++)
|
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]);
|
free(ifdict[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failure)
|
||||||
|
exit_with_error(errno);
|
||||||
|
|
||||||
ksft_exit_pass();
|
ksft_exit_pass();
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#define MAX_SOCKS 1
|
#define MAX_SOCKS 1
|
||||||
#define MAX_TEARDOWN_ITER 10
|
#define MAX_TEARDOWN_ITER 10
|
||||||
#define MAX_BIDI_ITER 2
|
#define MAX_BIDI_ITER 2
|
||||||
|
#define MAX_BPF_ITER 2
|
||||||
#define PKT_HDR_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + \
|
#define PKT_HDR_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + \
|
||||||
sizeof(struct udphdr))
|
sizeof(struct udphdr))
|
||||||
#define MIN_PKT_SIZE 64
|
#define MIN_PKT_SIZE 64
|
||||||
|
@ -33,14 +34,11 @@
|
||||||
#define IP_PKT_TOS 0x9
|
#define IP_PKT_TOS 0x9
|
||||||
#define UDP_PKT_SIZE (IP_PKT_SIZE - sizeof(struct iphdr))
|
#define UDP_PKT_SIZE (IP_PKT_SIZE - sizeof(struct iphdr))
|
||||||
#define UDP_PKT_DATA_SIZE (UDP_PKT_SIZE - sizeof(struct udphdr))
|
#define UDP_PKT_DATA_SIZE (UDP_PKT_SIZE - sizeof(struct udphdr))
|
||||||
#define TMOUT_SEC (3)
|
|
||||||
#define EOT (-1)
|
#define EOT (-1)
|
||||||
#define USLEEP_MAX 200000
|
#define USLEEP_MAX 200000
|
||||||
#define THREAD_STACK 60000000
|
|
||||||
#define SOCK_RECONF_CTR 10
|
#define SOCK_RECONF_CTR 10
|
||||||
#define BATCH_SIZE 64
|
#define BATCH_SIZE 64
|
||||||
#define POLL_TMOUT 1000
|
#define POLL_TMOUT 1000
|
||||||
#define NEED_WAKEUP true
|
|
||||||
#define DEFAULT_PKT_CNT 10000
|
#define DEFAULT_PKT_CNT 10000
|
||||||
#define RX_FULL_RXQSIZE 32
|
#define RX_FULL_RXQSIZE 32
|
||||||
|
|
||||||
|
@ -63,6 +61,7 @@ enum TEST_TYPES {
|
||||||
TEST_TYPE_TEARDOWN,
|
TEST_TYPE_TEARDOWN,
|
||||||
TEST_TYPE_BIDI,
|
TEST_TYPE_BIDI,
|
||||||
TEST_TYPE_STATS,
|
TEST_TYPE_STATS,
|
||||||
|
TEST_TYPE_BPF_RES,
|
||||||
TEST_TYPE_MAX
|
TEST_TYPE_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,11 +76,9 @@ enum STAT_TEST_TYPES {
|
||||||
static int configured_mode = TEST_MODE_UNCONFIGURED;
|
static int configured_mode = TEST_MODE_UNCONFIGURED;
|
||||||
static u8 debug_pkt_dump;
|
static u8 debug_pkt_dump;
|
||||||
static u32 num_frames;
|
static u32 num_frames;
|
||||||
static u8 switching_notify;
|
static bool second_step;
|
||||||
static u8 bidi_pass;
|
|
||||||
static int test_type;
|
static int test_type;
|
||||||
|
|
||||||
static int opt_queue;
|
|
||||||
static int opt_pkt_count;
|
static int opt_pkt_count;
|
||||||
static u8 opt_verbose;
|
static u8 opt_verbose;
|
||||||
|
|
||||||
|
@ -125,48 +122,32 @@ struct generic_data {
|
||||||
u32 seqnum;
|
u32 seqnum;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ifaceconfigobj {
|
|
||||||
u8 dst_mac[ETH_ALEN];
|
|
||||||
u8 src_mac[ETH_ALEN];
|
|
||||||
struct in_addr dst_ip;
|
|
||||||
struct in_addr src_ip;
|
|
||||||
u16 src_port;
|
|
||||||
u16 dst_port;
|
|
||||||
} *ifaceconfig;
|
|
||||||
|
|
||||||
struct ifobject {
|
struct ifobject {
|
||||||
int ifindex;
|
|
||||||
int ifdict_index;
|
|
||||||
char ifname[MAX_INTERFACE_NAME_CHARS];
|
char ifname[MAX_INTERFACE_NAME_CHARS];
|
||||||
char nsname[MAX_INTERFACES_NAMESPACE_CHARS];
|
char nsname[MAX_INTERFACES_NAMESPACE_CHARS];
|
||||||
struct flow_vector fv;
|
|
||||||
struct xsk_socket_info *xsk;
|
struct xsk_socket_info *xsk;
|
||||||
|
struct xsk_socket_info **xsk_arr;
|
||||||
|
struct xsk_umem_info **umem_arr;
|
||||||
struct xsk_umem_info *umem;
|
struct xsk_umem_info *umem;
|
||||||
u8 dst_mac[ETH_ALEN];
|
void *(*func_ptr)(void *arg);
|
||||||
u8 src_mac[ETH_ALEN];
|
struct flow_vector fv;
|
||||||
|
int ns_fd;
|
||||||
|
int ifdict_index;
|
||||||
u32 dst_ip;
|
u32 dst_ip;
|
||||||
u32 src_ip;
|
u32 src_ip;
|
||||||
u16 src_port;
|
u16 src_port;
|
||||||
u16 dst_port;
|
u16 dst_port;
|
||||||
|
u8 dst_mac[ETH_ALEN];
|
||||||
|
u8 src_mac[ETH_ALEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct ifobject *ifdict[MAX_INTERFACES];
|
static struct ifobject *ifdict[MAX_INTERFACES];
|
||||||
|
static struct ifobject *ifdict_rx;
|
||||||
|
static struct ifobject *ifdict_tx;
|
||||||
|
|
||||||
/*threads*/
|
/*threads*/
|
||||||
atomic_int spinning_tx;
|
pthread_barrier_t barr;
|
||||||
atomic_int spinning_rx;
|
pthread_t t0, t1;
|
||||||
pthread_mutex_t sync_mutex;
|
|
||||||
pthread_mutex_t sync_mutex_tx;
|
|
||||||
pthread_cond_t signal_rx_condition;
|
|
||||||
pthread_cond_t signal_tx_condition;
|
|
||||||
pthread_t t0, t1, ns_thread;
|
|
||||||
pthread_attr_t attr;
|
|
||||||
|
|
||||||
struct targs {
|
|
||||||
u8 retptr;
|
|
||||||
int idx;
|
|
||||||
u32 flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
TAILQ_HEAD(head_s, pkt) head = TAILQ_HEAD_INITIALIZER(head);
|
TAILQ_HEAD(head_s, pkt) head = TAILQ_HEAD_INITIALIZER(head);
|
||||||
struct head_s *head_p;
|
struct head_s *head_p;
|
||||||
|
|
Loading…
Reference in New Issue