2017-04-19 18:22:26 +05:30
/*
* Dynamic Ftrace based Kprobes Optimization
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
* Copyright ( C ) Hitachi Ltd . , 2012
* Copyright 2016 Naveen N . Rao < naveen . n . rao @ linux . vnet . ibm . com >
* IBM Corporation
*/
# include <linux/kprobes.h>
# include <linux/ptrace.h>
# include <linux/hardirq.h>
# include <linux/preempt.h>
# include <linux/ftrace.h>
2017-09-22 14:40:45 +05:30
/*
* This is called from ftrace code after invoking registered handlers to
* disambiguate regs - > nip changes done by jprobes and livepatch . We check if
* there is an active jprobe at the provided address ( mcount location ) .
*/
int __is_active_jprobe ( unsigned long addr )
{
2017-09-22 14:40:46 +05:30
if ( ! preemptible ( ) ) {
struct kprobe * p = raw_cpu_read ( current_kprobe ) ;
return ( p & & ( unsigned long ) p - > addr = = addr ) ? 1 : 0 ;
}
return 0 ;
2017-09-22 14:40:45 +05:30
}
2017-04-19 18:22:26 +05:30
static nokprobe_inline
int __skip_singlestep ( struct kprobe * p , struct pt_regs * regs ,
struct kprobe_ctlblk * kcb , unsigned long orig_nip )
{
/*
* Emulate singlestep ( and also recover regs - > nip )
* as if there is a nop
*/
regs - > nip = ( unsigned long ) p - > addr + MCOUNT_INSN_SIZE ;
if ( unlikely ( p - > post_handler ) ) {
kcb - > kprobe_status = KPROBE_HIT_SSDONE ;
p - > post_handler ( p , regs , 0 ) ;
}
__this_cpu_write ( current_kprobe , NULL ) ;
if ( orig_nip )
regs - > nip = orig_nip ;
return 1 ;
}
int skip_singlestep ( struct kprobe * p , struct pt_regs * regs ,
struct kprobe_ctlblk * kcb )
{
if ( kprobe_ftrace ( p ) )
return __skip_singlestep ( p , regs , kcb , 0 ) ;
else
return 0 ;
}
NOKPROBE_SYMBOL ( skip_singlestep ) ;
/* Ftrace callback handler for kprobes */
void kprobe_ftrace_handler ( unsigned long nip , unsigned long parent_nip ,
struct ftrace_ops * ops , struct pt_regs * regs )
{
struct kprobe * p ;
struct kprobe_ctlblk * kcb ;
2017-09-22 14:40:47 +05:30
preempt_disable ( ) ;
2017-04-19 18:22:26 +05:30
p = get_kprobe ( ( kprobe_opcode_t * ) nip ) ;
if ( unlikely ( ! p ) | | kprobe_disabled ( p ) )
goto end ;
kcb = get_kprobe_ctlblk ( ) ;
if ( kprobe_running ( ) ) {
kprobes_inc_nmissed_count ( p ) ;
} else {
unsigned long orig_nip = regs - > nip ;
/*
* On powerpc , NIP is * before * this instruction for the
* pre handler
*/
regs - > nip - = MCOUNT_INSN_SIZE ;
__this_cpu_write ( current_kprobe , p ) ;
kcb - > kprobe_status = KPROBE_HIT_ACTIVE ;
if ( ! p - > pre_handler | | ! p - > pre_handler ( p , regs ) )
__skip_singlestep ( p , regs , kcb , orig_nip ) ;
2017-09-22 14:40:47 +05:30
else {
/*
* If pre_handler returns ! 0 , it sets regs - > nip and
2017-10-23 22:07:39 +05:30
* resets current kprobe . In this case , we should not
* re - enable preemption .
2017-09-22 14:40:47 +05:30
*/
return ;
}
2017-04-19 18:22:26 +05:30
}
end :
2017-09-22 14:40:47 +05:30
preempt_enable_no_resched ( ) ;
2017-04-19 18:22:26 +05:30
}
NOKPROBE_SYMBOL ( kprobe_ftrace_handler ) ;
int arch_prepare_kprobe_ftrace ( struct kprobe * p )
{
p - > ainsn . insn = NULL ;
p - > ainsn . boostable = - 1 ;
return 0 ;
}