Skip to content

Commit 5b0ab78

Browse files
mhiramatAlexei Starovoitov
authored andcommitted
fprobe: Add exit_handler support
Add exit_handler to fprobe. fprobe + rethook allows us to hook the kernel function return. The rethook will be enabled only if the fprobe::exit_handler is set. Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]> Tested-by: Steven Rostedt (Google) <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/164735290790.1084943.10601965782208052202.stgit@devnote2
1 parent 515a491 commit 5b0ab78

File tree

3 files changed

+122
-9
lines changed

3 files changed

+122
-9
lines changed

include/linux/fprobe.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55

66
#include <linux/compiler.h>
77
#include <linux/ftrace.h>
8+
#include <linux/rethook.h>
89

910
/**
1011
* struct fprobe - ftrace based probe.
1112
* @ops: The ftrace_ops.
1213
* @nmissed: The counter for missing events.
1314
* @flags: The status flag.
15+
* @rethook: The rethook data structure. (internal data)
1416
* @entry_handler: The callback function for function entry.
17+
* @exit_handler: The callback function for function exit.
1518
*/
1619
struct fprobe {
1720
#ifdef CONFIG_FUNCTION_TRACER
@@ -25,7 +28,10 @@ struct fprobe {
2528
#endif
2629
unsigned long nmissed;
2730
unsigned int flags;
31+
struct rethook *rethook;
32+
2833
void (*entry_handler)(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs);
34+
void (*exit_handler)(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs);
2935
};
3036

3137
#define FPROBE_FL_DISABLED 1

kernel/trace/Kconfig

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,14 @@ config FPROBE
251251
bool "Kernel Function Probe (fprobe)"
252252
depends on FUNCTION_TRACER
253253
depends on DYNAMIC_FTRACE_WITH_REGS
254+
depends on HAVE_RETHOOK
255+
select RETHOOK
254256
default n
255257
help
256-
This option enables kernel function probe (fprobe) based on ftrace,
257-
which is similar to kprobes, but probes only for kernel function
258-
entries and it can probe multiple functions by one fprobe.
258+
This option enables kernel function probe (fprobe) based on ftrace.
259+
The fprobe is similar to kprobes, but probes only for kernel function
260+
entries and exits. This also can probe multiple functions by one
261+
fprobe.
259262

260263
If unsure, say N.
261264

kernel/trace/fprobe.c

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@
88
#include <linux/fprobe.h>
99
#include <linux/kallsyms.h>
1010
#include <linux/kprobes.h>
11+
#include <linux/rethook.h>
1112
#include <linux/slab.h>
1213
#include <linux/sort.h>
1314

15+
#include "trace.h"
16+
17+
struct fprobe_rethook_node {
18+
struct rethook_node node;
19+
unsigned long entry_ip;
20+
};
21+
1422
static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
1523
struct ftrace_ops *ops, struct ftrace_regs *fregs)
1624
{
25+
struct fprobe_rethook_node *fpr;
26+
struct rethook_node *rh;
1727
struct fprobe *fp;
1828
int bit;
1929

@@ -30,10 +40,37 @@ static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
3040
if (fp->entry_handler)
3141
fp->entry_handler(fp, ip, ftrace_get_regs(fregs));
3242

43+
if (fp->exit_handler) {
44+
rh = rethook_try_get(fp->rethook);
45+
if (!rh) {
46+
fp->nmissed++;
47+
goto out;
48+
}
49+
fpr = container_of(rh, struct fprobe_rethook_node, node);
50+
fpr->entry_ip = ip;
51+
rethook_hook(rh, ftrace_get_regs(fregs), true);
52+
}
53+
54+
out:
3355
ftrace_test_recursion_unlock(bit);
3456
}
3557
NOKPROBE_SYMBOL(fprobe_handler);
3658

59+
static void fprobe_exit_handler(struct rethook_node *rh, void *data,
60+
struct pt_regs *regs)
61+
{
62+
struct fprobe *fp = (struct fprobe *)data;
63+
struct fprobe_rethook_node *fpr;
64+
65+
if (!fp || fprobe_disabled(fp))
66+
return;
67+
68+
fpr = container_of(rh, struct fprobe_rethook_node, node);
69+
70+
fp->exit_handler(fp, fpr->entry_ip, regs);
71+
}
72+
NOKPROBE_SYMBOL(fprobe_exit_handler);
73+
3774
/* Convert ftrace location address from symbols */
3875
static unsigned long *get_ftrace_locations(const char **syms, int num)
3976
{
@@ -77,6 +114,48 @@ static void fprobe_init(struct fprobe *fp)
77114
fp->ops.flags |= FTRACE_OPS_FL_SAVE_REGS;
78115
}
79116

117+
static int fprobe_init_rethook(struct fprobe *fp, int num)
118+
{
119+
int i, size;
120+
121+
if (num < 0)
122+
return -EINVAL;
123+
124+
if (!fp->exit_handler) {
125+
fp->rethook = NULL;
126+
return 0;
127+
}
128+
129+
/* Initialize rethook if needed */
130+
size = num * num_possible_cpus() * 2;
131+
if (size < 0)
132+
return -E2BIG;
133+
134+
fp->rethook = rethook_alloc((void *)fp, fprobe_exit_handler);
135+
for (i = 0; i < size; i++) {
136+
struct rethook_node *node;
137+
138+
node = kzalloc(sizeof(struct fprobe_rethook_node), GFP_KERNEL);
139+
if (!node) {
140+
rethook_free(fp->rethook);
141+
fp->rethook = NULL;
142+
return -ENOMEM;
143+
}
144+
rethook_add_node(fp->rethook, node);
145+
}
146+
return 0;
147+
}
148+
149+
static void fprobe_fail_cleanup(struct fprobe *fp)
150+
{
151+
if (fp->rethook) {
152+
/* Don't need to cleanup rethook->handler because this is not used. */
153+
rethook_free(fp->rethook);
154+
fp->rethook = NULL;
155+
}
156+
ftrace_free_filter(&fp->ops);
157+
}
158+
80159
/**
81160
* register_fprobe() - Register fprobe to ftrace by pattern.
82161
* @fp: A fprobe data structure to be registered.
@@ -90,6 +169,7 @@ static void fprobe_init(struct fprobe *fp)
90169
*/
91170
int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter)
92171
{
172+
struct ftrace_hash *hash;
93173
unsigned char *str;
94174
int ret, len;
95175

@@ -114,10 +194,21 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter
114194
goto out;
115195
}
116196

117-
ret = register_ftrace_function(&fp->ops);
197+
/* TODO:
198+
* correctly calculate the total number of filtered symbols
199+
* from both filter and notfilter.
200+
*/
201+
hash = fp->ops.local_hash.filter_hash;
202+
if (WARN_ON_ONCE(!hash))
203+
goto out;
204+
205+
ret = fprobe_init_rethook(fp, (int)hash->count);
206+
if (!ret)
207+
ret = register_ftrace_function(&fp->ops);
208+
118209
out:
119210
if (ret)
120-
ftrace_free_filter(&fp->ops);
211+
fprobe_fail_cleanup(fp);
121212
return ret;
122213
}
123214
EXPORT_SYMBOL_GPL(register_fprobe);
@@ -145,12 +236,15 @@ int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num)
145236
fprobe_init(fp);
146237

147238
ret = ftrace_set_filter_ips(&fp->ops, addrs, num, 0, 0);
239+
if (ret)
240+
return ret;
241+
242+
ret = fprobe_init_rethook(fp, num);
148243
if (!ret)
149244
ret = register_ftrace_function(&fp->ops);
150245

151246
if (ret)
152-
ftrace_free_filter(&fp->ops);
153-
247+
fprobe_fail_cleanup(fp);
154248
return ret;
155249
}
156250
EXPORT_SYMBOL_GPL(register_fprobe_ips);
@@ -201,10 +295,20 @@ int unregister_fprobe(struct fprobe *fp)
201295
if (!fp || fp->ops.func != fprobe_handler)
202296
return -EINVAL;
203297

298+
/*
299+
* rethook_free() starts disabling the rethook, but the rethook handlers
300+
* may be running on other processors at this point. To make sure that all
301+
* current running handlers are finished, call unregister_ftrace_function()
302+
* after this.
303+
*/
304+
if (fp->rethook)
305+
rethook_free(fp->rethook);
306+
204307
ret = unregister_ftrace_function(&fp->ops);
308+
if (ret < 0)
309+
return ret;
205310

206-
if (!ret)
207-
ftrace_free_filter(&fp->ops);
311+
ftrace_free_filter(&fp->ops);
208312

209313
return ret;
210314
}

0 commit comments

Comments
 (0)