Skip to content

Commit ca304b4

Browse files
rafaeldtinocoanakryiko
authored andcommitted
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
1 parent 2f38304 commit ca304b4

File tree

1 file changed

+124
-4
lines changed

1 file changed

+124
-4
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8993,9 +8993,57 @@ int bpf_link__unpin(struct bpf_link *link)
89938993
return 0;
89948994
}
89958995

8996+
static int poke_kprobe_events(bool add, const char *name, bool retprobe, uint64_t offset)
8997+
{
8998+
int fd, ret = 0;
8999+
pid_t p = getpid();
9000+
char cmd[260], probename[128], probefunc[128];
9001+
const char *file = "/sys/kernel/debug/tracing/kprobe_events";
9002+
9003+
if (retprobe)
9004+
snprintf(probename, sizeof(probename), "kretprobes/%s_libbpf_%u", name, p);
9005+
else
9006+
snprintf(probename, sizeof(probename), "kprobes/%s_libbpf_%u", name, p);
9007+
9008+
if (offset)
9009+
snprintf(probefunc, sizeof(probefunc), "%s+%zu", name, (size_t)offset);
9010+
9011+
if (add) {
9012+
snprintf(cmd, sizeof(cmd), "%c:%s %s",
9013+
retprobe ? 'r' : 'p',
9014+
probename,
9015+
offset ? probefunc : name);
9016+
} else {
9017+
snprintf(cmd, sizeof(cmd), "-:%s", probename);
9018+
}
9019+
9020+
fd = open(file, O_WRONLY | O_APPEND, 0);
9021+
if (!fd)
9022+
return -errno;
9023+
ret = write(fd, cmd, strlen(cmd));
9024+
if (ret < 0)
9025+
ret = -errno;
9026+
close(fd);
9027+
9028+
return ret;
9029+
}
9030+
9031+
static inline int add_kprobe_event_legacy(const char *name, bool retprobe, uint64_t offset)
9032+
{
9033+
return poke_kprobe_events(true, name, retprobe, offset);
9034+
}
9035+
9036+
static inline int remove_kprobe_event_legacy(const char *name, bool retprobe)
9037+
{
9038+
return poke_kprobe_events(false, name, retprobe, 0);
9039+
}
9040+
89969041
struct bpf_link_perf {
89979042
struct bpf_link link;
89989043
int perf_event_fd;
9044+
/* legacy kprobe support: keep track of probe identifier and type */
9045+
char *legacy_probe_name;
9046+
bool legacy_is_retprobe;
89999047
};
90009048

90019049
static int bpf_link_perf_detach(struct bpf_link *link)
@@ -9010,13 +9058,19 @@ static int bpf_link_perf_detach(struct bpf_link *link)
90109058
close(perf_link->perf_event_fd);
90119059
close(link->fd);
90129060

9013-
return libbpf_err(err);
9061+
/* legacy kprobe needs to be removed after perf event fd closure */
9062+
if (perf_link->legacy_probe_name)
9063+
err = remove_kprobe_event_legacy(perf_link->legacy_probe_name,
9064+
perf_link->legacy_is_retprobe);
9065+
9066+
return err;
90149067
}
90159068

90169069
static void bpf_link_perf_dealloc(struct bpf_link *link)
90179070
{
90189071
struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
90199072

9073+
free(perf_link->legacy_probe_name);
90209074
free(perf_link);
90219075
}
90229076

@@ -9130,6 +9184,18 @@ static int parse_uint_from_file(const char *file, const char *fmt)
91309184
return ret;
91319185
}
91329186

9187+
static int determine_kprobe_perf_type_legacy(const char *func_name, bool is_retprobe)
9188+
{
9189+
char file[192];
9190+
9191+
snprintf(file, sizeof(file),
9192+
"/sys/kernel/debug/tracing/events/%s/%s_libbpf_%d/id",
9193+
is_retprobe ? "kretprobes" : "kprobes",
9194+
func_name, getpid());
9195+
9196+
return parse_uint_from_file(file, "%d\n");
9197+
}
9198+
91339199
static int determine_kprobe_perf_type(void)
91349200
{
91359201
const char *file = "/sys/bus/event_source/devices/kprobe/type";
@@ -9212,16 +9278,52 @@ static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name,
92129278
return pfd;
92139279
}
92149280

9281+
static int perf_event_kprobe_open_legacy(bool retprobe, const char *name, uint64_t offset, int pid)
9282+
{
9283+
struct perf_event_attr attr = {};
9284+
char errmsg[STRERR_BUFSIZE];
9285+
int type, pfd, err;
9286+
9287+
err = add_kprobe_event_legacy(name, retprobe, offset);
9288+
if (err < 0) {
9289+
pr_warn("failed to add legacy kprobe event: %s\n",
9290+
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
9291+
return err;
9292+
}
9293+
type = determine_kprobe_perf_type_legacy(name, retprobe);
9294+
if (type < 0) {
9295+
pr_warn("failed to determine legacy kprobe event id: %s\n",
9296+
libbpf_strerror_r(type, errmsg, sizeof(errmsg)));
9297+
return type;
9298+
}
9299+
attr.size = sizeof(attr);
9300+
attr.config = type;
9301+
attr.type = PERF_TYPE_TRACEPOINT;
9302+
9303+
pfd = syscall(__NR_perf_event_open, &attr,
9304+
pid < 0 ? -1 : pid, /* pid */
9305+
pid == -1 ? 0 : -1, /* cpu */
9306+
-1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
9307+
if (pfd < 0) {
9308+
err = -errno;
9309+
pr_warn("legacy kprobe perf_event_open() failed: %s\n",
9310+
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
9311+
return err;
9312+
}
9313+
return pfd;
9314+
}
9315+
92159316
struct bpf_link *
92169317
bpf_program__attach_kprobe_opts(struct bpf_program *prog,
92179318
const char *func_name,
92189319
const struct bpf_kprobe_opts *opts)
92199320
{
92209321
DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts);
92219322
char errmsg[STRERR_BUFSIZE];
9323+
char *legacy_probe = NULL;
92229324
struct bpf_link *link;
92239325
unsigned long offset;
9224-
bool retprobe;
9326+
bool retprobe, legacy;
92259327
int pfd, err;
92269328

92279329
if (!OPTS_VALID(opts, bpf_kprobe_opts))
@@ -9231,8 +9333,19 @@ bpf_program__attach_kprobe_opts(struct bpf_program *prog,
92319333
offset = OPTS_GET(opts, offset, 0);
92329334
pe_opts.bpf_cookie = OPTS_GET(opts, bpf_cookie, 0);
92339335

9234-
pfd = perf_event_open_probe(false /* uprobe */, retprobe, func_name,
9235-
offset, -1 /* pid */, 0 /* ref_ctr_off */);
9336+
legacy = determine_kprobe_perf_type() < 0;
9337+
if (!legacy) {
9338+
pfd = perf_event_open_probe(false /* uprobe */, retprobe,
9339+
func_name, offset,
9340+
-1 /* pid */, 0 /* ref_ctr_off */);
9341+
} else {
9342+
legacy_probe = strdup(func_name);
9343+
if (!legacy_probe)
9344+
return libbpf_err_ptr(-ENOMEM);
9345+
9346+
pfd = perf_event_kprobe_open_legacy(retprobe, func_name,
9347+
offset, -1 /* pid */);
9348+
}
92369349
if (pfd < 0) {
92379350
pr_warn("prog '%s': failed to create %s '%s' perf event: %s\n",
92389351
prog->name, retprobe ? "kretprobe" : "kprobe", func_name,
@@ -9248,6 +9361,13 @@ bpf_program__attach_kprobe_opts(struct bpf_program *prog,
92489361
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
92499362
return libbpf_err_ptr(err);
92509363
}
9364+
if (legacy) {
9365+
struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
9366+
9367+
perf_link->legacy_probe_name = legacy_probe;
9368+
perf_link->legacy_is_retprobe = retprobe;
9369+
}
9370+
92519371
return link;
92529372
}
92539373

0 commit comments

Comments
 (0)