kprobes: Use rethook for kretprobe if possible
Use rethook for kretprobe function return hooking if the arch sets CONFIG_HAVE_RETHOOK=y. In this case, CONFIG_KRETPROBE_ON_RETHOOK is set to 'y' automatically, and the kretprobe internal data fields switches to use rethook. If not, it continues to use kretprobe specific function return hooks. Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://lore.kernel.org/bpf/164826162556.2455864.12255833167233452047.stgit@devnote2
This commit is contained in:
parent
ef8a257b4e
commit
73f9b911fa
5 changed files with 163 additions and 25 deletions
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/ftrace.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/freelist.h>
|
||||
#include <linux/rethook.h>
|
||||
#include <asm/kprobes.h>
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
|
|
@ -149,13 +150,20 @@ struct kretprobe {
|
|||
int maxactive;
|
||||
int nmissed;
|
||||
size_t data_size;
|
||||
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
|
||||
struct rethook *rh;
|
||||
#else
|
||||
struct freelist_head freelist;
|
||||
struct kretprobe_holder *rph;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define KRETPROBE_MAX_DATA_SIZE 4096
|
||||
|
||||
struct kretprobe_instance {
|
||||
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
|
||||
struct rethook_node node;
|
||||
#else
|
||||
union {
|
||||
struct freelist_node freelist;
|
||||
struct rcu_head rcu;
|
||||
|
|
@ -164,6 +172,7 @@ struct kretprobe_instance {
|
|||
struct kretprobe_holder *rph;
|
||||
kprobe_opcode_t *ret_addr;
|
||||
void *fp;
|
||||
#endif
|
||||
char data[];
|
||||
};
|
||||
|
||||
|
|
@ -186,10 +195,24 @@ extern void kprobe_busy_begin(void);
|
|||
extern void kprobe_busy_end(void);
|
||||
|
||||
#ifdef CONFIG_KRETPROBES
|
||||
extern void arch_prepare_kretprobe(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs);
|
||||
/* Check whether @p is used for implementing a trampoline. */
|
||||
extern int arch_trampoline_kprobe(struct kprobe *p);
|
||||
|
||||
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
|
||||
static nokprobe_inline struct kretprobe *get_kretprobe(struct kretprobe_instance *ri)
|
||||
{
|
||||
RCU_LOCKDEP_WARN(!rcu_read_lock_any_held(),
|
||||
"Kretprobe is accessed from instance under preemptive context");
|
||||
|
||||
return (struct kretprobe *)READ_ONCE(ri->node.rethook->data);
|
||||
}
|
||||
static nokprobe_inline unsigned long get_kretprobe_retaddr(struct kretprobe_instance *ri)
|
||||
{
|
||||
return ri->node.ret_addr;
|
||||
}
|
||||
#else
|
||||
extern void arch_prepare_kretprobe(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs);
|
||||
void arch_kretprobe_fixup_return(struct pt_regs *regs,
|
||||
kprobe_opcode_t *correct_ret_addr);
|
||||
|
||||
|
|
@ -232,6 +255,12 @@ static nokprobe_inline struct kretprobe *get_kretprobe(struct kretprobe_instance
|
|||
return READ_ONCE(ri->rph->rp);
|
||||
}
|
||||
|
||||
static nokprobe_inline unsigned long get_kretprobe_retaddr(struct kretprobe_instance *ri)
|
||||
{
|
||||
return (unsigned long)ri->ret_addr;
|
||||
}
|
||||
#endif /* CONFIG_KRETPROBE_ON_RETHOOK */
|
||||
|
||||
#else /* !CONFIG_KRETPROBES */
|
||||
static inline void arch_prepare_kretprobe(struct kretprobe *rp,
|
||||
struct pt_regs *regs)
|
||||
|
|
@ -395,7 +424,11 @@ void unregister_kretprobe(struct kretprobe *rp);
|
|||
int register_kretprobes(struct kretprobe **rps, int num);
|
||||
void unregister_kretprobes(struct kretprobe **rps, int num);
|
||||
|
||||
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
|
||||
#define kprobe_flush_task(tk) do {} while (0)
|
||||
#else
|
||||
void kprobe_flush_task(struct task_struct *tk);
|
||||
#endif
|
||||
|
||||
void kprobe_free_init_mem(void);
|
||||
|
||||
|
|
@ -509,6 +542,19 @@ static inline bool is_kprobe_optinsn_slot(unsigned long addr)
|
|||
#endif /* !CONFIG_OPTPROBES */
|
||||
|
||||
#ifdef CONFIG_KRETPROBES
|
||||
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
|
||||
static nokprobe_inline bool is_kretprobe_trampoline(unsigned long addr)
|
||||
{
|
||||
return is_rethook_trampoline(addr);
|
||||
}
|
||||
|
||||
static nokprobe_inline
|
||||
unsigned long kretprobe_find_ret_addr(struct task_struct *tsk, void *fp,
|
||||
struct llist_node **cur)
|
||||
{
|
||||
return rethook_find_ret_addr(tsk, (unsigned long)fp, cur);
|
||||
}
|
||||
#else
|
||||
static nokprobe_inline bool is_kretprobe_trampoline(unsigned long addr)
|
||||
{
|
||||
return (void *)addr == kretprobe_trampoline_addr();
|
||||
|
|
@ -516,6 +562,7 @@ static nokprobe_inline bool is_kretprobe_trampoline(unsigned long addr)
|
|||
|
||||
unsigned long kretprobe_find_ret_addr(struct task_struct *tsk, void *fp,
|
||||
struct llist_node **cur);
|
||||
#endif
|
||||
#else
|
||||
static nokprobe_inline bool is_kretprobe_trampoline(unsigned long addr)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue