linux/kernel/bpf/dispatcher.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* Copyright(c) 2019 Intel Corporation. */
   3
   4#include <linux/hash.h>
   5#include <linux/bpf.h>
   6#include <linux/filter.h>
   7
   8/* The BPF dispatcher is a multiway branch code generator. The
   9 * dispatcher is a mechanism to avoid the performance penalty of an
  10 * indirect call, which is expensive when retpolines are enabled. A
  11 * dispatch client registers a BPF program into the dispatcher, and if
  12 * there is available room in the dispatcher a direct call to the BPF
  13 * program will be generated. All calls to the BPF programs called via
  14 * the dispatcher will then be a direct call, instead of an
  15 * indirect. The dispatcher hijacks a trampoline function it via the
  16 * __fentry__ of the trampoline. The trampoline function has the
  17 * following signature:
  18 *
  19 * unsigned int trampoline(const void *ctx, const struct bpf_insn *insnsi,
  20 *                         unsigned int (*bpf_func)(const void *,
  21 *                                                  const struct bpf_insn *));
  22 */
  23
  24static struct bpf_dispatcher_prog *bpf_dispatcher_find_prog(
  25        struct bpf_dispatcher *d, struct bpf_prog *prog)
  26{
  27        int i;
  28
  29        for (i = 0; i < BPF_DISPATCHER_MAX; i++) {
  30                if (prog == d->progs[i].prog)
  31                        return &d->progs[i];
  32        }
  33        return NULL;
  34}
  35
  36static struct bpf_dispatcher_prog *bpf_dispatcher_find_free(
  37        struct bpf_dispatcher *d)
  38{
  39        return bpf_dispatcher_find_prog(d, NULL);
  40}
  41
  42static bool bpf_dispatcher_add_prog(struct bpf_dispatcher *d,
  43                                    struct bpf_prog *prog)
  44{
  45        struct bpf_dispatcher_prog *entry;
  46
  47        if (!prog)
  48                return false;
  49
  50        entry = bpf_dispatcher_find_prog(d, prog);
  51        if (entry) {
  52                refcount_inc(&entry->users);
  53                return false;
  54        }
  55
  56        entry = bpf_dispatcher_find_free(d);
  57        if (!entry)
  58                return false;
  59
  60        bpf_prog_inc(prog);
  61        entry->prog = prog;
  62        refcount_set(&entry->users, 1);
  63        d->num_progs++;
  64        return true;
  65}
  66
  67static bool bpf_dispatcher_remove_prog(struct bpf_dispatcher *d,
  68                                       struct bpf_prog *prog)
  69{
  70        struct bpf_dispatcher_prog *entry;
  71
  72        if (!prog)
  73                return false;
  74
  75        entry = bpf_dispatcher_find_prog(d, prog);
  76        if (!entry)
  77                return false;
  78
  79        if (refcount_dec_and_test(&entry->users)) {
  80                entry->prog = NULL;
  81                bpf_prog_put(prog);
  82                d->num_progs--;
  83                return true;
  84        }
  85        return false;
  86}
  87
  88int __weak arch_prepare_bpf_dispatcher(void *image, s64 *funcs, int num_funcs)
  89{
  90        return -ENOTSUPP;
  91}
  92
  93static int bpf_dispatcher_prepare(struct bpf_dispatcher *d, void *image)
  94{
  95        s64 ips[BPF_DISPATCHER_MAX] = {}, *ipsp = &ips[0];
  96        int i;
  97
  98        for (i = 0; i < BPF_DISPATCHER_MAX; i++) {
  99                if (d->progs[i].prog)
 100                        *ipsp++ = (s64)(uintptr_t)d->progs[i].prog->bpf_func;
 101        }
 102        return arch_prepare_bpf_dispatcher(image, &ips[0], d->num_progs);
 103}
 104
 105static void bpf_dispatcher_update(struct bpf_dispatcher *d, int prev_num_progs)
 106{
 107        void *old, *new;
 108        u32 noff;
 109        int err;
 110
 111        if (!prev_num_progs) {
 112                old = NULL;
 113                noff = 0;
 114        } else {
 115                old = d->image + d->image_off;
 116                noff = d->image_off ^ (PAGE_SIZE / 2);
 117        }
 118
 119        new = d->num_progs ? d->image + noff : NULL;
 120        if (new) {
 121                if (bpf_dispatcher_prepare(d, new))
 122                        return;
 123        }
 124
 125        err = bpf_arch_text_poke(d->func, BPF_MOD_JUMP, old, new);
 126        if (err || !new)
 127                return;
 128
 129        d->image_off = noff;
 130}
 131
 132void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from,
 133                                struct bpf_prog *to)
 134{
 135        bool changed = false;
 136        int prev_num_progs;
 137
 138        if (from == to)
 139                return;
 140
 141        mutex_lock(&d->mutex);
 142        if (!d->image) {
 143                d->image = bpf_jit_alloc_exec_page();
 144                if (!d->image)
 145                        goto out;
 146                bpf_image_ksym_add(d->image, &d->ksym);
 147        }
 148
 149        prev_num_progs = d->num_progs;
 150        changed |= bpf_dispatcher_remove_prog(d, from);
 151        changed |= bpf_dispatcher_add_prog(d, to);
 152
 153        if (!changed)
 154                goto out;
 155
 156        bpf_dispatcher_update(d, prev_num_progs);
 157out:
 158        mutex_unlock(&d->mutex);
 159}
 160