Skip to content

Commit 23cf4f3

Browse files
committed
Adds the draft of the XDP scheduler testing tool
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]>
1 parent ce71462 commit 23cf4f3

14 files changed

+1346
-0
lines changed

lib/util/logging.c

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#include <stdio.h>
4+
#include <stdarg.h>
5+
6+
#include <bpf/libbpf.h>
7+
#include <xdp/libxdp.h>
8+
9+
#include "logging.h"
10+
#include "util.h"
11+
12+
static enum logging_print_level log_level = LOG_INFO;
13+
14+
static int print_func(enum logging_print_level level, const char *format,
15+
va_list args)
16+
{
17+
if (level > log_level)
18+
return 0;
19+
20+
return vfprintf(stderr, format, args);
21+
}
22+
23+
static int libbpf_print_func(enum libbpf_print_level level, const char *format,
24+
va_list args)
25+
{
26+
return print_func(level + 1, format, args);
27+
}
28+
29+
static int libbpf_silent_func(__unused enum libbpf_print_level level,
30+
__unused const char *format,
31+
__unused va_list args)
32+
{
33+
return 0;
34+
}
35+
36+
static int libxdp_print_func(enum libxdp_print_level level, const char *format,
37+
va_list args)
38+
{
39+
return print_func(level + 1, format, args);
40+
}
41+
42+
static int libxdp_silent_func(__unused enum libxdp_print_level level,
43+
__unused const char *format,
44+
__unused va_list args)
45+
{
46+
return 0;
47+
}
48+
49+
#define __printf(a, b) __attribute__((format(printf, a, b)))
50+
51+
__printf(2, 3) void logging_print(enum logging_print_level level,
52+
const char *format, ...)
53+
{
54+
va_list args;
55+
56+
va_start(args, format);
57+
print_func(level, format, args);
58+
va_end(args);
59+
}
60+
61+
void init_lib_logging(void)
62+
{
63+
libbpf_set_print(libbpf_print_func);
64+
libxdp_set_print(libxdp_print_func);
65+
}
66+
67+
void silence_libbpf_logging(void)
68+
{
69+
if (log_level < LOG_VERBOSE)
70+
libbpf_set_print(libbpf_silent_func);
71+
}
72+
73+
void silence_libxdp_logging(void)
74+
{
75+
if (log_level < LOG_VERBOSE)
76+
libxdp_set_print(libxdp_silent_func);
77+
}
78+
79+
enum logging_print_level set_log_level(enum logging_print_level level)
80+
{
81+
enum logging_print_level old_level = log_level;
82+
83+
log_level = level;
84+
return old_level;
85+
}
86+
87+
enum logging_print_level increase_log_level(void)
88+
{
89+
if (log_level < LOG_VERBOSE)
90+
log_level++;
91+
return log_level;
92+
}

lib/util/logging.h

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#ifndef __LOGGING_H
4+
#define __LOGGING_H
5+
6+
/* This matches the libbpf logging levels, but with an additional VERBOSE level;
7+
* we demote all libbpf messages by one level so debug messages only show up on
8+
* VERBOSE.
9+
*/
10+
enum logging_print_level {
11+
LOG_WARN,
12+
LOG_INFO,
13+
LOG_DEBUG,
14+
LOG_VERBOSE,
15+
};
16+
17+
extern void logging_print(enum logging_print_level level, const char *format,
18+
...) __attribute__((format(printf, 2, 3)));
19+
20+
#define __pr(level, fmt, ...) \
21+
do { \
22+
logging_print(level, fmt, ##__VA_ARGS__); \
23+
} while (0)
24+
25+
#define pr_warn(fmt, ...) __pr(LOG_WARN, fmt, ##__VA_ARGS__)
26+
#define pr_info(fmt, ...) __pr(LOG_INFO, fmt, ##__VA_ARGS__)
27+
#define pr_debug(fmt, ...) __pr(LOG_DEBUG, fmt, ##__VA_ARGS__)
28+
29+
void init_lib_logging(void);
30+
void silence_libbpf_logging(void);
31+
void silence_libxdp_logging(void);
32+
enum logging_print_level set_log_level(enum logging_print_level level);
33+
enum logging_print_level increase_log_level();
34+
35+
#endif

xdp-scheduler-tester/Makefile

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
2+
3+
USER_TARGETS := xdp_scheduler_tester
4+
BPF_TARGETS := $(patsubst %.c,%,$(wildcard *.bpf.c))
5+
6+
LIB_DIR = ../lib
7+
8+
include $(LIB_DIR)/common.mk
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2+
3+
#define EEXIST 17 /* File exists */
4+
5+
#define BPF_MAP_TYPE_PIFO 31
6+
7+
/*
8+
* bpf_packet_dequeue
9+
*
10+
* Dequeue the packet at the head of the PIFO in *map* and return a pointer
11+
* to the packet (or NULL if the PIFO is empty).
12+
*
13+
* Returns
14+
* On success, a pointer to the packet, or NULL if the PIFO is empty. The
15+
* packet pointer must be freed using *bpf_packet_drop()* or returning
16+
* the packet pointer. The *rank* pointer will be set to the rank of
17+
* the dequeued packet on success, or a negative error code on error.
18+
*/
19+
static long (*bpf_packet_dequeue)(void *ctx, void *map, __u64 flags, __u64 *rank) = (void *) 194;
20+
static long (*bpf_packet_drop)(void *ctx, void *pkt) = (void *) 195;
21+
22+
struct flow_address {
23+
struct in6_addr ip;
24+
__u16 port;
25+
__u16 reserved;
26+
};
27+
28+
struct network_tuple {
29+
struct flow_address saddr;
30+
struct flow_address daddr;
31+
__u16 proto;
32+
__u8 ipv;
33+
__u8 reserved;
34+
};
35+
36+
struct flow_state {
37+
__u32 pkts;
38+
__u32 finish_bytes;
39+
};
40+
41+
42+
static __always_inline void *
43+
bpf_map_lookup_or_try_init(void *map, const void *key, const void *init)
44+
{
45+
void *val;
46+
long err;
47+
48+
val = bpf_map_lookup_elem(map, key);
49+
if (val)
50+
return val;
51+
52+
err = bpf_map_update_elem(map, key, init, BPF_NOEXIST);
53+
if (err && err != -EEXIST)
54+
return NULL;
55+
56+
return bpf_map_lookup_elem(map, key);
57+
}
58+
59+
static __always_inline int bpf_max(__u64 left, __u64 right)
60+
{
61+
return right > left ? right : left;
62+
}
63+
64+
65+
/*
66+
* Maps an IPv4 address into an IPv6 address according to RFC 4291 sec 2.5.5.2
67+
*/
68+
static void map_ipv4_to_ipv6(struct in6_addr *ipv6, __be32 ipv4)
69+
{
70+
__builtin_memset(&ipv6->in6_u.u6_addr8[0], 0x00, 10);
71+
__builtin_memset(&ipv6->in6_u.u6_addr8[10], 0xff, 2);
72+
ipv6->in6_u.u6_addr32[3] = ipv4;
73+
}

xdp-scheduler-tester/xdp_debug.trace

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Used for debugging the xdp_scheduler_tester syntax
2+
global bpf file=./xdp_scheduler_fifo.bpf.o
3+
4+
udp eth proto=1 dst port=8080 # In-line comment
5+
udp eth proto=2 dst port=8081 payload length=32
6+
udp eth proto=3 dst port=8082 repeat=2
7+
dequeue udp eth proto=1 dst port=8080
8+
dequeue udp eth proto=2 dst port=8081 payload length=32
9+
dequeue udp eth proto=3 dst port=8082 repeat=2

xdp-scheduler-tester/xdp_fifo.trace

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
global bpf file=./xdp_scheduler_fifo.bpf.o
2+
udp dst port=8080
3+
udp dst port=8081
4+
udp dst port=8082
5+
dequeue udp dst port=8080
6+
dequeue udp dst port=8081
7+
dequeue udp dst port=8082

xdp-scheduler-tester/xdp_pifo.trace

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
global bpf file=./xdp_scheduler_pifo.bpf.o
2+
udp dst port=8002
3+
udp dst port=8000
4+
udp dst port=8001
5+
dequeue udp dst port=8000
6+
dequeue udp dst port=8001
7+
dequeue udp dst port=8002
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2022 Freysteinn Alfredsson <[email protected]> */
3+
4+
#include <vmlinux_local.h>
5+
#include <linux/bpf.h>
6+
#include <bpf/bpf_endian.h>
7+
#include <bpf/bpf_helpers.h>
8+
#include <xdp/parsing_helpers.h>
9+
10+
#include "bpf_local_helpers.h"
11+
12+
struct {
13+
__uint(type, BPF_MAP_TYPE_PIFO);
14+
__uint(key_size, sizeof(__u32));
15+
__uint(value_size, sizeof(__u32));
16+
__uint(max_entries, 1024);
17+
} pifo_map SEC(".maps");
18+
19+
/* Simple FIFO */
20+
SEC("xdp")
21+
int enqueue_prog(struct xdp_md *xdp)
22+
{
23+
void *data = (void *)(long)xdp->data;
24+
void *data_end = (void *)(long)xdp->data_end;
25+
struct ethhdr *eth = data;
26+
27+
if (eth + 1 > data_end)
28+
return XDP_DROP;
29+
30+
return bpf_redirect_map(&pifo_map, 0, 0);
31+
}
32+
33+
SEC("dequeue")
34+
void *dequeue_prog(struct dequeue_ctx *ctx)
35+
{
36+
__u64 prio = 0;
37+
void *pkt = (void *) bpf_packet_dequeue(ctx, &pifo_map, 0, &prio);
38+
if (!pkt)
39+
return 0;
40+
41+
return pkt;
42+
}
43+
44+
char _license[] SEC("license") = "GPL";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2022 Freysteinn Alfredsson <[email protected]> */
3+
4+
#include <vmlinux_local.h>
5+
#include <linux/bpf.h>
6+
#include <bpf/bpf_endian.h>
7+
#include <bpf/bpf_helpers.h>
8+
#include <xdp/parsing_helpers.h>
9+
10+
#include "bpf_local_helpers.h"
11+
12+
struct {
13+
__uint(type, BPF_MAP_TYPE_PIFO);
14+
__uint(key_size, sizeof(__u32));
15+
__uint(value_size, sizeof(__u32));
16+
__uint(max_entries, 1024);
17+
} pifo_map SEC(".maps");
18+
19+
20+
/* Simple PIFO strict priority */
21+
SEC("xdp")
22+
int enqueue_prog(struct xdp_md *xdp)
23+
{
24+
void *data_end = (void *)(long)xdp->data_end;
25+
void *data = (void *)(long)xdp->data;
26+
struct hdr_cursor nh = { .pos = data };
27+
struct ethhdr *eth;
28+
int eth_type;
29+
struct iphdr *iphdr;
30+
struct ipv6hdr *ipv6hdr;
31+
int ip_type;
32+
struct udphdr *udphdr;
33+
int udp_dst_port;
34+
__u16 prio = 0;
35+
36+
/* Parse Ethernet and IP/IPv6 headers */
37+
eth_type = parse_ethhdr(&nh, data_end, &eth);
38+
if (eth_type == bpf_htons(ETH_P_IP)) {
39+
ip_type = parse_iphdr(&nh, data_end, &iphdr);
40+
if (ip_type != IPPROTO_UDP)
41+
goto err;
42+
} else if (eth_type == bpf_htons(ETH_P_IPV6)) {
43+
ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr);
44+
if (ip_type != IPPROTO_UDP)
45+
goto err;
46+
} else {
47+
goto err;
48+
}
49+
50+
/* Parse UDP header */
51+
if (parse_udphdr(&nh, data_end, &udphdr) < 0)
52+
goto err;
53+
udp_dst_port = bpf_htons(udphdr->dest);
54+
55+
/* Calculate scheduling priority */
56+
prio = 0;
57+
if (udp_dst_port == 8001)
58+
prio = 1;
59+
else if (udp_dst_port > 8001)
60+
prio = 2;
61+
62+
63+
bpf_printk("XDP PIFO scheduled with priority %d", prio);
64+
return bpf_redirect_map(&pifo_map, prio, 0);
65+
err:
66+
bpf_printk("XDP PIFO failed");
67+
return XDP_DROP;
68+
}
69+
70+
SEC("dequeue")
71+
void *dequeue_prog(struct dequeue_ctx *ctx)
72+
{
73+
__u64 prio = 0;
74+
void *pkt = (void *) bpf_packet_dequeue(ctx, &pifo_map, 0, &prio);
75+
if (!pkt)
76+
return 0;
77+
return pkt;
78+
}
79+
80+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)