1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | /* Copyright (c) 2023 Isovalent */ |
3 | #ifndef __NET_TCX_H |
4 | #define __NET_TCX_H |
5 | |
6 | #include <linux/bpf.h> |
7 | #include <linux/bpf_mprog.h> |
8 | |
9 | #include <net/sch_generic.h> |
10 | |
11 | struct mini_Qdisc; |
12 | |
13 | struct tcx_entry { |
14 | struct mini_Qdisc __rcu *miniq; |
15 | struct bpf_mprog_bundle bundle; |
16 | bool miniq_active; |
17 | struct rcu_head rcu; |
18 | }; |
19 | |
20 | struct tcx_link { |
21 | struct bpf_link link; |
22 | struct net_device *dev; |
23 | u32 location; |
24 | }; |
25 | |
26 | static inline void tcx_set_ingress(struct sk_buff *skb, bool ingress) |
27 | { |
28 | #ifdef CONFIG_NET_XGRESS |
29 | skb->tc_at_ingress = ingress; |
30 | #endif |
31 | } |
32 | |
33 | #ifdef CONFIG_NET_XGRESS |
34 | static inline struct tcx_entry *tcx_entry(struct bpf_mprog_entry *entry) |
35 | { |
36 | struct bpf_mprog_bundle *bundle = entry->parent; |
37 | |
38 | return container_of(bundle, struct tcx_entry, bundle); |
39 | } |
40 | |
41 | static inline struct tcx_link *tcx_link(const struct bpf_link *link) |
42 | { |
43 | return container_of(link, struct tcx_link, link); |
44 | } |
45 | |
46 | void tcx_inc(void); |
47 | void tcx_dec(void); |
48 | |
49 | static inline void tcx_entry_sync(void) |
50 | { |
51 | /* bpf_mprog_entry got a/b swapped, therefore ensure that |
52 | * there are no inflight users on the old one anymore. |
53 | */ |
54 | synchronize_rcu(); |
55 | } |
56 | |
57 | static inline void |
58 | tcx_entry_update(struct net_device *dev, struct bpf_mprog_entry *entry, |
59 | bool ingress) |
60 | { |
61 | ASSERT_RTNL(); |
62 | if (ingress) |
63 | rcu_assign_pointer(dev->tcx_ingress, entry); |
64 | else |
65 | rcu_assign_pointer(dev->tcx_egress, entry); |
66 | } |
67 | |
68 | static inline struct bpf_mprog_entry * |
69 | tcx_entry_fetch(struct net_device *dev, bool ingress) |
70 | { |
71 | ASSERT_RTNL(); |
72 | if (ingress) |
73 | return rcu_dereference_rtnl(dev->tcx_ingress); |
74 | else |
75 | return rcu_dereference_rtnl(dev->tcx_egress); |
76 | } |
77 | |
78 | static inline struct bpf_mprog_entry *tcx_entry_create(void) |
79 | { |
80 | struct tcx_entry *tcx = kzalloc(size: sizeof(*tcx), GFP_KERNEL); |
81 | |
82 | if (tcx) { |
83 | bpf_mprog_bundle_init(bundle: &tcx->bundle); |
84 | return &tcx->bundle.a; |
85 | } |
86 | return NULL; |
87 | } |
88 | |
89 | static inline void tcx_entry_free(struct bpf_mprog_entry *entry) |
90 | { |
91 | kfree_rcu(tcx_entry(entry), rcu); |
92 | } |
93 | |
94 | static inline struct bpf_mprog_entry * |
95 | tcx_entry_fetch_or_create(struct net_device *dev, bool ingress, bool *created) |
96 | { |
97 | struct bpf_mprog_entry *entry = tcx_entry_fetch(dev, ingress); |
98 | |
99 | *created = false; |
100 | if (!entry) { |
101 | entry = tcx_entry_create(); |
102 | if (!entry) |
103 | return NULL; |
104 | *created = true; |
105 | } |
106 | return entry; |
107 | } |
108 | |
109 | static inline void tcx_skeys_inc(bool ingress) |
110 | { |
111 | tcx_inc(); |
112 | if (ingress) |
113 | net_inc_ingress_queue(); |
114 | else |
115 | net_inc_egress_queue(); |
116 | } |
117 | |
118 | static inline void tcx_skeys_dec(bool ingress) |
119 | { |
120 | if (ingress) |
121 | net_dec_ingress_queue(); |
122 | else |
123 | net_dec_egress_queue(); |
124 | tcx_dec(); |
125 | } |
126 | |
127 | static inline void tcx_miniq_set_active(struct bpf_mprog_entry *entry, |
128 | const bool active) |
129 | { |
130 | ASSERT_RTNL(); |
131 | tcx_entry(entry)->miniq_active = active; |
132 | } |
133 | |
134 | static inline bool tcx_entry_is_active(struct bpf_mprog_entry *entry) |
135 | { |
136 | ASSERT_RTNL(); |
137 | return bpf_mprog_total(entry) || tcx_entry(entry)->miniq_active; |
138 | } |
139 | |
140 | static inline enum tcx_action_base tcx_action_code(struct sk_buff *skb, |
141 | int code) |
142 | { |
143 | switch (code) { |
144 | case TCX_PASS: |
145 | skb->tc_index = qdisc_skb_cb(skb)->tc_classid; |
146 | fallthrough; |
147 | case TCX_DROP: |
148 | case TCX_REDIRECT: |
149 | return code; |
150 | case TCX_NEXT: |
151 | default: |
152 | return TCX_NEXT; |
153 | } |
154 | } |
155 | #endif /* CONFIG_NET_XGRESS */ |
156 | |
157 | #if defined(CONFIG_NET_XGRESS) && defined(CONFIG_BPF_SYSCALL) |
158 | int tcx_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog); |
159 | int tcx_link_attach(const union bpf_attr *attr, struct bpf_prog *prog); |
160 | int tcx_prog_detach(const union bpf_attr *attr, struct bpf_prog *prog); |
161 | void tcx_uninstall(struct net_device *dev, bool ingress); |
162 | |
163 | int tcx_prog_query(const union bpf_attr *attr, |
164 | union bpf_attr __user *uattr); |
165 | |
166 | static inline void dev_tcx_uninstall(struct net_device *dev) |
167 | { |
168 | ASSERT_RTNL(); |
169 | tcx_uninstall(dev, ingress: true); |
170 | tcx_uninstall(dev, ingress: false); |
171 | } |
172 | #else |
173 | static inline int tcx_prog_attach(const union bpf_attr *attr, |
174 | struct bpf_prog *prog) |
175 | { |
176 | return -EINVAL; |
177 | } |
178 | |
179 | static inline int tcx_link_attach(const union bpf_attr *attr, |
180 | struct bpf_prog *prog) |
181 | { |
182 | return -EINVAL; |
183 | } |
184 | |
185 | static inline int tcx_prog_detach(const union bpf_attr *attr, |
186 | struct bpf_prog *prog) |
187 | { |
188 | return -EINVAL; |
189 | } |
190 | |
191 | static inline int tcx_prog_query(const union bpf_attr *attr, |
192 | union bpf_attr __user *uattr) |
193 | { |
194 | return -EINVAL; |
195 | } |
196 | |
197 | static inline void dev_tcx_uninstall(struct net_device *dev) |
198 | { |
199 | } |
200 | #endif /* CONFIG_NET_XGRESS && CONFIG_BPF_SYSCALL */ |
201 | #endif /* __NET_TCX_H */ |
202 | |