Skip to content

Commit cb7e598

Browse files
Amery HungMartin KaFai Lau
Amery Hung
authored and
Martin KaFai Lau
committed
selftests/bpf: Add a basic fifo qdisc test
This selftest includes a bare minimum fifo qdisc, which simply enqueues sk_buffs into the back of a bpf list and dequeues from the front of the list. Signed-off-by: Amery Hung <[email protected]> Signed-off-by: Martin KaFai Lau <[email protected]> Acked-by: Toke Høiland-Jørgensen <[email protected]> Link: https://patch.msgid.link/[email protected]
1 parent 8c52471 commit cb7e598

File tree

4 files changed

+230
-0
lines changed

4 files changed

+230
-0
lines changed

tools/testing/selftests/bpf/config

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ CONFIG_NET_IPGRE=y
7171
CONFIG_NET_IPGRE_DEMUX=y
7272
CONFIG_NET_IPIP=y
7373
CONFIG_NET_MPLS_GSO=y
74+
CONFIG_NET_SCH_BPF=y
7475
CONFIG_NET_SCH_FQ=y
7576
CONFIG_NET_SCH_INGRESS=y
7677
CONFIG_NET_SCHED=y
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <linux/pkt_sched.h>
4+
#include <linux/rtnetlink.h>
5+
#include <test_progs.h>
6+
7+
#include "network_helpers.h"
8+
#include "bpf_qdisc_fifo.skel.h"
9+
10+
#define LO_IFINDEX 1
11+
12+
static const unsigned int total_bytes = 10 * 1024 * 1024;
13+
14+
static void do_test(char *qdisc)
15+
{
16+
DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = LO_IFINDEX,
17+
.attach_point = BPF_TC_QDISC,
18+
.parent = TC_H_ROOT,
19+
.handle = 0x8000000,
20+
.qdisc = qdisc);
21+
int srv_fd = -1, cli_fd = -1;
22+
int err;
23+
24+
err = bpf_tc_hook_create(&hook);
25+
if (!ASSERT_OK(err, "attach qdisc"))
26+
return;
27+
28+
srv_fd = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
29+
if (!ASSERT_OK_FD(srv_fd, "start server"))
30+
goto done;
31+
32+
cli_fd = connect_to_fd(srv_fd, 0);
33+
if (!ASSERT_OK_FD(cli_fd, "connect to client"))
34+
goto done;
35+
36+
err = send_recv_data(srv_fd, cli_fd, total_bytes);
37+
ASSERT_OK(err, "send_recv_data");
38+
39+
done:
40+
if (srv_fd != -1)
41+
close(srv_fd);
42+
if (cli_fd != -1)
43+
close(cli_fd);
44+
45+
bpf_tc_hook_destroy(&hook);
46+
}
47+
48+
static void test_fifo(void)
49+
{
50+
struct bpf_qdisc_fifo *fifo_skel;
51+
struct bpf_link *link;
52+
53+
fifo_skel = bpf_qdisc_fifo__open_and_load();
54+
if (!ASSERT_OK_PTR(fifo_skel, "bpf_qdisc_fifo__open_and_load"))
55+
return;
56+
57+
link = bpf_map__attach_struct_ops(fifo_skel->maps.fifo);
58+
if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops")) {
59+
bpf_qdisc_fifo__destroy(fifo_skel);
60+
return;
61+
}
62+
63+
do_test("bpf_fifo");
64+
65+
bpf_link__destroy(link);
66+
bpf_qdisc_fifo__destroy(fifo_skel);
67+
}
68+
69+
void test_bpf_qdisc(void)
70+
{
71+
struct netns_obj *netns;
72+
73+
netns = netns_new("bpf_qdisc_ns", true);
74+
if (!ASSERT_OK_PTR(netns, "netns_new"))
75+
return;
76+
77+
if (test__start_subtest("fifo"))
78+
test_fifo();
79+
80+
netns_free(netns);
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#ifndef _BPF_QDISC_COMMON_H
4+
#define _BPF_QDISC_COMMON_H
5+
6+
#define NET_XMIT_SUCCESS 0x00
7+
#define NET_XMIT_DROP 0x01 /* skb dropped */
8+
#define NET_XMIT_CN 0x02 /* congestion notification */
9+
10+
#define TC_PRIO_CONTROL 7
11+
#define TC_PRIO_MAX 15
12+
13+
#define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8)))
14+
15+
u32 bpf_skb_get_hash(struct sk_buff *p) __ksym;
16+
void bpf_kfree_skb(struct sk_buff *p) __ksym;
17+
void bpf_qdisc_skb_drop(struct sk_buff *p, struct bpf_sk_buff_ptr *to_free) __ksym;
18+
void bpf_qdisc_watchdog_schedule(struct Qdisc *sch, u64 expire, u64 delta_ns) __ksym;
19+
void bpf_qdisc_bstats_update(struct Qdisc *sch, const struct sk_buff *skb) __ksym;
20+
21+
static struct qdisc_skb_cb *qdisc_skb_cb(const struct sk_buff *skb)
22+
{
23+
return (struct qdisc_skb_cb *)skb->cb;
24+
}
25+
26+
static inline unsigned int qdisc_pkt_len(const struct sk_buff *skb)
27+
{
28+
return qdisc_skb_cb(skb)->pkt_len;
29+
}
30+
31+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <vmlinux.h>
4+
#include "bpf_experimental.h"
5+
#include "bpf_qdisc_common.h"
6+
7+
char _license[] SEC("license") = "GPL";
8+
9+
struct skb_node {
10+
struct sk_buff __kptr * skb;
11+
struct bpf_list_node node;
12+
};
13+
14+
private(A) struct bpf_spin_lock q_fifo_lock;
15+
private(A) struct bpf_list_head q_fifo __contains(skb_node, node);
16+
17+
SEC("struct_ops/bpf_fifo_enqueue")
18+
int BPF_PROG(bpf_fifo_enqueue, struct sk_buff *skb, struct Qdisc *sch,
19+
struct bpf_sk_buff_ptr *to_free)
20+
{
21+
struct skb_node *skbn;
22+
u32 pkt_len;
23+
24+
if (sch->q.qlen == sch->limit)
25+
goto drop;
26+
27+
skbn = bpf_obj_new(typeof(*skbn));
28+
if (!skbn)
29+
goto drop;
30+
31+
pkt_len = qdisc_pkt_len(skb);
32+
33+
sch->q.qlen++;
34+
skb = bpf_kptr_xchg(&skbn->skb, skb);
35+
if (skb)
36+
bpf_qdisc_skb_drop(skb, to_free);
37+
38+
bpf_spin_lock(&q_fifo_lock);
39+
bpf_list_push_back(&q_fifo, &skbn->node);
40+
bpf_spin_unlock(&q_fifo_lock);
41+
42+
sch->qstats.backlog += pkt_len;
43+
return NET_XMIT_SUCCESS;
44+
drop:
45+
bpf_qdisc_skb_drop(skb, to_free);
46+
return NET_XMIT_DROP;
47+
}
48+
49+
SEC("struct_ops/bpf_fifo_dequeue")
50+
struct sk_buff *BPF_PROG(bpf_fifo_dequeue, struct Qdisc *sch)
51+
{
52+
struct bpf_list_node *node;
53+
struct sk_buff *skb = NULL;
54+
struct skb_node *skbn;
55+
56+
bpf_spin_lock(&q_fifo_lock);
57+
node = bpf_list_pop_front(&q_fifo);
58+
bpf_spin_unlock(&q_fifo_lock);
59+
if (!node)
60+
return NULL;
61+
62+
skbn = container_of(node, struct skb_node, node);
63+
skb = bpf_kptr_xchg(&skbn->skb, skb);
64+
bpf_obj_drop(skbn);
65+
if (!skb)
66+
return NULL;
67+
68+
sch->qstats.backlog -= qdisc_pkt_len(skb);
69+
bpf_qdisc_bstats_update(sch, skb);
70+
sch->q.qlen--;
71+
72+
return skb;
73+
}
74+
75+
SEC("struct_ops/bpf_fifo_init")
76+
int BPF_PROG(bpf_fifo_init, struct Qdisc *sch, struct nlattr *opt,
77+
struct netlink_ext_ack *extack)
78+
{
79+
sch->limit = 1000;
80+
return 0;
81+
}
82+
83+
SEC("struct_ops/bpf_fifo_reset")
84+
void BPF_PROG(bpf_fifo_reset, struct Qdisc *sch)
85+
{
86+
struct bpf_list_node *node;
87+
struct skb_node *skbn;
88+
int i;
89+
90+
bpf_for(i, 0, sch->q.qlen) {
91+
struct sk_buff *skb = NULL;
92+
93+
bpf_spin_lock(&q_fifo_lock);
94+
node = bpf_list_pop_front(&q_fifo);
95+
bpf_spin_unlock(&q_fifo_lock);
96+
97+
if (!node)
98+
break;
99+
100+
skbn = container_of(node, struct skb_node, node);
101+
skb = bpf_kptr_xchg(&skbn->skb, skb);
102+
if (skb)
103+
bpf_kfree_skb(skb);
104+
bpf_obj_drop(skbn);
105+
}
106+
sch->q.qlen = 0;
107+
}
108+
109+
SEC(".struct_ops")
110+
struct Qdisc_ops fifo = {
111+
.enqueue = (void *)bpf_fifo_enqueue,
112+
.dequeue = (void *)bpf_fifo_dequeue,
113+
.init = (void *)bpf_fifo_init,
114+
.reset = (void *)bpf_fifo_reset,
115+
.id = "bpf_fifo",
116+
};
117+

0 commit comments

Comments
 (0)