bpf: Refactor trampoline update code
As we need to introduce a third type of attachment for trampolines, the flattened signature of arch_prepare_bpf_trampoline gets even more complicated. Refactor the prog and count argument to arch_prepare_bpf_trampoline to use bpf_tramp_progs to simplify the addition and accounting for new attachment types. Signed-off-by: KP Singh <kpsingh@google.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Andrii Nakryiko <andriin@fb.com> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Link: https://lore.kernel.org/bpf/20200304191853.1529-2-kpsingh@chromium.org
This commit is contained in:
parent
cc6fa77102
commit
88fd9e5352
@ -1362,12 +1362,12 @@ static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_args,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,
|
static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,
|
||||||
struct bpf_prog **progs, int prog_cnt, int stack_size)
|
struct bpf_tramp_progs *tp, int stack_size)
|
||||||
{
|
{
|
||||||
u8 *prog = *pprog;
|
u8 *prog = *pprog;
|
||||||
int cnt = 0, i;
|
int cnt = 0, i;
|
||||||
|
|
||||||
for (i = 0; i < prog_cnt; i++) {
|
for (i = 0; i < tp->nr_progs; i++) {
|
||||||
if (emit_call(&prog, __bpf_prog_enter, prog))
|
if (emit_call(&prog, __bpf_prog_enter, prog))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
/* remember prog start time returned by __bpf_prog_enter */
|
/* remember prog start time returned by __bpf_prog_enter */
|
||||||
@ -1376,17 +1376,17 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,
|
|||||||
/* arg1: lea rdi, [rbp - stack_size] */
|
/* arg1: lea rdi, [rbp - stack_size] */
|
||||||
EMIT4(0x48, 0x8D, 0x7D, -stack_size);
|
EMIT4(0x48, 0x8D, 0x7D, -stack_size);
|
||||||
/* arg2: progs[i]->insnsi for interpreter */
|
/* arg2: progs[i]->insnsi for interpreter */
|
||||||
if (!progs[i]->jited)
|
if (!tp->progs[i]->jited)
|
||||||
emit_mov_imm64(&prog, BPF_REG_2,
|
emit_mov_imm64(&prog, BPF_REG_2,
|
||||||
(long) progs[i]->insnsi >> 32,
|
(long) tp->progs[i]->insnsi >> 32,
|
||||||
(u32) (long) progs[i]->insnsi);
|
(u32) (long) tp->progs[i]->insnsi);
|
||||||
/* call JITed bpf program or interpreter */
|
/* call JITed bpf program or interpreter */
|
||||||
if (emit_call(&prog, progs[i]->bpf_func, prog))
|
if (emit_call(&prog, tp->progs[i]->bpf_func, prog))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* arg1: mov rdi, progs[i] */
|
/* arg1: mov rdi, progs[i] */
|
||||||
emit_mov_imm64(&prog, BPF_REG_1, (long) progs[i] >> 32,
|
emit_mov_imm64(&prog, BPF_REG_1, (long) tp->progs[i] >> 32,
|
||||||
(u32) (long) progs[i]);
|
(u32) (long) tp->progs[i]);
|
||||||
/* arg2: mov rsi, rbx <- start time in nsec */
|
/* arg2: mov rsi, rbx <- start time in nsec */
|
||||||
emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6);
|
emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6);
|
||||||
if (emit_call(&prog, __bpf_prog_exit, prog))
|
if (emit_call(&prog, __bpf_prog_exit, prog))
|
||||||
@ -1458,12 +1458,13 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,
|
|||||||
*/
|
*/
|
||||||
int arch_prepare_bpf_trampoline(void *image, void *image_end,
|
int arch_prepare_bpf_trampoline(void *image, void *image_end,
|
||||||
const struct btf_func_model *m, u32 flags,
|
const struct btf_func_model *m, u32 flags,
|
||||||
struct bpf_prog **fentry_progs, int fentry_cnt,
|
struct bpf_tramp_progs *tprogs,
|
||||||
struct bpf_prog **fexit_progs, int fexit_cnt,
|
|
||||||
void *orig_call)
|
void *orig_call)
|
||||||
{
|
{
|
||||||
int cnt = 0, nr_args = m->nr_args;
|
int cnt = 0, nr_args = m->nr_args;
|
||||||
int stack_size = nr_args * 8;
|
int stack_size = nr_args * 8;
|
||||||
|
struct bpf_tramp_progs *fentry = &tprogs[BPF_TRAMP_FENTRY];
|
||||||
|
struct bpf_tramp_progs *fexit = &tprogs[BPF_TRAMP_FEXIT];
|
||||||
u8 *prog;
|
u8 *prog;
|
||||||
|
|
||||||
/* x86-64 supports up to 6 arguments. 7+ can be added in the future */
|
/* x86-64 supports up to 6 arguments. 7+ can be added in the future */
|
||||||
@ -1492,12 +1493,12 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end,
|
|||||||
|
|
||||||
save_regs(m, &prog, nr_args, stack_size);
|
save_regs(m, &prog, nr_args, stack_size);
|
||||||
|
|
||||||
if (fentry_cnt)
|
if (fentry->nr_progs)
|
||||||
if (invoke_bpf(m, &prog, fentry_progs, fentry_cnt, stack_size))
|
if (invoke_bpf(m, &prog, fentry, stack_size))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
||||||
if (fentry_cnt)
|
if (fentry->nr_progs)
|
||||||
restore_regs(m, &prog, nr_args, stack_size);
|
restore_regs(m, &prog, nr_args, stack_size);
|
||||||
|
|
||||||
/* call original function */
|
/* call original function */
|
||||||
@ -1507,8 +1508,8 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end,
|
|||||||
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
|
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fexit_cnt)
|
if (fexit->nr_progs)
|
||||||
if (invoke_bpf(m, &prog, fexit_progs, fexit_cnt, stack_size))
|
if (invoke_bpf(m, &prog, fexit, stack_size))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (flags & BPF_TRAMP_F_RESTORE_REGS)
|
if (flags & BPF_TRAMP_F_RESTORE_REGS)
|
||||||
|
@ -433,6 +433,16 @@ struct btf_func_model {
|
|||||||
*/
|
*/
|
||||||
#define BPF_TRAMP_F_SKIP_FRAME BIT(2)
|
#define BPF_TRAMP_F_SKIP_FRAME BIT(2)
|
||||||
|
|
||||||
|
/* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
|
||||||
|
* bytes on x86. Pick a number to fit into BPF_IMAGE_SIZE / 2
|
||||||
|
*/
|
||||||
|
#define BPF_MAX_TRAMP_PROGS 40
|
||||||
|
|
||||||
|
struct bpf_tramp_progs {
|
||||||
|
struct bpf_prog *progs[BPF_MAX_TRAMP_PROGS];
|
||||||
|
int nr_progs;
|
||||||
|
};
|
||||||
|
|
||||||
/* Different use cases for BPF trampoline:
|
/* Different use cases for BPF trampoline:
|
||||||
* 1. replace nop at the function entry (kprobe equivalent)
|
* 1. replace nop at the function entry (kprobe equivalent)
|
||||||
* flags = BPF_TRAMP_F_RESTORE_REGS
|
* flags = BPF_TRAMP_F_RESTORE_REGS
|
||||||
@ -455,8 +465,7 @@ struct btf_func_model {
|
|||||||
*/
|
*/
|
||||||
int arch_prepare_bpf_trampoline(void *image, void *image_end,
|
int arch_prepare_bpf_trampoline(void *image, void *image_end,
|
||||||
const struct btf_func_model *m, u32 flags,
|
const struct btf_func_model *m, u32 flags,
|
||||||
struct bpf_prog **fentry_progs, int fentry_cnt,
|
struct bpf_tramp_progs *tprogs,
|
||||||
struct bpf_prog **fexit_progs, int fexit_cnt,
|
|
||||||
void *orig_call);
|
void *orig_call);
|
||||||
/* these two functions are called from generated trampoline */
|
/* these two functions are called from generated trampoline */
|
||||||
u64 notrace __bpf_prog_enter(void);
|
u64 notrace __bpf_prog_enter(void);
|
||||||
|
@ -320,6 +320,7 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
|||||||
struct bpf_struct_ops_value *uvalue, *kvalue;
|
struct bpf_struct_ops_value *uvalue, *kvalue;
|
||||||
const struct btf_member *member;
|
const struct btf_member *member;
|
||||||
const struct btf_type *t = st_ops->type;
|
const struct btf_type *t = st_ops->type;
|
||||||
|
struct bpf_tramp_progs *tprogs = NULL;
|
||||||
void *udata, *kdata;
|
void *udata, *kdata;
|
||||||
int prog_fd, err = 0;
|
int prog_fd, err = 0;
|
||||||
void *image;
|
void *image;
|
||||||
@ -343,6 +344,10 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
|||||||
if (uvalue->state || refcount_read(&uvalue->refcnt))
|
if (uvalue->state || refcount_read(&uvalue->refcnt))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
tprogs = kcalloc(BPF_TRAMP_MAX, sizeof(*tprogs), GFP_KERNEL);
|
||||||
|
if (!tprogs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
uvalue = (struct bpf_struct_ops_value *)st_map->uvalue;
|
uvalue = (struct bpf_struct_ops_value *)st_map->uvalue;
|
||||||
kvalue = (struct bpf_struct_ops_value *)&st_map->kvalue;
|
kvalue = (struct bpf_struct_ops_value *)&st_map->kvalue;
|
||||||
|
|
||||||
@ -425,10 +430,12 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
|||||||
goto reset_unlock;
|
goto reset_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tprogs[BPF_TRAMP_FENTRY].progs[0] = prog;
|
||||||
|
tprogs[BPF_TRAMP_FENTRY].nr_progs = 1;
|
||||||
err = arch_prepare_bpf_trampoline(image,
|
err = arch_prepare_bpf_trampoline(image,
|
||||||
st_map->image + PAGE_SIZE,
|
st_map->image + PAGE_SIZE,
|
||||||
&st_ops->func_models[i], 0,
|
&st_ops->func_models[i], 0,
|
||||||
&prog, 1, NULL, 0, NULL);
|
tprogs, NULL);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto reset_unlock;
|
goto reset_unlock;
|
||||||
|
|
||||||
@ -469,6 +476,7 @@ reset_unlock:
|
|||||||
memset(uvalue, 0, map->value_size);
|
memset(uvalue, 0, map->value_size);
|
||||||
memset(kvalue, 0, map->value_size);
|
memset(kvalue, 0, map->value_size);
|
||||||
unlock:
|
unlock:
|
||||||
|
kfree(tprogs);
|
||||||
mutex_unlock(&st_map->lock);
|
mutex_unlock(&st_map->lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -190,40 +190,49 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
|
static struct bpf_tramp_progs *
|
||||||
* bytes on x86. Pick a number to fit into BPF_IMAGE_SIZE / 2
|
bpf_trampoline_get_progs(const struct bpf_trampoline *tr, int *total)
|
||||||
*/
|
{
|
||||||
#define BPF_MAX_TRAMP_PROGS 40
|
const struct bpf_prog_aux *aux;
|
||||||
|
struct bpf_tramp_progs *tprogs;
|
||||||
|
struct bpf_prog **progs;
|
||||||
|
int kind;
|
||||||
|
|
||||||
|
*total = 0;
|
||||||
|
tprogs = kcalloc(BPF_TRAMP_MAX, sizeof(*tprogs), GFP_KERNEL);
|
||||||
|
if (!tprogs)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
for (kind = 0; kind < BPF_TRAMP_MAX; kind++) {
|
||||||
|
tprogs[kind].nr_progs = tr->progs_cnt[kind];
|
||||||
|
*total += tr->progs_cnt[kind];
|
||||||
|
progs = tprogs[kind].progs;
|
||||||
|
|
||||||
|
hlist_for_each_entry(aux, &tr->progs_hlist[kind], tramp_hlist)
|
||||||
|
*progs++ = aux->prog;
|
||||||
|
}
|
||||||
|
return tprogs;
|
||||||
|
}
|
||||||
|
|
||||||
static int bpf_trampoline_update(struct bpf_trampoline *tr)
|
static int bpf_trampoline_update(struct bpf_trampoline *tr)
|
||||||
{
|
{
|
||||||
void *old_image = tr->image + ((tr->selector + 1) & 1) * BPF_IMAGE_SIZE/2;
|
void *old_image = tr->image + ((tr->selector + 1) & 1) * BPF_IMAGE_SIZE/2;
|
||||||
void *new_image = tr->image + (tr->selector & 1) * BPF_IMAGE_SIZE/2;
|
void *new_image = tr->image + (tr->selector & 1) * BPF_IMAGE_SIZE/2;
|
||||||
struct bpf_prog *progs_to_run[BPF_MAX_TRAMP_PROGS];
|
struct bpf_tramp_progs *tprogs;
|
||||||
int fentry_cnt = tr->progs_cnt[BPF_TRAMP_FENTRY];
|
|
||||||
int fexit_cnt = tr->progs_cnt[BPF_TRAMP_FEXIT];
|
|
||||||
struct bpf_prog **progs, **fentry, **fexit;
|
|
||||||
u32 flags = BPF_TRAMP_F_RESTORE_REGS;
|
u32 flags = BPF_TRAMP_F_RESTORE_REGS;
|
||||||
struct bpf_prog_aux *aux;
|
int err, total;
|
||||||
int err;
|
|
||||||
|
|
||||||
if (fentry_cnt + fexit_cnt == 0) {
|
tprogs = bpf_trampoline_get_progs(tr, &total);
|
||||||
|
if (IS_ERR(tprogs))
|
||||||
|
return PTR_ERR(tprogs);
|
||||||
|
|
||||||
|
if (total == 0) {
|
||||||
err = unregister_fentry(tr, old_image);
|
err = unregister_fentry(tr, old_image);
|
||||||
tr->selector = 0;
|
tr->selector = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* populate fentry progs */
|
if (tprogs[BPF_TRAMP_FEXIT].nr_progs)
|
||||||
fentry = progs = progs_to_run;
|
|
||||||
hlist_for_each_entry(aux, &tr->progs_hlist[BPF_TRAMP_FENTRY], tramp_hlist)
|
|
||||||
*progs++ = aux->prog;
|
|
||||||
|
|
||||||
/* populate fexit progs */
|
|
||||||
fexit = progs;
|
|
||||||
hlist_for_each_entry(aux, &tr->progs_hlist[BPF_TRAMP_FEXIT], tramp_hlist)
|
|
||||||
*progs++ = aux->prog;
|
|
||||||
|
|
||||||
if (fexit_cnt)
|
|
||||||
flags = BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_SKIP_FRAME;
|
flags = BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_SKIP_FRAME;
|
||||||
|
|
||||||
/* Though the second half of trampoline page is unused a task could be
|
/* Though the second half of trampoline page is unused a task could be
|
||||||
@ -232,12 +241,11 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr)
|
|||||||
* preempted task. Hence wait for tasks to voluntarily schedule or go
|
* preempted task. Hence wait for tasks to voluntarily schedule or go
|
||||||
* to userspace.
|
* to userspace.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
synchronize_rcu_tasks();
|
synchronize_rcu_tasks();
|
||||||
|
|
||||||
err = arch_prepare_bpf_trampoline(new_image, new_image + BPF_IMAGE_SIZE / 2,
|
err = arch_prepare_bpf_trampoline(new_image, new_image + BPF_IMAGE_SIZE / 2,
|
||||||
&tr->func.model, flags,
|
&tr->func.model, flags, tprogs,
|
||||||
fentry, fentry_cnt,
|
|
||||||
fexit, fexit_cnt,
|
|
||||||
tr->func.addr);
|
tr->func.addr);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -252,6 +260,7 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr)
|
|||||||
goto out;
|
goto out;
|
||||||
tr->selector++;
|
tr->selector++;
|
||||||
out:
|
out:
|
||||||
|
kfree(tprogs);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,8 +418,7 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start)
|
|||||||
int __weak
|
int __weak
|
||||||
arch_prepare_bpf_trampoline(void *image, void *image_end,
|
arch_prepare_bpf_trampoline(void *image, void *image_end,
|
||||||
const struct btf_func_model *m, u32 flags,
|
const struct btf_func_model *m, u32 flags,
|
||||||
struct bpf_prog **fentry_progs, int fentry_cnt,
|
struct bpf_tramp_progs *tprogs,
|
||||||
struct bpf_prog **fexit_progs, int fexit_cnt,
|
|
||||||
void *orig_call)
|
void *orig_call)
|
||||||
{
|
{
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user