Messages in this thread Patch in this message | | | Date | Thu, 15 Oct 2015 14:26:23 +0900 | From | Namhyung Kim <> | Subject | Re: [PATCH 16/31] perf tools: Add prologue for BPF programs for fetching arguments |
| |
On Wed, Oct 14, 2015 at 12:41:27PM +0000, Wang Nan wrote: > From: He Kuang <hekuang@huawei.com> > > This patch generates prologue for a BPF program which fetch arguments > for it. With this patch, the program can have arguments as follow: > > SEC("lock_page=__lock_page page->flags") > int lock_page(struct pt_regs *ctx, int err, unsigned long flags) > { > return 1; > } > > This patch passes at most 3 arguments from r3, r4 and r5. r1 is still > the ctx pointer. r2 is used to indicate the successfulness of > dereferencing. > > This patch uses r6 to hold ctx (struct pt_regs) and r7 to hold stack > pointer for result. Result of each arguments first store on stack: > > low address > BPF_REG_FP - 24 ARG3 > BPF_REG_FP - 16 ARG2 > BPF_REG_FP - 8 ARG1 > BPF_REG_FP > high address > > Then loaded into r3, r4 and r5. > > The output prologue for offn(...off2(off1(reg)))) should be: > > r6 <- r1 // save ctx into a callee saved register > r7 <- fp > r7 <- r7 - stack_offset // pointer to result slot > /* load r3 with the offset in pt_regs of 'reg' */ > (r7) <- r3 // make slot valid > r3 <- r3 + off1 // prepare to read unsafe pointer > r2 <- 8 > r1 <- r7 // result put onto stack > call probe_read // read unsafe pointer > jnei r0, 0, err // error checking > r3 <- (r7) // read result > r3 <- r3 + off2 // prepare to read unsafe pointer > r2 <- 8 > r1 <- r7 > call probe_read > jnei r0, 0, err > ... > /* load r2, r3, r4 from stack */ > goto success > err: > r2 <- 1 > /* load r3, r4, r5 with 0 */ > goto usercode > success: > r2 <- 0 > usercode: > r1 <- r6 // restore ctx > // original user code > > If all of arguments reside in register (dereferencing is not > required), gen_prologue_fastpath() will be used to create > fast prologue: > > r3 <- (r1 + offset of reg1) > r4 <- (r1 + offset of reg2) > r5 <- (r1 + offset of reg3) > r2 <- 0 > > P.S. > > eBPF calling convention is defined as: > > * r0 - return value from in-kernel function, and exit value > for eBPF program > * r1 - r5 - arguments from eBPF program to in-kernel function > * r6 - r9 - callee saved registers that in-kernel function will > preserve > * r10 - read-only frame pointer to access stack > > Signed-off-by: He Kuang <hekuang@huawei.com> > Signed-off-by: Wang Nan <wangnan0@huawei.com> > Cc: Alexei Starovoitov <ast@plumgrid.com> > Cc: Brendan Gregg <brendan.d.gregg@gmail.com> > Cc: Daniel Borkmann <daniel@iogearbox.net> > Cc: David Ahern <dsahern@gmail.com> > Cc: He Kuang <hekuang@huawei.com> > Cc: Jiri Olsa <jolsa@kernel.org> > Cc: Kaixu Xia <xiakaixu@huawei.com> > Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> > Cc: Namhyung Kim <namhyung@kernel.org> > Cc: Paul Mackerras <paulus@samba.org> > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> > Cc: Zefan Li <lizefan@huawei.com> > Cc: pi3orama@163.com > Cc: Arnaldo Carvalho de Melo <acme@redhat.com> > Link: http://lkml.kernel.org/n/ebpf-6yw9eg0ej3l4jnqhinngkw86@git.kernel.org > ---
[SNIP] > +int bpf__gen_prologue(struct probe_trace_arg *args, int nargs, > + struct bpf_insn *new_prog, size_t *new_cnt, > + size_t cnt_space) > +{ > + struct bpf_insn *success_code = NULL; > + struct bpf_insn *error_code = NULL; > + struct bpf_insn *user_code = NULL; > + struct bpf_insn_pos pos; > + bool fastpath = true; > + int i; > + > + if (!new_prog || !new_cnt) > + return -EINVAL; > + > + pos.begin = new_prog; > + pos.end = new_prog + cnt_space; > + pos.pos = new_prog; > + > + if (!nargs) { > + ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0), > + &pos); > + > + if (check_pos(&pos)) > + goto errout; > + > + *new_cnt = pos_get_cnt(&pos); > + return 0; > + } > + > + if (nargs > BPF_PROLOGUE_MAX_ARGS) > + nargs = BPF_PROLOGUE_MAX_ARGS;
Wouldn't it be better to inform user if it ignored some arguments?
Thanks, Namhyung
> + if (cnt_space > BPF_MAXINSNS) > + cnt_space = BPF_MAXINSNS; > + > + /* First pass: validation */ > + for (i = 0; i < nargs; i++) { > + struct probe_trace_arg_ref *ref = args[i].ref; > + > + if (args[i].value[0] == '@') { > + /* TODO: fetch global variable */ > + pr_err("bpf: prologue: global %s%+ld not support\n", > + args[i].value, ref ? ref->offset : 0); > + return -ENOTSUP; > + } > + > + while (ref) { > + /* fastpath is true if all args has ref == NULL */ > + fastpath = false; > + > + /* > + * Instruction encodes immediate value using > + * s32, ref->offset is long. On systems which > + * can't fill long in s32, refuse to process if > + * ref->offset too large (or small). > + */ > +#ifdef __LP64__ > +#define OFFSET_MAX ((1LL << 31) - 1) > +#define OFFSET_MIN ((1LL << 31) * -1) > + if (ref->offset > OFFSET_MAX || > + ref->offset < OFFSET_MIN) { > + pr_err("bpf: prologue: offset out of bound: %ld\n", > + ref->offset); > + return -E2BIG; > + } > +#endif > + ref = ref->next; > + } > + } > + pr_debug("prologue: pass validation\n"); > + > + if (fastpath) { > + /* If all variables are registers... */ > + pr_debug("prologue: fast path\n"); > + if (gen_prologue_fastpath(&pos, args, nargs)) > + goto errout; > + } else { > + pr_debug("prologue: slow path\n"); > + > + /* Initialization: move ctx to a callee saved register. */ > + ins(BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1), &pos); > + > + if (gen_prologue_slowpath(&pos, args, nargs)) > + goto errout; > + /* > + * start of ERROR_CODE (only slow pass needs error code) > + * mov r2 <- 1 > + * goto usercode > + */ > + error_code = pos.pos; > + ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 1), > + &pos); > + > + for (i = 0; i < nargs; i++) > + ins(BPF_ALU64_IMM(BPF_MOV, > + BPF_PROLOGUE_START_ARG_REG + i, > + 0), > + &pos); > + ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_USER_CODE), > + &pos); > + } > + > + /* > + * start of SUCCESS_CODE: > + * mov r2 <- 0 > + * goto usercode // skip > + */ > + success_code = pos.pos; > + ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0), &pos); > + > + /* > + * start of USER_CODE: > + * Restore ctx to r1 > + */ > + user_code = pos.pos; > + if (!fastpath) { > + /* > + * Only slow path needs restoring of ctx. In fast path, > + * register are loaded directly from r1. > + */ > + ins(BPF_MOV64_REG(BPF_REG_ARG1, BPF_REG_CTX), &pos); > + if (prologue_relocate(&pos, error_code, success_code, > + user_code)) > + goto errout; > + } > + > + if (check_pos(&pos)) > + goto errout; > + > + *new_cnt = pos_get_cnt(&pos); > + return 0; > +errout: > + return -ERANGE; > +} > diff --git a/tools/perf/util/bpf-prologue.h b/tools/perf/util/bpf-prologue.h > new file mode 100644 > index 0000000..f1e4c5d > --- /dev/null > +++ b/tools/perf/util/bpf-prologue.h > @@ -0,0 +1,34 @@ > +/* > + * Copyright (C) 2015, He Kuang <hekuang@huawei.com> > + * Copyright (C) 2015, Huawei Inc. > + */ > +#ifndef __BPF_PROLOGUE_H > +#define __BPF_PROLOGUE_H > + > +#include <linux/compiler.h> > +#include <linux/filter.h> > +#include "probe-event.h" > + > +#define BPF_PROLOGUE_MAX_ARGS 3 > +#define BPF_PROLOGUE_START_ARG_REG BPF_REG_3 > +#define BPF_PROLOGUE_FETCH_RESULT_REG BPF_REG_2 > + > +#ifdef HAVE_BPF_PROLOGUE > +int bpf__gen_prologue(struct probe_trace_arg *args, int nargs, > + struct bpf_insn *new_prog, size_t *new_cnt, > + size_t cnt_space); > +#else > +static inline int > +bpf__gen_prologue(struct probe_trace_arg *args __maybe_unused, > + int nargs __maybe_unused, > + struct bpf_insn *new_prog __maybe_unused, > + size_t *new_cnt, > + size_t cnt_space __maybe_unused) > +{ > + if (!new_cnt) > + return -EINVAL; > + *new_cnt = 0; > + return 0; > +} > +#endif > +#endif /* __BPF_PROLOGUE_H */ > -- > 1.8.3.4
| |