Skip to content

Commit 8c1bbdf

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 gotten the five-tuple flow definitions to work. The WFQ uses my five-tuples flow implementation. However, I have not gotten it past the BPF verifier in this commit. To trigger the WFQ BPF verifier issue, please run: bash-5.1# ./xdp_scheduler_tester -f xdp_debug.trace I have added the length keyword to this commit, which can resize the packets. It is fully functional. However, the dequeue hook needs a size parameter, which I set to 4096 octets. The question is, what would be a suitable size? The xdp_scheduler_tester program also includes a -v flag that prints out the enqueued packet. Signed-off-by: Frey Alfredsson <[email protected]>
1 parent ce71462 commit 8c1bbdf

11 files changed

+1297
-0
lines changed

xdp-scheduler-tester/Makefile

Lines changed: 8 additions & 0 deletions
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
Lines changed: 73 additions & 0 deletions
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+
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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
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+
dequeue udp eth proto=1 dst port=8080
7+
dequeue udp eth proto=2 dst port=8081 payload length=32

xdp-scheduler-tester/xdp_fifo.trace

Lines changed: 7 additions & 0 deletions
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

Lines changed: 7 additions & 0 deletions
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
Lines changed: 44 additions & 0 deletions
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";
Lines changed: 80 additions & 0 deletions
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)