Skip to content

Commit

Permalink
Adds the draft of the XDP scheduler testing tool
Browse files Browse the repository at this point in the history
This commit contains the XDP scheduling framework. It consists of a
testing program called xdp_scheduler_tester used to test schedulers
using the XDP and DEQUEUE hooks. It does this using trace files that the
xdp_scheduler_tester program uses to check the XDP schedulers for
correctness.

The FIFO and PIFO schedulers are fully functional in this commit.
However, it defines flows as UDP port numbers until I have added a way
to express priorities to flows from the trace files. For now, the WFQ
uses my five-tuples flow implementation with a fixed weight of 256.

The xdp_scheduler_tester program includes a -v flag that prints out the
enqueued packet. This flag prints more information about each packet to
determine if it is correct. It is helpful while changing the code, but I
think I will change this behavior in the future because it does not add
much for users of the tool.

I have an issue with this commit because I cannot make the PIFO in the
WFQ larger than 4095, making it unable to process packet priority as
virtual-time-bytes.

Another issue I will need help with is adding the libbpf logging to the
project. I have added the logging.h and logging.c files to lib/util.
However, we need to change the Makefiles to include the required
dependencies for the logging.c file for it to work.

The final issue, I am not sure if it is related to my code or if it is
something that is an issue in the kernel. However, my kernel runs out of
memory if I run the traces too often.

bash-5.1# for i in {1..1000000}; do ./xdp_scheduler_tester --file=./xdp_wfq.trace; done

Signed-off-by: Frey Alfredsson <[email protected]>
  • Loading branch information
freysteinn committed Apr 6, 2022
1 parent ce71462 commit 144e7f1
Show file tree
Hide file tree
Showing 14 changed files with 1,346 additions and 0 deletions.
92 changes: 92 additions & 0 deletions lib/util/logging.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/* SPDX-License-Identifier: GPL-2.0 */

#include <stdio.h>
#include <stdarg.h>

#include <bpf/libbpf.h>
#include <xdp/libxdp.h>

#include "logging.h"
#include "util.h"

static enum logging_print_level log_level = LOG_INFO;

static int print_func(enum logging_print_level level, const char *format,
va_list args)
{
if (level > log_level)
return 0;

return vfprintf(stderr, format, args);
}

static int libbpf_print_func(enum libbpf_print_level level, const char *format,
va_list args)
{
return print_func(level + 1, format, args);
}

static int libbpf_silent_func(__unused enum libbpf_print_level level,
__unused const char *format,
__unused va_list args)
{
return 0;
}

static int libxdp_print_func(enum libxdp_print_level level, const char *format,
va_list args)
{
return print_func(level + 1, format, args);
}

static int libxdp_silent_func(__unused enum libxdp_print_level level,
__unused const char *format,
__unused va_list args)
{
return 0;
}

#define __printf(a, b) __attribute__((format(printf, a, b)))

__printf(2, 3) void logging_print(enum logging_print_level level,
const char *format, ...)
{
va_list args;

va_start(args, format);
print_func(level, format, args);
va_end(args);
}

void init_lib_logging(void)
{
libbpf_set_print(libbpf_print_func);
libxdp_set_print(libxdp_print_func);
}

void silence_libbpf_logging(void)
{
if (log_level < LOG_VERBOSE)
libbpf_set_print(libbpf_silent_func);
}

void silence_libxdp_logging(void)
{
if (log_level < LOG_VERBOSE)
libxdp_set_print(libxdp_silent_func);
}

enum logging_print_level set_log_level(enum logging_print_level level)
{
enum logging_print_level old_level = log_level;

log_level = level;
return old_level;
}

enum logging_print_level increase_log_level(void)
{
if (log_level < LOG_VERBOSE)
log_level++;
return log_level;
}
35 changes: 35 additions & 0 deletions lib/util/logging.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __LOGGING_H
#define __LOGGING_H

/* This matches the libbpf logging levels, but with an additional VERBOSE level;
* we demote all libbpf messages by one level so debug messages only show up on
* VERBOSE.
*/
enum logging_print_level {
LOG_WARN,
LOG_INFO,
LOG_DEBUG,
LOG_VERBOSE,
};

extern void logging_print(enum logging_print_level level, const char *format,
...) __attribute__((format(printf, 2, 3)));

#define __pr(level, fmt, ...) \
do { \
logging_print(level, fmt, ##__VA_ARGS__); \
} while (0)

#define pr_warn(fmt, ...) __pr(LOG_WARN, fmt, ##__VA_ARGS__)
#define pr_info(fmt, ...) __pr(LOG_INFO, fmt, ##__VA_ARGS__)
#define pr_debug(fmt, ...) __pr(LOG_DEBUG, fmt, ##__VA_ARGS__)

void init_lib_logging(void);
void silence_libbpf_logging(void);
void silence_libxdp_logging(void);
enum logging_print_level set_log_level(enum logging_print_level level);
enum logging_print_level increase_log_level();

#endif
8 changes: 8 additions & 0 deletions xdp-scheduler-tester/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)

USER_TARGETS := xdp_scheduler_tester
BPF_TARGETS := $(patsubst %.c,%,$(wildcard *.bpf.c))

LIB_DIR = ../lib

include $(LIB_DIR)/common.mk
73 changes: 73 additions & 0 deletions xdp-scheduler-tester/bpf_local_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)

#define EEXIST 17 /* File exists */

#define BPF_MAP_TYPE_PIFO 31

/*
* bpf_packet_dequeue
*
* Dequeue the packet at the head of the PIFO in *map* and return a pointer
* to the packet (or NULL if the PIFO is empty).
*
* Returns
* On success, a pointer to the packet, or NULL if the PIFO is empty. The
* packet pointer must be freed using *bpf_packet_drop()* or returning
* the packet pointer. The *rank* pointer will be set to the rank of
* the dequeued packet on success, or a negative error code on error.
*/
static long (*bpf_packet_dequeue)(void *ctx, void *map, __u64 flags, __u64 *rank) = (void *) 194;
static long (*bpf_packet_drop)(void *ctx, void *pkt) = (void *) 195;

struct flow_address {
struct in6_addr ip;
__u16 port;
__u16 reserved;
};

struct network_tuple {
struct flow_address saddr;
struct flow_address daddr;
__u16 proto;
__u8 ipv;
__u8 reserved;
};

struct flow_state {
__u32 pkts;
__u32 finish_bytes;
};


static __always_inline void *
bpf_map_lookup_or_try_init(void *map, const void *key, const void *init)
{
void *val;
long err;

val = bpf_map_lookup_elem(map, key);
if (val)
return val;

err = bpf_map_update_elem(map, key, init, BPF_NOEXIST);
if (err && err != -EEXIST)
return NULL;

return bpf_map_lookup_elem(map, key);
}

static __always_inline int bpf_max(__u64 left, __u64 right)
{
return right > left ? right : left;
}


/*
* Maps an IPv4 address into an IPv6 address according to RFC 4291 sec 2.5.5.2
*/
static void map_ipv4_to_ipv6(struct in6_addr *ipv6, __be32 ipv4)
{
__builtin_memset(&ipv6->in6_u.u6_addr8[0], 0x00, 10);
__builtin_memset(&ipv6->in6_u.u6_addr8[10], 0xff, 2);
ipv6->in6_u.u6_addr32[3] = ipv4;
}
9 changes: 9 additions & 0 deletions xdp-scheduler-tester/xdp_debug.trace
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Used for debugging the xdp_scheduler_tester syntax
global bpf file=./xdp_scheduler_fifo.bpf.o

udp eth proto=1 dst port=8080 # In-line comment
udp eth proto=2 dst port=8081 payload length=32
udp eth proto=3 dst port=8082 repeat=2
dequeue udp eth proto=1 dst port=8080
dequeue udp eth proto=2 dst port=8081 payload length=32
dequeue udp eth proto=3 dst port=8082 repeat=2
7 changes: 7 additions & 0 deletions xdp-scheduler-tester/xdp_fifo.trace
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
global bpf file=./xdp_scheduler_fifo.bpf.o
udp dst port=8080
udp dst port=8081
udp dst port=8082
dequeue udp dst port=8080
dequeue udp dst port=8081
dequeue udp dst port=8082
7 changes: 7 additions & 0 deletions xdp-scheduler-tester/xdp_pifo.trace
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
global bpf file=./xdp_scheduler_pifo.bpf.o
udp dst port=8002
udp dst port=8000
udp dst port=8001
dequeue udp dst port=8000
dequeue udp dst port=8001
dequeue udp dst port=8002
44 changes: 44 additions & 0 deletions xdp-scheduler-tester/xdp_scheduler_fifo.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Freysteinn Alfredsson <[email protected]> */

#include <vmlinux_local.h>
#include <linux/bpf.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
#include <xdp/parsing_helpers.h>

#include "bpf_local_helpers.h"

struct {
__uint(type, BPF_MAP_TYPE_PIFO);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
__uint(max_entries, 1024);
} pifo_map SEC(".maps");

/* Simple FIFO */
SEC("xdp")
int enqueue_prog(struct xdp_md *xdp)
{
void *data = (void *)(long)xdp->data;
void *data_end = (void *)(long)xdp->data_end;
struct ethhdr *eth = data;

if (eth + 1 > data_end)
return XDP_DROP;

return bpf_redirect_map(&pifo_map, 0, 0);
}

SEC("dequeue")
void *dequeue_prog(struct dequeue_ctx *ctx)
{
__u64 prio = 0;
void *pkt = (void *) bpf_packet_dequeue(ctx, &pifo_map, 0, &prio);
if (!pkt)
return 0;

return pkt;
}

char _license[] SEC("license") = "GPL";
80 changes: 80 additions & 0 deletions xdp-scheduler-tester/xdp_scheduler_pifo.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Freysteinn Alfredsson <[email protected]> */

#include <vmlinux_local.h>
#include <linux/bpf.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
#include <xdp/parsing_helpers.h>

#include "bpf_local_helpers.h"

struct {
__uint(type, BPF_MAP_TYPE_PIFO);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
__uint(max_entries, 1024);
} pifo_map SEC(".maps");


/* Simple PIFO strict priority */
SEC("xdp")
int enqueue_prog(struct xdp_md *xdp)
{
void *data_end = (void *)(long)xdp->data_end;
void *data = (void *)(long)xdp->data;
struct hdr_cursor nh = { .pos = data };
struct ethhdr *eth;
int eth_type;
struct iphdr *iphdr;
struct ipv6hdr *ipv6hdr;
int ip_type;
struct udphdr *udphdr;
int udp_dst_port;
__u16 prio = 0;

/* Parse Ethernet and IP/IPv6 headers */
eth_type = parse_ethhdr(&nh, data_end, &eth);
if (eth_type == bpf_htons(ETH_P_IP)) {
ip_type = parse_iphdr(&nh, data_end, &iphdr);
if (ip_type != IPPROTO_UDP)
goto err;
} else if (eth_type == bpf_htons(ETH_P_IPV6)) {
ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr);
if (ip_type != IPPROTO_UDP)
goto err;
} else {
goto err;
}

/* Parse UDP header */
if (parse_udphdr(&nh, data_end, &udphdr) < 0)
goto err;
udp_dst_port = bpf_htons(udphdr->dest);

/* Calculate scheduling priority */
prio = 0;
if (udp_dst_port == 8001)
prio = 1;
else if (udp_dst_port > 8001)
prio = 2;


bpf_printk("XDP PIFO scheduled with priority %d", prio);
return bpf_redirect_map(&pifo_map, prio, 0);
err:
bpf_printk("XDP PIFO failed");
return XDP_DROP;
}

SEC("dequeue")
void *dequeue_prog(struct dequeue_ctx *ctx)
{
__u64 prio = 0;
void *pkt = (void *) bpf_packet_dequeue(ctx, &pifo_map, 0, &prio);
if (!pkt)
return 0;
return pkt;
}

char _license[] SEC("license") = "GPL";
Loading

0 comments on commit 144e7f1

Please sign in to comment.