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 bpf_mprog_entry *entry;
127 struct net_device *dev;
131 dev = __dev_get_by_index(net, attr->query.target_ifindex);
136 entry = tcx_entry_fetch(dev, ingress);
141 ret = bpf_mprog_query(attr, uattr, entry);
147 static int tcx_link_prog_attach(struct bpf_link *link, u32 flags, u32 id_or_fd,
150 struct tcx_link *tcx = tcx_link(link);
151 bool created, ingress = tcx->location == BPF_TCX_INGRESS;
152 struct bpf_mprog_entry *entry, *entry_new;
153 struct net_device *dev = tcx->dev;
157 entry = tcx_entry_fetch_or_create(dev, ingress, &created);
160 ret = bpf_mprog_attach(entry, &entry_new, link->prog, link, NULL, flags,
163 if (entry != entry_new) {
164 tcx_entry_update(dev, entry_new, ingress);
166 tcx_skeys_inc(ingress);
168 bpf_mprog_commit(entry);
169 } else if (created) {
170 tcx_entry_free(entry);
175 static void tcx_link_release(struct bpf_link *link)
177 struct tcx_link *tcx = tcx_link(link);
178 bool ingress = tcx->location == BPF_TCX_INGRESS;
179 struct bpf_mprog_entry *entry, *entry_new;
180 struct net_device *dev;
187 entry = tcx_entry_fetch(dev, ingress);
192 ret = bpf_mprog_detach(entry, &entry_new, link->prog, link, 0, 0, 0);
194 if (!tcx_entry_is_active(entry_new))
196 tcx_entry_update(dev, entry_new, ingress);
198 tcx_skeys_dec(ingress);
199 bpf_mprog_commit(entry);
201 tcx_entry_free(entry);
209 static int tcx_link_update(struct bpf_link *link, struct bpf_prog *nprog,
210 struct bpf_prog *oprog)
212 struct tcx_link *tcx = tcx_link(link);
213 bool ingress = tcx->location == BPF_TCX_INGRESS;
214 struct bpf_mprog_entry *entry, *entry_new;
215 struct net_device *dev;
224 if (oprog && link->prog != oprog) {
229 if (oprog == nprog) {
233 entry = tcx_entry_fetch(dev, ingress);
238 ret = bpf_mprog_attach(entry, &entry_new, nprog, link, oprog,
239 BPF_F_REPLACE | BPF_F_ID,
240 link->prog->aux->id, 0);
242 WARN_ON_ONCE(entry != entry_new);
243 oprog = xchg(&link->prog, nprog);
245 bpf_mprog_commit(entry);
252 static void tcx_link_dealloc(struct bpf_link *link)
254 kfree(tcx_link(link));
257 static void tcx_link_fdinfo(const struct bpf_link *link, struct seq_file *seq)
259 const struct tcx_link *tcx = tcx_link_const(link);
264 ifindex = tcx->dev->ifindex;
267 seq_printf(seq, "ifindex:\t%u\n", ifindex);
268 seq_printf(seq, "attach_type:\t%u (%s)\n",
270 tcx->location == BPF_TCX_INGRESS ? "ingress" : "egress");
273 static int tcx_link_fill_info(const struct bpf_link *link,
274 struct bpf_link_info *info)
276 const struct tcx_link *tcx = tcx_link_const(link);
281 ifindex = tcx->dev->ifindex;
284 info->tcx.ifindex = ifindex;
285 info->tcx.attach_type = tcx->location;
289 static int tcx_link_detach(struct bpf_link *link)
291 tcx_link_release(link);
295 static const struct bpf_link_ops tcx_link_lops = {
296 .release = tcx_link_release,
297 .detach = tcx_link_detach,
298 .dealloc = tcx_link_dealloc,
299 .update_prog = tcx_link_update,
300 .show_fdinfo = tcx_link_fdinfo,
301 .fill_link_info = tcx_link_fill_info,
304 static int tcx_link_init(struct tcx_link *tcx,
305 struct bpf_link_primer *link_primer,
306 const union bpf_attr *attr,
307 struct net_device *dev,
308 struct bpf_prog *prog)
310 bpf_link_init(&tcx->link, BPF_LINK_TYPE_TCX, &tcx_link_lops, prog);
311 tcx->location = attr->link_create.attach_type;
313 return bpf_link_prime(&tcx->link, link_primer);
316 int tcx_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
318 struct net *net = current->nsproxy->net_ns;
319 struct bpf_link_primer link_primer;
320 struct net_device *dev;
321 struct tcx_link *tcx;
325 dev = __dev_get_by_index(net, attr->link_create.target_ifindex);
330 tcx = kzalloc(sizeof(*tcx), GFP_USER);
335 ret = tcx_link_init(tcx, &link_primer, attr, dev, prog);
340 ret = tcx_link_prog_attach(&tcx->link, attr->link_create.flags,
341 attr->link_create.tcx.relative_fd,
342 attr->link_create.tcx.expected_revision);
345 bpf_link_cleanup(&link_primer);
348 ret = bpf_link_settle(&link_primer);