fprobe: Pass entry_data to handlers
authorMasami Hiramatsu (Google) <mhiramat@kernel.org>
Wed, 1 Feb 2023 15:56:01 +0000 (00:56 +0900)
committerSteven Rostedt (Google) <rostedt@goodmis.org>
Tue, 28 Mar 2023 22:52:22 +0000 (18:52 -0400)
Pass the private entry_data to the entry and exit handlers so that
they can share the context data, something like saved function
arguments etc.
User must specify the private entry_data size by @entry_data_size
field before registering the fprobe.

Link: https://lkml.kernel.org/r/167526696173.433354.17408372048319432574.stgit@mhiramat.roam.corp.google.com
Cc: Florent Revest <revest@chromium.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
include/linux/fprobe.h
kernel/trace/bpf_trace.c
kernel/trace/fprobe.c
lib/test_fprobe.c
samples/fprobe/fprobe_example.c

index 1c2bde0ead73697dec4cf3a21b94b5255dee4bba..e0d4e61362491554d79c8788e55839c7cf46b898 100644 (file)
@@ -13,6 +13,7 @@
  * @nmissed: The counter for missing events.
  * @flags: The status flag.
  * @rethook: The rethook data structure. (internal data)
+ * @entry_data_size: The private data storage size.
  * @entry_handler: The callback function for function entry.
  * @exit_handler: The callback function for function exit.
  */
@@ -29,9 +30,12 @@ struct fprobe {
        unsigned long           nmissed;
        unsigned int            flags;
        struct rethook          *rethook;
+       size_t                  entry_data_size;
 
-       void (*entry_handler)(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs);
-       void (*exit_handler)(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs);
+       void (*entry_handler)(struct fprobe *fp, unsigned long entry_ip,
+                             struct pt_regs *regs, void *entry_data);
+       void (*exit_handler)(struct fprobe *fp, unsigned long entry_ip,
+                            struct pt_regs *regs, void *entry_data);
 };
 
 /* This fprobe is soft-disabled. */
index e8da032bb6fc8cbf9e9be3700fd1b47833c365f2..fa403c32350189eb83c5cba3fbe94ee66e08040c 100644 (file)
@@ -2646,7 +2646,7 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
 
 static void
 kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip,
-                         struct pt_regs *regs)
+                         struct pt_regs *regs, void *data)
 {
        struct bpf_kprobe_multi_link *link;
 
index e8143e3680744aa54e65b3061a9479661b3da1ce..fa25d09c9d57f438d4df57c62ba036ceaf9be8b1 100644 (file)
 struct fprobe_rethook_node {
        struct rethook_node node;
        unsigned long entry_ip;
+       char data[];
 };
 
 static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
                           struct ftrace_ops *ops, struct ftrace_regs *fregs)
 {
        struct fprobe_rethook_node *fpr;
-       struct rethook_node *rh;
+       struct rethook_node *rh = NULL;
        struct fprobe *fp;
+       void *entry_data = NULL;
        int bit;
 
        fp = container_of(ops, struct fprobe, ops);
@@ -37,9 +39,6 @@ static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
                return;
        }
 
-       if (fp->entry_handler)
-               fp->entry_handler(fp, ip, ftrace_get_regs(fregs));
-
        if (fp->exit_handler) {
                rh = rethook_try_get(fp->rethook);
                if (!rh) {
@@ -48,9 +47,16 @@ static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
                }
                fpr = container_of(rh, struct fprobe_rethook_node, node);
                fpr->entry_ip = ip;
-               rethook_hook(rh, ftrace_get_regs(fregs), true);
+               if (fp->entry_data_size)
+                       entry_data = fpr->data;
        }
 
+       if (fp->entry_handler)
+               fp->entry_handler(fp, ip, ftrace_get_regs(fregs), entry_data);
+
+       if (rh)
+               rethook_hook(rh, ftrace_get_regs(fregs), true);
+
 out:
        ftrace_test_recursion_unlock(bit);
 }
@@ -81,7 +87,8 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data,
 
        fpr = container_of(rh, struct fprobe_rethook_node, node);
 
-       fp->exit_handler(fp, fpr->entry_ip, regs);
+       fp->exit_handler(fp, fpr->entry_ip, regs,
+                        fp->entry_data_size ? (void *)fpr->data : NULL);
 }
 NOKPROBE_SYMBOL(fprobe_exit_handler);
 
@@ -146,7 +153,7 @@ static int fprobe_init_rethook(struct fprobe *fp, int num)
        for (i = 0; i < size; i++) {
                struct fprobe_rethook_node *node;
 
-               node = kzalloc(sizeof(*node), GFP_KERNEL);
+               node = kzalloc(sizeof(*node) + fp->entry_data_size, GFP_KERNEL);
                if (!node) {
                        rethook_free(fp->rethook);
                        fp->rethook = NULL;
index 1fb56cf5e5ce62f6abd458e3ede76ad78b7d255b..e4f65d114ed23e591819b8c92b6fb7e7547c1efb 100644 (file)
@@ -30,7 +30,8 @@ static noinline u32 fprobe_selftest_target2(u32 value)
        return (value / div_factor) + 1;
 }
 
-static notrace void fp_entry_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs)
+static notrace void fp_entry_handler(struct fprobe *fp, unsigned long ip,
+                                    struct pt_regs *regs, void *data)
 {
        KUNIT_EXPECT_FALSE(current_test, preemptible());
        /* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */
@@ -39,7 +40,8 @@ static notrace void fp_entry_handler(struct fprobe *fp, unsigned long ip, struct
        entry_val = (rand1 / div_factor);
 }
 
-static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs)
+static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
+                                   struct pt_regs *regs, void *data)
 {
        unsigned long ret = regs_return_value(regs);
 
index e22da8573116e0500c2eb41f2f8672ade8e2bd84..dd794990ad7eccd10985e1c5e66c0273aa651cca 100644 (file)
@@ -48,7 +48,8 @@ static void show_backtrace(void)
        stack_trace_print(stacks, len, 24);
 }
 
-static void sample_entry_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs)
+static void sample_entry_handler(struct fprobe *fp, unsigned long ip,
+                                struct pt_regs *regs, void *data)
 {
        if (use_trace)
                /*
@@ -63,7 +64,8 @@ static void sample_entry_handler(struct fprobe *fp, unsigned long ip, struct pt_
                show_backtrace();
 }
 
-static void sample_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs)
+static void sample_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs,
+                               void *data)
 {
        unsigned long rip = instruction_pointer(regs);