History

bpf: support deferring bpf_link dealloc to after RCU grace period

BPF link for some program types is passed as a "context" which can be used by those BPF programs to look up additional information. E.g., for multi-kprobes and multi-uprobes, link is used to fetch BPF cookie values. Because of this runtime dependency, when bpf_link refcnt drops to zero there could still be active BPF programs running accessing link data. This patch adds generic support to defer bpf_link dealloc callback to after RCU GP, if requested. This is done by exposing two different deallocation callbacks, one synchronous and one deferred. If deferred one is provided, bpf_link_free() will schedule dealloc_deferred() callback to happen after RCU GP. BPF is using two flavors of RCU: "classic" non-sleepable one and RCU tasks trace one. The latter is used when sleepable BPF programs are used. bpf_link_free() accommodates that by checking underlying BPF program's sleepable flag, and goes either through normal RCU GP only for non-sleepable, or through RCU tasks trace GP *and* then normal RCU GP (taking into account rcu_trace_implies_rcu_gp() optimization), if BPF program is sleepable. We use this for multi-kprobe and multi-uprobe links, which dereference link during program run. We also preventively switch raw_tp link to use deferred dealloc callback, as upcoming changes in bpf-next tree expose raw_tp link data (specifically, cookie value) to BPF program at runtime as well.

Andrii Nakryiko committed 2 years ago

Commit 1a80dbc1 parent e9c856c3 files · +49 −6Browse files

Unified · Split

3 files
  1. include/linux/bpf.h +15 −1
  2. kernel/bpf/syscall.c +32 −3
  3. kernel/trace/bpf_trace.c +2 −2
include/linux/bpf.h+15−1
@@ -1574,12 +1574,26 @@ struct bpf_link { · 1573 unmodified lines
15741574 enum bpf_link_type type;
15751575 const struct bpf_link_ops *ops;
15761576 struct bpf_prog *prog;
1577− struct work_struct work;
1577+ /* rcu is used before freeing, work can be used to schedule that
1578+ * RCU-based freeing before that, so they never overlap
1579+ */
1580+ union {
1581+ struct rcu_head rcu;
1582+ struct work_struct work;
1583+ };
15781584 };
15791585
15801586 struct bpf_link_ops {
15811587 void (*release)(struct bpf_link *link);
1588+ /* deallocate link resources callback, called without RCU grace period
1589+ * waiting
1590+ */
15821591 void (*dealloc)(struct bpf_link *link);
1592+ /* deallocate link resources callback, called after RCU grace period;
1593+ * if underlying BPF program is sleepable we go through tasks trace
1594+ * RCU GP and then "classic" RCU GP
1595+ */
1596+ void (*dealloc_deferred)(struct bpf_link *link);
15831597 int (*detach)(struct bpf_link *link);
15841598 int (*update_prog)(struct bpf_link *link, struct bpf_prog *new_prog,
15851599 struct bpf_prog *old_prog);
kernel/bpf/syscall.c+32−3
@@ -3024,17 +3024,46 @@ void bpf_link_inc(struct bpf_link *link) · 3023 unmodified lines
30243024 atomic64_inc(&link->refcnt);
30253025 }
30263026
3027+static void bpf_link_defer_dealloc_rcu_gp(struct rcu_head *rcu)
3028+{
3029+ struct bpf_link *link = container_of(rcu, struct bpf_link, rcu);
3030+
3031+ /* free bpf_link and its containing memory */
3032+ link->ops->dealloc_deferred(link);
3033+}
3034+
3035+static void bpf_link_defer_dealloc_mult_rcu_gp(struct rcu_head *rcu)
3036+{
3037+ if (rcu_trace_implies_rcu_gp())
3038+ bpf_link_defer_dealloc_rcu_gp(rcu);
3039+ else
3040+ call_rcu(rcu, bpf_link_defer_dealloc_rcu_gp);
3041+}
3042+
30273043 /* bpf_link_free is guaranteed to be called from process context */
30283044 static void bpf_link_free(struct bpf_link *link)
30293045 {
3046+ bool sleepable = false;
3047+
30303048 bpf_link_free_id(link->id);
30313049 if (link->prog) {
3050+ sleepable = link->prog->sleepable;
30323051 /* detach BPF program, clean up used resources */
30333052 link->ops->release(link);
30343053 bpf_prog_put(link->prog);
30353054 }
3036− /* free bpf_link and its containing memory */
3037− link->ops->dealloc(link);
3055+ if (link->ops->dealloc_deferred) {
3056+ /* schedule BPF link deallocation; if underlying BPF program
3057+ * is sleepable, we need to first wait for RCU tasks trace
3058+ * sync, then go through "classic" RCU grace period
3059+ */
3060+ if (sleepable)
3061+ call_rcu_tasks_trace(&link->rcu, bpf_link_defer_dealloc_mult_rcu_gp);
3062+ else
3063+ call_rcu(&link->rcu, bpf_link_defer_dealloc_rcu_gp);
3064+ }
3065+ if (link->ops->dealloc)
3066+ link->ops->dealloc(link);
30383067 }
30393068
30403069 static void bpf_link_put_deferred(struct work_struct *work)
@@ -3544,7 +3573,7 @@ static int bpf_raw_tp_link_fill_link_info(const struct bpf_link *link, · 503 unmodified lines
35443573
35453574 static const struct bpf_link_ops bpf_raw_tp_link_lops = {
35463575 .release = bpf_raw_tp_link_release,
3547− .dealloc = bpf_raw_tp_link_dealloc,
3576+ .dealloc_deferred = bpf_raw_tp_link_dealloc,
35483577 .show_fdinfo = bpf_raw_tp_link_show_fdinfo,
35493578 .fill_link_info = bpf_raw_tp_link_fill_link_info,
35503579 };
kernel/trace/bpf_trace.c+2−2
@@ -2728,7 +2728,7 @@ static int bpf_kprobe_multi_link_fill_link_info(const struct bpf_link *link, · 2727 unmodified lines
27282728
27292729 static const struct bpf_link_ops bpf_kprobe_multi_link_lops = {
27302730 .release = bpf_kprobe_multi_link_release,
2731− .dealloc = bpf_kprobe_multi_link_dealloc,
2731+ .dealloc_deferred = bpf_kprobe_multi_link_dealloc,
27322732 .fill_link_info = bpf_kprobe_multi_link_fill_link_info,
27332733 };
27342734
@@ -3242,7 +3242,7 @@ static int bpf_uprobe_multi_link_fill_link_info(const struct bpf_link *link, · 507 unmodified lines
32423242
32433243 static const struct bpf_link_ops bpf_uprobe_multi_link_lops = {
32443244 .release = bpf_uprobe_multi_link_release,
3245− .dealloc = bpf_uprobe_multi_link_dealloc,
3245+ .dealloc_deferred = bpf_uprobe_multi_link_dealloc,
32463246 .fill_link_info = bpf_uprobe_multi_link_fill_link_info,
32473247 };
32483248

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t