Skip to content

Commit ead514d

Browse files
rnavmpe
authored andcommitted
powerpc/kprobes: Add support for KPROBES_ON_FTRACE
Allow kprobes to be placed on ftrace _mcount() call sites. This optimization avoids the use of a trap, by riding on ftrace infrastructure. This depends on HAVE_DYNAMIC_FTRACE_WITH_REGS which depends on MPROFILE_KERNEL, which is only currently enabled on powerpc64le with newer toolchains. Based on the x86 code by Masami. Signed-off-by: Naveen N. Rao <[email protected]> Signed-off-by: Michael Ellerman <[email protected]>
1 parent 2f59be5 commit ead514d

File tree

6 files changed

+126
-2
lines changed

6 files changed

+126
-2
lines changed

Documentation/features/debug/kprobes-on-ftrace/arch-support.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
| nios2: | TODO |
2828
| openrisc: | TODO |
2929
| parisc: | TODO |
30-
| powerpc: | TODO |
30+
| powerpc: | ok |
3131
| s390: | TODO |
3232
| score: | TODO |
3333
| sh: | TODO |

arch/powerpc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ config PPC
142142
select HAVE_IRQ_EXIT_ON_IRQ_STACK
143143
select HAVE_KERNEL_GZIP
144144
select HAVE_KPROBES
145+
select HAVE_KPROBES_ON_FTRACE
145146
select HAVE_KRETPROBES
146147
select HAVE_LIVEPATCH if HAVE_DYNAMIC_FTRACE_WITH_REGS
147148
select HAVE_MEMBLOCK

arch/powerpc/include/asm/kprobes.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ extern int kprobe_exceptions_notify(struct notifier_block *self,
103103
extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
104104
extern int kprobe_handler(struct pt_regs *regs);
105105
extern int kprobe_post_handler(struct pt_regs *regs);
106+
#ifdef CONFIG_KPROBES_ON_FTRACE
107+
extern int skip_singlestep(struct kprobe *p, struct pt_regs *regs,
108+
struct kprobe_ctlblk *kcb);
109+
#else
110+
static inline int skip_singlestep(struct kprobe *p, struct pt_regs *regs,
111+
struct kprobe_ctlblk *kcb)
112+
{
113+
return 0;
114+
}
115+
#endif
106116
#else
107117
static inline int kprobe_handler(struct pt_regs *regs) { return 0; }
108118
static inline int kprobe_post_handler(struct pt_regs *regs) { return 0; }

arch/powerpc/kernel/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ obj-$(CONFIG_BOOTX_TEXT) += btext.o
9797
obj-$(CONFIG_SMP) += smp.o
9898
obj-$(CONFIG_KPROBES) += kprobes.o
9999
obj-$(CONFIG_OPTPROBES) += optprobes.o optprobes_head.o
100+
obj-$(CONFIG_KPROBES_ON_FTRACE) += kprobes-ftrace.o
100101
obj-$(CONFIG_UPROBES) += uprobes.o
101102
obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o
102103
obj-$(CONFIG_STACKTRACE) += stacktrace.o
@@ -150,6 +151,8 @@ GCOV_PROFILE_machine_kexec_32.o := n
150151
UBSAN_SANITIZE_machine_kexec_32.o := n
151152
GCOV_PROFILE_kprobes.o := n
152153
UBSAN_SANITIZE_kprobes.o := n
154+
GCOV_PROFILE_kprobes-ftrace.o := n
155+
UBSAN_SANITIZE_kprobes-ftrace.o := n
153156
UBSAN_SANITIZE_vdso.o := n
154157

155158
extra-$(CONFIG_PPC_FPU) += fpu.o

arch/powerpc/kernel/kprobes-ftrace.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Dynamic Ftrace based Kprobes Optimization
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*
18+
* Copyright (C) Hitachi Ltd., 2012
19+
* Copyright 2016 Naveen N. Rao <[email protected]>
20+
* IBM Corporation
21+
*/
22+
#include <linux/kprobes.h>
23+
#include <linux/ptrace.h>
24+
#include <linux/hardirq.h>
25+
#include <linux/preempt.h>
26+
#include <linux/ftrace.h>
27+
28+
static nokprobe_inline
29+
int __skip_singlestep(struct kprobe *p, struct pt_regs *regs,
30+
struct kprobe_ctlblk *kcb, unsigned long orig_nip)
31+
{
32+
/*
33+
* Emulate singlestep (and also recover regs->nip)
34+
* as if there is a nop
35+
*/
36+
regs->nip = (unsigned long)p->addr + MCOUNT_INSN_SIZE;
37+
if (unlikely(p->post_handler)) {
38+
kcb->kprobe_status = KPROBE_HIT_SSDONE;
39+
p->post_handler(p, regs, 0);
40+
}
41+
__this_cpu_write(current_kprobe, NULL);
42+
if (orig_nip)
43+
regs->nip = orig_nip;
44+
return 1;
45+
}
46+
47+
int skip_singlestep(struct kprobe *p, struct pt_regs *regs,
48+
struct kprobe_ctlblk *kcb)
49+
{
50+
if (kprobe_ftrace(p))
51+
return __skip_singlestep(p, regs, kcb, 0);
52+
else
53+
return 0;
54+
}
55+
NOKPROBE_SYMBOL(skip_singlestep);
56+
57+
/* Ftrace callback handler for kprobes */
58+
void kprobe_ftrace_handler(unsigned long nip, unsigned long parent_nip,
59+
struct ftrace_ops *ops, struct pt_regs *regs)
60+
{
61+
struct kprobe *p;
62+
struct kprobe_ctlblk *kcb;
63+
unsigned long flags;
64+
65+
/* Disable irq for emulating a breakpoint and avoiding preempt */
66+
local_irq_save(flags);
67+
hard_irq_disable();
68+
69+
p = get_kprobe((kprobe_opcode_t *)nip);
70+
if (unlikely(!p) || kprobe_disabled(p))
71+
goto end;
72+
73+
kcb = get_kprobe_ctlblk();
74+
if (kprobe_running()) {
75+
kprobes_inc_nmissed_count(p);
76+
} else {
77+
unsigned long orig_nip = regs->nip;
78+
79+
/*
80+
* On powerpc, NIP is *before* this instruction for the
81+
* pre handler
82+
*/
83+
regs->nip -= MCOUNT_INSN_SIZE;
84+
85+
__this_cpu_write(current_kprobe, p);
86+
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
87+
if (!p->pre_handler || !p->pre_handler(p, regs))
88+
__skip_singlestep(p, regs, kcb, orig_nip);
89+
/*
90+
* If pre_handler returns !0, it sets regs->nip and
91+
* resets current kprobe.
92+
*/
93+
}
94+
end:
95+
local_irq_restore(flags);
96+
}
97+
NOKPROBE_SYMBOL(kprobe_ftrace_handler);
98+
99+
int arch_prepare_kprobe_ftrace(struct kprobe *p)
100+
{
101+
p->ainsn.insn = NULL;
102+
p->ainsn.boostable = -1;
103+
return 0;
104+
}

arch/powerpc/kernel/kprobes.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,11 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
192192
bool arch_function_offset_within_entry(unsigned long offset)
193193
{
194194
#ifdef PPC64_ELF_ABI_v2
195+
#ifdef CONFIG_KPROBES_ON_FTRACE
196+
return offset <= 16;
197+
#else
195198
return offset <= 8;
199+
#endif
196200
#else
197201
return !offset;
198202
#endif
@@ -301,7 +305,9 @@ int __kprobes kprobe_handler(struct pt_regs *regs)
301305
}
302306
p = __this_cpu_read(current_kprobe);
303307
if (p->break_handler && p->break_handler(p, regs)) {
304-
goto ss_probe;
308+
if (!skip_singlestep(p, regs, kcb))
309+
goto ss_probe;
310+
ret = 1;
305311
}
306312
}
307313
goto no_kprobe;

0 commit comments

Comments
 (0)