bpf: Add bpf_line_info support
This patch adds bpf_line_info support. It accepts an array of bpf_line_info objects during BPF_PROG_LOAD. The "line_info", "line_info_cnt" and "line_info_rec_size" are added to the "union bpf_attr". The "line_info_rec_size" makes bpf_line_info extensible in the future. The new "check_btf_line()" ensures the userspace line_info is valid for the kernel to use. When the verifier is translating/patching the bpf_prog (through "bpf_patch_insn_single()"), the line_infos' insn_off is also adjusted by the newly added "bpf_adj_linfo()". If the bpf_prog is jited, this patch also provides the jited addrs (in aux->jited_linfo) for the corresponding line_info.insn_off. "bpf_prog_fill_jited_linfo()" is added to fill the aux->jited_linfo. It is currently called by the x86 jit. Other jits can also use "bpf_prog_fill_jited_linfo()" and it will be done in the followup patches. In the future, if it deemed necessary, a particular jit could also provide its own "bpf_prog_fill_jited_linfo()" implementation. A few "*line_info*" fields are added to the bpf_prog_info such that the user can get the xlated line_info back (i.e. the line_info with its insn_off reflecting the translated prog). The jited_line_info is available if the prog is jited. It is an array of __u64. If the prog is not jited, jited_line_info_cnt is 0. The verifier's verbose log with line_info will be done in a follow up patch. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Yonghong Song <yhs@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:

committed by
Alexei Starovoitov

parent
6baefa1aa4
commit
c454a46b5e
@@ -105,6 +105,91 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_prog_alloc);
|
||||
|
||||
int bpf_prog_alloc_jited_linfo(struct bpf_prog *prog)
|
||||
{
|
||||
if (!prog->aux->nr_linfo || !prog->jit_requested)
|
||||
return 0;
|
||||
|
||||
prog->aux->jited_linfo = kcalloc(prog->aux->nr_linfo,
|
||||
sizeof(*prog->aux->jited_linfo),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!prog->aux->jited_linfo)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bpf_prog_free_jited_linfo(struct bpf_prog *prog)
|
||||
{
|
||||
kfree(prog->aux->jited_linfo);
|
||||
prog->aux->jited_linfo = NULL;
|
||||
}
|
||||
|
||||
void bpf_prog_free_unused_jited_linfo(struct bpf_prog *prog)
|
||||
{
|
||||
if (prog->aux->jited_linfo && !prog->aux->jited_linfo[0])
|
||||
bpf_prog_free_jited_linfo(prog);
|
||||
}
|
||||
|
||||
/* The jit engine is responsible to provide an array
|
||||
* for insn_off to the jited_off mapping (insn_to_jit_off).
|
||||
*
|
||||
* The idx to this array is the insn_off. Hence, the insn_off
|
||||
* here is relative to the prog itself instead of the main prog.
|
||||
* This array has one entry for each xlated bpf insn.
|
||||
*
|
||||
* jited_off is the byte off to the last byte of the jited insn.
|
||||
*
|
||||
* Hence, with
|
||||
* insn_start:
|
||||
* The first bpf insn off of the prog. The insn off
|
||||
* here is relative to the main prog.
|
||||
* e.g. if prog is a subprog, insn_start > 0
|
||||
* linfo_idx:
|
||||
* The prog's idx to prog->aux->linfo and jited_linfo
|
||||
*
|
||||
* jited_linfo[linfo_idx] = prog->bpf_func
|
||||
*
|
||||
* For i > linfo_idx,
|
||||
*
|
||||
* jited_linfo[i] = prog->bpf_func +
|
||||
* insn_to_jit_off[linfo[i].insn_off - insn_start - 1]
|
||||
*/
|
||||
void bpf_prog_fill_jited_linfo(struct bpf_prog *prog,
|
||||
const u32 *insn_to_jit_off)
|
||||
{
|
||||
u32 linfo_idx, insn_start, insn_end, nr_linfo, i;
|
||||
const struct bpf_line_info *linfo;
|
||||
void **jited_linfo;
|
||||
|
||||
if (!prog->aux->jited_linfo)
|
||||
/* Userspace did not provide linfo */
|
||||
return;
|
||||
|
||||
linfo_idx = prog->aux->linfo_idx;
|
||||
linfo = &prog->aux->linfo[linfo_idx];
|
||||
insn_start = linfo[0].insn_off;
|
||||
insn_end = insn_start + prog->len;
|
||||
|
||||
jited_linfo = &prog->aux->jited_linfo[linfo_idx];
|
||||
jited_linfo[0] = prog->bpf_func;
|
||||
|
||||
nr_linfo = prog->aux->nr_linfo - linfo_idx;
|
||||
|
||||
for (i = 1; i < nr_linfo && linfo[i].insn_off < insn_end; i++)
|
||||
/* The verifier ensures that linfo[i].insn_off is
|
||||
* strictly increasing
|
||||
*/
|
||||
jited_linfo[i] = prog->bpf_func +
|
||||
insn_to_jit_off[linfo[i].insn_off - insn_start - 1];
|
||||
}
|
||||
|
||||
void bpf_prog_free_linfo(struct bpf_prog *prog)
|
||||
{
|
||||
bpf_prog_free_jited_linfo(prog);
|
||||
kvfree(prog->aux->linfo);
|
||||
}
|
||||
|
||||
struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
|
||||
gfp_t gfp_extra_flags)
|
||||
{
|
||||
@@ -294,6 +379,26 @@ static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bpf_adj_linfo(struct bpf_prog *prog, u32 off, u32 delta)
|
||||
{
|
||||
struct bpf_line_info *linfo;
|
||||
u32 i, nr_linfo;
|
||||
|
||||
nr_linfo = prog->aux->nr_linfo;
|
||||
if (!nr_linfo || !delta)
|
||||
return;
|
||||
|
||||
linfo = prog->aux->linfo;
|
||||
|
||||
for (i = 0; i < nr_linfo; i++)
|
||||
if (off < linfo[i].insn_off)
|
||||
break;
|
||||
|
||||
/* Push all off < linfo[i].insn_off by delta */
|
||||
for (; i < nr_linfo; i++)
|
||||
linfo[i].insn_off += delta;
|
||||
}
|
||||
|
||||
struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
|
||||
const struct bpf_insn *patch, u32 len)
|
||||
{
|
||||
@@ -349,6 +454,8 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
|
||||
*/
|
||||
BUG_ON(bpf_adj_branches(prog_adj, off, insn_delta, false));
|
||||
|
||||
bpf_adj_linfo(prog_adj, off, insn_delta);
|
||||
|
||||
return prog_adj;
|
||||
}
|
||||
|
||||
@@ -1591,13 +1698,20 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
|
||||
* be JITed, but falls back to the interpreter.
|
||||
*/
|
||||
if (!bpf_prog_is_dev_bound(fp->aux)) {
|
||||
*err = bpf_prog_alloc_jited_linfo(fp);
|
||||
if (*err)
|
||||
return fp;
|
||||
|
||||
fp = bpf_int_jit_compile(fp);
|
||||
#ifdef CONFIG_BPF_JIT_ALWAYS_ON
|
||||
if (!fp->jited) {
|
||||
bpf_prog_free_jited_linfo(fp);
|
||||
#ifdef CONFIG_BPF_JIT_ALWAYS_ON
|
||||
*err = -ENOTSUPP;
|
||||
return fp;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
bpf_prog_free_unused_jited_linfo(fp);
|
||||
}
|
||||
} else {
|
||||
*err = bpf_prog_offload_compile(fp);
|
||||
if (*err)
|
||||
|
Reference in New Issue
Block a user