59dc5bfca0
When an interrupt is taken, the SRR registers are set to return to where it left off. Unless they are modified in the meantime, or the return address or MSR are modified, there is no need to reload these registers when returning from interrupt. Introduce per-CPU flags that track the validity of SRR and HSRR registers. These are cleared when returning from interrupt, when using the registers for something else (e.g., OPAL calls), when adjusting the return address or MSR of a context, and when context switching (which changes the return address and MSR). This improves the performance of interrupt returns. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> [mpe: Fold in fixup patch from Nick] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20210617155116.2167984-5-npiggin@gmail.com
75 lines
1.8 KiB
C
75 lines
1.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Dynamic Ftrace based Kprobes Optimization
|
|
*
|
|
* 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>
|
|
|
|
/* Ftrace callback handler for kprobes */
|
|
void kprobe_ftrace_handler(unsigned long nip, unsigned long parent_nip,
|
|
struct ftrace_ops *ops, struct ftrace_regs *fregs)
|
|
{
|
|
struct kprobe *p;
|
|
struct kprobe_ctlblk *kcb;
|
|
struct pt_regs *regs;
|
|
int bit;
|
|
|
|
bit = ftrace_test_recursion_trylock(nip, parent_nip);
|
|
if (bit < 0)
|
|
return;
|
|
|
|
regs = ftrace_get_regs(fregs);
|
|
preempt_disable_notrace();
|
|
p = get_kprobe((kprobe_opcode_t *)nip);
|
|
if (unlikely(!p) || kprobe_disabled(p))
|
|
goto out;
|
|
|
|
kcb = get_kprobe_ctlblk();
|
|
if (kprobe_running()) {
|
|
kprobes_inc_nmissed_count(p);
|
|
} else {
|
|
/*
|
|
* On powerpc, NIP is *before* this instruction for the
|
|
* pre handler
|
|
*/
|
|
regs_add_return_ip(regs, -MCOUNT_INSN_SIZE);
|
|
|
|
__this_cpu_write(current_kprobe, p);
|
|
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
|
|
if (!p->pre_handler || !p->pre_handler(p, regs)) {
|
|
/*
|
|
* Emulate singlestep (and also recover regs->nip)
|
|
* as if there is a nop
|
|
*/
|
|
regs_add_return_ip(regs, MCOUNT_INSN_SIZE);
|
|
if (unlikely(p->post_handler)) {
|
|
kcb->kprobe_status = KPROBE_HIT_SSDONE;
|
|
p->post_handler(p, regs, 0);
|
|
}
|
|
}
|
|
/*
|
|
* If pre_handler returns !0, it changes regs->nip. We have to
|
|
* skip emulating post_handler.
|
|
*/
|
|
__this_cpu_write(current_kprobe, NULL);
|
|
}
|
|
out:
|
|
preempt_enable_notrace();
|
|
ftrace_test_recursion_unlock(bit);
|
|
}
|
|
NOKPROBE_SYMBOL(kprobe_ftrace_handler);
|
|
|
|
int arch_prepare_kprobe_ftrace(struct kprobe *p)
|
|
{
|
|
p->ainsn.insn = NULL;
|
|
p->ainsn.boostable = -1;
|
|
return 0;
|
|
}
|