mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 23:21:31 +00:00
c86df29d11
The dispatcher function is currently abusing the ftrace __fentry__ call location for its own purposes -- this obviously gives trouble when the dispatcher and ftrace are both in use. A previous solution tried using __attribute__((patchable_function_entry())) which works, except it is GCC-8+ only, breaking the build on the earlier still supported compilers. Instead use static_call() -- which has its own annotations and does not conflict with ftrace -- to rewrite the dispatch function. By using: return static_call()(ctx, insni, bpf_func) you get a perfect forwarding tail call as function body (iow a single jmp instruction). By having the default static_call() target be bpf_dispatcher_nop_func() it retains the default behaviour (an indirect call to the argument function). Only once a dispatcher program is attached is the target rewritten to directly call the JIT'ed image. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Tested-by: Björn Töpel <bjorn@kernel.org> Tested-by: Jiri Olsa <jolsa@kernel.org> Acked-by: Björn Töpel <bjorn@kernel.org> Acked-by: Jiri Olsa <jolsa@kernel.org> Link: https://lkml.kernel.org/r/Y1/oBlK0yFk5c/Im@hirez.programming.kicks-ass.net Link: https://lore.kernel.org/bpf/20221103120647.796772565@infradead.org
169 lines
4.1 KiB
C
169 lines
4.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright(c) 2019 Intel Corporation. */
|
|
|
|
#include <linux/hash.h>
|
|
#include <linux/bpf.h>
|
|
#include <linux/filter.h>
|
|
#include <linux/static_call.h>
|
|
|
|
/* The BPF dispatcher is a multiway branch code generator. The
|
|
* dispatcher is a mechanism to avoid the performance penalty of an
|
|
* indirect call, which is expensive when retpolines are enabled. A
|
|
* dispatch client registers a BPF program into the dispatcher, and if
|
|
* there is available room in the dispatcher a direct call to the BPF
|
|
* program will be generated. All calls to the BPF programs called via
|
|
* the dispatcher will then be a direct call, instead of an
|
|
* indirect. The dispatcher hijacks a trampoline function it via the
|
|
* __fentry__ of the trampoline. The trampoline function has the
|
|
* following signature:
|
|
*
|
|
* unsigned int trampoline(const void *ctx, const struct bpf_insn *insnsi,
|
|
* unsigned int (*bpf_func)(const void *,
|
|
* const struct bpf_insn *));
|
|
*/
|
|
|
|
static struct bpf_dispatcher_prog *bpf_dispatcher_find_prog(
|
|
struct bpf_dispatcher *d, struct bpf_prog *prog)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < BPF_DISPATCHER_MAX; i++) {
|
|
if (prog == d->progs[i].prog)
|
|
return &d->progs[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct bpf_dispatcher_prog *bpf_dispatcher_find_free(
|
|
struct bpf_dispatcher *d)
|
|
{
|
|
return bpf_dispatcher_find_prog(d, NULL);
|
|
}
|
|
|
|
static bool bpf_dispatcher_add_prog(struct bpf_dispatcher *d,
|
|
struct bpf_prog *prog)
|
|
{
|
|
struct bpf_dispatcher_prog *entry;
|
|
|
|
if (!prog)
|
|
return false;
|
|
|
|
entry = bpf_dispatcher_find_prog(d, prog);
|
|
if (entry) {
|
|
refcount_inc(&entry->users);
|
|
return false;
|
|
}
|
|
|
|
entry = bpf_dispatcher_find_free(d);
|
|
if (!entry)
|
|
return false;
|
|
|
|
bpf_prog_inc(prog);
|
|
entry->prog = prog;
|
|
refcount_set(&entry->users, 1);
|
|
d->num_progs++;
|
|
return true;
|
|
}
|
|
|
|
static bool bpf_dispatcher_remove_prog(struct bpf_dispatcher *d,
|
|
struct bpf_prog *prog)
|
|
{
|
|
struct bpf_dispatcher_prog *entry;
|
|
|
|
if (!prog)
|
|
return false;
|
|
|
|
entry = bpf_dispatcher_find_prog(d, prog);
|
|
if (!entry)
|
|
return false;
|
|
|
|
if (refcount_dec_and_test(&entry->users)) {
|
|
entry->prog = NULL;
|
|
bpf_prog_put(prog);
|
|
d->num_progs--;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int __weak arch_prepare_bpf_dispatcher(void *image, void *buf, s64 *funcs, int num_funcs)
|
|
{
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
static int bpf_dispatcher_prepare(struct bpf_dispatcher *d, void *image, void *buf)
|
|
{
|
|
s64 ips[BPF_DISPATCHER_MAX] = {}, *ipsp = &ips[0];
|
|
int i;
|
|
|
|
for (i = 0; i < BPF_DISPATCHER_MAX; i++) {
|
|
if (d->progs[i].prog)
|
|
*ipsp++ = (s64)(uintptr_t)d->progs[i].prog->bpf_func;
|
|
}
|
|
return arch_prepare_bpf_dispatcher(image, buf, &ips[0], d->num_progs);
|
|
}
|
|
|
|
static void bpf_dispatcher_update(struct bpf_dispatcher *d, int prev_num_progs)
|
|
{
|
|
void *new, *tmp;
|
|
u32 noff = 0;
|
|
|
|
if (prev_num_progs)
|
|
noff = d->image_off ^ (PAGE_SIZE / 2);
|
|
|
|
new = d->num_progs ? d->image + noff : NULL;
|
|
tmp = d->num_progs ? d->rw_image + noff : NULL;
|
|
if (new) {
|
|
/* Prepare the dispatcher in d->rw_image. Then use
|
|
* bpf_arch_text_copy to update d->image, which is RO+X.
|
|
*/
|
|
if (bpf_dispatcher_prepare(d, new, tmp))
|
|
return;
|
|
if (IS_ERR(bpf_arch_text_copy(new, tmp, PAGE_SIZE / 2)))
|
|
return;
|
|
}
|
|
|
|
__BPF_DISPATCHER_UPDATE(d, new ?: &bpf_dispatcher_nop_func);
|
|
|
|
if (new)
|
|
d->image_off = noff;
|
|
}
|
|
|
|
void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from,
|
|
struct bpf_prog *to)
|
|
{
|
|
bool changed = false;
|
|
int prev_num_progs;
|
|
|
|
if (from == to)
|
|
return;
|
|
|
|
mutex_lock(&d->mutex);
|
|
if (!d->image) {
|
|
d->image = bpf_prog_pack_alloc(PAGE_SIZE, bpf_jit_fill_hole_with_zero);
|
|
if (!d->image)
|
|
goto out;
|
|
d->rw_image = bpf_jit_alloc_exec(PAGE_SIZE);
|
|
if (!d->rw_image) {
|
|
u32 size = PAGE_SIZE;
|
|
|
|
bpf_arch_text_copy(d->image, &size, sizeof(size));
|
|
bpf_prog_pack_free((struct bpf_binary_header *)d->image);
|
|
d->image = NULL;
|
|
goto out;
|
|
}
|
|
bpf_image_ksym_add(d->image, &d->ksym);
|
|
}
|
|
|
|
prev_num_progs = d->num_progs;
|
|
changed |= bpf_dispatcher_remove_prog(d, from);
|
|
changed |= bpf_dispatcher_add_prog(d, to);
|
|
|
|
if (!changed)
|
|
goto out;
|
|
|
|
bpf_dispatcher_update(d, prev_num_progs);
|
|
out:
|
|
mutex_unlock(&d->mutex);
|
|
}
|