Skip to content

Commit

Permalink
powerpc: Keep track of emulated instructions
Browse files Browse the repository at this point in the history
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: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
  • Loading branch information
Geert Uytterhoeven authored and Benjamin Herrenschmidt committed May 21, 2009
1 parent f312deb commit 80947e7
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 6 deletions.
13 changes: 13 additions & 0 deletions arch/powerpc/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
73 changes: 73 additions & 0 deletions arch/powerpc/include/asm/emulated_ops.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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 */
20 changes: 16 additions & 4 deletions arch/powerpc/kernel/align.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) &&
Expand All @@ -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
Expand Down
96 changes: 94 additions & 2 deletions arch/powerpc/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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:
Expand All @@ -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);
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */

0 comments on commit 80947e7

Please sign in to comment.