bpf: Provide recursion prevention helpers
authorThomas Gleixner <tglx@linutronix.de>
Mon, 24 Feb 2020 14:01:47 +0000 (15:01 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 25 Feb 2020 00:20:09 +0000 (16:20 -0800)
The places which need to prevent the execution of trace type BPF programs
to prevent deadlocks on the hash bucket lock do this open coded.

Provide two inline functions, bpf_disable/enable_instrumentation() to
replace these open coded protection constructs.

Use migrate_disable/enable() instead of preempt_disable/enable() right away
so this works on RT enabled kernels. On a !RT kernel migrate_disable /
enable() are mapped to preempt_disable/enable().

These helpers use this_cpu_inc/dec() instead of __this_cpu_inc/dec() on an
RT enabled kernel because migrate disabled regions are preemptible and
preemption might hit in the middle of a RMW operation which can lead to
inconsistent state.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200224145644.103910133@linutronix.de
include/linux/bpf.h

index 76b3a0e..1acd5bf 100644 (file)
@@ -961,6 +961,36 @@ _out:                                                      \
 #ifdef CONFIG_BPF_SYSCALL
 DECLARE_PER_CPU(int, bpf_prog_active);
 
+/*
+ * Block execution of BPF programs attached to instrumentation (perf,
+ * kprobes, tracepoints) to prevent deadlocks on map operations as any of
+ * these events can happen inside a region which holds a map bucket lock
+ * and can deadlock on it.
+ *
+ * Use the preemption safe inc/dec variants on RT because migrate disable
+ * is preemptible on RT and preemption in the middle of the RMW operation
+ * might lead to inconsistent state. Use the raw variants for non RT
+ * kernels as migrate_disable() maps to preempt_disable() so the slightly
+ * more expensive save operation can be avoided.
+ */
+static inline void bpf_disable_instrumentation(void)
+{
+       migrate_disable();
+       if (IS_ENABLED(CONFIG_PREEMPT_RT))
+               this_cpu_inc(bpf_prog_active);
+       else
+               __this_cpu_inc(bpf_prog_active);
+}
+
+static inline void bpf_enable_instrumentation(void)
+{
+       if (IS_ENABLED(CONFIG_PREEMPT_RT))
+               this_cpu_dec(bpf_prog_active);
+       else
+               __this_cpu_dec(bpf_prog_active);
+       migrate_enable();
+}
+
 extern const struct file_operations bpf_map_fops;
 extern const struct file_operations bpf_prog_fops;