Skip to content

Commit 9d5ba29

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 xdq-tester used to test schedulers using the XDP and DEQUEUE hooks. It does this using trace files written in Lua that the xdq-tester program uses to check the XDP schedulers for correctness. I changed the name to xdq-tester because I found the old name too long. I reused the XDQ name because I sometimes find it hard to distinguish between the XDP hook or the XDP and DEQUEUE hooks in text or code. I am sure we can find a better name later. The FIFO, SPRIO, and WFQ are fully functional in this commit. The SPRIO and WFQ have an API to set the weights from the Lua scripts. One issue is that I would like to find a sweet spot for the maximum priority. My initial thought was that if the queue is 4096, I should multiply the queue length with the MTU of 1500 and double the size. That should ensure that the swapping mechanism has enough room to swap PIFOs. However, the current implementation does not allow me to specify 1500*4096*2 regardless of how much memory I have. I would even think we would need a higher number when using high weights. Signed-off-by: Frey Alfredsson <[email protected]>
1 parent daefd11 commit 9d5ba29

12 files changed

+1931
-0
lines changed

xdq-tester/Makefile

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
2+
3+
USER_TARGETS := xdq-tester
4+
BPF_TARGETS := $(patsubst %.c,%,$(wildcard *.bpf.c))
5+
6+
USER_LIBS = -llua -ldl -lm
7+
8+
LIB_DIR = ../lib
9+
10+
include $(LIB_DIR)/common.mk

xdq-tester/bpf_local_helpers.h

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2+
3+
#ifndef BPF_LOCAL_HELPERS_H_
4+
#define BPF_LOCAL_HELPERS_H_
5+
6+
#include "bpf_shared_data.h"
7+
8+
#define EEXIST 17 /* File exists */
9+
10+
#define BPF_MAP_TYPE_PIFO_GENERIC 31
11+
#define BPF_MAP_TYPE_PIFO_XDP 32
12+
13+
/*
14+
* bpf_packet_dequeue
15+
*
16+
* Dequeue the packet at the head of the PIFO in *map* and return a pointer
17+
* to the packet (or NULL if the PIFO is empty).
18+
*
19+
* Returns
20+
* On success, a pointer to the packet, or NULL if the PIFO is empty. The
21+
* packet pointer must be freed using *bpf_packet_drop()* or returning
22+
* the packet pointer. The *rank* pointer will be set to the rank of
23+
* the dequeued packet on success, or a negative error code on error.
24+
*/
25+
static long (*bpf_packet_dequeue)(void *ctx, void *map, __u64 flags, __u64 *rank) = (void *) 194;
26+
static long (*bpf_packet_drop)(void *ctx, void *pkt) = (void *) 195;
27+
28+
struct parsing_context {
29+
void *data; // Start of eth hdr
30+
void *data_end; // End of safe acessible area
31+
struct hdr_cursor nh; // Position to parse next
32+
__u32 pkt_len; // Full packet length (headers+data)
33+
};
34+
35+
#pragma GCC diagnostic push
36+
#pragma GCC diagnostic ignored "-Wunused-function"
37+
static __always_inline void *
38+
bpf_map_lookup_or_try_init(void *map, const void *key, const void *init)
39+
{
40+
void *val;
41+
long err;
42+
43+
val = bpf_map_lookup_elem(map, key);
44+
if (val)
45+
return val;
46+
47+
err = bpf_map_update_elem(map, key, init, BPF_NOEXIST);
48+
if (err && err != -EEXIST)
49+
return NULL;
50+
51+
return bpf_map_lookup_elem(map, key);
52+
}
53+
54+
static __always_inline int bpf_max(__u64 left, __u64 right)
55+
{
56+
return right > left ? right : left;
57+
}
58+
59+
60+
/*
61+
* Maps an IPv4 address into an IPv6 address according to RFC 4291 sec 2.5.5.2
62+
*/
63+
static void map_ipv4_to_ipv6(struct in6_addr *ipv6, __be32 ipv4)
64+
{
65+
__builtin_memset(&ipv6->in6_u.u6_addr8[0], 0x00, 10);
66+
__builtin_memset(&ipv6->in6_u.u6_addr8[10], 0xff, 2);
67+
ipv6->in6_u.u6_addr32[3] = ipv4;
68+
}
69+
70+
/*
71+
* Five-tuple helpers
72+
*/
73+
74+
/* This function currently only supports UDP packets */
75+
static __always_inline int parse_packet(struct parsing_context *pctx, struct packet_info *p_info)
76+
{
77+
/* Parse Ethernet and IP/IPv6 headers */
78+
p_info->eth_type = parse_ethhdr(&pctx->nh, pctx->data_end, &p_info->eth);
79+
if (p_info->eth_type == bpf_htons(ETH_P_IP)) {
80+
p_info->ip_type = parse_iphdr(&pctx->nh, pctx->data_end, &p_info->iph);
81+
if (p_info->ip_type < 0)
82+
goto err;
83+
p_info->nt.ipv = 4;
84+
map_ipv4_to_ipv6(&p_info->nt.saddr.ip, p_info->iph->saddr);
85+
map_ipv4_to_ipv6(&p_info->nt.daddr.ip, p_info->iph->daddr);
86+
} else if (p_info->eth_type == bpf_htons(ETH_P_IPV6)) {
87+
p_info->ip_type = parse_ip6hdr(&pctx->nh, pctx->data_end, &p_info->ip6h);
88+
if (p_info->ip_type < 0)
89+
goto err;
90+
p_info->nt.ipv = 6;
91+
p_info->nt.saddr.ip = p_info->ip6h->saddr;
92+
p_info->nt.daddr.ip = p_info->ip6h->daddr;
93+
} else {
94+
goto err;
95+
}
96+
97+
/* Parse UDP header */
98+
if (p_info->ip_type != IPPROTO_UDP)
99+
goto err;
100+
if (parse_udphdr(&pctx->nh, pctx->data_end, &p_info->udph) < 0)
101+
goto err;
102+
103+
p_info->nt.proto = IPPROTO_UDP;
104+
p_info->nt.saddr.port = p_info->udph->source;
105+
p_info->nt.daddr.port = p_info->udph->dest;
106+
107+
return 0;
108+
err:
109+
bpf_printk("Failed to parse UDP packet");
110+
return -1;
111+
}
112+
113+
#pragma GCC diagnostic pop
114+
115+
#endif // BPF_LOCAL_HELPERS_H_

xdq-tester/bpf_shared_data.h

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#ifndef BPF_SHARED_DATA_H_
2+
#define BPF_SHARED_DATA_H_
3+
4+
struct flow_address {
5+
struct in6_addr ip;
6+
__u16 port;
7+
__u16 reserved;
8+
};
9+
10+
struct network_tuple {
11+
struct flow_address saddr;
12+
struct flow_address daddr;
13+
__u16 proto;
14+
__u8 ipv;
15+
__u8 reserved;
16+
};
17+
18+
struct flow_state {
19+
__u32 pkts;
20+
__u32 finish_bytes;
21+
__u32 weight;
22+
__u32 persistent;
23+
};
24+
25+
struct packet_info {
26+
struct ethhdr *eth;
27+
union {
28+
struct iphdr *iph;
29+
struct ipv6hdr *ip6h;
30+
};
31+
union {
32+
struct udphdr *udph;
33+
};
34+
struct network_tuple nt;
35+
int eth_type;
36+
int ip_type;
37+
};
38+
39+
#endif // BPF_SHARED_DATA_H_

xdq-tester/fifo.lua

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
-- SPDX-License-Identifier: GPL-2.0
2+
-- Copyright (c) 2022 Freysteinn Alfredsson <[email protected]>
3+
4+
-- FIFO scheduler
5+
config.bpf.file = "./sched_fifo.bpf.o"
6+
7+
-- Setup flows
8+
packet_flow1 = Udp:new()
9+
packet_flow1.udp.port = 8080
10+
11+
packet_flow2 = Udp:new()
12+
packet_flow2.udp.port = 8081
13+
14+
packet_flow3 = Udp:new()
15+
packet_flow3.udp.port = 8082
16+
17+
18+
-- Test scheduler
19+
enqueue(packet_flow1)
20+
enqueue(packet_flow2)
21+
enqueue(packet_flow3)
22+
23+
dequeue_cmp(packet_flow1)
24+
dequeue_cmp(packet_flow2)
25+
dequeue_cmp(packet_flow3)

0 commit comments

Comments
 (0)