Skip to content
Permalink
Browse files
fprobe: Add exit_handler support
Add exit_handler to fprobe. fprobe + rethook allows us
to hook the kernel function return without fgraph tracer.
Eventually, the fgraph tracer will be generic array based
return hooking and fprobe may use it if user requests.
Since both array-based approach and list-based approach
have Pros and Cons, (e.g. memory consumption v.s. less
missing events) it is better to keep both but fprobe
will provide the same exit-handler interface.

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
  • Loading branch information
mhiramat authored and intel-lab-lkp committed Jan 11, 2022
1 parent 3727c0e commit c0e0471b58c3c9122bbff7523f97a363558284eb
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 2 deletions.
@@ -5,6 +5,7 @@

#include <linux/compiler.h>
#include <linux/ftrace.h>
#include <linux/rethook.h>

/*
* fprobe_entry - function entry for fprobe
@@ -27,7 +28,10 @@ struct fprobe {
struct ftrace_ops ftrace;
unsigned long nmissed;
unsigned int flags;
struct rethook *rethook;

void (*entry_handler) (struct fprobe *, unsigned long, struct pt_regs *);
void (*exit_handler) (struct fprobe *, unsigned long, struct pt_regs *);
};

#define FPROBE_FL_DISABLED 1
@@ -238,6 +238,7 @@ config FPROBES
bool "Kernel Function Probe (fprobe)"
depends on FUNCTION_TRACER
depends on DYNAMIC_FTRACE_WITH_REGS
select RETHOOK
default n
help
This option enables kernel function probe feature, which is
@@ -5,12 +5,20 @@
#include <linux/fprobes.h>
#include <linux/kallsyms.h>
#include <linux/kprobes.h>
#include <linux/rethook.h>
#include <linux/slab.h>
#include <linux/sort.h>

struct fprobe_rethook_node {
struct rethook_node node;
unsigned long entry_ip;
};

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 fprobe *fp;
int bit;

@@ -27,10 +35,34 @@ static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
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) {
fp->nmissed++;
goto out;
}
fpr = container_of(rh, struct fprobe_rethook_node, node);
fpr->entry_ip = ip;
rethook_hook_current(rh, ftrace_get_regs(fregs));
}

out:
ftrace_test_recursion_unlock(bit);
}
NOKPROBE_SYMBOL(fprobe_handler);

static void fprobe_exit_handler(struct rethook_node *rh, void *data,
struct pt_regs *regs)
{
struct fprobe *fp = (struct fprobe *)data;
struct fprobe_rethook_node *fpr;

fpr = container_of(rh, struct fprobe_rethook_node, node);

fp->exit_handler(fp, fpr->entry_ip, regs);
}
NOKPROBE_SYMBOL(fprobe_exit_handler);

static int convert_func_addresses(struct fprobe *fp)
{
unsigned int i;
@@ -88,7 +120,7 @@ static int fprobe_comp_func(const void *a, const void *b)
*/
int register_fprobe(struct fprobe *fp)
{
unsigned int i;
unsigned int i, size;
int ret;

if (!fp || !fp->nentry || !fp->entries)
@@ -114,6 +146,23 @@ int register_fprobe(struct fprobe *fp)
return ret;
}

/* Initialize rethook if needed */
if (fp->exit_handler) {
size = fp->nentry * num_possible_cpus() * 2;
fp->rethook = rethook_alloc((void *)fp, fprobe_exit_handler);
for (i = 0; i < size; i++) {
struct rethook_node *node;

node = kzalloc(sizeof(struct fprobe_rethook_node), GFP_KERNEL);
if (!node) {
rethook_free(fp->rethook);
return -ENOMEM;
}
rethook_add_node(fp->rethook, node);
}
} else
fp->rethook = NULL;

return register_ftrace_function(&fp->ftrace);
}
EXPORT_SYMBOL_GPL(register_fprobe);
@@ -124,9 +173,15 @@ EXPORT_SYMBOL_GPL(register_fprobe);
*/
int unregister_fprobe(struct fprobe *fp)
{
int ret;

if (!fp || !fp->nentry || !fp->entries)
return -EINVAL;

return unregister_ftrace_function(&fp->ftrace);
ret = unregister_ftrace_function(&fp->ftrace);
if (!ret)
rethook_free(fp->rethook);

return ret;
}
EXPORT_SYMBOL_GPL(unregister_fprobe);

0 comments on commit c0e0471

Please sign in to comment.