Skip to content

Commit 2e4913e

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
libbpf: Wire up USDT API and bpf_link integration
Wire up libbpf USDT support APIs without yet implementing all the nitty-gritty details of USDT discovery, spec parsing, and BPF map initialization. User-visible user-space API is simple and is conceptually very similar to uprobe API. bpf_program__attach_usdt() API allows to programmatically attach given BPF program to a USDT, specified through binary path (executable or shared lib), USDT provider and name. Also, just like in uprobe case, PID filter is specified (0 - self, -1 - any process, or specific PID). Optionally, USDT cookie value can be specified. Such single API invocation will try to discover given USDT in specified binary and will use (potentially many) BPF uprobes to attach this program in correct locations. Just like any bpf_program__attach_xxx() APIs, bpf_link is returned that represents this attachment. It is a virtual BPF link that doesn't have direct kernel object, as it can consist of multiple underlying BPF uprobe links. As such, attachment is not atomic operation and there can be brief moment when some USDT call sites are attached while others are still in the process of attaching. This should be taken into consideration by user. But bpf_program__attach_usdt() guarantees that in the case of success all USDT call sites are successfully attached, or all the successfuly attachments will be detached as soon as some USDT call sites failed to be attached. So, in theory, there could be cases of failed bpf_program__attach_usdt() call which did trigger few USDT program invocations. This is unavoidable due to multi-uprobe nature of USDT and has to be handled by user, if it's important to create an illusion of atomicity. USDT BPF programs themselves are marked in BPF source code as either SEC("usdt"), in which case they won't be auto-attached through skeleton's <skel>__attach() method, or it can have a full definition, which follows the spirit of fully-specified uprobes: SEC("usdt/<path>:<provider>:<name>"). In the latter case skeleton's attach method will attempt auto-attachment. Similarly, generic bpf_program__attach() will have enought information to go off of for parameterless attachment. USDT BPF programs are actually uprobes, and as such for kernel they are marked as BPF_PROG_TYPE_KPROBE. Another part of this patch is USDT-related feature probing: - BPF cookie support detection from user-space; - detection of kernel support for auto-refcounting of USDT semaphore. The latter is optional. If kernel doesn't support such feature and USDT doesn't rely on USDT semaphores, no error is returned. But if libbpf detects that USDT requires setting semaphores and kernel doesn't support this, libbpf errors out with explicit pr_warn() message. Libbpf doesn't support poking process's memory directly to increment semaphore value, like BCC does on legacy kernels, due to inherent raciness and danger of such process memory manipulation. Libbpf let's kernel take care of this properly or gives up. Logistically, all the extra USDT-related infrastructure of libbpf is put into a separate usdt.c file and abstracted behind struct usdt_manager. Each bpf_object has lazily-initialized usdt_manager pointer, which is only instantiated if USDT programs are attempted to be attached. Closing BPF object frees up usdt_manager resources. usdt_manager keeps track of USDT spec ID assignment and few other small things. Subsequent patches will fill out remaining missing pieces of USDT initialization and setup logic. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Reviewed-by: Alan Maguire <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent d72e296 commit 2e4913e

File tree

6 files changed

+587
-11
lines changed

6 files changed

+587
-11
lines changed

tools/lib/bpf/Build

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \
22
netlink.o bpf_prog_linfo.o libbpf_probes.o xsk.o hashmap.o \
3-
btf_dump.o ringbuf.o strset.o linker.o gen_loader.o relo_core.o
3+
btf_dump.o ringbuf.o strset.o linker.o gen_loader.o relo_core.o \
4+
usdt.o

tools/lib/bpf/libbpf.c

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ struct elf_state {
483483
int st_ops_shndx;
484484
};
485485

486+
struct usdt_manager;
487+
486488
struct bpf_object {
487489
char name[BPF_OBJ_NAME_LEN];
488490
char license[64];
@@ -545,6 +547,8 @@ struct bpf_object {
545547
size_t fd_array_cap;
546548
size_t fd_array_cnt;
547549

550+
struct usdt_manager *usdt_man;
551+
548552
char path[];
549553
};
550554

@@ -4678,6 +4682,18 @@ static int probe_perf_link(void)
46784682
return link_fd < 0 && err == -EBADF;
46794683
}
46804684

4685+
static int probe_kern_bpf_cookie(void)
4686+
{
4687+
struct bpf_insn insns[] = {
4688+
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_attach_cookie),
4689+
BPF_EXIT_INSN(),
4690+
};
4691+
int ret, insn_cnt = ARRAY_SIZE(insns);
4692+
4693+
ret = bpf_prog_load(BPF_PROG_TYPE_KPROBE, NULL, "GPL", insns, insn_cnt, NULL);
4694+
return probe_fd(ret);
4695+
}
4696+
46814697
enum kern_feature_result {
46824698
FEAT_UNKNOWN = 0,
46834699
FEAT_SUPPORTED = 1,
@@ -4740,6 +4756,9 @@ static struct kern_feature_desc {
47404756
[FEAT_MEMCG_ACCOUNT] = {
47414757
"memcg-based memory accounting", probe_memcg_account,
47424758
},
4759+
[FEAT_BPF_COOKIE] = {
4760+
"BPF cookie support", probe_kern_bpf_cookie,
4761+
},
47434762
};
47444763

47454764
bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
@@ -8200,6 +8219,9 @@ void bpf_object__close(struct bpf_object *obj)
82008219
if (obj->clear_priv)
82018220
obj->clear_priv(obj, obj->priv);
82028221

8222+
usdt_manager_free(obj->usdt_man);
8223+
obj->usdt_man = NULL;
8224+
82038225
bpf_gen__free(obj->gen_loader);
82048226
bpf_object__elf_finish(obj);
82058227
bpf_object_unload(obj);
@@ -8631,6 +8653,7 @@ int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log
86318653

86328654
static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link);
86338655
static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link);
8656+
static int attach_usdt(const struct bpf_program *prog, long cookie, struct bpf_link **link);
86348657
static int attach_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link);
86358658
static int attach_raw_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link);
86368659
static int attach_trace(const struct bpf_program *prog, long cookie, struct bpf_link **link);
@@ -8648,6 +8671,7 @@ static const struct bpf_sec_def section_defs[] = {
86488671
SEC_DEF("uretprobe+", KPROBE, 0, SEC_NONE, attach_uprobe),
86498672
SEC_DEF("kprobe.multi/", KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
86508673
SEC_DEF("kretprobe.multi/", KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
8674+
SEC_DEF("usdt+", KPROBE, 0, SEC_NONE, attach_usdt),
86518675
SEC_DEF("tc", SCHED_CLS, 0, SEC_NONE),
86528676
SEC_DEF("classifier", SCHED_CLS, 0, SEC_NONE | SEC_SLOPPY_PFX | SEC_DEPRECATED),
86538677
SEC_DEF("action", SCHED_ACT, 0, SEC_NONE | SEC_SLOPPY_PFX),
@@ -9693,14 +9717,6 @@ int bpf_prog_load_deprecated(const char *file, enum bpf_prog_type type,
96939717
return bpf_prog_load_xattr2(&attr, pobj, prog_fd);
96949718
}
96959719

9696-
struct bpf_link {
9697-
int (*detach)(struct bpf_link *link);
9698-
void (*dealloc)(struct bpf_link *link);
9699-
char *pin_path; /* NULL, if not pinned */
9700-
int fd; /* hook FD, -1 if not applicable */
9701-
bool disconnected;
9702-
};
9703-
97049720
/* Replace link's underlying BPF program with the new one */
97059721
int bpf_link__update_program(struct bpf_link *link, struct bpf_program *prog)
97069722
{
@@ -10810,8 +10826,8 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
1081010826
err = resolve_full_path(binary_path, full_binary_path,
1081110827
sizeof(full_binary_path));
1081210828
if (err) {
10813-
pr_warn("prog '%s': failed to resolve full path for '%s'\n",
10814-
prog->name, binary_path);
10829+
pr_warn("prog '%s': failed to resolve full path for '%s': %d\n",
10830+
prog->name, binary_path, err);
1081510831
return libbpf_err_ptr(err);
1081610832
}
1081710833
binary_path = full_binary_path;
@@ -10963,6 +10979,85 @@ struct bpf_link *bpf_program__attach_uprobe(const struct bpf_program *prog,
1096310979
return bpf_program__attach_uprobe_opts(prog, pid, binary_path, func_offset, &opts);
1096410980
}
1096510981

10982+
struct bpf_link *bpf_program__attach_usdt(const struct bpf_program *prog,
10983+
pid_t pid, const char *binary_path,
10984+
const char *usdt_provider, const char *usdt_name,
10985+
const struct bpf_usdt_opts *opts)
10986+
{
10987+
char resolved_path[512];
10988+
struct bpf_object *obj = prog->obj;
10989+
struct bpf_link *link;
10990+
long usdt_cookie;
10991+
int err;
10992+
10993+
if (!OPTS_VALID(opts, bpf_uprobe_opts))
10994+
return libbpf_err_ptr(-EINVAL);
10995+
10996+
if (bpf_program__fd(prog) < 0) {
10997+
pr_warn("prog '%s': can't attach BPF program w/o FD (did you load it?)\n",
10998+
prog->name);
10999+
return libbpf_err_ptr(-EINVAL);
11000+
}
11001+
11002+
if (!strchr(binary_path, '/')) {
11003+
err = resolve_full_path(binary_path, resolved_path, sizeof(resolved_path));
11004+
if (err) {
11005+
pr_warn("prog '%s': failed to resolve full path for '%s': %d\n",
11006+
prog->name, binary_path, err);
11007+
return libbpf_err_ptr(err);
11008+
}
11009+
binary_path = resolved_path;
11010+
}
11011+
11012+
/* USDT manager is instantiated lazily on first USDT attach. It will
11013+
* be destroyed together with BPF object in bpf_object__close().
11014+
*/
11015+
if (IS_ERR(obj->usdt_man))
11016+
return libbpf_ptr(obj->usdt_man);
11017+
if (!obj->usdt_man) {
11018+
obj->usdt_man = usdt_manager_new(obj);
11019+
if (IS_ERR(obj->usdt_man))
11020+
return libbpf_ptr(obj->usdt_man);
11021+
}
11022+
11023+
usdt_cookie = OPTS_GET(opts, usdt_cookie, 0);
11024+
link = usdt_manager_attach_usdt(obj->usdt_man, prog, pid, binary_path,
11025+
usdt_provider, usdt_name, usdt_cookie);
11026+
err = libbpf_get_error(link);
11027+
if (err)
11028+
return libbpf_err_ptr(err);
11029+
return link;
11030+
}
11031+
11032+
static int attach_usdt(const struct bpf_program *prog, long cookie, struct bpf_link **link)
11033+
{
11034+
char *path = NULL, *provider = NULL, *name = NULL;
11035+
const char *sec_name;
11036+
int n, err;
11037+
11038+
sec_name = bpf_program__section_name(prog);
11039+
if (strcmp(sec_name, "usdt") == 0) {
11040+
/* no auto-attach for just SEC("usdt") */
11041+
*link = NULL;
11042+
return 0;
11043+
}
11044+
11045+
n = sscanf(sec_name, "usdt/%m[^:]:%m[^:]:%m[^:]", &path, &provider, &name);
11046+
if (n != 3) {
11047+
pr_warn("invalid section '%s', expected SEC(\"usdt/<path>:<provider>:<name>\")\n",
11048+
sec_name);
11049+
err = -EINVAL;
11050+
} else {
11051+
*link = bpf_program__attach_usdt(prog, -1 /* any process */, path,
11052+
provider, name, NULL);
11053+
err = libbpf_get_error(*link);
11054+
}
11055+
free(path);
11056+
free(provider);
11057+
free(name);
11058+
return err;
11059+
}
11060+
1096611061
static int determine_tracepoint_id(const char *tp_category,
1096711062
const char *tp_name)
1096811063
{

tools/lib/bpf/libbpf.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,37 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
511511
const char *binary_path, size_t func_offset,
512512
const struct bpf_uprobe_opts *opts);
513513

514+
struct bpf_usdt_opts {
515+
/* size of this struct, for forward/backward compatibility */
516+
size_t sz;
517+
/* custom user-provided value accessible through usdt_cookie() */
518+
__u64 usdt_cookie;
519+
size_t :0;
520+
};
521+
#define bpf_usdt_opts__last_field usdt_cookie
522+
523+
/**
524+
* @brief **bpf_program__attach_usdt()** is just like
525+
* bpf_program__attach_uprobe_opts() except it covers USDT (User-space
526+
* Statically Defined Tracepoint) attachment, instead of attaching to
527+
* user-space function entry or exit.
528+
*
529+
* @param prog BPF program to attach
530+
* @param pid Process ID to attach the uprobe to, 0 for self (own process),
531+
* -1 for all processes
532+
* @param binary_path Path to binary that contains provided USDT probe
533+
* @param usdt_provider USDT provider name
534+
* @param usdt_name USDT probe name
535+
* @param opts Options for altering program attachment
536+
* @return Reference to the newly created BPF link; or NULL is returned on error,
537+
* error code is stored in errno
538+
*/
539+
LIBBPF_API struct bpf_link *
540+
bpf_program__attach_usdt(const struct bpf_program *prog,
541+
pid_t pid, const char *binary_path,
542+
const char *usdt_provider, const char *usdt_name,
543+
const struct bpf_usdt_opts *opts);
544+
514545
struct bpf_tracepoint_opts {
515546
/* size of this struct, for forward/backward compatiblity */
516547
size_t sz;

tools/lib/bpf/libbpf.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ LIBBPF_0.8.0 {
444444
global:
445445
bpf_object__destroy_subskeleton;
446446
bpf_object__open_subskeleton;
447+
bpf_program__attach_usdt;
447448
libbpf_register_prog_handler;
448449
libbpf_unregister_prog_handler;
449450
bpf_program__attach_kprobe_multi_opts;

tools/lib/bpf/libbpf_internal.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@ do { \
148148
#ifndef __has_builtin
149149
#define __has_builtin(x) 0
150150
#endif
151+
152+
struct bpf_link {
153+
int (*detach)(struct bpf_link *link);
154+
void (*dealloc)(struct bpf_link *link);
155+
char *pin_path; /* NULL, if not pinned */
156+
int fd; /* hook FD, -1 if not applicable */
157+
bool disconnected;
158+
};
159+
151160
/*
152161
* Re-implement glibc's reallocarray() for libbpf internal-only use.
153162
* reallocarray(), unfortunately, is not available in all versions of glibc,
@@ -329,6 +338,8 @@ enum kern_feature_id {
329338
FEAT_BTF_TYPE_TAG,
330339
/* memcg-based accounting for BPF maps and progs */
331340
FEAT_MEMCG_ACCOUNT,
341+
/* BPF cookie (bpf_get_attach_cookie() BPF helper) support */
342+
FEAT_BPF_COOKIE,
332343
__FEAT_CNT,
333344
};
334345

@@ -543,4 +554,12 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
543554
struct bpf_core_cand_list *cands);
544555
void bpf_core_free_cands(struct bpf_core_cand_list *cands);
545556

557+
struct usdt_manager *usdt_manager_new(struct bpf_object *obj);
558+
void usdt_manager_free(struct usdt_manager *man);
559+
struct bpf_link * usdt_manager_attach_usdt(struct usdt_manager *man,
560+
const struct bpf_program *prog,
561+
pid_t pid, const char *path,
562+
const char *usdt_provider, const char *usdt_name,
563+
long usdt_cookie);
564+
546565
#endif /* __LIBBPF_LIBBPF_INTERNAL_H */

0 commit comments

Comments
 (0)