bpf: Avoid get_kernel_nofault() to fetch kprobe entry IP
authorAndrii Nakryiko <andrii@kernel.org>
Tue, 19 Mar 2024 21:20:13 +0000 (14:20 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Mon, 25 Mar 2024 16:05:48 +0000 (17:05 +0100)
get_kernel_nofault() (or, rather, underlying copy_from_kernel_nofault())
is not free and it does pop up in performance profiles when
kprobes are heavily utilized with CONFIG_X86_KERNEL_IBT=y config.

Let's avoid using it if we know that fentry_ip - 4 can't cross page
boundary. We do that by masking lowest 12 bits and checking if they are

Another benefit (and actually what caused a closer look at this part of
code) is that now LBR record is (typically) not wasted on
copy_from_kernel_nofault() call and code, which helps tools like
retsnoop that grab LBR records from inside BPF code in kretprobes.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Link: https://lore.kernel.org/bpf/20240319212013.1046779-1-andrii@kernel.org
kernel/trace/bpf_trace.c

index 434e3ece6688efcf7764e81bbd02941aa435fd56..6d0c95638e1bf9f53710324dcea5ad2125dc4df8 100644 (file)
@@ -1053,9 +1053,15 @@ static unsigned long get_entry_ip(unsigned long fentry_ip)
 {
        u32 instr;
 
-       /* Being extra safe in here in case entry ip is on the page-edge. */
-       if (get_kernel_nofault(instr, (u32 *) fentry_ip - 1))
-               return fentry_ip;
+       /* We want to be extra safe in case entry ip is on the page edge,
+        * but otherwise we need to avoid get_kernel_nofault()'s overhead.
+        */
+       if ((fentry_ip & ~PAGE_MASK) < ENDBR_INSN_SIZE) {
+               if (get_kernel_nofault(instr, (u32 *)(fentry_ip - ENDBR_INSN_SIZE)))
+                       return fentry_ip;
+       } else {
+               instr = *(u32 *)(fentry_ip - ENDBR_INSN_SIZE);
+       }
        if (is_endbr(instr))
                fentry_ip -= ENDBR_INSN_SIZE;
        return fentry_ip;