Messages in this thread | | | Date | Sat, 07 Feb 2015 19:08:04 +0900 | From | Masami Hiramatsu <> | Subject | Re: [RFC PATCH] x86: kprobes: enable optmize relative call insn |
| |
(2015/02/06 19:30), Wang Nan wrote: > In reply to Masami Hiramatsu's question on my previous early kprobe > patch series at: > > http://lists.infradead.org/pipermail/linux-arm-kernel/2015-January/315771.html > > that on x86, early kprobe's applications range is limited by the type of > optimizable instructions, I made this patch, which enables optimizing > relative call instructions by introducing specific template for them. > Such instructions make up about 7% of the kernel. In addition, when > ftrace is enabled, funtion entry will be it, so early kprobe will be > much useful than before.
Sorry, I couldn't understand this part. If you put kprobe on ftrace site after ftrace enabled, it uses ftrace directly, instead of int3 or jump. Anyway, ftrace-site instruction should be controlled by ftrace, not kprobes.
> > The relationship between ftrace and kprobe is interesting. Under normal > circumstances, kprobe utilizes ftrace. However, under early case, > there's no way to tell whether the probing instruction is an ftrace > entry. Another possible method on that is to move part of ftrace init > ahead. However, to allow optimize more instructions should also be > good for performance. > > Masami, I'd like to hear your reply on it. Do you think this patch is > also useful for the normal cases?
Expanding "optimizability" is good, but I don't like add new asm-templates which reduces maintainability less. Perhaps, we'd better start with reviewing can_boost table again...
Thank you,
> > Signed-off-by: Wang Nan <wangnan0@huawei.com> > --- > arch/x86/include/asm/kprobes.h | 17 +++++++-- > arch/x86/kernel/kprobes/opt.c | 82 ++++++++++++++++++++++++++++++++++++++++-- > 2 files changed, 94 insertions(+), 5 deletions(-) > > diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h > index 017f4bb..3627694 100644 > --- a/arch/x86/include/asm/kprobes.h > +++ b/arch/x86/include/asm/kprobes.h > @@ -31,6 +31,7 @@ > #define RELATIVEJUMP_OPCODE 0xe9 > #define RELATIVEJUMP_SIZE 5 > #define RELATIVECALL_OPCODE 0xe8 > +#define RELATIVECALL_SIZE 5 > #define RELATIVE_ADDR_SIZE 4 > #define MAX_STACK_SIZE 64 > #define MAX_OPTIMIZED_LENGTH (MAX_INSN_SIZE + RELATIVE_ADDR_SIZE) > @@ -38,8 +39,10 @@ > #ifdef __ASSEMBLY__ > > #define KPROBE_OPCODE_SIZE 1 > +#define OPT_CALL_TEMPLATE_SIZE (optprobe_call_template_end - \ > + optprobe_call_template_entry) > #define MAX_OPTINSN_SIZE ((optprobe_template_end - optprobe_template_entry) + \ > - MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE) > + MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE + OPT_CALL_TEMPLATE_SIZE) > > #ifdef CONFIG_EARLY_KPROBES > # define EARLY_KPROBES_CODES_AREA \ > @@ -81,10 +84,20 @@ extern __visible kprobe_opcode_t optprobe_template_entry; > extern __visible kprobe_opcode_t optprobe_template_val; > extern __visible kprobe_opcode_t optprobe_template_call; > extern __visible kprobe_opcode_t optprobe_template_end; > + > +extern __visible kprobe_opcode_t optprobe_call_template_entry; > +extern __visible kprobe_opcode_t optprobe_call_template_val_destaddr; > +extern __visible kprobe_opcode_t optprobe_call_template_val_retaddr; > +extern __visible kprobe_opcode_t optprobe_call_template_end; > + > +#define OPT_CALL_TEMPLATE_SIZE \ > + ((unsigned long)&optprobe_call_template_end - \ > + (unsigned long)&optprobe_call_template_entry) > #define MAX_OPTINSN_SIZE \ > (((unsigned long)&optprobe_template_end - \ > (unsigned long)&optprobe_template_entry) + \ > - MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE) > + MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE + \ > + OPT_CALL_TEMPLATE_SIZE) > > extern const int kretprobe_blacklist_size; > > diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c > index dc5fccb..05dd06f 100644 > --- a/arch/x86/kernel/kprobes/opt.c > +++ b/arch/x86/kernel/kprobes/opt.c > @@ -39,6 +39,23 @@ > > #include "common.h" > > +static inline bool > +is_relcall(u8 *addr) > +{ > + return (*(u8 *)(addr) == RELATIVECALL_OPCODE); > +} > + > +static inline void * > +get_relcall_target(u8 *addr) > +{ > + struct __arch_relative_insn { > + u8 op; > + s32 raddr; > + } __packed *insn; > + insn = (struct __arch_relative_insn *)addr; > + return (void *)((unsigned long)addr + RELATIVECALL_SIZE + insn->raddr); > +} > + > unsigned long __recover_optprobed_insn(kprobe_opcode_t *buf, unsigned long addr) > { > struct optimized_kprobe *op; > @@ -89,6 +106,48 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val) > } > > asm ( > +#ifdef CONFIG_X86_64 > + ".global optprobe_call_template_entry\n" > + "optprobe_call_template_entry:" > + "pushq %rdi\n" > + ".global optprobe_call_template_val_destaddr\n" > + "optprobe_call_template_val_destaddr:" > + ASM_NOP5 > + ASM_NOP5 > + "pushq %rdi\n" > + ".global optprobe_call_template_val_retaddr\n" > + "optprobe_call_template_val_retaddr:" > + ASM_NOP5 > + ASM_NOP5 > + "xchgq %rdi, 8(%rsp)\n" > + "retq\n" > +#else /* CONFIG_X86_32 */ > + ".global optprobe_call_template_entry\n" > + "optprobe_call_template_entry:" > + "push %edi\n" > + ".global optprobe_call_template_val_destaddr\n" > + "optprobe_call_template_val_destaddr:" > + ASM_NOP5 > + "push %edi\n" > + ".global optprobe_call_template_val_retaddr\n" > + "optprobe_call_template_val_retaddr:" > + ASM_NOP5 > + "xchg %edi, 4(%esp)\n" > + "ret\n" > +#endif > + ".global optprobe_call_template_end\n" > + "optprobe_call_template_end:\n" > +); > + > +#define __OPTCALL_TMPL_MOVE_DESTADDR_IDX \ > + ((long)&optprobe_call_template_val_destaddr - (long)&optprobe_call_template_entry) > +#define __OPTCALL_TMPL_MOVE_RETADDR_IDX \ > + ((long)&optprobe_call_template_val_retaddr - (long)&optprobe_call_template_entry) > +#define __OPTCALL_TMPL_END_IDX \ > + ((long)&optprobe_call_template_end - (long)&optprobe_call_template_entry) > +#define OPTCALL_TMPL_SIZE __OPTCALL_TMPL_END_IDX > + > +asm ( > ".global optprobe_template_entry\n" > "optprobe_template_entry:\n" > #ifdef CONFIG_X86_64 > @@ -135,6 +194,10 @@ asm ( > #define TMPL_END_IDX \ > ((long)&optprobe_template_end - (long)&optprobe_template_entry) > > +#define TMPL_OPTCALL_MOVE_DESTADDR_IDX (TMPL_END_IDX + __OPTCALL_TMPL_MOVE_DESTADDR_IDX) > +#define TMPL_OPTCALL_MOVE_RETADDR_IDX (TMPL_END_IDX + __OPTCALL_TMPL_MOVE_RETADDR_IDX) > +#define TMPL_OPTCALL_END_IDX (TMPL_END_IDX + __OPTCALL_TMPL_END_IDX) > + > #define INT3_SIZE sizeof(kprobe_opcode_t) > > /* Optimized kprobe call back function: called from optinsn */ > @@ -175,6 +238,12 @@ static int copy_optimized_instructions(u8 *dest, u8 *src) > { > int len = 0, ret; > > + if (is_relcall(src)) { > + memcpy(dest, &optprobe_call_template_entry, > + OPTCALL_TMPL_SIZE); > + return OPTCALL_TMPL_SIZE; > + } > + > while (len < RELATIVEJUMP_SIZE) { > ret = __copy_instruction(dest + len, src + len); > if (!ret || !can_boost(dest + len)) > @@ -365,9 +434,16 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, > /* Set probe function call */ > synthesize_relcall(buf + TMPL_CALL_IDX, optimized_callback); > > - /* Set returning jmp instruction at the tail of out-of-line buffer */ > - synthesize_reljump(buf + TMPL_END_IDX + op->optinsn.size, > - (u8 *)op->kp.addr + op->optinsn.size); > + if (!is_relcall(op->kp.addr)) { > + /* Set returning jmp instruction at the tail of out-of-line buffer */ > + synthesize_reljump(buf + TMPL_END_IDX + op->optinsn.size, > + (u8 *)op->kp.addr + op->optinsn.size); > + } else { > + synthesize_set_arg1(buf + TMPL_OPTCALL_MOVE_DESTADDR_IDX, > + (unsigned long)(get_relcall_target(op->kp.addr))); > + synthesize_set_arg1(buf + TMPL_OPTCALL_MOVE_RETADDR_IDX, > + (unsigned long)(op->kp.addr + RELATIVECALL_SIZE)); > + } > > flush_icache_range((unsigned long) buf, > (unsigned long) buf + TMPL_END_IDX + >
-- Masami HIRAMATSU Software Platform Research Dept. Linux Technology Research Center Hitachi, Ltd., Yokohama Research Laboratory E-mail: masami.hiramatsu.pt@hitachi.com
| |