1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2023 Isovalent */
5 #include <linux/bpf_mprog.h>
6 #include <linux/netdevice.h>
10 int tcx_prog_attach(const union bpf_attr
*attr
, struct bpf_prog
*prog
)
12 bool created
, ingress
= attr
->attach_type
== BPF_TCX_INGRESS
;
13 struct net
*net
= current
->nsproxy
->net_ns
;
14 struct bpf_mprog_entry
*entry
, *entry_new
;
15 struct bpf_prog
*replace_prog
= NULL
;
16 struct net_device
*dev
;
20 dev
= __dev_get_by_index(net
, attr
->target_ifindex
);
25 if (attr
->attach_flags
& BPF_F_REPLACE
) {
26 replace_prog
= bpf_prog_get_type(attr
->replace_bpf_fd
,
28 if (IS_ERR(replace_prog
)) {
29 ret
= PTR_ERR(replace_prog
);
34 entry
= tcx_entry_fetch_or_create(dev
, ingress
, &created
);
39 ret
= bpf_mprog_attach(entry
, &entry_new
, prog
, NULL
, replace_prog
,
40 attr
->attach_flags
, attr
->relative_fd
,
41 attr
->expected_revision
);
43 if (entry
!= entry_new
) {
44 tcx_entry_update(dev
, entry_new
, ingress
);
46 tcx_skeys_inc(ingress
);
48 bpf_mprog_commit(entry
);
50 tcx_entry_free(entry
);
54 bpf_prog_put(replace_prog
);
59 int tcx_prog_detach(const union bpf_attr
*attr
, struct bpf_prog
*prog
)
61 bool ingress
= attr
->attach_type
== BPF_TCX_INGRESS
;
62 struct net
*net
= current
->nsproxy
->net_ns
;
63 struct bpf_mprog_entry
*entry
, *entry_new
;
64 struct net_device
*dev
;
68 dev
= __dev_get_by_index(net
, attr
->target_ifindex
);
73 entry
= tcx_entry_fetch(dev
, ingress
);
78 ret
= bpf_mprog_detach(entry
, &entry_new
, prog
, NULL
, attr
->attach_flags
,
79 attr
->relative_fd
, attr
->expected_revision
);
81 if (!tcx_entry_is_active(entry_new
))
83 tcx_entry_update(dev
, entry_new
, ingress
);
85 tcx_skeys_dec(ingress
);
86 bpf_mprog_commit(entry
);
88 tcx_entry_free(entry
);
95 void tcx_uninstall(struct net_device
*dev
, bool ingress
)
97 struct bpf_mprog_entry
*entry
, *entry_new
= NULL
;
98 struct bpf_tuple tuple
= {};
99 struct bpf_mprog_fp
*fp
;
100 struct bpf_mprog_cp
*cp
;
103 entry
= tcx_entry_fetch(dev
, ingress
);
106 active
= tcx_entry(entry
)->miniq_active
;
108 bpf_mprog_clear_all(entry
, &entry_new
);
109 tcx_entry_update(dev
, entry_new
, ingress
);
111 bpf_mprog_foreach_tuple(entry
, fp
, cp
, tuple
) {
113 tcx_link(tuple
.link
)->dev
= NULL
;
115 bpf_prog_put(tuple
.prog
);
116 tcx_skeys_dec(ingress
);
119 tcx_entry_free(entry
);
122 int tcx_prog_query(const union bpf_attr
*attr
, union bpf_attr __user
*uattr
)
124 bool ingress
= attr
->query
.attach_type
== BPF_TCX_INGRESS
;
125 struct net
*net
= current
->nsproxy
->net_ns
;
126 struct net_device
*dev
;
130 dev
= __dev_get_by_index(net
, attr
->query
.target_ifindex
);
135 ret
= bpf_mprog_query(attr
, uattr
, tcx_entry_fetch(dev
, ingress
));
141 static int tcx_link_prog_attach(struct bpf_link
*link
, u32 flags
, u32 id_or_fd
,
144 struct tcx_link
*tcx
= tcx_link(link
);
145 bool created
, ingress
= tcx
->location
== BPF_TCX_INGRESS
;
146 struct bpf_mprog_entry
*entry
, *entry_new
;
147 struct net_device
*dev
= tcx
->dev
;
151 entry
= tcx_entry_fetch_or_create(dev
, ingress
, &created
);
154 ret
= bpf_mprog_attach(entry
, &entry_new
, link
->prog
, link
, NULL
, flags
,
157 if (entry
!= entry_new
) {
158 tcx_entry_update(dev
, entry_new
, ingress
);
160 tcx_skeys_inc(ingress
);
162 bpf_mprog_commit(entry
);
163 } else if (created
) {
164 tcx_entry_free(entry
);
169 static void tcx_link_release(struct bpf_link
*link
)
171 struct tcx_link
*tcx
= tcx_link(link
);
172 bool ingress
= tcx
->location
== BPF_TCX_INGRESS
;
173 struct bpf_mprog_entry
*entry
, *entry_new
;
174 struct net_device
*dev
;
181 entry
= tcx_entry_fetch(dev
, ingress
);
186 ret
= bpf_mprog_detach(entry
, &entry_new
, link
->prog
, link
, 0, 0, 0);
188 if (!tcx_entry_is_active(entry_new
))
190 tcx_entry_update(dev
, entry_new
, ingress
);
192 tcx_skeys_dec(ingress
);
193 bpf_mprog_commit(entry
);
195 tcx_entry_free(entry
);
203 static int tcx_link_update(struct bpf_link
*link
, struct bpf_prog
*nprog
,
204 struct bpf_prog
*oprog
)
206 struct tcx_link
*tcx
= tcx_link(link
);
207 bool ingress
= tcx
->location
== BPF_TCX_INGRESS
;
208 struct bpf_mprog_entry
*entry
, *entry_new
;
209 struct net_device
*dev
;
218 if (oprog
&& link
->prog
!= oprog
) {
223 if (oprog
== nprog
) {
227 entry
= tcx_entry_fetch(dev
, ingress
);
232 ret
= bpf_mprog_attach(entry
, &entry_new
, nprog
, link
, oprog
,
233 BPF_F_REPLACE
| BPF_F_ID
,
234 link
->prog
->aux
->id
, 0);
236 WARN_ON_ONCE(entry
!= entry_new
);
237 oprog
= xchg(&link
->prog
, nprog
);
239 bpf_mprog_commit(entry
);
246 static void tcx_link_dealloc(struct bpf_link
*link
)
248 kfree(tcx_link(link
));
251 static void tcx_link_fdinfo(const struct bpf_link
*link
, struct seq_file
*seq
)
253 const struct tcx_link
*tcx
= tcx_link(link
);
258 ifindex
= tcx
->dev
->ifindex
;
261 seq_printf(seq
, "ifindex:\t%u\n", ifindex
);
262 seq_printf(seq
, "attach_type:\t%u (%s)\n",
264 tcx
->location
== BPF_TCX_INGRESS
? "ingress" : "egress");
267 static int tcx_link_fill_info(const struct bpf_link
*link
,
268 struct bpf_link_info
*info
)
270 const struct tcx_link
*tcx
= tcx_link(link
);
275 ifindex
= tcx
->dev
->ifindex
;
278 info
->tcx
.ifindex
= ifindex
;
279 info
->tcx
.attach_type
= tcx
->location
;
283 static int tcx_link_detach(struct bpf_link
*link
)
285 tcx_link_release(link
);
289 static const struct bpf_link_ops tcx_link_lops
= {
290 .release
= tcx_link_release
,
291 .detach
= tcx_link_detach
,
292 .dealloc
= tcx_link_dealloc
,
293 .update_prog
= tcx_link_update
,
294 .show_fdinfo
= tcx_link_fdinfo
,
295 .fill_link_info
= tcx_link_fill_info
,
298 static int tcx_link_init(struct tcx_link
*tcx
,
299 struct bpf_link_primer
*link_primer
,
300 const union bpf_attr
*attr
,
301 struct net_device
*dev
,
302 struct bpf_prog
*prog
)
304 bpf_link_init(&tcx
->link
, BPF_LINK_TYPE_TCX
, &tcx_link_lops
, prog
);
305 tcx
->location
= attr
->link_create
.attach_type
;
307 return bpf_link_prime(&tcx
->link
, link_primer
);
310 int tcx_link_attach(const union bpf_attr
*attr
, struct bpf_prog
*prog
)
312 struct net
*net
= current
->nsproxy
->net_ns
;
313 struct bpf_link_primer link_primer
;
314 struct net_device
*dev
;
315 struct tcx_link
*tcx
;
319 dev
= __dev_get_by_index(net
, attr
->link_create
.target_ifindex
);
324 tcx
= kzalloc(sizeof(*tcx
), GFP_USER
);
329 ret
= tcx_link_init(tcx
, &link_primer
, attr
, dev
, prog
);
334 ret
= tcx_link_prog_attach(&tcx
->link
, attr
->link_create
.flags
,
335 attr
->link_create
.tcx
.relative_fd
,
336 attr
->link_create
.tcx
.expected_revision
);
339 bpf_link_cleanup(&link_primer
);
342 ret
= bpf_link_settle(&link_primer
);