#include <linux/hash.h>
 #include <linux/bpf.h>
 #include <linux/filter.h>
+#include <linux/ftrace.h>
 
 /* btf_vmlinux has ~22k attachable functions. 1k htab is enough. */
 #define TRAMPOLINE_HASH_BITS 10
        return tr;
 }
 
+static int is_ftrace_location(void *ip)
+{
+       long addr;
+
+       addr = ftrace_location((long)ip);
+       if (!addr)
+               return 0;
+       if (WARN_ON_ONCE(addr != (long)ip))
+               return -EFAULT;
+       return 1;
+}
+
+static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr)
+{
+       void *ip = tr->func.addr;
+       int ret;
+
+       if (tr->func.ftrace_managed)
+               ret = unregister_ftrace_direct((long)ip, (long)old_addr);
+       else
+               ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL);
+       return ret;
+}
+
+static int modify_fentry(struct bpf_trampoline *tr, void *old_addr, void *new_addr)
+{
+       void *ip = tr->func.addr;
+       int ret;
+
+       if (tr->func.ftrace_managed)
+               ret = modify_ftrace_direct((long)ip, (long)old_addr, (long)new_addr);
+       else
+               ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, new_addr);
+       return ret;
+}
+
+/* first time registering */
+static int register_fentry(struct bpf_trampoline *tr, void *new_addr)
+{
+       void *ip = tr->func.addr;
+       int ret;
+
+       ret = is_ftrace_location(ip);
+       if (ret < 0)
+               return ret;
+       tr->func.ftrace_managed = ret;
+
+       if (tr->func.ftrace_managed)
+               ret = register_ftrace_direct((long)ip, (long)new_addr);
+       else
+               ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, NULL, new_addr);
+       return ret;
+}
+
 /* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
  * bytes on x86.  Pick a number to fit into PAGE_SIZE / 2
  */
        int err;
 
        if (fentry_cnt + fexit_cnt == 0) {
-               err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_CALL,
-                                        old_image, NULL);
+               err = unregister_fentry(tr, old_image);
                tr->selector = 0;
                goto out;
        }
 
        if (tr->selector)
                /* progs already running at this address */
-               err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_CALL,
-                                        old_image, new_image);
+               err = modify_fentry(tr, old_image, new_image);
        else
                /* first time registering */
-               err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_CALL, NULL,
-                                        new_image);
+               err = register_fentry(tr, new_image);
        if (err)
                goto out;
        tr->selector++;