Commit 126217da authored by Joanne Hugé's avatar Joanne Hugé

Add AF_XDP sockets

Describe XDP option in help
Add option to link with local libbpf folder
Update wrapper server script
Fix min max functions conflicts
Compile xdp-kern.o
Add IFLAGS to CFLAGS in makefile for XDP
Add bpf_helpers and xdp_kern files
Close XDP socket before exiting program
Add tracecmd option for XDP
Print stats for XDP too
Add an active polling mode
parent 682b8420
...@@ -27,15 +27,35 @@ CFLAGS += -MD -MP ...@@ -27,15 +27,35 @@ CFLAGS += -MD -MP
CFLAGS += -I $(SRCDIR) CFLAGS += -I $(SRCDIR)
CFLAGS += -std=gnu99 CFLAGS += -std=gnu99
LDFLAGS = -pthread LLIBS = -pthread
ifneq ($(WITH_XDP),)
CFLAGS += -D WITH_XDP
LDFLAGS += -L/usr/lib -lbpf
else ifneq ($(WITH_GIT_XDP),)
IFLAGS += -I ${HOME}/libbpf/include
CFLAGS += -D WITH_XDP $(IFLAGS)
LDIRS += -L${HOME}/libbpf/src
LLIBS += -lelf -lz -l:libbpf.a
endif
vpath %.c $(SRCDIR) vpath %.c $(SRCDIR)
xdp_kern.o: xdp_kern.c
clang $(IFLAGS) -isystem /usr/include/arm-linux-gnueabihf -S -target bpf -D __BPF_TRACING__ -Wall -O2 -emit-llvm -c -g -o xdp_kern.ll $^
llc -march=bpf -filetype=obj -o $@ xdp_kern.ll
ifneq ($(WITH_GIT_XDP),)
$(SERVER_PROG): $(SERVER_OBJS) xdp_kern.o
$(CC) $(LDFLAGS) $(LDIRS) $(SERVER_OBJS) $(LLIBS) -o $@
else
$(SERVER_PROG): $(SERVER_OBJS) $(SERVER_PROG): $(SERVER_OBJS)
$(CC) $(LDFLAGS) $^ -o $@ $(CC) $(LDFLAGS) $(LDIRS) $^ $(LLIBS) -o $@
endif
$(CLIENT_PROG): $(CLIENT_OBJS) $(CLIENT_PROG): $(CLIENT_OBJS)
$(CC) $(LDFLAGS) $^ -o $@ $(CC) $(LDFLAGS) $(LDIRS) $^ $(LLIBS) -o $@
-include $(subst .c,.d,$(SERVER_SRCS)) -include $(subst .c,.d,$(SERVER_SRCS))
-include $(subst .c,.d,$(CLIENT_SRCS)) -include $(subst .c,.d,$(CLIENT_SRCS))
......
/* SPDX-License-Identifier: GPL-2.0 */
/* Copied from $(LINUX)/tools/testing/selftests/bpf/bpf_helpers.h */
/* Added to fix compilation on old Ubuntu systems - please preserve when
updating file! */
#ifndef __always_inline
# define __always_inline inline __attribute__((always_inline))
#endif
#ifndef __BPF_HELPERS_H
#define __BPF_HELPERS_H
/* helper macro to place programs, maps, license in
* different sections in elf_bpf file. Section names
* are interpreted by elf_bpf loader
*/
#define SEC(NAME) __attribute__((section(NAME), used))
/* helper functions called from eBPF programs written in C */
static void *(*bpf_map_lookup_elem)(void *map, void *key) =
(void *) BPF_FUNC_map_lookup_elem;
static int (*bpf_map_update_elem)(void *map, void *key, void *value,
unsigned long long flags) =
(void *) BPF_FUNC_map_update_elem;
static int (*bpf_map_delete_elem)(void *map, void *key) =
(void *) BPF_FUNC_map_delete_elem;
static int (*bpf_map_push_elem)(void *map, void *value,
unsigned long long flags) =
(void *) BPF_FUNC_map_push_elem;
static int (*bpf_map_pop_elem)(void *map, void *value) =
(void *) BPF_FUNC_map_pop_elem;
static int (*bpf_map_peek_elem)(void *map, void *value) =
(void *) BPF_FUNC_map_peek_elem;
static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) =
(void *) BPF_FUNC_probe_read;
static unsigned long long (*bpf_ktime_get_ns)(void) =
(void *) BPF_FUNC_ktime_get_ns;
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *) BPF_FUNC_trace_printk;
static void (*bpf_tail_call)(void *ctx, void *map, int index) =
(void *) BPF_FUNC_tail_call;
static unsigned long long (*bpf_get_smp_processor_id)(void) =
(void *) BPF_FUNC_get_smp_processor_id;
static unsigned long long (*bpf_get_current_pid_tgid)(void) =
(void *) BPF_FUNC_get_current_pid_tgid;
static unsigned long long (*bpf_get_current_uid_gid)(void) =
(void *) BPF_FUNC_get_current_uid_gid;
static int (*bpf_get_current_comm)(void *buf, int buf_size) =
(void *) BPF_FUNC_get_current_comm;
static unsigned long long (*bpf_perf_event_read)(void *map,
unsigned long long flags) =
(void *) BPF_FUNC_perf_event_read;
static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) =
(void *) BPF_FUNC_clone_redirect;
static int (*bpf_redirect)(int ifindex, int flags) =
(void *) BPF_FUNC_redirect;
static int (*bpf_redirect_map)(void *map, int key, int flags) =
(void *) BPF_FUNC_redirect_map;
static int (*bpf_perf_event_output)(void *ctx, void *map,
unsigned long long flags, void *data,
int size) =
(void *) BPF_FUNC_perf_event_output;
static int (*bpf_get_stackid)(void *ctx, void *map, int flags) =
(void *) BPF_FUNC_get_stackid;
static int (*bpf_probe_write_user)(void *dst, void *src, int size) =
(void *) BPF_FUNC_probe_write_user;
static int (*bpf_current_task_under_cgroup)(void *map, int index) =
(void *) BPF_FUNC_current_task_under_cgroup;
static int (*bpf_skb_get_tunnel_key)(void *ctx, void *key, int size, int flags) =
(void *) BPF_FUNC_skb_get_tunnel_key;
static int (*bpf_skb_set_tunnel_key)(void *ctx, void *key, int size, int flags) =
(void *) BPF_FUNC_skb_set_tunnel_key;
static int (*bpf_skb_get_tunnel_opt)(void *ctx, void *md, int size) =
(void *) BPF_FUNC_skb_get_tunnel_opt;
static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) =
(void *) BPF_FUNC_skb_set_tunnel_opt;
static unsigned long long (*bpf_get_prandom_u32)(void) =
(void *) BPF_FUNC_get_prandom_u32;
static int (*bpf_xdp_adjust_head)(void *ctx, int offset) =
(void *) BPF_FUNC_xdp_adjust_head;
static int (*bpf_xdp_adjust_meta)(void *ctx, int offset) =
(void *) BPF_FUNC_xdp_adjust_meta;
static int (*bpf_get_socket_cookie)(void *ctx) =
(void *) BPF_FUNC_get_socket_cookie;
static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval,
int optlen) =
(void *) BPF_FUNC_setsockopt;
static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval,
int optlen) =
(void *) BPF_FUNC_getsockopt;
static int (*bpf_sock_ops_cb_flags_set)(void *ctx, int flags) =
(void *) BPF_FUNC_sock_ops_cb_flags_set;
static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) =
(void *) BPF_FUNC_sk_redirect_map;
static int (*bpf_sk_redirect_hash)(void *ctx, void *map, void *key, int flags) =
(void *) BPF_FUNC_sk_redirect_hash;
static int (*bpf_sock_map_update)(void *map, void *key, void *value,
unsigned long long flags) =
(void *) BPF_FUNC_sock_map_update;
static int (*bpf_sock_hash_update)(void *map, void *key, void *value,
unsigned long long flags) =
(void *) BPF_FUNC_sock_hash_update;
static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags,
void *buf, unsigned int buf_size) =
(void *) BPF_FUNC_perf_event_read_value;
static int (*bpf_perf_prog_read_value)(void *ctx, void *buf,
unsigned int buf_size) =
(void *) BPF_FUNC_perf_prog_read_value;
static int (*bpf_override_return)(void *ctx, unsigned long rc) =
(void *) BPF_FUNC_override_return;
static int (*bpf_msg_redirect_map)(void *ctx, void *map, int key, int flags) =
(void *) BPF_FUNC_msg_redirect_map;
static int (*bpf_msg_redirect_hash)(void *ctx,
void *map, void *key, int flags) =
(void *) BPF_FUNC_msg_redirect_hash;
static int (*bpf_msg_apply_bytes)(void *ctx, int len) =
(void *) BPF_FUNC_msg_apply_bytes;
static int (*bpf_msg_cork_bytes)(void *ctx, int len) =
(void *) BPF_FUNC_msg_cork_bytes;
static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
(void *) BPF_FUNC_msg_pull_data;
static int (*bpf_msg_push_data)(void *ctx, int start, int end, int flags) =
(void *) BPF_FUNC_msg_push_data;
static int (*bpf_msg_pop_data)(void *ctx, int start, int cut, int flags) =
(void *) BPF_FUNC_msg_pop_data;
static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
(void *) BPF_FUNC_bind;
static int (*bpf_xdp_adjust_tail)(void *ctx, int offset) =
(void *) BPF_FUNC_xdp_adjust_tail;
static int (*bpf_skb_get_xfrm_state)(void *ctx, int index, void *state,
int size, int flags) =
(void *) BPF_FUNC_skb_get_xfrm_state;
static int (*bpf_sk_select_reuseport)(void *ctx, void *map, void *key, __u32 flags) =
(void *) BPF_FUNC_sk_select_reuseport;
static int (*bpf_get_stack)(void *ctx, void *buf, int size, int flags) =
(void *) BPF_FUNC_get_stack;
static int (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params,
int plen, __u32 flags) =
(void *) BPF_FUNC_fib_lookup;
static int (*bpf_lwt_push_encap)(void *ctx, unsigned int type, void *hdr,
unsigned int len) =
(void *) BPF_FUNC_lwt_push_encap;
static int (*bpf_lwt_seg6_store_bytes)(void *ctx, unsigned int offset,
void *from, unsigned int len) =
(void *) BPF_FUNC_lwt_seg6_store_bytes;
static int (*bpf_lwt_seg6_action)(void *ctx, unsigned int action, void *param,
unsigned int param_len) =
(void *) BPF_FUNC_lwt_seg6_action;
static int (*bpf_lwt_seg6_adjust_srh)(void *ctx, unsigned int offset,
unsigned int len) =
(void *) BPF_FUNC_lwt_seg6_adjust_srh;
static int (*bpf_rc_repeat)(void *ctx) =
(void *) BPF_FUNC_rc_repeat;
static int (*bpf_rc_keydown)(void *ctx, unsigned int protocol,
unsigned long long scancode, unsigned int toggle) =
(void *) BPF_FUNC_rc_keydown;
static unsigned long long (*bpf_get_current_cgroup_id)(void) =
(void *) BPF_FUNC_get_current_cgroup_id;
static void *(*bpf_get_local_storage)(void *map, unsigned long long flags) =
(void *) BPF_FUNC_get_local_storage;
static unsigned long long (*bpf_skb_cgroup_id)(void *ctx) =
(void *) BPF_FUNC_skb_cgroup_id;
static unsigned long long (*bpf_skb_ancestor_cgroup_id)(void *ctx, int level) =
(void *) BPF_FUNC_skb_ancestor_cgroup_id;
static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx,
struct bpf_sock_tuple *tuple,
int size, unsigned long long netns_id,
unsigned long long flags) =
(void *) BPF_FUNC_sk_lookup_tcp;
static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx,
struct bpf_sock_tuple *tuple,
int size, unsigned long long netns_id,
unsigned long long flags) =
(void *) BPF_FUNC_sk_lookup_udp;
static int (*bpf_sk_release)(struct bpf_sock *sk) =
(void *) BPF_FUNC_sk_release;
static int (*bpf_skb_vlan_push)(void *ctx, __be16 vlan_proto, __u16 vlan_tci) =
(void *) BPF_FUNC_skb_vlan_push;
static int (*bpf_skb_vlan_pop)(void *ctx) =
(void *) BPF_FUNC_skb_vlan_pop;
static int (*bpf_rc_pointer_rel)(void *ctx, int rel_x, int rel_y) =
(void *) BPF_FUNC_rc_pointer_rel;
static void (*bpf_spin_lock)(struct bpf_spin_lock *lock) =
(void *) BPF_FUNC_spin_lock;
static void (*bpf_spin_unlock)(struct bpf_spin_lock *lock) =
(void *) BPF_FUNC_spin_unlock;
static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) =
(void *) BPF_FUNC_sk_fullsock;
static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) =
(void *) BPF_FUNC_tcp_sock;
static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) =
(void *) BPF_FUNC_get_listener_sock;
static int (*bpf_skb_ecn_set_ce)(void *ctx) =
(void *) BPF_FUNC_skb_ecn_set_ce;
/* llvm builtin functions that eBPF C program may use to
* emit BPF_LD_ABS and BPF_LD_IND instructions
*/
struct sk_buff;
unsigned long long load_byte(void *skb,
unsigned long long off) asm("llvm.bpf.load.byte");
unsigned long long load_half(void *skb,
unsigned long long off) asm("llvm.bpf.load.half");
unsigned long long load_word(void *skb,
unsigned long long off) asm("llvm.bpf.load.word");
/* a helper structure used by eBPF C program
* to describe map attributes to elf_bpf loader
*/
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
unsigned int map_flags;
unsigned int inner_map_idx;
unsigned int numa_node;
};
#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \
struct ____btf_map_##name { \
type_key key; \
type_val value; \
}; \
struct ____btf_map_##name \
__attribute__ ((section(".maps." #name), used)) \
____btf_map_##name = { }
static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) =
(void *) BPF_FUNC_skb_load_bytes;
static int (*bpf_skb_load_bytes_relative)(void *ctx, int off, void *to, int len, __u32 start_header) =
(void *) BPF_FUNC_skb_load_bytes_relative;
static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) =
(void *) BPF_FUNC_skb_store_bytes;
static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) =
(void *) BPF_FUNC_l3_csum_replace;
static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) =
(void *) BPF_FUNC_l4_csum_replace;
static int (*bpf_csum_diff)(void *from, int from_size, void *to, int to_size, int seed) =
(void *) BPF_FUNC_csum_diff;
static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) =
(void *) BPF_FUNC_skb_under_cgroup;
static int (*bpf_skb_change_head)(void *, int len, int flags) =
(void *) BPF_FUNC_skb_change_head;
static int (*bpf_skb_pull_data)(void *, int len) =
(void *) BPF_FUNC_skb_pull_data;
static unsigned int (*bpf_get_cgroup_classid)(void *ctx) =
(void *) BPF_FUNC_get_cgroup_classid;
static unsigned int (*bpf_get_route_realm)(void *ctx) =
(void *) BPF_FUNC_get_route_realm;
static int (*bpf_skb_change_proto)(void *ctx, __be16 proto, __u64 flags) =
(void *) BPF_FUNC_skb_change_proto;
static int (*bpf_skb_change_type)(void *ctx, __u32 type) =
(void *) BPF_FUNC_skb_change_type;
static unsigned int (*bpf_get_hash_recalc)(void *ctx) =
(void *) BPF_FUNC_get_hash_recalc;
static unsigned long long (*bpf_get_current_task)(void *ctx) =
(void *) BPF_FUNC_get_current_task;
static int (*bpf_skb_change_tail)(void *ctx, __u32 len, __u64 flags) =
(void *) BPF_FUNC_skb_change_tail;
static long long (*bpf_csum_update)(void *ctx, __u32 csum) =
(void *) BPF_FUNC_csum_update;
static void (*bpf_set_hash_invalid)(void *ctx) =
(void *) BPF_FUNC_set_hash_invalid;
static int (*bpf_get_numa_node_id)(void) =
(void *) BPF_FUNC_get_numa_node_id;
static int (*bpf_probe_read_str)(void *ctx, __u32 size,
const void *unsafe_ptr) =
(void *) BPF_FUNC_probe_read_str;
static unsigned int (*bpf_get_socket_uid)(void *ctx) =
(void *) BPF_FUNC_get_socket_uid;
static unsigned int (*bpf_set_hash)(void *ctx, __u32 hash) =
(void *) BPF_FUNC_set_hash;
static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
unsigned long long flags) =
(void *) BPF_FUNC_skb_adjust_room;
/* Scan the ARCH passed in from ARCH env variable (see Makefile) */
#if defined(__TARGET_ARCH_x86)
#define bpf_target_x86
#define bpf_target_defined
#elif defined(__TARGET_ARCH_s930x)
#define bpf_target_s930x
#define bpf_target_defined
#elif defined(__TARGET_ARCH_arm64)
#define bpf_target_arm64
#define bpf_target_defined
#elif defined(__TARGET_ARCH_mips)
#define bpf_target_mips
#define bpf_target_defined
#elif defined(__TARGET_ARCH_powerpc)
#define bpf_target_powerpc
#define bpf_target_defined
#elif defined(__TARGET_ARCH_sparc)
#define bpf_target_sparc
#define bpf_target_defined
#else
#undef bpf_target_defined
#endif
/* Fall back to what the compiler says */
#ifndef bpf_target_defined
#if defined(__x86_64__)
#define bpf_target_x86
#elif defined(__s390x__)
#define bpf_target_s930x
#elif defined(__aarch64__)
#define bpf_target_arm64
#elif defined(__mips__)
#define bpf_target_mips
#elif defined(__powerpc__)
#define bpf_target_powerpc
#elif defined(__sparc__)
#define bpf_target_sparc
#endif
#endif
#if defined(bpf_target_x86)
#define PT_REGS_PARM1(x) ((x)->di)
#define PT_REGS_PARM2(x) ((x)->si)
#define PT_REGS_PARM3(x) ((x)->dx)
#define PT_REGS_PARM4(x) ((x)->cx)
#define PT_REGS_PARM5(x) ((x)->r8)
#define PT_REGS_RET(x) ((x)->sp)
#define PT_REGS_FP(x) ((x)->bp)
#define PT_REGS_RC(x) ((x)->ax)
#define PT_REGS_SP(x) ((x)->sp)
#define PT_REGS_IP(x) ((x)->ip)
#elif defined(bpf_target_s390x)
#define PT_REGS_PARM1(x) ((x)->gprs[2])
#define PT_REGS_PARM2(x) ((x)->gprs[3])
#define PT_REGS_PARM3(x) ((x)->gprs[4])
#define PT_REGS_PARM4(x) ((x)->gprs[5])
#define PT_REGS_PARM5(x) ((x)->gprs[6])
#define PT_REGS_RET(x) ((x)->gprs[14])
#define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */
#define PT_REGS_RC(x) ((x)->gprs[2])
#define PT_REGS_SP(x) ((x)->gprs[15])
#define PT_REGS_IP(x) ((x)->psw.addr)
#elif defined(bpf_target_arm64)
#define PT_REGS_PARM1(x) ((x)->regs[0])
#define PT_REGS_PARM2(x) ((x)->regs[1])
#define PT_REGS_PARM3(x) ((x)->regs[2])
#define PT_REGS_PARM4(x) ((x)->regs[3])
#define PT_REGS_PARM5(x) ((x)->regs[4])
#define PT_REGS_RET(x) ((x)->regs[30])
#define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */
#define PT_REGS_RC(x) ((x)->regs[0])
#define PT_REGS_SP(x) ((x)->sp)
#define PT_REGS_IP(x) ((x)->pc)
#elif defined(bpf_target_mips)
#define PT_REGS_PARM1(x) ((x)->regs[4])
#define PT_REGS_PARM2(x) ((x)->regs[5])
#define PT_REGS_PARM3(x) ((x)->regs[6])
#define PT_REGS_PARM4(x) ((x)->regs[7])
#define PT_REGS_PARM5(x) ((x)->regs[8])
#define PT_REGS_RET(x) ((x)->regs[31])
#define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */
#define PT_REGS_RC(x) ((x)->regs[1])
#define PT_REGS_SP(x) ((x)->regs[29])
#define PT_REGS_IP(x) ((x)->cp0_epc)
#elif defined(bpf_target_powerpc)
#define PT_REGS_PARM1(x) ((x)->gpr[3])
#define PT_REGS_PARM2(x) ((x)->gpr[4])
#define PT_REGS_PARM3(x) ((x)->gpr[5])
#define PT_REGS_PARM4(x) ((x)->gpr[6])
#define PT_REGS_PARM5(x) ((x)->gpr[7])
#define PT_REGS_RC(x) ((x)->gpr[3])
#define PT_REGS_SP(x) ((x)->sp)
#define PT_REGS_IP(x) ((x)->nip)
#elif defined(bpf_target_sparc)
#define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0])
#define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1])
#define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2])
#define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3])
#define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4])
#define PT_REGS_RET(x) ((x)->u_regs[UREG_I7])
#define PT_REGS_RC(x) ((x)->u_regs[UREG_I0])
#define PT_REGS_SP(x) ((x)->u_regs[UREG_FP])
/* Should this also be a bpf_target check for the sparc case? */
#if defined(__arch64__)
#define PT_REGS_IP(x) ((x)->tpc)
#else
#define PT_REGS_IP(x) ((x)->pc)
#endif
#endif
#ifdef bpf_target_powerpc
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; })
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
#elif bpf_target_sparc
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); })
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
#else
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ \
bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); })
#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ \
bpf_probe_read(&(ip), sizeof(ip), \
(void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
#endif
#endif
...@@ -30,25 +30,25 @@ ...@@ -30,25 +30,25 @@
enum TSNTask { SEND_PACKET_TASK, RTT_TASK }; enum TSNTask { SEND_PACKET_TASK, RTT_TASK };
typedef struct rtt_stat { typedef struct rtt_stat {
int min_rtt; int min_rtt;
int avg_rtt; int avg_rtt;
int max_rtt; int max_rtt;
} rtt_stat_t; } rtt_stat_t;
typedef struct thread_param { typedef struct thread_param {
int interval; int interval;
unsigned int max_cycles; unsigned int max_cycles;
int priority; int priority;
int etf_offset; int etf_offset;
} thread_param_t; } thread_param_t;
typedef struct main_param { typedef struct main_param {
int refresh_rate; int refresh_rate;
int verbose; int verbose;
int enable_tracing; int enable_tracing;
int enable_graph; int enable_graph;
} main_param_t; } main_param_t;
// Static functions // Static functions
...@@ -72,9 +72,9 @@ static ingress_param_t ingress_params; ...@@ -72,9 +72,9 @@ static ingress_param_t ingress_params;
static rtt_stat_t rtt_stats = {.min_rtt = INT_MAX}; static rtt_stat_t rtt_stats = {.min_rtt = INT_MAX};
static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX, static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX}; .min_interval = INT_MAX};
static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX, static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX}; .min_interval = INT_MAX};
static int enable_histograms; static int enable_histograms;
static int enable_affinity; static int enable_affinity;
...@@ -89,94 +89,104 @@ static struct timespec measures_end; ...@@ -89,94 +89,104 @@ static struct timespec measures_end;
static char send_data[MAX_BUFFER_SIZE]; static char send_data[MAX_BUFFER_SIZE];
static void help(char *argv[]) { static void help(char *argv[]) {
printf( printf(
"Usage: %s -f IF [-abthgv] [-e ETF_OFFSET] [-d BUF_LEN] [-i USEC] [-l N]" "Usage: %s -f IF [-abthgv] [-e ETF_OFFSET] [-d BUF_LEN] [-i USEC] "
"[-p PRIO] [-q PACKET_PRIO] [-r USEC] [-T LATENCY_THRESHOLD -G]\n\n" "[-l N]"
" -a Run the real time thread on CPU1\n" "[-p PRIO] [-q PACKET_PRIO] [-r USEC] [-T LATENCY_THRESHOLD -G]\n\n"
" -b Measure RTT\n" " -a Run the real time thread on CPU1\n"
" -d BUF_LEN Set the length of tx buffer\n" " -b Measure RTT\n"
" -e ETF_OFFSET Set a txtime with an offset of ETF_OFFSET " " -d BUF_LEN Set the length of tx buffer\n"
"us (to be used in an ETF qdisc)\n" " -e ETF_OFFSET Set a txtime with an offset of "
" -f IF Set the network interface to be used\n" "ETF_OFFSET "
" -g Print histograms to sdtout on exit\n" "us (to be used in an ETF qdisc)\n"
" -h Show help\n" " -f IF Set the network interface to be "
" -i USEC Wake up the real time thread every USEC " "used\n"
"microseconds (Default: 10ms)\n" " -g Print histograms to sdtout on exit\n"
" -l N Wake up the real time thread N times " " -h Show help\n"
"(Default: 0)\n" " -i USEC Wake up the real time thread every "
" -p PRIO Run the real time thread at priority " "USEC "
"PRIO\n" "microseconds (Default: 10ms)\n"
" -q PACKET_PRIO Send packets with PACKET_PRIO priority\n" " -l N Wake up the real time thread N times "
" -r USEC Refresh the non real time main thread " "(Default: 0)\n"
"every USEC microseconds (Default: 50ms)\n" " -p PRIO Run the real time thread at priority "
" -t Enable timestamps\n" "PRIO\n"
" -v Verbose\n" " -q PACKET_PRIO Send packets with PACKET_PRIO "
" -T Enable tracing until deadline is " "priority\n"
"missed\n" " -r USEC Refresh the non real time main "
"\n", "thread "
argv[0]); "every USEC microseconds (Default: 50ms)\n"
" -t Enable timestamps\n"
" -v Verbose\n"
" -T Enable tracing until deadline is "
"missed\n"
"\n",
argv[0]);
} }
/* /*
* Real-time thread: Sends packets at a regular intervall * Real-time thread: Sends packets at a regular intervall
*/ */
static void *packet_sending_thread(void *p) { static void *packet_sending_thread(void *p) {
(void)p; (void)p;
struct timespec next, current, previous; struct timespec next, current, previous;
uint64_t next_txtime; uint64_t next_txtime;
cpu_set_t mask; cpu_set_t mask;
// Set thread CPU affinity // Set thread CPU affinity
if (enable_affinity) { if (enable_affinity) {
CPU_ZERO(&mask); CPU_ZERO(&mask);
CPU_SET(1, &mask); CPU_SET(1, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask)) if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU #1\n"); error(EXIT_FAILURE, errno,
} "Could not set CPU affinity to CPU #1\n");
}
if (enable_etf) {
// Measure from CLOCK_TAI to generate timestamp if (enable_etf) {
clock_gettime(CLOCK_TAI, &next); // Measure from CLOCK_TAI to generate timestamp
next_txtime = next.tv_sec * NSEC_PER_SEC + next.tv_nsec; clock_gettime(CLOCK_TAI, &next);
next_txtime += thread_params.etf_offset; next_txtime = next.tv_sec * NSEC_PER_SEC + next.tv_nsec;
} else { next_txtime += thread_params.etf_offset;
next_txtime = 0; } else {
} next_txtime = 0;
}
clock_gettime(CLOCK_MONOTONIC, &next);
clock_gettime(CLOCK_MONOTONIC, &measures_start); clock_gettime(CLOCK_MONOTONIC, &next);
clock_gettime(CLOCK_MONOTONIC, &measures_start);
// Packet sending loop
for (nb_cycles = 0;; nb_cycles++) { // Packet sending loop
if (thread_params.max_cycles && for (nb_cycles = 0;; nb_cycles++) {
nb_cycles >= ((unsigned int)thread_params.max_cycles)) if (thread_params.max_cycles &&
break; nb_cycles >= ((unsigned int)thread_params.max_cycles))
break;
clock_gettime(CLOCK_MONOTONIC, &current);
clock_gettime(CLOCK_MONOTONIC, &current);
sprintf(send_data, "%d", (int)(nb_cycles % 1000));
do_tsn_task(send_data, next_txtime); sprintf(send_data, "%d", (int)(nb_cycles % 1000));
do_tsn_task(send_data, next_txtime);
add_ns(&next, thread_params.interval);
add_ns(&next, thread_params.interval);
if (enable_etf) next_txtime += thread_params.interval;
if (enable_etf) next_txtime += thread_params.interval;
if (nb_cycles) {
int interval_us = calcdiff_ns(current, previous) / 1000; if (nb_cycles) {
int interval_us = calcdiff_ns(current, previous) / 1000;
egress_stats.min_interval = min(interval_us, egress_stats.min_interval);
egress_stats.max_interval = max(interval_us, egress_stats.max_interval); egress_stats.min_interval =
egress_stats.avg_interval = _min_(interval_us, egress_stats.min_interval);
(egress_stats.avg_interval * nb_cycles + interval_us) / egress_stats.max_interval =
(nb_cycles + 1); _max_(interval_us, egress_stats.max_interval);
} egress_stats.avg_interval =
(egress_stats.avg_interval * nb_cycles +
previous = current; interval_us) /
(nb_cycles + 1);
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL); }
}
previous = current;
return NULL;
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
}
return NULL;
} }
/* /*
...@@ -184,287 +194,301 @@ static void *packet_sending_thread(void *p) { ...@@ -184,287 +194,301 @@ static void *packet_sending_thread(void *p) {
* Handles the IO and creates the real time thread * Handles the IO and creates the real time thread
*/ */
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
pthread_t thread; pthread_t thread;
struct sched_param param; struct sched_param param;
pthread_attr_t attr; pthread_attr_t attr;
// Default configuration values // Default configuration values
thread_params.interval = 100000 * 1000; thread_params.interval = 100000 * 1000;
thread_params.max_cycles = 0; thread_params.max_cycles = 0;
thread_params.priority = 99; thread_params.priority = 99;
main_params.refresh_rate = 50000; main_params.refresh_rate = 50000;
main_params.verbose = 0; main_params.verbose = 0;
main_params.enable_tracing = 0; main_params.enable_tracing = 0;
main_params.enable_graph = 0; main_params.enable_graph = 0;
enable_affinity = 0; enable_affinity = 0;
enable_etf = 0; enable_etf = 0;
enable_timestamps = 0; enable_timestamps = 0;
enable_histograms = 0; enable_histograms = 0;
tsn_task = SEND_PACKET_TASK; tsn_task = SEND_PACKET_TASK;
egress_params.packet_priority = 3; egress_params.packet_priority = 3;
egress_params.tx_buffer_len = 1024; egress_params.tx_buffer_len = 1024;
/* Lock all current and future pages from preventing of being paged to swap */ /* Lock all current and future pages from preventing of being paged to
if (mlockall(MCL_CURRENT | MCL_FUTURE)) { * swap */
perror("mlockall failed"); if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
/* exit(-1) or do error handling */ perror("mlockall failed");
} /* exit(-1) or do error handling */
}
// Process bash options
process_options(argc, argv); // Process bash options
process_options(argc, argv);
egress_params.use_etf = enable_etf;
egress_params.use_timestamps = enable_timestamps; egress_params.use_etf = enable_etf;
egress_params.use_timestamps = enable_timestamps;
if (enable_histograms) {
// Init histograms if (enable_histograms) {
memset(kernel_latency_hist, 0, sizeof(kernel_latency_hist)); // Init histograms
memset(rtt_hist, 0, sizeof(rtt_hist)); memset(kernel_latency_hist, 0, sizeof(kernel_latency_hist));
} memset(rtt_hist, 0, sizeof(rtt_hist));
}
if (main_params.enable_tracing) {
// Enable ftrace if (main_params.enable_tracing) {
open_fds(); // Enable ftrace
} open_fds();
}
// Catch breaks with sighand to print the histograms
init_signals(sighand, enable_histograms); // Catch breaks with sighand to print the histograms
init_signals(sighand);
// Initialize the UDP packet sending socket
init_udp_send(&egress_params, &egress_stats, enable_histograms, main_params.enable_tracing, // Initialize the UDP packet sending socket
kernel_latency_hist); init_udp_send(&egress_params, &egress_stats, enable_histograms,
main_params.enable_tracing, kernel_latency_hist);
// Initialize the UDP packet receiving socket if RTT is measured
if (tsn_task == RTT_TASK) // Initialize the UDP packet receiving socket if RTT is measured
init_udp_recv(&ingress_params, &ingress_stats, enable_histograms, if (tsn_task == RTT_TASK)
kernel_latency_hist); init_udp_recv(&ingress_params, &ingress_stats,
enable_histograms, kernel_latency_hist);
/* Initialize pthread attributes (default values) */
if (pthread_attr_init(&attr)) { /* Initialize pthread attributes (default values) */
fprintf(stderr, "init pthread attributes failed\n"); if (pthread_attr_init(&attr)) {
exit(EXIT_FAILURE); fprintf(stderr, "init pthread attributes failed\n");
} exit(EXIT_FAILURE);
/* Set a specific stack size */ }
if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)) { /* Set a specific stack size */
fprintf(stderr, "pthread setstacksize failed\n"); if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)) {
exit(EXIT_FAILURE); fprintf(stderr, "pthread setstacksize failed\n");
} exit(EXIT_FAILURE);
/* Set scheduler policy and priority of pthread */ }
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { /* Set scheduler policy and priority of pthread */
fprintf(stderr, "pthread setschedpolicy failed\n"); if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
exit(EXIT_FAILURE); fprintf(stderr, "pthread setschedpolicy failed\n");
} exit(EXIT_FAILURE);
param.sched_priority = thread_params.priority; }
if (pthread_attr_setschedparam(&attr, &param)) { param.sched_priority = thread_params.priority;
fprintf(stderr, "pthread setschedparam failed\n"); if (pthread_attr_setschedparam(&attr, &param)) {
exit(EXIT_FAILURE); fprintf(stderr, "pthread setschedparam failed\n");
} exit(EXIT_FAILURE);
/* Use scheduling parameters of attr */ }
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { /* Use scheduling parameters of attr */
fprintf(stderr, "pthread setinheritsched failed\n"); if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) {
exit(EXIT_FAILURE); fprintf(stderr, "pthread setinheritsched failed\n");
} exit(EXIT_FAILURE);
}
// Create the real time thread
if (pthread_create(&thread, &attr, packet_sending_thread, NULL)) // Create the real time thread
error(EXIT_FAILURE, errno, "Couldn't create packet sending thread"); if (pthread_create(&thread, &attr, packet_sending_thread, NULL))
error(EXIT_FAILURE, errno,
// Verbose loop "Couldn't create packet sending thread");
for (;;) {
usleep(main_params.refresh_rate); // Verbose loop
for (;;) {
if (main_params.verbose) { usleep(main_params.refresh_rate);
if (tsn_task == RTT_TASK) {
printf("%9" PRIu64 ": RTT: %4d %4d %4d\n", nb_cycles, rtt_stats.min_rtt, if (main_params.verbose) {
rtt_stats.avg_rtt, rtt_stats.max_rtt); if (tsn_task == RTT_TASK) {
printf("\033[%dA", 1); printf("%9" PRIu64 ": RTT: %4d %4d %4d\n",
} else { nb_cycles, rtt_stats.min_rtt,
printf("%9" PRIu64 ": [%4d, %4d], I (10us): %3d %3d %3d", nb_cycles, rtt_stats.avg_rtt, rtt_stats.max_rtt);
(int)egress_stats.invalid_parameter, printf("\033[%dA", 1);
(int)egress_stats.missed_deadline, } else {
egress_stats.min_interval / 10, egress_stats.avg_interval / 10, printf("%9" PRIu64
egress_stats.max_interval / 10); ": [%4d, %4d], I (10us): %3d %3d %3d",
nb_cycles,
if (enable_timestamps) { (int)egress_stats.invalid_parameter,
printf(", K: %4d %4d %4d [%4d]\n", egress_stats.min_kernel_latency, (int)egress_stats.missed_deadline,
egress_stats.avg_kernel_latency, egress_stats.min_interval / 10,
egress_stats.max_kernel_latency, egress_stats.avg_interval / 10,
(int)egress_stats.high_kernel_latency); egress_stats.max_interval / 10);
} else {
printf("\n"); if (enable_timestamps) {
} printf(", K: %4d %4d %4d [%4d]\n",
printf("\033[%dA", 1); egress_stats.min_kernel_latency,
} egress_stats.avg_kernel_latency,
} egress_stats.max_kernel_latency,
(int)egress_stats
if (thread_params.max_cycles) .high_kernel_latency);
if (thread_params.max_cycles == nb_cycles) break; } else {
} printf("\n");
}
if (enable_histograms) print_histograms(); printf("\033[%dA", 1);
}
exit(EXIT_SUCCESS); }
if (thread_params.max_cycles)
if (thread_params.max_cycles == nb_cycles) break;
}
if (enable_histograms) print_histograms();
exit(EXIT_SUCCESS);
} }
// Critical TSN task // Critical TSN task
static void do_tsn_task(char *data, uint64_t next_txtime) { static void do_tsn_task(char *data, uint64_t next_txtime) {
struct timespec t1, t2; struct timespec t1, t2;
int rtt_us; int rtt_us;
// One way packet sending // One way packet sending
if (tsn_task == SEND_PACKET_TASK) { if (tsn_task == SEND_PACKET_TASK) {
send_udp_packet(data, next_txtime); send_udp_packet(data, next_txtime);
// Round Trip Time measurement // Round Trip Time measurement
} else if (tsn_task == RTT_TASK) { } else if (tsn_task == RTT_TASK) {
clock_gettime(CLOCK_MONOTONIC, &t1); clock_gettime(CLOCK_MONOTONIC, &t1);
send_udp_packet(data, next_txtime); send_udp_packet(data, next_txtime);
recv_udp_packet(0, 0, NULL); recv_udp_packet();
clock_gettime(CLOCK_MONOTONIC, &t2); clock_gettime(CLOCK_MONOTONIC, &t2);
rtt_us = calcdiff_ns(t2, t1) / 1000; rtt_us = calcdiff_ns(t2, t1) / 1000;
rtt_stats.min_rtt = min(rtt_us, rtt_stats.min_rtt); rtt_stats.min_rtt = _min_(rtt_us, rtt_stats.min_rtt);
rtt_stats.max_rtt = max(rtt_us, rtt_stats.max_rtt); rtt_stats.max_rtt = _max_(rtt_us, rtt_stats.max_rtt);
rtt_stats.avg_rtt = rtt_stats.avg_rtt =
(((uint64_t)rtt_stats.avg_rtt) * (nb_cycles - 1) + rtt_us) / nb_cycles; (((uint64_t)rtt_stats.avg_rtt) * (nb_cycles - 1) + rtt_us) /
nb_cycles;
if (rtt_us > MAX_RTT)
fprintf(stderr, "RTT value higher than MAX_RTT : %d ( > %d)\n", rtt_us, if (rtt_us > MAX_RTT)
MAX_RTT); fprintf(stderr,
else "RTT value higher than MAX_RTT : %d ( > %d)\n",
rtt_hist[rtt_us]++; rtt_us, MAX_RTT);
} else
rtt_hist[rtt_us]++;
}
} }
// Print histograms in .json format // Print histograms in .json format
static void print_histograms() { static void print_histograms() {
uint64_t duration; uint64_t duration;
int duration_hour, duration_minutes, interval; int duration_hour, duration_minutes, interval;
int max_hist_val; int max_hist_val;
uint64_t *histogram; uint64_t *histogram;
clock_gettime(CLOCK_MONOTONIC, &measures_end); clock_gettime(CLOCK_MONOTONIC, &measures_end);
duration = calcdiff_ns(measures_end, measures_start); duration = calcdiff_ns(measures_end, measures_start);
duration_hour = duration / NSEC_PER_SEC / 3600; duration_hour = duration / NSEC_PER_SEC / 3600;
duration_minutes = duration / NSEC_PER_SEC / 60 - duration_hour * 60; duration_minutes = duration / NSEC_PER_SEC / 60 - duration_hour * 60;
interval = thread_params.interval / 1000; interval = thread_params.interval / 1000;
printf( printf(
"{\"measure_sets\": [{" "{\"measure_sets\": [{"
"\"measure_type\": \"%s\"," "\"measure_type\": \"%s\","
"\"props_names\": [\"%s\"]," "\"props_names\": [\"%s\"],"
"\"units\": [\"us\"]," "\"units\": [\"us\"],"
"\"props_type\": \"histogram\"," "\"props_type\": \"histogram\","
"\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\", " "\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\", "
"\"etf_offset\": \"%dus\"}," "\"etf_offset\": \"%dus\"},"
"\"props\": [[", "\"props\": [[",
(tsn_task == SEND_PACKET_TASK) ? "packet_send_timestamps" : "packet_rtt", (tsn_task == SEND_PACKET_TASK) ? "packet_send_timestamps"
(tsn_task == SEND_PACKET_TASK) ? "kernel_space" : "rtt", interval, : "packet_rtt",
duration_hour, duration_minutes, thread_params.etf_offset); (tsn_task == SEND_PACKET_TASK) ? "kernel_space" : "rtt", interval,
duration_hour, duration_minutes, thread_params.etf_offset);
if (tsn_task == SEND_PACKET_TASK) {
histogram = kernel_latency_hist; if (tsn_task == SEND_PACKET_TASK) {
max_hist_val = histogram_max(histogram, MAX_KERNEL_LATENCY - 1); histogram = kernel_latency_hist;
} else { max_hist_val = histogram_max(histogram, MAX_KERNEL_LATENCY - 1);
histogram = rtt_hist; } else {
max_hist_val = histogram_max(histogram, MAX_RTT - 1); histogram = rtt_hist;
} max_hist_val = histogram_max(histogram, MAX_RTT - 1);
}
for (int j = 0; j < max_hist_val; j++)
printf("%" PRIi64 "%s", histogram[j], (j + 1 < max_hist_val ? ", " : "")); for (int j = 0; j < max_hist_val; j++)
printf("]]}]}\n"); printf("%" PRIi64 "%s", histogram[j],
(j + 1 < max_hist_val ? ", " : ""));
printf("]]}]}\n");
} }
static void sighand(int sig_num) { static void sighand(int sig_num) {
(void)sig_num; (void)sig_num;
print_histograms(); if (enable_histograms) print_histograms();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
// Process bash options // Process bash options
static void process_options(int argc, char *argv[]) { static void process_options(int argc, char *argv[]) {
int network_if_specified = 0; int network_if_specified = 0;
for (;;) { for (;;) {
int c = getopt(argc, argv, "abd:e:f:ghi:l:p:q:r:tvT"); int c = getopt(argc, argv, "abd:e:f:ghi:l:p:q:r:tvT");
if (c == -1) break; if (c == -1) break;
switch (c) { switch (c) {
case 'a': case 'a':
enable_affinity = 1; enable_affinity = 1;
break; break;
case 'b': case 'b':
tsn_task = RTT_TASK; tsn_task = RTT_TASK;
break; break;
case 'd': case 'd':
egress_params.tx_buffer_len = atoi(optarg); egress_params.tx_buffer_len = atoi(optarg);
if (egress_params.tx_buffer_len < 1) { if (egress_params.tx_buffer_len < 1) {
fprintf(stderr, "BUF_LEN should be greater than 1\n"); fprintf(stderr,
exit(EXIT_FAILURE); "BUF_LEN should be greater "
} "than 1\n");
break; exit(EXIT_FAILURE);
case 'e': }
enable_etf = 1; break;
thread_params.etf_offset = atoi(optarg) * 1000; case 'e':
break; enable_etf = 1;
case 'f': thread_params.etf_offset = atoi(optarg) * 1000;
network_if_specified = 1; break;
strcpy(egress_params.network_if, optarg); case 'f':
break; network_if_specified = 1;
case 'g': strcpy(egress_params.network_if, optarg);
enable_histograms = 1; break;
break; case 'g':
case 'h': enable_histograms = 1;
help(argv); break;
exit(EXIT_SUCCESS); case 'h':
break; help(argv);
case 'i': exit(EXIT_SUCCESS);
thread_params.interval = atoi(optarg) * 1000; break;
break; case 'i':
case 'l': thread_params.interval = atoi(optarg) * 1000;
thread_params.max_cycles = atoi(optarg); break;
break; case 'l':
case 'p': thread_params.max_cycles = atoi(optarg);
thread_params.priority = atoi(optarg); break;
break; case 'p':
case 'q': thread_params.priority = atoi(optarg);
egress_params.packet_priority = atoi(optarg); break;
break; case 'q':
case 'r': egress_params.packet_priority = atoi(optarg);
main_params.refresh_rate = atoi(optarg); break;
break; case 'r':
case 't': main_params.refresh_rate = atoi(optarg);
enable_timestamps = 1; break;
break; case 't':
case 'v': enable_timestamps = 1;
main_params.verbose = 1; break;
break; case 'v':
case 'T': main_params.verbose = 1;
main_params.enable_tracing = 1; break;
break; case 'T':
} main_params.enable_tracing = 1;
} break;
}
if (!network_if_specified) { }
fprintf(stderr, "You need to specifiy an network interface\n");
help(argv); if (!network_if_specified) {
exit(EXIT_FAILURE); fprintf(stderr, "You need to specifiy an network interface\n");
} help(argv);
exit(EXIT_FAILURE);
if (argc != optind + 1) { }
if (argc < optind + 1)
fprintf(stderr, "You need to specifiy an IP address\n"); if (argc != optind + 1) {
else if (argc < optind + 1)
fprintf(stderr, "Too many arguments\n"); fprintf(stderr, "You need to specifiy an IP address\n");
help(argv); else
exit(EXIT_FAILURE); fprintf(stderr, "Too many arguments\n");
} help(argv);
strcpy(egress_params.server_ip, argv[optind]); exit(EXIT_FAILURE);
}
strcpy(egress_params.server_ip, argv[optind]);
} }
#define _GNU_SOURCE #define _GNU_SOURCE
#include "common.h"
#include <inttypes.h> #include <inttypes.h>
#include <signal.h> #include <signal.h>
#include <stdint.h> #include <stdint.h>
...@@ -7,73 +9,65 @@ ...@@ -7,73 +9,65 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include "common.h"
void (*previous_handlers[NSIG])(int); void (*previous_handlers[NSIG])(int);
static void (*sighand)(int); static void (*sighand)(int);
uint64_t ts_to_uint(struct timespec t) { uint64_t ts_to_uint(struct timespec t) {
return t.tv_sec * NSEC_PER_SEC + t.tv_nsec; return t.tv_sec * NSEC_PER_SEC + t.tv_nsec;
} }
void add_ns(struct timespec *t, uint64_t ns) { void add_ns(struct timespec *t, uint64_t ns) {
t->tv_nsec += ns; t->tv_nsec += ns;
while ((unsigned int)t->tv_nsec >= NSEC_PER_SEC) { while ((unsigned int)t->tv_nsec >= NSEC_PER_SEC) {
t->tv_sec += 1; t->tv_sec += 1;
t->tv_nsec -= NSEC_PER_SEC; t->tv_nsec -= NSEC_PER_SEC;
} }
} }
uint64_t calcdiff_ns(struct timespec t1, struct timespec t2) { uint64_t calcdiff_ns(struct timespec t1, struct timespec t2) {
uint64_t diff; uint64_t diff;
diff = NSEC_PER_SEC * (uint64_t)((int)t1.tv_sec - (int)t2.tv_sec); diff = NSEC_PER_SEC * (uint64_t)((int)t1.tv_sec - (int)t2.tv_sec);
diff += ((int)t1.tv_nsec - (int)t2.tv_nsec); diff += ((int)t1.tv_nsec - (int)t2.tv_nsec);
return diff; return diff;
} }
int max(int a, int b) { return a > b ? a : b; } int _max_(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; } int _min_(int a, int b) { return a < b ? a : b; }
int histogram_min(uint64_t * histogram, int max_value) { int histogram_min(uint64_t *histogram, int max_value) {
int ret = max_value; int ret = max_value;
for (int i = max_value; i >= 0; i--) for (int i = max_value; i >= 0; i--) ret = histogram[i] ? i : ret;
ret = histogram[i] ? i : ret; return ret;
return ret;
} }
int histogram_max(uint64_t * histogram, int max_value) { int histogram_max(uint64_t *histogram, int max_value) {
int ret = 0; int ret = 0;
for (int i = 0; i <= max_value; i++) for (int i = 0; i <= max_value; i++) ret = histogram[i] ? i : ret;
ret = histogram[i] ? i : ret; return ret;
return ret;
} }
static void sighand_wrapper(int sig) { static void sighand_wrapper(int sig) {
// If we get un unexpected signal, report it, if not print the histogram
// If we get un unexpected signal, report it, if not print the histogram if (sig == SIGINT || sig == SIGTERM)
if (sig == SIGINT || sig == SIGTERM) (*sighand)(sig); // Will print the histogram
(*sighand)(sig); // Will print the histogram else
else printf("Uknown signal interrupt: %s (%d)\n", strsignal(sig),
printf("Uknown signal interrupt: %s (%d)\n", strsignal(sig), sig); sig);
// Execute the default handler // Execute the default handler
if (previous_handlers[sig] == SIG_DFL) { if (previous_handlers[sig] == SIG_DFL) {
signal(sig, SIG_DFL); signal(sig, SIG_DFL);
raise(sig); raise(sig);
} else if (previous_handlers[sig] == SIG_IGN) { } else if (previous_handlers[sig] == SIG_IGN) {
return; return;
} else { } else {
(*previous_handlers[sig])(sig); (*previous_handlers[sig])(sig);
} }
} }
void init_signals(void (*_sighand)(int), int enable_histograms) { void init_signals(void (*_sighand)(int)) {
sighand = _sighand;
sighand = _sighand;
if (enable_histograms) for (int i = 0; i < NSIG; i++) signal(i, sighand_wrapper);
for (int i = 0; i < NSIG; i++)
signal(i, sighand_wrapper);
} }
...@@ -8,6 +8,17 @@ ...@@ -8,6 +8,17 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#ifdef WITH_XDP
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <bpf/xsk.h>
#include <linux/if_ether.h>
#include <linux/if_link.h>
#include <linux/if_xdp.h>
#include <linux/ip.h>
#include <linux/udp.h>
#endif
#define NSEC_PER_SEC UINT64_C(1000000000) #define NSEC_PER_SEC UINT64_C(1000000000)
#define SERVER_PORT "50000" #define SERVER_PORT "50000"
#define SERVER_PORT_INT 50000 #define SERVER_PORT_INT 50000
...@@ -19,18 +30,25 @@ ...@@ -19,18 +30,25 @@
#define MAX_BUFFER_SIZE 1024 #define MAX_BUFFER_SIZE 1024
#define TIMESTAMP_BUFFER_SIZE 4096 #define TIMESTAMP_BUFFER_SIZE 4096
#define err(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0)
#define err_errno(...) error(EXIT_FAILURE, errno, __VA_ARGS__);
uint64_t ts_to_uint(struct timespec t); uint64_t ts_to_uint(struct timespec t);
void add_ns(struct timespec *t, uint64_t ns); void add_ns(struct timespec *t, uint64_t ns);
uint64_t calcdiff_ns(struct timespec t1, struct timespec t2); uint64_t calcdiff_ns(struct timespec t1, struct timespec t2);
void init_signals(void (*_sighand)(int), int enable_histograms); void init_signals(void (*_sighand)(int));
int min(int a, int b); int _min_(int a, int b);
int max(int a, int b); int _max_(int a, int b);
int histogram_min(uint64_t * histogram, int max_value); int histogram_min(uint64_t *histogram, int max_value);
int histogram_max(uint64_t * histogram, int max_value); int histogram_max(uint64_t *histogram, int max_value);
extern void (*previous_handlers[NSIG])(int); extern void (*previous_handlers[NSIG])(int);
......
#define _GNU_SOURCE #define _GNU_SOURCE
#include "recv_packet.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <error.h> #include <error.h>
#include <fcntl.h> #include <fcntl.h>
#include <getopt.h>
#include <ifaddrs.h> #include <ifaddrs.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h>
#include <linux/errqueue.h> #include <linux/errqueue.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/net_tstamp.h> #include <linux/net_tstamp.h>
...@@ -12,6 +16,7 @@ ...@@ -12,6 +16,7 @@
#include <net/if.h> #include <net/if.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <poll.h>
#include <pthread.h> #include <pthread.h>
#include <sched.h> #include <sched.h>
#include <signal.h> #include <signal.h>
...@@ -20,13 +25,25 @@ ...@@ -20,13 +25,25 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include "common.h" #include "common.h"
#include "recv_packet.h"
#ifdef WITH_XDP
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <bpf/xsk.h>
#include <linux/if_ether.h>
#include <linux/if_link.h>
#include <linux/if_xdp.h>
#include <linux/ip.h>
#include <linux/udp.h>
#endif
static char rx_buffer[MAX_BUFFER_SIZE]; static char rx_buffer[MAX_BUFFER_SIZE];
static int sock_fd; static int sock_fd;
...@@ -42,187 +59,364 @@ static int use_histogram; ...@@ -42,187 +59,364 @@ static int use_histogram;
uint64_t post_kernel_timestamp; uint64_t post_kernel_timestamp;
// Sets the interface // Sets the interface
static int set_if() { static int set_if(void) {
struct ifreq ifreq; struct ifreq ifreq;
memset(&ifreq, 0, sizeof(ifreq)); memset(&ifreq, 0, sizeof(ifreq));
strncpy(ifreq.ifr_name, params->network_if, sizeof(ifreq.ifr_name) - 1); strncpy(ifreq.ifr_name, params->network_if, sizeof(ifreq.ifr_name) - 1);
if (ioctl(sock_fd, SIOCGIFINDEX, &ifreq)) if (ioctl(sock_fd, SIOCGIFINDEX, &ifreq))
error(EXIT_FAILURE, errno, "ioctl SIOCGIFINDEX failed\n"); error(EXIT_FAILURE, errno, "ioctl SIOCGIFINDEX failed\n");
return ifreq.ifr_ifindex; return ifreq.ifr_ifindex;
} }
void init_udp_recv(ingress_param_t *_params, void init_udp_recv(ingress_param_t *_params, ingress_stat_t *_stats,
ingress_stat_t *_stats, int _use_histogram, uint64_t *_kernel_latency_hist) {
int _use_histogram, int getaddrinfo_err;
uint64_t *_kernel_latency_hist) { int set_if_err;
int getaddrinfo_err; struct addrinfo hints, *servinfo, *servinfo_it;
int set_if_err;
struct addrinfo hints, *servinfo, *servinfo_it; params = _params;
stats = _stats;
params = _params; use_histogram = _use_histogram;
stats = _stats; kernel_latency_hist = _kernel_latency_hist;
use_histogram = _use_histogram;
kernel_latency_hist = _kernel_latency_hist; memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
memset(&hints, 0, sizeof hints); hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; getaddrinfo_err = getaddrinfo(NULL, SERVER_PORT, &hints, &servinfo);
if (getaddrinfo_err != 0) {
getaddrinfo_err = getaddrinfo(NULL, SERVER_PORT, &hints, &servinfo); fprintf(stderr, "getaddrinfo: %s\n",
if (getaddrinfo_err != 0) { gai_strerror(getaddrinfo_err));
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(getaddrinfo_err)); exit(EXIT_FAILURE);
exit(EXIT_FAILURE); }
}
for (servinfo_it = servinfo; servinfo_it;
for (servinfo_it = servinfo; servinfo_it; servinfo_it = servinfo_it->ai_next) {
servinfo_it = servinfo_it->ai_next) { sock_fd = socket(servinfo->ai_family, servinfo->ai_socktype,
sock_fd = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
servinfo->ai_protocol);
if (bind(sock_fd, servinfo_it->ai_addr,
if (bind(sock_fd, servinfo_it->ai_addr, servinfo_it->ai_addrlen) == -1) { servinfo_it->ai_addrlen) == -1) {
close(sock_fd); close(sock_fd);
continue; continue;
} }
break; break;
} }
freeaddrinfo(servinfo); freeaddrinfo(servinfo);
if (sock_fd == -1) if (sock_fd == -1)
error(EXIT_FAILURE, errno, "Couldn't create receive socket"); error(EXIT_FAILURE, errno, "Couldn't create receive socket");
set_if_err = set_if(params->network_if); set_if_err = set_if();
if (set_if_err < 0) if (set_if_err < 0)
error(EXIT_FAILURE, errno, "Couldn't set interface\n"); error(EXIT_FAILURE, errno, "Couldn't set interface\n");
if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, params->network_if, if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, params->network_if,
strlen(params->network_if))) strlen(params->network_if)))
error(EXIT_FAILURE, errno, "setsockopt SO_BINDTODEVICE failed\n"); error(EXIT_FAILURE, errno,
"setsockopt SO_BINDTODEVICE failed\n");
if (params->use_timestamps) {
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping_flags, if (params->use_timestamps) {
sizeof(so_timestamping_flags))) if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING,
error(EXIT_FAILURE, errno, "setsockopt SO_TIMESTAMPING failed\n"); &so_timestamping_flags,
} sizeof(so_timestamping_flags)))
error(EXIT_FAILURE, errno,
"setsockopt SO_TIMESTAMPING failed\n");
}
} }
/* /*
* Receives udp packets * Receive UDP packet
*/ */
void recv_udp_packet() { void recv_udp_packet() {
struct cmsghdr *cmsg;
struct msghdr msg; // Message hardware, sent to the socket
struct iovec iov; // The iovec structures stores the RX buffer
struct sockaddr_in sin;
struct {
struct cmsghdr cm;
char control[512];
} control;
int recvmsgerr;
struct timespec ts;
iov.iov_base = &rx_buffer;
iov.iov_len = MAX_BUFFER_SIZE - 1;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sin;
msg.msg_namelen = sizeof(sin);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
recvmsgerr = recvmsg(sock_fd, &msg, 0);
if (recvmsgerr < 0)
error(EXIT_FAILURE, errno, "recvmsg failed, ret value: %d\n",
recvmsgerr);
if (params->use_timestamps) {
clock_gettime(CLOCK_REALTIME, &ts);
post_kernel_timestamp = ts_to_uint(ts);
}
if (params->use_timestamps) {
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SO_TIMESTAMPING) {
struct timespec *stamp =
(struct timespec *)CMSG_DATA(cmsg);
uint64_t kernel_latency =
post_kernel_timestamp - ts_to_uint(*stamp);
kernel_latency /= 1000u;
stats->min_kernel_latency = _min_(
kernel_latency, stats->min_kernel_latency);
stats->max_kernel_latency = _max_(
kernel_latency, stats->max_kernel_latency);
stats->avg_kernel_latency =
(stats->max_kernel_latency *
(stats->packets_received) +
kernel_latency) /
(stats->packets_received + 1);
if (use_histogram) {
if (kernel_latency > MAX_KERNEL_LATENCY)
stats->high_kernel_latency++;
else
kernel_latency_hist
[kernel_latency]++;
}
}
}
}
strcpy(stats->data, rx_buffer);
}
struct cmsghdr *cmsg; #ifdef WITH_XDP
struct msghdr msg; // Message hardware, sent to the socket static int xdp_flags = XDP_FLAGS_DRV_MODE;
struct iovec iov; // The iovec structures stores the RX buffer static struct pollfd fds[1] = {0};
struct sockaddr_in sin; static unsigned int ifindex;
struct { static struct xdpsock xdp_socket;
struct cmsghdr cm;
char control[512]; static void open_xdp_socket(char *network_if) {
} control; struct xsk_socket_config xsk_cfg;
uint32_t idx;
int recvmsgerr; int ret, i;
struct timespec ts; /* Create XDP socket */
xsk_cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
iov.iov_base = &rx_buffer; xsk_cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
iov.iov_len = MAX_BUFFER_SIZE - 1; xsk_cfg.libbpf_flags = 0;
xsk_cfg.xdp_flags = xdp_flags;
xsk_cfg.bind_flags = 0;
ret = xsk_socket__create(&xdp_socket.xsk, network_if, 0,
xdp_socket.umem.umem, &xdp_socket.rx,
&xdp_socket.tx, &xsk_cfg);
if (ret) err("xsk_socket__create() failed");
/* Add some buffers */
ret = xsk_ring_prod__reserve(&xdp_socket.umem.fq,
XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx);
if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS)
err("xsk_ring_prod__reserve() failed");
for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++)
*xsk_ring_prod__fill_addr(&xdp_socket.umem.fq, idx++) =
i * FRAME_SIZE;
xsk_ring_prod__submit(&xdp_socket.umem.fq,
XSK_RING_PROD__DEFAULT_NUM_DESCS);
}
memset(&msg, 0, sizeof(msg)); /*
msg.msg_name = &sin; * Init XDP socket
msg.msg_namelen = sizeof(sin); */
msg.msg_iov = &iov; void init_xdp_recv(ingress_param_t * _params) {
msg.msg_iovlen = 1; int ret, prog_fd, xsks_map = 0;
msg.msg_control = &control; struct bpf_prog_load_attr prog_load_attr = {
msg.msg_controllen = sizeof(control); .prog_type = BPF_PROG_TYPE_XDP,
.file = "/home/oli/rt-measures/packet-exchange/build/xdp_kern.o",
};
struct xsk_umem_config cfg = {
.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
.frame_size = FRAME_SIZE,
.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM,
.flags = 0,
};
struct bpf_object *obj;
struct bpf_map *map;
void *buffer = NULL;
params = _params;
ret = bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd);
if (ret || prog_fd < 0) err("bpf_prog_load_xattr() failed");
map = bpf_object__find_map_by_name(obj, "xsks_map");
xsks_map = bpf_map__fd(map);
if (xsks_map < 0) err("No xsks_map found!");
ifindex = if_nametoindex(params->network_if);
if (!ifindex) err_errno("if_nametoindex() failed");
/* Use XDP _only_ in conjuction with driver assisted mode */
ret = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags);
if (ret) err("bpf_set_link_xdp_fd() failed");
/* Allocate user space memory for xdp frames */
ret = posix_memalign(&buffer, sysconf(_SC_PAGE_SIZE),
NUM_FRAMES * FRAME_SIZE);
if (ret) err_errno("posix_memalign() failed");
ret = xsk_umem__create(&xdp_socket.umem.umem, buffer,
NUM_FRAMES * FRAME_SIZE, &xdp_socket.umem.fq,
&xdp_socket.umem.cq, &cfg);
if (ret) err("xsk_umem__create() failed");
xdp_socket.umem.buffer = buffer;
/* Open and bind socket */
open_xdp_socket(params->network_if);
}
recvmsgerr = recvmsg(sock_fd, &msg, 0); void setup_poll_fd(void) {
if (recvmsgerr < 0) fds[0].fd = xsk_socket__fd(xdp_socket.xsk);
error(EXIT_FAILURE, errno, "recvmsg failed, ret value: %d\n", recvmsgerr); fds[0].events = POLLIN;
}
if (params->use_timestamps) { static int received;
clock_gettime(CLOCK_REALTIME, &ts); static uint32_t idx_rx = 0, idx;
post_kernel_timestamp = ts_to_uint(ts);
}
/*
* Receive XDP socket
*/
int recv_xdp_packet(void) {
int ret;
if (params->xdp_polling_mode == 0) {
ret = poll(fds, 1, -1);
if (ret == 0) {
return -1;
} else if (ret < 0)
error(EXIT_FAILURE, errno, "poll failed");
received = xsk_ring_cons__peek(&xdp_socket.rx, 1, &idx_rx);
} else {
do {
received =
xsk_ring_cons__peek(&xdp_socket.rx, 1, &idx_rx);
} while (!received);
}
if (!received) return 1;
if (received != 1)
error(EXIT_FAILURE, errno,
"Received more packets than expected");
return 0;
}
void recv_xdp_cleanup(void) {
uint64_t addr;
int ret;
if (params->use_timestamps) { /* Cleanup */
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { xsk_ring_cons__release(&xdp_socket.rx, received);
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPING) {
struct timespec *stamp = (struct timespec *)CMSG_DATA(cmsg); /* Add that particular buffer back to the fill queue */
if (xsk_prod_nb_free(&xdp_socket.umem.fq, received)) {
ret =
xsk_ring_prod__reserve(&xdp_socket.umem.fq, received, &idx);
uint64_t kernel_latency = post_kernel_timestamp - ts_to_uint(*stamp); if (ret != received) err("xsk_ring_prod__reserve() failed");
kernel_latency /= 1000u;
stats->min_kernel_latency = min(kernel_latency, stats->min_kernel_latency); *xsk_ring_prod__fill_addr(&xdp_socket.umem.fq, idx) =
stats->max_kernel_latency = max(kernel_latency, stats->max_kernel_latency); xsk_umem__extract_addr(addr);
stats->avg_kernel_latency = (stats->max_kernel_latency * (stats->packets_received) + kernel_latency) / (stats->packets_received + 1);
if (use_histogram) { xsk_ring_prod__submit(&xdp_socket.umem.fq, received);
if (kernel_latency > MAX_KERNEL_LATENCY) }
stats->high_kernel_latency++; }
else
kernel_latency_hist[kernel_latency]++;
}
}
}
}
strcpy(stats->data, rx_buffer); void close_xdp_socket(void) {
xsk_socket__delete(xdp_socket.xsk);
xsk_umem__delete(xdp_socket.umem.umem);
bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
} }
#else
void init_xdp_recv(ingress_param_t * _params) { (void) _params; }
void setup_poll_fd(void) {}
void close_xdp_socket(void) {}
int recv_xdp_packet(void) { return 0; }
void recv_xdp_cleanup(void) {}
#endif
#ifdef DEBUG #ifdef DEBUG
/* /*
* Code from scheduled_tx_tools * Code from scheduled_tx_tools
*/ */
static int process_socket_error_queue() { static int process_socket_error_queue(void) {
uint8_t msg_control[CMSG_SPACE(sizeof(struct sock_extended_err))]; uint8_t msg_control[CMSG_SPACE(sizeof(struct sock_extended_err))];
unsigned char err_buffer[256]; unsigned char err_buffer[256];
struct sock_extended_err *serr; struct sock_extended_err *serr;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
__u64 tstamp = 0; __u64 tstamp = 0;
struct iovec iov = {.iov_base = err_buffer, .iov_len = sizeof(err_buffer)}; struct iovec iov = {.iov_base = err_buffer,
struct msghdr msg = {.msg_iov = &iov, .iov_len = sizeof(err_buffer)};
.msg_iovlen = 1, struct msghdr msg = {.msg_iov = &iov,
.msg_control = msg_control, .msg_iovlen = 1,
.msg_controllen = sizeof(msg_control)}; .msg_control = msg_control,
.msg_controllen = sizeof(msg_control)};
if (recvmsg(sock_fd, &msg, MSG_ERRQUEUE) == -1) {
fprintf(stderr, "recvmsg failed"); if (recvmsg(sock_fd, &msg, MSG_ERRQUEUE) == -1) {
return -1; fprintf(stderr, "recvmsg failed");
} return -1;
}
cmsg = CMSG_FIRSTHDR(&msg);
while (cmsg != NULL) { cmsg = CMSG_FIRSTHDR(&msg);
serr = (void *)CMSG_DATA(cmsg); while (cmsg != NULL) {
if (serr->ee_origin == SO_EE_ORIGIN_TXTIME) { serr = (void *)CMSG_DATA(cmsg);
tstamp = ((__u64)serr->ee_data << 32) + serr->ee_info; if (serr->ee_origin == SO_EE_ORIGIN_TXTIME) {
tstamp = ((__u64)serr->ee_data << 32) + serr->ee_info;
switch (serr->ee_code) {
case SO_EE_CODE_TXTIME_INVALID_PARAM: switch (serr->ee_code) {
fprintf(stderr, case SO_EE_CODE_TXTIME_INVALID_PARAM:
"packet with tstamp %llu dropped due to invalid params\n", fprintf(
tstamp); stderr,
return 0; "packet with tstamp %llu dropped "
case SO_EE_CODE_TXTIME_MISSED: "due to invalid params\n",
fprintf(stderr, tstamp);
"packet with tstamp %llu dropped due to missed deadline\n", return 0;
tstamp); case SO_EE_CODE_TXTIME_MISSED:
return 0; fprintf(
default: stderr,
return -1; "packet with tstamp %llu dropped "
} "due to missed deadline\n",
} tstamp);
return 0;
cmsg = CMSG_NXTHDR(&msg, cmsg); default:
} return -1;
}
return 0; }
cmsg = CMSG_NXTHDR(&msg, cmsg);
}
return 0;
} }
#endif #endif
...@@ -4,37 +4,61 @@ ...@@ -4,37 +4,61 @@
#include "common.h" #include "common.h"
typedef struct ingress_param { typedef struct ingress_param {
char network_if[16]; char network_if[16];
int use_timestamps; int use_timestamps;
int xdp_polling_mode;
int interval;
size_t tx_buffer_len; size_t tx_buffer_len;
} ingress_param_t; } ingress_param_t;
typedef struct ingress_stat { typedef struct ingress_stat {
int min_kernel_latency;
int avg_kernel_latency;
int max_kernel_latency;
int min_kernel_latency; int min_interval;
int avg_kernel_latency; int avg_interval;
int max_kernel_latency; int max_interval;
int min_interval; uint64_t packets_received;
int avg_interval; uint64_t high_kernel_latency;
int max_interval; uint64_t high_jitter;
int lost_packets;
uint64_t packets_received; char data[MAX_BUFFER_SIZE];
uint64_t high_kernel_latency;
uint64_t high_jitter;
int lost_packets;
char data[MAX_BUFFER_SIZE];
} ingress_stat_t; } ingress_stat_t;
void init_udp_recv(ingress_param_t *_params, void init_udp_recv(ingress_param_t *_params, ingress_stat_t *stats,
ingress_stat_t *stats, int use_histogram, uint64_t *_kernel_latency_hist);
int use_histogram,
uint64_t *_kernel_latency_hist); void recv_udp_packet(void);
#ifdef WITH_XDP
#define NUM_FRAMES 4096
#define FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE
struct xsk_umem_info {
struct xsk_ring_prod fq;
struct xsk_ring_cons cq;
struct xsk_umem *umem;
void *buffer;
};
struct xdpsock {
struct xsk_ring_cons rx;
struct xsk_ring_prod tx;
struct xsk_umem_info umem;
struct xsk_socket *xsk;
int fd;
};
#endif
void recv_udp_packet(); void init_xdp_recv(ingress_param_t * _params);
int recv_xdp_packet(void);
void recv_xdp_cleanup(void);
void setup_poll_fd(void);
void close_xdp_socket(void);
#endif #endif
...@@ -222,9 +222,9 @@ static void process_error_queue() { ...@@ -222,9 +222,9 @@ static void process_error_queue() {
ts_buf_read_index = (ts_buf_read_index + 1) % TIMESTAMP_BUFFER_SIZE; ts_buf_read_index = (ts_buf_read_index + 1) % TIMESTAMP_BUFFER_SIZE;
stats->min_kernel_latency = stats->min_kernel_latency =
min(kernel_latency, stats->min_kernel_latency); _min_(kernel_latency, stats->min_kernel_latency);
stats->max_kernel_latency = stats->max_kernel_latency =
max(kernel_latency, stats->max_kernel_latency); _max_(kernel_latency, stats->max_kernel_latency);
stats->avg_kernel_latency = stats->avg_kernel_latency =
(stats->max_kernel_latency * packets_sent + kernel_latency) / (stats->max_kernel_latency * packets_sent + kernel_latency) /
(packets_sent + 1); (packets_sent + 1);
......
...@@ -33,20 +33,21 @@ ...@@ -33,20 +33,21 @@
// Structs // Structs
enum TSNTask { RECV_PACKET_TASK, RTT_TASK }; enum TSNTask { RECV_PACKET_TASK, RTT_TASK, XDP_TASK };
typedef struct thread_param { typedef struct thread_param {
int interval; int interval;
int priority; int priority;
uint64_t latency_threshold; uint64_t latency_threshold;
} thread_param_t; } thread_param_t;
typedef struct main_params { typedef struct main_params {
int refresh_rate; int refresh_rate;
int verbose; int verbose;
int enable_tracing; int enable_tracing;
int enable_graph; int enable_xdp_tracing;
int enable_graph;
} main_param_t; } main_param_t;
static void process_options(int argc, char *argv[]); static void process_options(int argc, char *argv[]);
...@@ -65,7 +66,7 @@ static egress_param_t egress_params; ...@@ -65,7 +66,7 @@ static egress_param_t egress_params;
static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX}; static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX};
static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX, static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX}; .min_interval = INT_MAX};
static int enable_histograms; static int enable_histograms;
static int enable_affinity; static int enable_affinity;
...@@ -77,368 +78,439 @@ static struct timespec measures_start; ...@@ -77,368 +78,439 @@ static struct timespec measures_start;
static struct timespec measures_end; static struct timespec measures_end;
static void help(char *argv[]) { static void help(char *argv[]) {
printf( printf(
"Usage: %s [-aghtv] [-b CLIENT_IP] [-d BUF_LEN] [-f IF] [-i USEC] [-p " "Usage: %s [-aghtv] [-b CLIENT_IP] [-d BUF_LEN] [-f IF] [-i USEC] "
"PRIO] [-T LATENCY_THRESHOLD -G]" "[-p "
" [-r USEC]\n\n" "PRIO] [-T LATENCY_THRESHOLD -G]"
" -a Run the real time thread on CPU1\n" " [-r USEC]\n\n"
" -b CLIENT_IP Server side RTT\n" " -a Run the real time thread on CPU1\n"
" -d BUF_LEN Set the length of tx buffer\n" " -b CLIENT_IP Server side RTT\n"
" -f IF Set the network interface to be used\n" " -d BUF_LEN Set the length of tx buffer\n"
" -g Print histograms to sdtout on exit\n" " -f IF Set the network interface to be "
" -h Show help\n" "used\n"
" -i USEC Wake up the real time thread every USEC " " -g Print histograms to sdtout on exit\n"
"microseconds (Default: 10ms)\n" " -h Show help\n"
" -p PRIO Run the real time thread at priority PRIO\n" " -i USEC Wake up the real time thread every "
" -r USEC Refresh the non real time main thread " "USEC "
"every USEC microseconds\n" "microseconds (Default: 10ms)\n"
" -t Enable timestamps\n" " -p PRIO Run the real time thread at priority "
" -v Verbose\n" "PRIO\n"
" -T LATENCY_THRESHOLD Enable tracing until LATENCY_THRESHOLD is " " -r USEC Refresh the non real time main "
"reached\n" "thread "
" -G Enable function_graph tracer, used with " "every USEC microseconds\n"
"-T\n" " -t Enable timestamps\n"
"\n", " -v Verbose\n"
argv[0]); " -x POLL_MODE Use AF_XDP sockets\n"
" POLL_MODE: 0 for polling, 1 for combination of both\n"
" -X Trace during XDP packet reception\n"
" -T LATENCY_THRESHOLD Enable tracing until "
"LATENCY_THRESHOLD is "
"reached\n"
" -G Enable function_graph tracer, used "
"with "
"-T\n"
"\n",
argv[0]);
} }
// Real-time thread // Real-time thread
// Measures intervals between packet receptions // Measures intervals between packet receptions
static void *packet_receiving_thread(void *p) { static void *packet_receiving_thread(void *p) {
(void)p; (void)p;
struct timespec current, previous; struct timespec current, previous;
cpu_set_t mask; cpu_set_t mask;
int prev_packet_id = 0; int prev_packet_id = 0;
char tracemark_message[128]; char tracemark_message[128];
// Set thread CPU affinity // Set thread CPU affinity
if (enable_affinity) { if (enable_affinity) {
CPU_ZERO(&mask); CPU_ZERO(&mask);
CPU_SET(1, &mask); CPU_SET(1, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask)) if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU #1\n"); error(EXIT_FAILURE, errno,
} "Could not set CPU affinity to CPU #1\n");
}
clock_gettime(CLOCK_MONOTONIC, &measures_start);
clock_gettime(CLOCK_MONOTONIC, &measures_start);
// Packet receiving loop
for (ingress_stats.packets_received = 0;; ingress_stats.packets_received++) { if(tsn_task == XDP_TASK)
if (tsn_task == RTT_TASK) { setup_poll_fd();
recv_udp_packet();
send_udp_packet("", 0); // Packet receiving loop
for (ingress_stats.packets_received = 0;;
} else if (tsn_task == RECV_PACKET_TASK) { ingress_stats.packets_received++) {
int current_packet_id; if (tsn_task == RTT_TASK) {
recv_udp_packet();
recv_udp_packet(); send_udp_packet("", 0);
clock_gettime(CLOCK_MONOTONIC, &current); } else if (tsn_task == RECV_PACKET_TASK ||
tsn_task == XDP_TASK) {
current_packet_id = atoi(ingress_stats.data); int current_packet_id;
if (ingress_stats.packets_received) { if (tsn_task == RECV_PACKET_TASK)
int interval_us = calcdiff_ns(current, previous) / 1000; recv_udp_packet();
else
ingress_stats.min_interval = recv_xdp_packet();
min(interval_us, ingress_stats.min_interval);
ingress_stats.max_interval = clock_gettime(CLOCK_MONOTONIC, &current);
max(interval_us, ingress_stats.max_interval);
ingress_stats.avg_interval = recv_xdp_cleanup();
(ingress_stats.avg_interval * ingress_stats.packets_received +
interval_us) / if (ingress_stats.packets_received) {
(ingress_stats.packets_received + 1); int interval_us =
calcdiff_ns(current, previous) / 1000;
// Check if packets were lost
ingress_stats.lost_packets += ingress_stats.min_interval = _min_(
(current_packet_id - prev_packet_id - 1) % 1000; interval_us, ingress_stats.min_interval);
ingress_stats.max_interval = _max_(
if (enable_histograms) { interval_us, ingress_stats.max_interval);
int dist_to_interval = interval_us - (thread_params.interval / 1000); ingress_stats.avg_interval =
dist_to_interval += MAX_JITTER / 2; (ingress_stats.avg_interval *
ingress_stats.packets_received +
if (dist_to_interval > ((int)MAX_JITTER) || dist_to_interval < 0) interval_us) /
ingress_stats.high_jitter++; (ingress_stats.packets_received + 1);
else
jitter_hist[dist_to_interval]++; if (tsn_task == RECV_PACKET_TASK) {
} // Check if packets were lost
} current_packet_id = atoi(ingress_stats.data);
ingress_stats.lost_packets +=
// If the latency hits the tracing threshold, stop tracing (current_packet_id -
if (main_params.enable_tracing && prev_packet_id - 1) %
(ingress_stats.max_interval > ((int)thread_params.latency_threshold))) { 1000;
sprintf(tracemark_message, "Jitter threshold hit: %dus\n", ingress_stats.max_interval); prev_packet_id = current_packet_id;
tracemark(tracemark_message); }
tracing(0);
break; if (enable_histograms) {
} int dist_to_interval =
interval_us -
previous = current; (thread_params.interval / 1000);
prev_packet_id = current_packet_id; dist_to_interval += MAX_JITTER / 2;
}
} if (dist_to_interval >
((int)MAX_JITTER) ||
return NULL; dist_to_interval < 0)
ingress_stats.high_jitter++;
else
jitter_hist[dist_to_interval]++;
}
}
// If the latency hits the tracing threshold, stop
// tracing
if (main_params.enable_tracing &&
(ingress_stats.max_interval >
((int)thread_params.latency_threshold))) {
sprintf(tracemark_message,
"Jitter threshold hit: %dus\n",
ingress_stats.max_interval);
tracemark(tracemark_message);
tracing(0);
break;
}
previous = current;
}
}
return NULL;
} }
// Main thread, has non-real time priority // Main thread, has non-real time priority
// Handles the IO and creates real time threads // Handles the IO and creates real time threads
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
pthread_t thread; pthread_t thread;
struct sched_param param; struct sched_param param;
pthread_attr_t attr; pthread_attr_t attr;
ingress_stats.min_interval = INT_MAX; ingress_stats.min_interval = INT_MAX;
ingress_stats.avg_interval = 0; ingress_stats.avg_interval = 0;
ingress_stats.max_interval = 0; ingress_stats.max_interval = 0;
ingress_stats.min_kernel_latency = INT_MAX; ingress_stats.min_kernel_latency = INT_MAX;
ingress_stats.avg_kernel_latency = 0; ingress_stats.avg_kernel_latency = 0;
ingress_stats.max_kernel_latency = 0; ingress_stats.max_kernel_latency = 0;
ingress_stats.packets_received = 0; ingress_stats.packets_received = 0;
// Default configuration values // Default configuration values
thread_params.interval = 100000 * 1000; thread_params.interval = 100000 * 1000;
thread_params.priority = 99; thread_params.priority = 99;
main_params.refresh_rate = 50000; main_params.refresh_rate = 50000;
main_params.verbose = 0; main_params.verbose = 0;
main_params.enable_tracing = 0; main_params.enable_tracing = 0;
main_params.enable_graph = 0; main_params.enable_graph = 0;
enable_affinity = 0; enable_affinity = 0;
enable_timestamps = 0; enable_timestamps = 0;
enable_histograms = 0; enable_histograms = 0;
tsn_task = RECV_PACKET_TASK; tsn_task = RECV_PACKET_TASK;
ingress_params.tx_buffer_len = 1024; ingress_params.tx_buffer_len = 1024;
// Process bash options // Process bash options
process_options(argc, argv); process_options(argc, argv);
ingress_params.use_timestamps = enable_timestamps; ingress_params.use_timestamps = enable_timestamps;
ingress_params.interval = thread_params.interval;
if (enable_histograms) {
// Init histograms if (enable_histograms) {
memset(kernel_latency_hist, 0, sizeof(kernel_latency_hist)); // Init histograms
memset(jitter_hist, 0, sizeof(jitter_hist)); memset(kernel_latency_hist, 0, sizeof(kernel_latency_hist));
} memset(jitter_hist, 0, sizeof(jitter_hist));
}
if (main_params.enable_tracing) {
// Enable ftrace if (main_params.enable_tracing || main_params.enable_xdp_tracing) {
open_fds(); // Enable ftrace
} open_fds();
}
// Catch breaks with sighand to print the histograms
init_signals(sighand, enable_histograms); // Catch breaks with sighand to print the histograms or exit XDP
init_signals(sighand);
// Initialize the UDP packet receiving socket
init_udp_recv(&ingress_params, &ingress_stats, enable_histograms, if (tsn_task == XDP_TASK) {
kernel_latency_hist); // Initialize the XDP packet receiving socket
init_xdp_recv(&ingress_params);
// Initialize the UDP packet sending socket if RTT is measured } else {
if (tsn_task == RTT_TASK) // Initialize the UDP packet receiving socket
init_udp_send(&egress_params, &egress_stats, 0, 0, NULL); init_udp_recv(&ingress_params, &ingress_stats,
enable_histograms, kernel_latency_hist);
/* Initialize pthread attributes (default values) */ }
if (pthread_attr_init(&attr)) {
fprintf(stderr, "init pthread attributes failed\n"); // Initialize the UDP packet sending socket if RTT is measured
exit(EXIT_FAILURE); if (tsn_task == RTT_TASK)
} init_udp_send(&egress_params, &egress_stats, 0, 0, NULL);
/* Set a specific stack size */
if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)) { /* Initialize pthread attributes (default values) */
fprintf(stderr, "pthread setstacksize failed\n"); if (pthread_attr_init(&attr)) {
exit(EXIT_FAILURE); fprintf(stderr, "init pthread attributes failed\n");
} exit(EXIT_FAILURE);
/* Set scheduler policy and priority of pthread */ }
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { /* Set a specific stack size */
fprintf(stderr, "pthread setschedpolicy failed\n"); if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)) {
exit(EXIT_FAILURE); fprintf(stderr, "pthread setstacksize failed\n");
} exit(EXIT_FAILURE);
param.sched_priority = thread_params.priority; }
if (pthread_attr_setschedparam(&attr, &param)) { /* Set scheduler policy and priority of pthread */
fprintf(stderr, "pthread setschedparam failed\n"); if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
exit(EXIT_FAILURE); fprintf(stderr, "pthread setschedpolicy failed\n");
} exit(EXIT_FAILURE);
/* Use scheduling parameters of attr */ }
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { param.sched_priority = thread_params.priority;
fprintf(stderr, "pthread setinheritsched failed\n"); if (pthread_attr_setschedparam(&attr, &param)) {
exit(EXIT_FAILURE); fprintf(stderr, "pthread setschedparam failed\n");
} exit(EXIT_FAILURE);
}
// Create the real time thread /* Use scheduling parameters of attr */
if (pthread_create(&thread, &attr, packet_receiving_thread, NULL)) if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) {
error(EXIT_FAILURE, errno, "Couldn't create packet receiving thread"); fprintf(stderr, "pthread setinheritsched failed\n");
exit(EXIT_FAILURE);
// Verbose loop }
for (;;) {
usleep(main_params.refresh_rate); // Create the real time thread
if (pthread_create(&thread, &attr, packet_receiving_thread, NULL))
if (main_params.verbose && ingress_stats.packets_received > 1) { error(EXIT_FAILURE, errno,
if (tsn_task == RECV_PACKET_TASK) { "Couldn't create packet receiving thread");
int jitter = ingress_stats.max_interval - ingress_stats.min_interval;
// Verbose loop
printf("%9" PRIu64 ": J: %5d, I (10us): %3d %3d %3d [%3d,%3d]", for (;;) {
ingress_stats.packets_received, jitter, usleep(main_params.refresh_rate);
ingress_stats.min_interval / 10, ingress_stats.avg_interval / 10,
ingress_stats.max_interval / 10, (int)ingress_stats.high_jitter, if (main_params.verbose && ingress_stats.packets_received > 1) {
(int)ingress_stats.lost_packets); if (tsn_task == RECV_PACKET_TASK ||
tsn_task == XDP_TASK) {
if (enable_timestamps) { int jitter = ingress_stats.max_interval -
printf(", K: %4d %4d %4d [%3d]\n", ingress_stats.min_kernel_latency, ingress_stats.min_interval;
ingress_stats.avg_kernel_latency,
ingress_stats.max_kernel_latency, printf(
(int)ingress_stats.high_kernel_latency); "%9" PRIu64
} else { ": J: %5d, I (10us): %3d %3d %3d [%3d,%3d]",
printf("\n"); ingress_stats.packets_received, jitter,
} ingress_stats.min_interval / 10,
ingress_stats.avg_interval / 10,
printf("\033[%dA", 1); ingress_stats.max_interval / 10,
} (int)ingress_stats.high_jitter,
} (int)ingress_stats.lost_packets);
if (main_params.enable_tracing && if (enable_timestamps) {
(ingress_stats.max_interval >= ((int)thread_params.latency_threshold))) printf(", K: %4d %4d %4d [%3d]\n",
break; ingress_stats.min_kernel_latency,
} ingress_stats.avg_kernel_latency,
ingress_stats.max_kernel_latency,
(int)ingress_stats
.high_kernel_latency);
} else {
printf("\n");
}
printf("\033[%dA", 1);
}
}
if (main_params.enable_tracing &&
(ingress_stats.max_interval >=
((int)thread_params.latency_threshold)))
break;
}
close_xdp_socket();
} }
// Print histograms in .json format // Print histograms in .json format
static void print_histograms() { static void print_histograms() {
uint64_t duration; uint64_t duration;
int duration_hour, duration_minutes, interval; int duration_hour, duration_minutes, interval;
int max_latency, max_jitter, min_jitter; int max_latency, max_jitter, min_jitter;
clock_gettime(CLOCK_MONOTONIC, &measures_end); clock_gettime(CLOCK_MONOTONIC, &measures_end);
duration = calcdiff_ns(measures_end, measures_start); duration = calcdiff_ns(measures_end, measures_start);
duration_hour = duration / NSEC_PER_SEC / 3600; duration_hour = duration / NSEC_PER_SEC / 3600;
duration_minutes = duration / NSEC_PER_SEC / 60 - duration_hour * 60; duration_minutes = duration / NSEC_PER_SEC / 60 - duration_hour * 60;
interval = thread_params.interval / 1000; interval = thread_params.interval / 1000;
if (enable_timestamps) { if (enable_timestamps) {
printf( printf(
"{\"measure_sets\": [{" "{\"measure_sets\": [{"
"\"measure_type\": \"packet_recv_timestamps\"," "\"measure_type\": \"packet_recv_timestamps\","
"\"props_names\": [\"kernel_space\"]," "\"props_names\": [\"kernel_space\"],"
"\"units\": [\"us\"]," "\"units\": [\"us\"],"
"\"props_type\": \"histogram\"," "\"props_type\": \"histogram\","
"\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\"," "\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\","
"\"lost_packets\": \"%d\"}," "\"lost_packets\": \"%d\"},"
"\"props\": [[", "\"props\": [[",
interval, duration_hour, duration_minutes, interval, duration_hour, duration_minutes,
ingress_stats.lost_packets + ((int)ingress_stats.high_jitter)); ingress_stats.lost_packets +
((int)ingress_stats.high_jitter));
max_latency = histogram_max(kernel_latency_hist, MAX_KERNEL_LATENCY - 1);
max_latency =
for (int j = 0; j < max_latency; j++) histogram_max(kernel_latency_hist, MAX_KERNEL_LATENCY - 1);
printf("%" PRIi64 "%s", kernel_latency_hist[j],
(j + 1 < max_latency ? ", " : "")); for (int j = 0; j < max_latency; j++)
printf("]]"); printf("%" PRIi64 "%s", kernel_latency_hist[j],
} (j + 1 < max_latency ? ", " : ""));
printf("]]");
max_jitter = histogram_max(jitter_hist, MAX_JITTER - 1); }
min_jitter = histogram_min(jitter_hist, MAX_JITTER - 1);
max_jitter = histogram_max(jitter_hist, MAX_JITTER - 1);
printf("%s", enable_timestamps ? "}, {" : "{\"measure_sets\": [{"); min_jitter = histogram_min(jitter_hist, MAX_JITTER - 1);
printf( printf("%s", enable_timestamps ? "}, {" : "{\"measure_sets\": [{");
"\"measure_type\": \"packet_jitter\","
"\"props_names\": [\"jitter\"]," printf(
"\"units\": [\"us\"]," "\"measure_type\": \"packet_jitter\","
"\"props_type\": \"histogram\"," "\"props_names\": [\"jitter\"],"
"\"middle\": \"%d\"," "\"units\": [\"us\"],"
"\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\"," "\"props_type\": \"histogram\","
"\"lost_packets\": \"%d\"}," "\"middle\": \"%d\","
"\"props\": [[", "\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\","
MAX_JITTER / 2 - min_jitter, interval, duration_hour, duration_minutes, "\"lost_packets\": \"%d\"},"
ingress_stats.lost_packets + ((int)ingress_stats.high_jitter)); "\"props\": [[",
MAX_JITTER / 2 - min_jitter, interval, duration_hour,
for (int j = min_jitter; j < max_jitter; j++) duration_minutes,
printf("%" PRIi64 "%s", jitter_hist[j], (j + 1 < max_jitter ? ", " : "")); ingress_stats.lost_packets + ((int)ingress_stats.high_jitter));
printf("]]}]}\n");
for (int j = min_jitter; j < max_jitter; j++)
printf("%" PRIi64 "%s", jitter_hist[j],
(j + 1 < max_jitter ? ", " : ""));
printf("]]}]}\n");
} }
static void sighand(int sig_num) { static void sighand(int sig_num) {
(void)sig_num; (void)sig_num;
print_histograms(); if (enable_histograms) {
print_histograms();
}
if (ingress_stats.lost_packets) if (tsn_task == XDP_TASK) {
fprintf(stderr, "%d packets were lost\n", ingress_stats.lost_packets); close_xdp_socket();
}
exit(EXIT_SUCCESS); if (ingress_stats.lost_packets)
fprintf(stderr, "%d packets were lost\n",
ingress_stats.lost_packets);
exit(EXIT_SUCCESS);
} }
// Process bash options // Process bash options
static void process_options(int argc, char *argv[]) { static void process_options(int argc, char *argv[]) {
int network_if_specified = 0; int network_if_specified = 0;
for (;;) { for (;;) {
int c = getopt(argc, argv, "ab:d:f:ghi:p:r:tvT:G"); int c = getopt(argc, argv, "ab:d:f:ghi:p:r:tvx:XT:G");
if (c == -1) break; if (c == -1) break;
switch (c) { switch (c) {
case 'a': case 'a':
enable_affinity = 1; enable_affinity = 1;
break; break;
case 'b': case 'b':
tsn_task = RTT_TASK; tsn_task = RTT_TASK;
strcpy(egress_params.server_ip, optarg); strcpy(egress_params.server_ip, optarg);
break; break;
case 'd': case 'd':
ingress_params.tx_buffer_len = atoi(optarg); ingress_params.tx_buffer_len = atoi(optarg);
if (ingress_params.tx_buffer_len < 1) { if (ingress_params.tx_buffer_len < 1) {
fprintf(stderr, "BUF_LEN should be greater than 1\n"); fprintf(stderr,
exit(EXIT_FAILURE); "BUF_LEN should be greater "
} "than 1\n");
break; exit(EXIT_FAILURE);
case 'f': }
network_if_specified = 1; break;
strcpy(ingress_params.network_if, optarg); case 'f':
break; network_if_specified = 1;
case 'h': strcpy(ingress_params.network_if, optarg);
help(argv); break;
exit(EXIT_SUCCESS); case 'h':
break; help(argv);
case 'i': exit(EXIT_SUCCESS);
thread_params.interval = atoi(optarg) * 1000; break;
break; case 'i':
case 'g': thread_params.interval = atoi(optarg) * 1000;
enable_histograms = 1; break;
break; case 'g':
case 'p': enable_histograms = 1;
thread_params.priority = atoi(optarg); break;
break; case 'p':
case 'r': thread_params.priority = atoi(optarg);
main_params.refresh_rate = atoi(optarg); break;
break; case 'r':
case 't': main_params.refresh_rate = atoi(optarg);
enable_timestamps = 1; break;
break; case 't':
case 'v': enable_timestamps = 1;
main_params.verbose = 1; break;
break; case 'v':
case 'T': main_params.verbose = 1;
main_params.enable_tracing = 1; break;
thread_params.latency_threshold = atoi(optarg); case 'x':
break; tsn_task = XDP_TASK;
case 'G': ingress_params.xdp_polling_mode = atoi(optarg);
main_params.enable_graph = 1; break;
break; case 'X':
} main_params.enable_xdp_tracing = 1;
} break;
case 'T':
if (!network_if_specified) { main_params.enable_tracing = 1;
fprintf(stderr, "You need to specifiy an network interface\n"); thread_params.latency_threshold = atoi(optarg);
help(argv); break;
exit(EXIT_FAILURE); case 'G':
} main_params.enable_graph = 1;
break;
if (argc != optind) { }
fprintf(stderr, "Too many arguments\n"); }
help(argv);
exit(EXIT_FAILURE); if (!network_if_specified) {
} fprintf(stderr, "You need to specifiy an network interface\n");
help(argv);
exit(EXIT_FAILURE);
}
if (argc != optind) {
fprintf(stderr, "Too many arguments\n");
help(argv);
exit(EXIT_FAILURE);
}
} }
...@@ -75,7 +75,7 @@ static void setkernvar(const char *name, char *value) { ...@@ -75,7 +75,7 @@ static void setkernvar(const char *name, char *value) {
fprintf(stderr, "could not set %s to %s\n", name, value); fprintf(stderr, "could not set %s to %s\n", name, value);
} }
void open_fds() { void open_fds(void) {
fileprefix = debugfileprefix; fileprefix = debugfileprefix;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#define TRACER_H #define TRACER_H
void setup_tracer(int enable_graph); void setup_tracer(int enable_graph);
void open_fds(); void open_fds(void);
void tracing(int on); void tracing(int on);
void tracemark(char * s); void tracemark(char * s);
......
#define KBUILD_MODNAME "blub"
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/if_link.h>
#include <linux/if_xdp.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include "bpf_helpers.h"
#define bpf_printk(fmt, ...) \
({ \
char ____fmt[] = fmt; \
bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
})
struct bpf_map_def SEC("maps") xsks_map = {
.type = BPF_MAP_TYPE_XSKMAP,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 64,
};
static inline int parse_ipv4(void *data, unsigned long long nh_off,
void *data_end) {
struct iphdr *iph = data + nh_off;
if ((void *)(iph + 1) > data_end) return 0;
return iph->protocol;
}
SEC("xdp_sock")
int xdp_sock_prog(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
int idx = ctx->rx_queue_index;
unsigned int ipproto = 0;
unsigned long long nh_off;
/* Check if it's a UDP frame: If UDP -> Redirect to active xsk for user
* space. If not -> pass to stack.
*/
nh_off = sizeof(*eth);
if (data + nh_off > data_end) return XDP_PASS;
if (eth->h_proto == __builtin_bswap16(ETH_P_IP))
ipproto = parse_ipv4(data, nh_off, data_end);
if (ipproto != IPPROTO_UDP) return XDP_PASS;
/* If socket bound to rx_queue then redirect to user space */
if (bpf_map_lookup_elem(&xsks_map, &idx))
return bpf_redirect_map(&xsks_map, idx, 0);
/* Else pass to Linux' network stack */
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
...@@ -3,16 +3,28 @@ ...@@ -3,16 +3,28 @@
script_dir=$(dirname $(realpath $0)) script_dir=$(dirname $(realpath $0))
usage() { usage() {
echo "Usage: $0 [[-bt] [-g INTERVAL]] | (-d NB_PACKETS [-i INTERVAL]) [-T LATENCY_THRESHOLD -G]" 1>&2; echo "Usage: $0 | " 1>&2;
exit 1;
}
usage() {
cat << ENDUSAGE
Usage: $0 SERVER | TCPDUMP [TRACE_OPTS]
SERVER: [-bt] [(-x | -X) POLL] [-g INTERVAL]
TCPDUMP: -d NB_PACKETS [-i INTERVAL]
TRACE_OPTS: (-T LATENCY_THRESHOLD -G) | -E
ENDUSAGE
1>&2;
exit 1; exit 1;
} }
# Default options # Default options
server_options="-a -p 99 -f eth0" server_options="-a -p 99 -f eth0"
make_opts=""
ip="10.100.21." ip="10.100.21."
tcpdump_interval=1000000 tcpdump_interval=1000000
while getopts "b:htd:i:g:T:G" opt; do while getopts "b:htx:X:d:i:g:T:GE" opt; do
case "${opt}" in case "${opt}" in
b ) b )
use_rtt=1 use_rtt=1
...@@ -37,12 +49,23 @@ while getopts "b:htd:i:g:T:G" opt; do ...@@ -37,12 +49,23 @@ while getopts "b:htd:i:g:T:G" opt; do
t ) t )
server_options+=" -t" server_options+=" -t"
;; ;;
x )
server_options+=" -x ${OPTARG}"
make_opts=" -e WITH_XDP=1"
;;
X )
server_options+=" -x ${OPTARG}"
make_opts=" -e WITH_GIT_XDP=1"
;;
T ) T )
server_options+=" -T ${OPTARG}" server_options+=" -T ${OPTARG}"
;; ;;
G ) G )
server_options+=" -G" server_options+=" -G"
;; ;;
E )
enable_xdp_events=1
;;
* ) * )
usage usage
;; ;;
...@@ -78,10 +101,14 @@ if [ -n "${use_tcpdump}" ]; then ...@@ -78,10 +101,14 @@ if [ -n "${use_tcpdump}" ]; then
$script_dir/txtime-stats.py -f server_stats_tmp.out -i $tcpdump_interval; $script_dir/txtime-stats.py -f server_stats_tmp.out -i $tcpdump_interval;
else else
echo "make server"; echo "make $make_opts server";
cd $script_dir/../packet-exchange/build;make server;cd $script_dir cd $script_dir/../packet-exchange/build;make $make_opts server;cd $script_dir
if [ -z "${use_histogram}" ]; then if [ -n "${enable_xdp_events}" ]; then
tracecmd_events="-e irq -e sched -e xdp"
echo "trace-cmd record $tracecmd_events $script_dir/../packet-exchange/build/server $server_options";
trace-cmd record $tracecmd_events $script_dir/../packet-exchange/build/server $server_options;
elif [ -z "${use_histogram}" ]; then
echo "server $server_options"; echo "server $server_options";
$script_dir/../packet-exchange/build/server $server_options; $script_dir/../packet-exchange/build/server $server_options;
else else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment