aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorRafael David Tinoco <rafaeldtinoco@gmail.com>2021-09-12 03:48:44 -0300
committerAndrii Nakryiko <andrii@kernel.org>2021-09-14 14:44:45 -0700
commitca304b40c20d5750f08200f0ad3445384646620c (patch)
tree1a5f76319547a38b8cf0429379dca875095bb18d /tools
parentlibbpf: Make libbpf_version.h non-auto-generated (diff)
downloadlinux-dev-ca304b40c20d5750f08200f0ad3445384646620c.tar.xz
linux-dev-ca304b40c20d5750f08200f0ad3445384646620c.zip
libbpf: Introduce legacy kprobe events support
Allow kprobe tracepoint events creation through legacy interface, as the kprobe dynamic PMUs support, used by default, was only created in v4.17. Store legacy kprobe name in struct bpf_perf_link, instead of creating a new "subclass" off of bpf_perf_link. This is ok as it's just two new fields, which are also going to be reused for legacy uprobe support in follow up patches. Signed-off-by: Rafael David Tinoco <rafaeldtinoco@gmail.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/bpf/20210912064844.3181742-1-rafaeldtinoco@gmail.com
Diffstat (limited to 'tools')
-rw-r--r--tools/lib/bpf/libbpf.c128
1 files changed, 124 insertions, 4 deletions
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 8f579c6666b2..9d99183bffba 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8993,9 +8993,57 @@ int bpf_link__unpin(struct bpf_link *link)
return 0;
}
+static int poke_kprobe_events(bool add, const char *name, bool retprobe, uint64_t offset)
+{
+ int fd, ret = 0;
+ pid_t p = getpid();
+ char cmd[260], probename[128], probefunc[128];
+ const char *file = "/sys/kernel/debug/tracing/kprobe_events";
+
+ if (retprobe)
+ snprintf(probename, sizeof(probename), "kretprobes/%s_libbpf_%u", name, p);
+ else
+ snprintf(probename, sizeof(probename), "kprobes/%s_libbpf_%u", name, p);
+
+ if (offset)
+ snprintf(probefunc, sizeof(probefunc), "%s+%zu", name, (size_t)offset);
+
+ if (add) {
+ snprintf(cmd, sizeof(cmd), "%c:%s %s",
+ retprobe ? 'r' : 'p',
+ probename,
+ offset ? probefunc : name);
+ } else {
+ snprintf(cmd, sizeof(cmd), "-:%s", probename);
+ }
+
+ fd = open(file, O_WRONLY | O_APPEND, 0);
+ if (!fd)
+ return -errno;
+ ret = write(fd, cmd, strlen(cmd));
+ if (ret < 0)
+ ret = -errno;
+ close(fd);
+
+ return ret;
+}
+
+static inline int add_kprobe_event_legacy(const char *name, bool retprobe, uint64_t offset)
+{
+ return poke_kprobe_events(true, name, retprobe, offset);
+}
+
+static inline int remove_kprobe_event_legacy(const char *name, bool retprobe)
+{
+ return poke_kprobe_events(false, name, retprobe, 0);
+}
+
struct bpf_link_perf {
struct bpf_link link;
int perf_event_fd;
+ /* legacy kprobe support: keep track of probe identifier and type */
+ char *legacy_probe_name;
+ bool legacy_is_retprobe;
};
static int bpf_link_perf_detach(struct bpf_link *link)
@@ -9010,13 +9058,19 @@ static int bpf_link_perf_detach(struct bpf_link *link)
close(perf_link->perf_event_fd);
close(link->fd);
- return libbpf_err(err);
+ /* legacy kprobe needs to be removed after perf event fd closure */
+ if (perf_link->legacy_probe_name)
+ err = remove_kprobe_event_legacy(perf_link->legacy_probe_name,
+ perf_link->legacy_is_retprobe);
+
+ return err;
}
static void bpf_link_perf_dealloc(struct bpf_link *link)
{
struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
+ free(perf_link->legacy_probe_name);
free(perf_link);
}
@@ -9130,6 +9184,18 @@ static int parse_uint_from_file(const char *file, const char *fmt)
return ret;
}
+static int determine_kprobe_perf_type_legacy(const char *func_name, bool is_retprobe)
+{
+ char file[192];
+
+ snprintf(file, sizeof(file),
+ "/sys/kernel/debug/tracing/events/%s/%s_libbpf_%d/id",
+ is_retprobe ? "kretprobes" : "kprobes",
+ func_name, getpid());
+
+ return parse_uint_from_file(file, "%d\n");
+}
+
static int determine_kprobe_perf_type(void)
{
const char *file = "/sys/bus/event_source/devices/kprobe/type";
@@ -9212,6 +9278,41 @@ static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name,
return pfd;
}
+static int perf_event_kprobe_open_legacy(bool retprobe, const char *name, uint64_t offset, int pid)
+{
+ struct perf_event_attr attr = {};
+ char errmsg[STRERR_BUFSIZE];
+ int type, pfd, err;
+
+ err = add_kprobe_event_legacy(name, retprobe, offset);
+ if (err < 0) {
+ pr_warn("failed to add legacy kprobe event: %s\n",
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return err;
+ }
+ type = determine_kprobe_perf_type_legacy(name, retprobe);
+ if (type < 0) {
+ pr_warn("failed to determine legacy kprobe event id: %s\n",
+ libbpf_strerror_r(type, errmsg, sizeof(errmsg)));
+ return type;
+ }
+ attr.size = sizeof(attr);
+ attr.config = type;
+ attr.type = PERF_TYPE_TRACEPOINT;
+
+ pfd = syscall(__NR_perf_event_open, &attr,
+ pid < 0 ? -1 : pid, /* pid */
+ pid == -1 ? 0 : -1, /* cpu */
+ -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
+ if (pfd < 0) {
+ err = -errno;
+ pr_warn("legacy kprobe perf_event_open() failed: %s\n",
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return err;
+ }
+ return pfd;
+}
+
struct bpf_link *
bpf_program__attach_kprobe_opts(struct bpf_program *prog,
const char *func_name,
@@ -9219,9 +9320,10 @@ bpf_program__attach_kprobe_opts(struct bpf_program *prog,
{
DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts);
char errmsg[STRERR_BUFSIZE];
+ char *legacy_probe = NULL;
struct bpf_link *link;
unsigned long offset;
- bool retprobe;
+ bool retprobe, legacy;
int pfd, err;
if (!OPTS_VALID(opts, bpf_kprobe_opts))
@@ -9231,8 +9333,19 @@ bpf_program__attach_kprobe_opts(struct bpf_program *prog,
offset = OPTS_GET(opts, offset, 0);
pe_opts.bpf_cookie = OPTS_GET(opts, bpf_cookie, 0);
- pfd = perf_event_open_probe(false /* uprobe */, retprobe, func_name,
- offset, -1 /* pid */, 0 /* ref_ctr_off */);
+ legacy = determine_kprobe_perf_type() < 0;
+ if (!legacy) {
+ pfd = perf_event_open_probe(false /* uprobe */, retprobe,
+ func_name, offset,
+ -1 /* pid */, 0 /* ref_ctr_off */);
+ } else {
+ legacy_probe = strdup(func_name);
+ if (!legacy_probe)
+ return libbpf_err_ptr(-ENOMEM);
+
+ pfd = perf_event_kprobe_open_legacy(retprobe, func_name,
+ offset, -1 /* pid */);
+ }
if (pfd < 0) {
pr_warn("prog '%s': failed to create %s '%s' perf event: %s\n",
prog->name, retprobe ? "kretprobe" : "kprobe", func_name,
@@ -9248,6 +9361,13 @@ bpf_program__attach_kprobe_opts(struct bpf_program *prog,
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
return libbpf_err_ptr(err);
}
+ if (legacy) {
+ struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
+
+ perf_link->legacy_probe_name = legacy_probe;
+ perf_link->legacy_is_retprobe = retprobe;
+ }
+
return link;
}