175ccbef6SBjörn Töpel // SPDX-License-Identifier: GPL-2.0-only
275ccbef6SBjörn Töpel /* Copyright(c) 2019 Intel Corporation. */
375ccbef6SBjörn Töpel
475ccbef6SBjörn Töpel #include <linux/hash.h>
575ccbef6SBjörn Töpel #include <linux/bpf.h>
675ccbef6SBjörn Töpel #include <linux/filter.h>
7c86df29dSPeter Zijlstra #include <linux/static_call.h>
875ccbef6SBjörn Töpel
975ccbef6SBjörn Töpel /* The BPF dispatcher is a multiway branch code generator. The
1075ccbef6SBjörn Töpel * dispatcher is a mechanism to avoid the performance penalty of an
1175ccbef6SBjörn Töpel * indirect call, which is expensive when retpolines are enabled. A
1275ccbef6SBjörn Töpel * dispatch client registers a BPF program into the dispatcher, and if
1375ccbef6SBjörn Töpel * there is available room in the dispatcher a direct call to the BPF
1475ccbef6SBjörn Töpel * program will be generated. All calls to the BPF programs called via
1575ccbef6SBjörn Töpel * the dispatcher will then be a direct call, instead of an
1675ccbef6SBjörn Töpel * indirect. The dispatcher hijacks a trampoline function it via the
1775ccbef6SBjörn Töpel * __fentry__ of the trampoline. The trampoline function has the
1875ccbef6SBjörn Töpel * following signature:
1975ccbef6SBjörn Töpel *
2075ccbef6SBjörn Töpel * unsigned int trampoline(const void *ctx, const struct bpf_insn *insnsi,
2175ccbef6SBjörn Töpel * unsigned int (*bpf_func)(const void *,
2275ccbef6SBjörn Töpel * const struct bpf_insn *));
2375ccbef6SBjörn Töpel */
2475ccbef6SBjörn Töpel
bpf_dispatcher_find_prog(struct bpf_dispatcher * d,struct bpf_prog * prog)2575ccbef6SBjörn Töpel static struct bpf_dispatcher_prog *bpf_dispatcher_find_prog(
2675ccbef6SBjörn Töpel struct bpf_dispatcher *d, struct bpf_prog *prog)
2775ccbef6SBjörn Töpel {
2875ccbef6SBjörn Töpel int i;
2975ccbef6SBjörn Töpel
3075ccbef6SBjörn Töpel for (i = 0; i < BPF_DISPATCHER_MAX; i++) {
3175ccbef6SBjörn Töpel if (prog == d->progs[i].prog)
3275ccbef6SBjörn Töpel return &d->progs[i];
3375ccbef6SBjörn Töpel }
3475ccbef6SBjörn Töpel return NULL;
3575ccbef6SBjörn Töpel }
3675ccbef6SBjörn Töpel
bpf_dispatcher_find_free(struct bpf_dispatcher * d)3775ccbef6SBjörn Töpel static struct bpf_dispatcher_prog *bpf_dispatcher_find_free(
3875ccbef6SBjörn Töpel struct bpf_dispatcher *d)
3975ccbef6SBjörn Töpel {
4075ccbef6SBjörn Töpel return bpf_dispatcher_find_prog(d, NULL);
4175ccbef6SBjörn Töpel }
4275ccbef6SBjörn Töpel
bpf_dispatcher_add_prog(struct bpf_dispatcher * d,struct bpf_prog * prog)4375ccbef6SBjörn Töpel static bool bpf_dispatcher_add_prog(struct bpf_dispatcher *d,
4475ccbef6SBjörn Töpel struct bpf_prog *prog)
4575ccbef6SBjörn Töpel {
4675ccbef6SBjörn Töpel struct bpf_dispatcher_prog *entry;
4775ccbef6SBjörn Töpel
4875ccbef6SBjörn Töpel if (!prog)
4975ccbef6SBjörn Töpel return false;
5075ccbef6SBjörn Töpel
5175ccbef6SBjörn Töpel entry = bpf_dispatcher_find_prog(d, prog);
5275ccbef6SBjörn Töpel if (entry) {
5375ccbef6SBjörn Töpel refcount_inc(&entry->users);
5475ccbef6SBjörn Töpel return false;
5575ccbef6SBjörn Töpel }
5675ccbef6SBjörn Töpel
5775ccbef6SBjörn Töpel entry = bpf_dispatcher_find_free(d);
5875ccbef6SBjörn Töpel if (!entry)
5975ccbef6SBjörn Töpel return false;
6075ccbef6SBjörn Töpel
6175ccbef6SBjörn Töpel bpf_prog_inc(prog);
6275ccbef6SBjörn Töpel entry->prog = prog;
6375ccbef6SBjörn Töpel refcount_set(&entry->users, 1);
6475ccbef6SBjörn Töpel d->num_progs++;
6575ccbef6SBjörn Töpel return true;
6675ccbef6SBjörn Töpel }
6775ccbef6SBjörn Töpel
bpf_dispatcher_remove_prog(struct bpf_dispatcher * d,struct bpf_prog * prog)6875ccbef6SBjörn Töpel static bool bpf_dispatcher_remove_prog(struct bpf_dispatcher *d,
6975ccbef6SBjörn Töpel struct bpf_prog *prog)
7075ccbef6SBjörn Töpel {
7175ccbef6SBjörn Töpel struct bpf_dispatcher_prog *entry;
7275ccbef6SBjörn Töpel
7375ccbef6SBjörn Töpel if (!prog)
7475ccbef6SBjörn Töpel return false;
7575ccbef6SBjörn Töpel
7675ccbef6SBjörn Töpel entry = bpf_dispatcher_find_prog(d, prog);
7775ccbef6SBjörn Töpel if (!entry)
7875ccbef6SBjörn Töpel return false;
7975ccbef6SBjörn Töpel
8075ccbef6SBjörn Töpel if (refcount_dec_and_test(&entry->users)) {
8175ccbef6SBjörn Töpel entry->prog = NULL;
8275ccbef6SBjörn Töpel bpf_prog_put(prog);
8375ccbef6SBjörn Töpel d->num_progs--;
8475ccbef6SBjörn Töpel return true;
8575ccbef6SBjörn Töpel }
8675ccbef6SBjörn Töpel return false;
8775ccbef6SBjörn Töpel }
8875ccbef6SBjörn Töpel
arch_prepare_bpf_dispatcher(void * image,void * buf,s64 * funcs,int num_funcs)8919c02415SSong Liu int __weak arch_prepare_bpf_dispatcher(void *image, void *buf, s64 *funcs, int num_funcs)
9075ccbef6SBjörn Töpel {
9175ccbef6SBjörn Töpel return -ENOTSUPP;
9275ccbef6SBjörn Töpel }
9375ccbef6SBjörn Töpel
bpf_dispatcher_prepare(struct bpf_dispatcher * d,void * image,void * buf)9419c02415SSong Liu static int bpf_dispatcher_prepare(struct bpf_dispatcher *d, void *image, void *buf)
9575ccbef6SBjörn Töpel {
9675ccbef6SBjörn Töpel s64 ips[BPF_DISPATCHER_MAX] = {}, *ipsp = &ips[0];
9775ccbef6SBjörn Töpel int i;
9875ccbef6SBjörn Töpel
9975ccbef6SBjörn Töpel for (i = 0; i < BPF_DISPATCHER_MAX; i++) {
10075ccbef6SBjörn Töpel if (d->progs[i].prog)
10175ccbef6SBjörn Töpel *ipsp++ = (s64)(uintptr_t)d->progs[i].prog->bpf_func;
10275ccbef6SBjörn Töpel }
10319c02415SSong Liu return arch_prepare_bpf_dispatcher(image, buf, &ips[0], d->num_progs);
10475ccbef6SBjörn Töpel }
10575ccbef6SBjörn Töpel
bpf_dispatcher_update(struct bpf_dispatcher * d,int prev_num_progs)10675ccbef6SBjörn Töpel static void bpf_dispatcher_update(struct bpf_dispatcher *d, int prev_num_progs)
10775ccbef6SBjörn Töpel {
108c86df29dSPeter Zijlstra void *new, *tmp;
109c86df29dSPeter Zijlstra u32 noff = 0;
11075ccbef6SBjörn Töpel
111c86df29dSPeter Zijlstra if (prev_num_progs)
1127ac88ebaSJiri Olsa noff = d->image_off ^ (PAGE_SIZE / 2);
11375ccbef6SBjörn Töpel
11475ccbef6SBjörn Töpel new = d->num_progs ? d->image + noff : NULL;
11519c02415SSong Liu tmp = d->num_progs ? d->rw_image + noff : NULL;
11675ccbef6SBjörn Töpel if (new) {
11719c02415SSong Liu /* Prepare the dispatcher in d->rw_image. Then use
11819c02415SSong Liu * bpf_arch_text_copy to update d->image, which is RO+X.
11919c02415SSong Liu */
12019c02415SSong Liu if (bpf_dispatcher_prepare(d, new, tmp))
12119c02415SSong Liu return;
12219c02415SSong Liu if (IS_ERR(bpf_arch_text_copy(new, tmp, PAGE_SIZE / 2)))
12375ccbef6SBjörn Töpel return;
12475ccbef6SBjörn Töpel }
12575ccbef6SBjörn Töpel
126a679120eSNathan Chancellor __BPF_DISPATCHER_UPDATE(d, new ?: (void *)&bpf_dispatcher_nop_func);
12775ccbef6SBjörn Töpel
128*4121d448SJiri Olsa /* Make sure all the callers executing the previous/old half of the
129*4121d448SJiri Olsa * image leave it, so following update call can modify it safely.
130*4121d448SJiri Olsa */
131*4121d448SJiri Olsa synchronize_rcu();
132*4121d448SJiri Olsa
133c86df29dSPeter Zijlstra if (new)
13475ccbef6SBjörn Töpel d->image_off = noff;
13575ccbef6SBjörn Töpel }
13675ccbef6SBjörn Töpel
bpf_dispatcher_change_prog(struct bpf_dispatcher * d,struct bpf_prog * from,struct bpf_prog * to)13775ccbef6SBjörn Töpel void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from,
13875ccbef6SBjörn Töpel struct bpf_prog *to)
13975ccbef6SBjörn Töpel {
14075ccbef6SBjörn Töpel bool changed = false;
14175ccbef6SBjörn Töpel int prev_num_progs;
14275ccbef6SBjörn Töpel
14375ccbef6SBjörn Töpel if (from == to)
14475ccbef6SBjörn Töpel return;
14575ccbef6SBjörn Töpel
14675ccbef6SBjörn Töpel mutex_lock(&d->mutex);
14775ccbef6SBjörn Töpel if (!d->image) {
14819c02415SSong Liu d->image = bpf_prog_pack_alloc(PAGE_SIZE, bpf_jit_fill_hole_with_zero);
14975ccbef6SBjörn Töpel if (!d->image)
15075ccbef6SBjörn Töpel goto out;
15119c02415SSong Liu d->rw_image = bpf_jit_alloc_exec(PAGE_SIZE);
15219c02415SSong Liu if (!d->rw_image) {
15319c02415SSong Liu u32 size = PAGE_SIZE;
15419c02415SSong Liu
15519c02415SSong Liu bpf_arch_text_copy(d->image, &size, sizeof(size));
15619c02415SSong Liu bpf_prog_pack_free((struct bpf_binary_header *)d->image);
15719c02415SSong Liu d->image = NULL;
15819c02415SSong Liu goto out;
15919c02415SSong Liu }
160517b75e4SJiri Olsa bpf_image_ksym_add(d->image, &d->ksym);
16175ccbef6SBjörn Töpel }
16275ccbef6SBjörn Töpel
16375ccbef6SBjörn Töpel prev_num_progs = d->num_progs;
16475ccbef6SBjörn Töpel changed |= bpf_dispatcher_remove_prog(d, from);
16575ccbef6SBjörn Töpel changed |= bpf_dispatcher_add_prog(d, to);
16675ccbef6SBjörn Töpel
16775ccbef6SBjörn Töpel if (!changed)
16875ccbef6SBjörn Töpel goto out;
16975ccbef6SBjörn Töpel
17075ccbef6SBjörn Töpel bpf_dispatcher_update(d, prev_num_progs);
17175ccbef6SBjörn Töpel out:
17275ccbef6SBjörn Töpel mutex_unlock(&d->mutex);
17375ccbef6SBjörn Töpel }
174