Commit 780d3d5a authored by Andrii Nakryiko's avatar Andrii Nakryiko

Merge branch 'perf tools: Fix prologue generation'

Jiri Olsa says:

====================

hi,
sending change we discussed some time ago [1] to get rid of
some deprecated functions we use in perf prologue code.

Despite the gloomy discussion I think the final code does
not look that bad ;-)

This patchset removes following libbpf functions from perf:
  bpf_program__set_prep
  bpf_program__nth_fd
  struct bpf_prog_prep_result

v5 changes:
  - squashed patches together so we don't break bisection [Arnaldo]

v4 changes:
  - fix typo [Andrii]

v3 changes:
  - removed R0/R1 zero init in libbpf_prog_prepare_load_fn,
    because it's not needed [Andrii]
  - rebased/post on top of bpf-next/master which now has
    all the needed perf/core changes

v2 changes:
  - use fallback section prog handler, so we don't need to
    use section prefix [Andrii]
  - realloc prog->insns array in bpf_program__set_insns [Andrii]
  - squash patch 1 from previous version with
    bpf_program__set_insns change [Daniel]
  - patch 3 already merged [Arnaldo]
  - added more comments

thanks,
jirka

[1] https://lore.kernel.org/bpf/CAEf4BzaiBO3_617kkXZdYJ8hS8YF--ZLgapNbgeeEJ-pY0H88g@mail.gmail.com/
====================
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents 41188e9e b168852e
......@@ -9,6 +9,7 @@
#include <linux/bpf.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include <linux/filter.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/string.h>
......@@ -49,6 +50,7 @@ struct bpf_prog_priv {
struct bpf_insn *insns_buf;
int nr_types;
int *type_mapping;
int *prologue_fds;
};
struct bpf_perf_object {
......@@ -56,6 +58,11 @@ struct bpf_perf_object {
struct bpf_object *obj;
};
struct bpf_preproc_result {
struct bpf_insn *new_insn_ptr;
int new_insn_cnt;
};
static LIST_HEAD(bpf_objects_list);
static struct hashmap *bpf_program_hash;
static struct hashmap *bpf_map_hash;
......@@ -86,6 +93,7 @@ bpf_perf_object__next(struct bpf_perf_object *prev)
(perf_obj) = (tmp), (tmp) = bpf_perf_object__next(tmp))
static bool libbpf_initialized;
static int libbpf_sec_handler;
static int bpf_perf_object__add(struct bpf_object *obj)
{
......@@ -99,12 +107,76 @@ static int bpf_perf_object__add(struct bpf_object *obj)
return perf_obj ? 0 : -ENOMEM;
}
static void *program_priv(const struct bpf_program *prog)
{
void *priv;
if (IS_ERR_OR_NULL(bpf_program_hash))
return NULL;
if (!hashmap__find(bpf_program_hash, prog, &priv))
return NULL;
return priv;
}
static struct bpf_insn prologue_init_insn[] = {
BPF_MOV64_IMM(BPF_REG_2, 0),
BPF_MOV64_IMM(BPF_REG_3, 0),
BPF_MOV64_IMM(BPF_REG_4, 0),
BPF_MOV64_IMM(BPF_REG_5, 0),
};
static int libbpf_prog_prepare_load_fn(struct bpf_program *prog,
struct bpf_prog_load_opts *opts __maybe_unused,
long cookie __maybe_unused)
{
size_t init_size_cnt = ARRAY_SIZE(prologue_init_insn);
size_t orig_insn_cnt, insn_cnt, init_size, orig_size;
struct bpf_prog_priv *priv = program_priv(prog);
const struct bpf_insn *orig_insn;
struct bpf_insn *insn;
if (IS_ERR_OR_NULL(priv)) {
pr_debug("bpf: failed to get private field\n");
return -BPF_LOADER_ERRNO__INTERNAL;
}
if (!priv->need_prologue)
return 0;
/* prepend initialization code to program instructions */
orig_insn = bpf_program__insns(prog);
orig_insn_cnt = bpf_program__insn_cnt(prog);
init_size = init_size_cnt * sizeof(*insn);
orig_size = orig_insn_cnt * sizeof(*insn);
insn_cnt = orig_insn_cnt + init_size_cnt;
insn = malloc(insn_cnt * sizeof(*insn));
if (!insn)
return -ENOMEM;
memcpy(insn, prologue_init_insn, init_size);
memcpy((char *) insn + init_size, orig_insn, orig_size);
bpf_program__set_insns(prog, insn, insn_cnt);
return 0;
}
static int libbpf_init(void)
{
LIBBPF_OPTS(libbpf_prog_handler_opts, handler_opts,
.prog_prepare_load_fn = libbpf_prog_prepare_load_fn,
);
if (libbpf_initialized)
return 0;
libbpf_set_print(libbpf_perf_print);
libbpf_sec_handler = libbpf_register_prog_handler(NULL, BPF_PROG_TYPE_KPROBE,
0, &handler_opts);
if (libbpf_sec_handler < 0) {
pr_debug("bpf: failed to register libbpf section handler: %d\n",
libbpf_sec_handler);
return -BPF_LOADER_ERRNO__INTERNAL;
}
libbpf_initialized = true;
return 0;
}
......@@ -188,14 +260,31 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
return obj;
}
static void close_prologue_programs(struct bpf_prog_priv *priv)
{
struct perf_probe_event *pev;
int i, fd;
if (!priv->need_prologue)
return;
pev = &priv->pev;
for (i = 0; i < pev->ntevs; i++) {
fd = priv->prologue_fds[i];
if (fd != -1)
close(fd);
}
}
static void
clear_prog_priv(const struct bpf_program *prog __maybe_unused,
void *_priv)
{
struct bpf_prog_priv *priv = _priv;
close_prologue_programs(priv);
cleanup_perf_probe_events(&priv->pev, 1);
zfree(&priv->insns_buf);
zfree(&priv->prologue_fds);
zfree(&priv->type_mapping);
zfree(&priv->sys_name);
zfree(&priv->evt_name);
......@@ -243,17 +332,6 @@ static bool ptr_equal(const void *key1, const void *key2,
return key1 == key2;
}
static void *program_priv(const struct bpf_program *prog)
{
void *priv;
if (IS_ERR_OR_NULL(bpf_program_hash))
return NULL;
if (!hashmap__find(bpf_program_hash, prog, &priv))
return NULL;
return priv;
}
static int program_set_priv(struct bpf_program *prog, void *priv)
{
void *old_priv;
......@@ -558,8 +636,8 @@ static int bpf__prepare_probe(void)
static int
preproc_gen_prologue(struct bpf_program *prog, int n,
struct bpf_insn *orig_insns, int orig_insns_cnt,
struct bpf_prog_prep_result *res)
const struct bpf_insn *orig_insns, int orig_insns_cnt,
struct bpf_preproc_result *res)
{
struct bpf_prog_priv *priv = program_priv(prog);
struct probe_trace_event *tev;
......@@ -607,7 +685,6 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
res->new_insn_ptr = buf;
res->new_insn_cnt = prologue_cnt + orig_insns_cnt;
res->pfd = NULL;
return 0;
errout:
......@@ -715,7 +792,7 @@ static int hook_load_preprocessor(struct bpf_program *prog)
struct bpf_prog_priv *priv = program_priv(prog);
struct perf_probe_event *pev;
bool need_prologue = false;
int err, i;
int i;
if (IS_ERR_OR_NULL(priv)) {
pr_debug("Internal error when hook preprocessor\n");
......@@ -753,6 +830,13 @@ static int hook_load_preprocessor(struct bpf_program *prog)
return -ENOMEM;
}
priv->prologue_fds = malloc(sizeof(int) * pev->ntevs);
if (!priv->prologue_fds) {
pr_debug("Not enough memory: alloc prologue fds failed\n");
return -ENOMEM;
}
memset(priv->prologue_fds, -1, sizeof(int) * pev->ntevs);
priv->type_mapping = malloc(sizeof(int) * pev->ntevs);
if (!priv->type_mapping) {
pr_debug("Not enough memory: alloc type_mapping failed\n");
......@@ -761,13 +845,7 @@ static int hook_load_preprocessor(struct bpf_program *prog)
memset(priv->type_mapping, -1,
sizeof(int) * pev->ntevs);
err = map_prologue(pev, priv->type_mapping, &priv->nr_types);
if (err)
return err;
err = bpf_program__set_prep(prog, priv->nr_types,
preproc_gen_prologue);
return err;
return map_prologue(pev, priv->type_mapping, &priv->nr_types);
}
int bpf__probe(struct bpf_object *obj)
......@@ -874,6 +952,77 @@ int bpf__unprobe(struct bpf_object *obj)
return ret;
}
static int bpf_object__load_prologue(struct bpf_object *obj)
{
int init_cnt = ARRAY_SIZE(prologue_init_insn);
const struct bpf_insn *orig_insns;
struct bpf_preproc_result res;
struct perf_probe_event *pev;
struct bpf_program *prog;
int orig_insns_cnt;
bpf_object__for_each_program(prog, obj) {
struct bpf_prog_priv *priv = program_priv(prog);
int err, i, fd;
if (IS_ERR_OR_NULL(priv)) {
pr_debug("bpf: failed to get private field\n");
return -BPF_LOADER_ERRNO__INTERNAL;
}
if (!priv->need_prologue)
continue;
/*
* For each program that needs prologue we do following:
*
* - take its current instructions and use them
* to generate the new code with prologue
* - load new instructions with bpf_prog_load
* and keep the fd in prologue_fds
* - new fd will be used in bpf__foreach_event
* to connect this program with perf evsel
*/
orig_insns = bpf_program__insns(prog);
orig_insns_cnt = bpf_program__insn_cnt(prog);
pev = &priv->pev;
for (i = 0; i < pev->ntevs; i++) {
/*
* Skipping artificall prologue_init_insn instructions
* (init_cnt), so the prologue can be generated instead
* of them.
*/
err = preproc_gen_prologue(prog, i,
orig_insns + init_cnt,
orig_insns_cnt - init_cnt,
&res);
if (err)
return err;
fd = bpf_prog_load(bpf_program__get_type(prog),
bpf_program__name(prog), "GPL",
res.new_insn_ptr,
res.new_insn_cnt, NULL);
if (fd < 0) {
char bf[128];
libbpf_strerror(-errno, bf, sizeof(bf));
pr_debug("bpf: load objects with prologue failed: err=%d: (%s)\n",
-errno, bf);
return -errno;
}
priv->prologue_fds[i] = fd;
}
/*
* We no longer need the original program,
* we can unload it.
*/
bpf_program__unload(prog);
}
return 0;
}
int bpf__load(struct bpf_object *obj)
{
int err;
......@@ -885,7 +1034,7 @@ int bpf__load(struct bpf_object *obj)
pr_debug("bpf: load objects failed: err=%d: (%s)\n", err, bf);
return err;
}
return 0;
return bpf_object__load_prologue(obj);
}
int bpf__foreach_event(struct bpf_object *obj,
......@@ -920,13 +1069,10 @@ int bpf__foreach_event(struct bpf_object *obj,
for (i = 0; i < pev->ntevs; i++) {
tev = &pev->tevs[i];
if (priv->need_prologue) {
int type = priv->type_mapping[i];
fd = bpf_program__nth_fd(prog, type);
} else {
if (priv->need_prologue)
fd = priv->prologue_fds[i];
else
fd = bpf_program__fd(prog);
}
if (fd < 0) {
pr_debug("bpf: failed to get file descriptor\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment