174 lines · 4.3 KB · C
RawDownload
// 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 ?: (void *)&bpf_dispatcher_nop_func); 	/* Make sure all the callers executing the previous/old half of the	 * image leave it, so following update call can modify it safely.	 */	synchronize_rcu(); 	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, false);		if (!d->image)			goto out;		/* d->rw_image doesn't need to be in module memory range, so we		 * can use vzalloc.		 */		d->rw_image = vzalloc(PAGE_SIZE);		if (!d->rw_image) {			bpf_prog_pack_free(d->image, PAGE_SIZE);			d->image = NULL;			goto out;		}		bpf_image_ksym_init(d->image, PAGE_SIZE, &d->ksym);		bpf_image_ksym_add(&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);}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t