xref: /linux/tools/testing/selftests/bpf/progs/bpf_qdisc_fifo.c (revision fe259a1bb26ec78842c975d992331705b0c2c2e8)
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 
118