Commit 80947e7c authored by Geert Uytterhoeven's avatar Geert Uytterhoeven Committed by Benjamin Herrenschmidt

powerpc: Keep track of emulated instructions

If CONFIG_PPC_EMULATED_STATS is enabled, make available counters for the
various classes of emulated instructions under
/sys/kernel/debug/powerpc/emulated_instructions/ (assumed debugfs is mounted on
/sys/kernel/debug).  Optionally (controlled by
/sys/kernel/debug/powerpc/emulated_instructions/do_warn), rate-limited warnings
can be printed to the console when instructions are emulated.
Signed-off-by: default avatarGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent f312deb4
......@@ -41,6 +41,19 @@ config HCALL_STATS
This option will add a small amount of overhead to all hypervisor
calls.
config PPC_EMULATED_STATS
bool "Emulated instructions tracking"
depends on DEBUG_FS
help
Adds code to keep track of the number of instructions that are
emulated by the in-kernel emulator. Counters for the various classes
of emulated instructions are available under
powerpc/emulated_instructions/ in the root of the debugfs file
system. Optionally (controlled by
powerpc/emulated_instructions/do_warn in debugfs), rate-limited
warnings can be printed to the console when instructions are
emulated.
config CODE_PATCHING_SELFTEST
bool "Run self-tests of the code-patching code."
depends on DEBUG_KERNEL
......
/*
* Copyright 2007 Sony Corporation
*
* 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; version 2 of the License.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ASM_POWERPC_EMULATED_OPS_H
#define _ASM_POWERPC_EMULATED_OPS_H
#include <asm/atomic.h>
#ifdef CONFIG_PPC_EMULATED_STATS
struct ppc_emulated_entry {
const char *name;
atomic_t val;
};
extern struct ppc_emulated {
#ifdef CONFIG_ALTIVEC
struct ppc_emulated_entry altivec;
#endif
struct ppc_emulated_entry dcba;
struct ppc_emulated_entry dcbz;
struct ppc_emulated_entry fp_pair;
struct ppc_emulated_entry isel;
struct ppc_emulated_entry mcrxr;
struct ppc_emulated_entry mfpvr;
struct ppc_emulated_entry multiple;
struct ppc_emulated_entry popcntb;
struct ppc_emulated_entry spe;
struct ppc_emulated_entry string;
struct ppc_emulated_entry unaligned;
#ifdef CONFIG_MATH_EMULATION
struct ppc_emulated_entry math;
#elif defined(CONFIG_8XX_MINIMAL_FPEMU)
struct ppc_emulated_entry 8xx;
#endif
#ifdef CONFIG_VSX
struct ppc_emulated_entry vsx;
#endif
} ppc_emulated;
extern u32 ppc_warn_emulated;
extern void ppc_warn_emulated_print(const char *type);
#define PPC_WARN_EMULATED(type) \
do { \
atomic_inc(&ppc_emulated.type.val); \
if (ppc_warn_emulated) \
ppc_warn_emulated_print(ppc_emulated.type.name); \
} while (0)
#else /* !CONFIG_PPC_EMULATED_STATS */
#define PPC_WARN_EMULATED(type) do { } while (0)
#endif /* !CONFIG_PPC_EMULATED_STATS */
#endif /* _ASM_POWERPC_EMULATED_OPS_H */
......@@ -24,6 +24,7 @@
#include <asm/system.h>
#include <asm/cache.h>
#include <asm/cputable.h>
#include <asm/emulated_ops.h>
struct aligninfo {
unsigned char len;
......@@ -730,8 +731,10 @@ int fix_alignment(struct pt_regs *regs)
areg = dsisr & 0x1f; /* register to update */
#ifdef CONFIG_SPE
if ((instr >> 26) == 0x4)
if ((instr >> 26) == 0x4) {
PPC_WARN_EMULATED(spe);
return emulate_spe(regs, reg, instr);
}
#endif
instr = (dsisr >> 10) & 0x7f;
......@@ -783,23 +786,28 @@ int fix_alignment(struct pt_regs *regs)
flags |= SPLT;
nb = 8;
}
PPC_WARN_EMULATED(vsx);
return emulate_vsx(addr, reg, areg, regs, flags, nb);
}
#endif
/* A size of 0 indicates an instruction we don't support, with
* the exception of DCBZ which is handled as a special case here
*/
if (instr == DCBZ)
if (instr == DCBZ) {
PPC_WARN_EMULATED(dcbz);
return emulate_dcbz(regs, addr);
}
if (unlikely(nb == 0))
return 0;
/* Load/Store Multiple instructions are handled in their own
* function
*/
if (flags & M)
if (flags & M) {
PPC_WARN_EMULATED(multiple);
return emulate_multiple(regs, addr, reg, nb,
flags, instr, swiz);
}
/* Verify the address of the operand */
if (unlikely(user_mode(regs) &&
......@@ -816,8 +824,12 @@ int fix_alignment(struct pt_regs *regs)
}
/* Special case for 16-byte FP loads and stores */
if (nb == 16)
if (nb == 16) {
PPC_WARN_EMULATED(fp_pair);
return emulate_fp_pair(addr, reg, flags);
}
PPC_WARN_EMULATED(unaligned);
/* If we are loading, get the data from user space, else
* get it from register values
......
......@@ -33,7 +33,9 @@
#include <linux/backlight.h>
#include <linux/bug.h>
#include <linux/kdebug.h>
#include <linux/debugfs.h>
#include <asm/emulated_ops.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/system.h>
......@@ -757,36 +759,44 @@ static int emulate_instruction(struct pt_regs *regs)
/* Emulate the mfspr rD, PVR. */
if ((instword & PPC_INST_MFSPR_PVR_MASK) == PPC_INST_MFSPR_PVR) {
PPC_WARN_EMULATED(mfpvr);
rd = (instword >> 21) & 0x1f;
regs->gpr[rd] = mfspr(SPRN_PVR);
return 0;
}
/* Emulating the dcba insn is just a no-op. */
if ((instword & PPC_INST_DCBA_MASK) == PPC_INST_DCBA)
if ((instword & PPC_INST_DCBA_MASK) == PPC_INST_DCBA) {
PPC_WARN_EMULATED(dcba);
return 0;
}
/* Emulate the mcrxr insn. */
if ((instword & PPC_INST_MCRXR_MASK) == PPC_INST_MCRXR) {
int shift = (instword >> 21) & 0x1c;
unsigned long msk = 0xf0000000UL >> shift;
PPC_WARN_EMULATED(mcrxr);
regs->ccr = (regs->ccr & ~msk) | ((regs->xer >> shift) & msk);
regs->xer &= ~0xf0000000UL;
return 0;
}
/* Emulate load/store string insn. */
if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING)
if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING) {
PPC_WARN_EMULATED(string);
return emulate_string_inst(regs, instword);
}
/* Emulate the popcntb (Population Count Bytes) instruction. */
if ((instword & PPC_INST_POPCNTB_MASK) == PPC_INST_POPCNTB) {
PPC_WARN_EMULATED(popcntb);
return emulate_popcntb_inst(regs, instword);
}
/* Emulate isel (Integer Select) instruction */
if ((instword & PPC_INST_ISEL_MASK) == PPC_INST_ISEL) {
PPC_WARN_EMULATED(isel);
return emulate_isel(regs, instword);
}
......@@ -984,6 +994,8 @@ void SoftwareEmulation(struct pt_regs *regs)
#ifdef CONFIG_MATH_EMULATION
errcode = do_mathemu(regs);
if (errcode >= 0)
PPC_WARN_EMULATED(math);
switch (errcode) {
case 0:
......@@ -1005,6 +1017,9 @@ void SoftwareEmulation(struct pt_regs *regs)
#elif defined(CONFIG_8XX_MINIMAL_FPEMU)
errcode = Soft_emulate_8xx(regs);
if (errcode >= 0)
PPC_WARN_EMULATED(8xx);
switch (errcode) {
case 0:
emulate_single_step(regs);
......@@ -1088,6 +1103,7 @@ void altivec_assist_exception(struct pt_regs *regs)
flush_altivec_to_thread(current);
PPC_WARN_EMULATED(altivec);
err = emulate_altivec(regs);
if (err == 0) {
regs->nip += 4; /* skip emulated instruction */
......@@ -1286,3 +1302,79 @@ void kernel_bad_stack(struct pt_regs *regs)
void __init trap_init(void)
{
}
#ifdef CONFIG_PPC_EMULATED_STATS
#define WARN_EMULATED_SETUP(type) .type = { .name = #type }
struct ppc_emulated ppc_emulated = {
#ifdef CONFIG_ALTIVEC
WARN_EMULATED_SETUP(altivec),
#endif
WARN_EMULATED_SETUP(dcba),
WARN_EMULATED_SETUP(dcbz),
WARN_EMULATED_SETUP(fp_pair),
WARN_EMULATED_SETUP(isel),
WARN_EMULATED_SETUP(mcrxr),
WARN_EMULATED_SETUP(mfpvr),
WARN_EMULATED_SETUP(multiple),
WARN_EMULATED_SETUP(popcntb),
WARN_EMULATED_SETUP(spe),
WARN_EMULATED_SETUP(string),
WARN_EMULATED_SETUP(unaligned),
#ifdef CONFIG_MATH_EMULATION
WARN_EMULATED_SETUP(math),
#elif defined(CONFIG_8XX_MINIMAL_FPEMU)
WARN_EMULATED_SETUP(8xx),
#endif
#ifdef CONFIG_VSX
WARN_EMULATED_SETUP(vsx),
#endif
};
u32 ppc_warn_emulated;
void ppc_warn_emulated_print(const char *type)
{
if (printk_ratelimit())
pr_warning("%s used emulated %s instruction\n", current->comm,
type);
}
static int __init ppc_warn_emulated_init(void)
{
struct dentry *dir, *d;
unsigned int i;
struct ppc_emulated_entry *entries = (void *)&ppc_emulated;
if (!powerpc_debugfs_root)
return -ENODEV;
dir = debugfs_create_dir("emulated_instructions",
powerpc_debugfs_root);
if (!dir)
return -ENOMEM;
d = debugfs_create_u32("do_warn", S_IRUGO | S_IWUSR, dir,
&ppc_warn_emulated);
if (!d)
goto fail;
for (i = 0; i < sizeof(ppc_emulated)/sizeof(*entries); i++) {
d = debugfs_create_u32(entries[i].name, S_IRUGO | S_IWUSR, dir,
(u32 *)&entries[i].val.counter);
if (!d)
goto fail;
}
return 0;
fail:
debugfs_remove_recursive(dir);
return -ENOMEM;
}
device_initcall(ppc_warn_emulated_init);
#endif /* CONFIG_PPC_EMULATED_STATS */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment