Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Add support for uprobe multi session attach

Adding support to attach BPF program for entry and return probe
of the same function. This is common use case which at the moment
requires to create two uprobe multi links.

Adding new BPF_TRACE_UPROBE_SESSION attach type that instructs
kernel to attach single link program to both entry and exit probe.

It's possible to control execution of the BPF program on return
probe simply by returning zero or non zero from the entry BPF
program execution to execute or not the BPF program on return
probe respectively.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20241108134544.480660-4-jolsa@kernel.org

authored by

Jiri Olsa and committed by
Andrii Nakryiko
d920179b f505005b

+38 -11
+1
include/uapi/linux/bpf.h
··· 1116 1116 BPF_NETKIT_PRIMARY, 1117 1117 BPF_NETKIT_PEER, 1118 1118 BPF_TRACE_KPROBE_SESSION, 1119 + BPF_TRACE_UPROBE_SESSION, 1119 1120 __MAX_BPF_ATTACH_TYPE 1120 1121 }; 1121 1122
+7 -2
kernel/bpf/syscall.c
··· 4103 4103 if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI && 4104 4104 attach_type != BPF_TRACE_UPROBE_MULTI) 4105 4105 return -EINVAL; 4106 + if (prog->expected_attach_type == BPF_TRACE_UPROBE_SESSION && 4107 + attach_type != BPF_TRACE_UPROBE_SESSION) 4108 + return -EINVAL; 4106 4109 if (attach_type != BPF_PERF_EVENT && 4107 4110 attach_type != BPF_TRACE_KPROBE_MULTI && 4108 4111 attach_type != BPF_TRACE_KPROBE_SESSION && 4109 - attach_type != BPF_TRACE_UPROBE_MULTI) 4112 + attach_type != BPF_TRACE_UPROBE_MULTI && 4113 + attach_type != BPF_TRACE_UPROBE_SESSION) 4110 4114 return -EINVAL; 4111 4115 return 0; 4112 4116 case BPF_PROG_TYPE_SCHED_CLS: ··· 5363 5359 else if (attr->link_create.attach_type == BPF_TRACE_KPROBE_MULTI || 5364 5360 attr->link_create.attach_type == BPF_TRACE_KPROBE_SESSION) 5365 5361 ret = bpf_kprobe_multi_link_attach(attr, prog); 5366 - else if (attr->link_create.attach_type == BPF_TRACE_UPROBE_MULTI) 5362 + else if (attr->link_create.attach_type == BPF_TRACE_UPROBE_MULTI || 5363 + attr->link_create.attach_type == BPF_TRACE_UPROBE_SESSION) 5367 5364 ret = bpf_uprobe_multi_link_attach(attr, prog); 5368 5365 break; 5369 5366 default:
+1
kernel/bpf/verifier.c
··· 16027 16027 case BPF_PROG_TYPE_KPROBE: 16028 16028 switch (env->prog->expected_attach_type) { 16029 16029 case BPF_TRACE_KPROBE_SESSION: 16030 + case BPF_TRACE_UPROBE_SESSION: 16030 16031 range = retval_range(0, 1); 16031 16032 break; 16032 16033 default:
+27 -9
kernel/trace/bpf_trace.c
··· 1581 1581 return prog->expected_attach_type == BPF_TRACE_KPROBE_SESSION; 1582 1582 } 1583 1583 1584 + static inline bool is_uprobe_multi(const struct bpf_prog *prog) 1585 + { 1586 + return prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI || 1587 + prog->expected_attach_type == BPF_TRACE_UPROBE_SESSION; 1588 + } 1589 + 1590 + static inline bool is_uprobe_session(const struct bpf_prog *prog) 1591 + { 1592 + return prog->expected_attach_type == BPF_TRACE_UPROBE_SESSION; 1593 + } 1594 + 1584 1595 static const struct bpf_func_proto * 1585 1596 kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 1586 1597 { ··· 1609 1598 case BPF_FUNC_get_func_ip: 1610 1599 if (is_kprobe_multi(prog)) 1611 1600 return &bpf_get_func_ip_proto_kprobe_multi; 1612 - if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI) 1601 + if (is_uprobe_multi(prog)) 1613 1602 return &bpf_get_func_ip_proto_uprobe_multi; 1614 1603 return &bpf_get_func_ip_proto_kprobe; 1615 1604 case BPF_FUNC_get_attach_cookie: 1616 1605 if (is_kprobe_multi(prog)) 1617 1606 return &bpf_get_attach_cookie_proto_kmulti; 1618 - if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI) 1607 + if (is_uprobe_multi(prog)) 1619 1608 return &bpf_get_attach_cookie_proto_umulti; 1620 1609 return &bpf_get_attach_cookie_proto_trace; 1621 1610 default: ··· 3107 3096 u64 cookie; 3108 3097 struct uprobe *uprobe; 3109 3098 struct uprobe_consumer consumer; 3099 + bool session; 3110 3100 }; 3111 3101 3112 3102 struct bpf_uprobe_multi_link { ··· 3279 3267 __u64 *data) 3280 3268 { 3281 3269 struct bpf_uprobe *uprobe; 3270 + int ret; 3282 3271 3283 3272 uprobe = container_of(con, struct bpf_uprobe, consumer); 3284 - return uprobe_prog_run(uprobe, instruction_pointer(regs), regs); 3273 + ret = uprobe_prog_run(uprobe, instruction_pointer(regs), regs); 3274 + if (uprobe->session) 3275 + return ret ? UPROBE_HANDLER_IGNORE : 0; 3276 + return 0; 3285 3277 } 3286 3278 3287 3279 static int ··· 3295 3279 struct bpf_uprobe *uprobe; 3296 3280 3297 3281 uprobe = container_of(con, struct bpf_uprobe, consumer); 3298 - return uprobe_prog_run(uprobe, func, regs); 3282 + uprobe_prog_run(uprobe, func, regs); 3283 + return 0; 3299 3284 } 3300 3285 3301 3286 static u64 bpf_uprobe_multi_entry_ip(struct bpf_run_ctx *ctx) ··· 3335 3318 if (sizeof(u64) != sizeof(void *)) 3336 3319 return -EOPNOTSUPP; 3337 3320 3338 - if (prog->expected_attach_type != BPF_TRACE_UPROBE_MULTI) 3321 + if (!is_uprobe_multi(prog)) 3339 3322 return -EINVAL; 3340 3323 3341 3324 flags = attr->link_create.uprobe_multi.flags; ··· 3411 3394 3412 3395 uprobes[i].link = link; 3413 3396 3414 - if (flags & BPF_F_UPROBE_MULTI_RETURN) 3415 - uprobes[i].consumer.ret_handler = uprobe_multi_link_ret_handler; 3416 - else 3397 + if (!(flags & BPF_F_UPROBE_MULTI_RETURN)) 3417 3398 uprobes[i].consumer.handler = uprobe_multi_link_handler; 3418 - 3399 + if (flags & BPF_F_UPROBE_MULTI_RETURN || is_uprobe_session(prog)) 3400 + uprobes[i].consumer.ret_handler = uprobe_multi_link_ret_handler; 3401 + if (is_uprobe_session(prog)) 3402 + uprobes[i].session = true; 3419 3403 if (pid) 3420 3404 uprobes[i].consumer.filter = uprobe_multi_link_filter; 3421 3405 }
+1
tools/include/uapi/linux/bpf.h
··· 1116 1116 BPF_NETKIT_PRIMARY, 1117 1117 BPF_NETKIT_PEER, 1118 1118 BPF_TRACE_KPROBE_SESSION, 1119 + BPF_TRACE_UPROBE_SESSION, 1119 1120 __MAX_BPF_ATTACH_TYPE 1120 1121 }; 1121 1122
+1
tools/lib/bpf/libbpf.c
··· 133 133 [BPF_NETKIT_PRIMARY] = "netkit_primary", 134 134 [BPF_NETKIT_PEER] = "netkit_peer", 135 135 [BPF_TRACE_KPROBE_SESSION] = "trace_kprobe_session", 136 + [BPF_TRACE_UPROBE_SESSION] = "trace_uprobe_session", 136 137 }; 137 138 138 139 static const char * const link_type_name[] = {