Skip to content

Commit

Permalink
MIPS: Collect FPU emulator statistics per-CPU.
Browse files Browse the repository at this point in the history
On SMP systems, the collection of statistics can cause cache line
bouncing in the lines associated with the counters.  Also there are
races incrementing the counters on multiple CPUs.

To fix both problems, we collect the statistics in per-CPU variables,
and add them up in the debugfs read operation.

As a test I ran the LTP float_bessel test on a 12 CPU Octeon system.

Without CONFIG_DEBUG_FS :             2602 seconds.
With CONFIG_DEBUG_FS:                 2640 seconds.
With non-cpu-local atomic statistics: 14569 seconds.

Signed-off-by: David Daney <ddaney@caviumnetworks.com>
Cc: linux-mips@linux-mips.org
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
  • Loading branch information
David Daney authored and Ralf Baechle committed Dec 17, 2009
1 parent 32028f1 commit b6ee75e
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 50 deletions.
24 changes: 17 additions & 7 deletions arch/mips/include/asm/fpu_emulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,27 @@

#include <asm/break.h>
#include <asm/inst.h>
#include <asm/local.h>

#ifdef CONFIG_DEBUG_FS

struct mips_fpu_emulator_stats {
unsigned int emulated;
unsigned int loads;
unsigned int stores;
unsigned int cp1ops;
unsigned int cp1xops;
unsigned int errors;
local_t emulated;
local_t loads;
local_t stores;
local_t cp1ops;
local_t cp1xops;
local_t errors;
};

extern struct mips_fpu_emulator_stats fpuemustats;
DECLARE_PER_CPU(struct mips_fpu_emulator_stats, fpuemustats);

#define MIPS_FPU_EMU_INC_STATS(M) \
cpu_local_wrap(__local_inc(&__get_cpu_var(fpuemustats).M))

#else
#define MIPS_FPU_EMU_INC_STATS(M) do { } while (0)
#endif /* CONFIG_DEBUG_FS */

extern int mips_dsemul(struct pt_regs *regs, mips_instruction ir,
unsigned long cpc);
Expand Down
102 changes: 61 additions & 41 deletions arch/mips/math-emu/cp1emu.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* better performance by compiling with -msoft-float!
*/
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/debugfs.h>

#include <asm/inst.h>
Expand Down Expand Up @@ -68,7 +69,9 @@ static int fpux_emu(struct pt_regs *,

/* Further private data for which no space exists in mips_fpu_struct */

struct mips_fpu_emulator_stats fpuemustats;
#ifdef CONFIG_DEBUG_FS
DEFINE_PER_CPU(struct mips_fpu_emulator_stats, fpuemustats);
#endif

/* Control registers */

Expand Down Expand Up @@ -209,7 +212,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
unsigned int cond;

if (get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}

Expand Down Expand Up @@ -240,7 +243,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
return SIGILL;
}
if (get_user(ir, (mips_instruction __user *) emulpc)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
/* __compute_return_epc() will have updated cp0_epc */
Expand All @@ -253,16 +256,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
}

emul:
fpuemustats.emulated++;
MIPS_FPU_EMU_INC_STATS(emulated);
switch (MIPSInst_OPCODE(ir)) {
case ldc1_op:{
u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
MIPSInst_SIMM(ir));
u64 val;

fpuemustats.loads++;
MIPS_FPU_EMU_INC_STATS(loads);
if (get_user(val, va)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
DITOREG(val, MIPSInst_RT(ir));
Expand All @@ -274,10 +277,10 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
MIPSInst_SIMM(ir));
u64 val;

fpuemustats.stores++;
MIPS_FPU_EMU_INC_STATS(stores);
DIFROMREG(val, MIPSInst_RT(ir));
if (put_user(val, va)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
break;
Expand All @@ -288,9 +291,9 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
MIPSInst_SIMM(ir));
u32 val;

fpuemustats.loads++;
MIPS_FPU_EMU_INC_STATS(loads);
if (get_user(val, va)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
SITOREG(val, MIPSInst_RT(ir));
Expand All @@ -302,10 +305,10 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
MIPSInst_SIMM(ir));
u32 val;

fpuemustats.stores++;
MIPS_FPU_EMU_INC_STATS(stores);
SIFROMREG(val, MIPSInst_RT(ir));
if (put_user(val, va)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
break;
Expand Down Expand Up @@ -429,7 +432,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)

if (get_user(ir,
(mips_instruction __user *) xcp->cp0_epc)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}

Expand Down Expand Up @@ -595,7 +598,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
{
unsigned rcsr = 0; /* resulting csr */

fpuemustats.cp1xops++;
MIPS_FPU_EMU_INC_STATS(cp1xops);

switch (MIPSInst_FMA_FFMT(ir)) {
case s_fmt:{ /* 0 */
Expand All @@ -610,9 +613,9 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
xcp->regs[MIPSInst_FT(ir)]);

fpuemustats.loads++;
MIPS_FPU_EMU_INC_STATS(loads);
if (get_user(val, va)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
SITOREG(val, MIPSInst_FD(ir));
Expand All @@ -622,11 +625,11 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
xcp->regs[MIPSInst_FT(ir)]);

fpuemustats.stores++;
MIPS_FPU_EMU_INC_STATS(stores);

SIFROMREG(val, MIPSInst_FS(ir));
if (put_user(val, va)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
break;
Expand Down Expand Up @@ -687,9 +690,9 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
xcp->regs[MIPSInst_FT(ir)]);

fpuemustats.loads++;
MIPS_FPU_EMU_INC_STATS(loads);
if (get_user(val, va)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
DITOREG(val, MIPSInst_FD(ir));
Expand All @@ -699,10 +702,10 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
xcp->regs[MIPSInst_FT(ir)]);

fpuemustats.stores++;
MIPS_FPU_EMU_INC_STATS(stores);
DIFROMREG(val, MIPSInst_FS(ir));
if (put_user(val, va)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
break;
Expand Down Expand Up @@ -769,7 +772,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
#endif
} rv; /* resulting value */

fpuemustats.cp1ops++;
MIPS_FPU_EMU_INC_STATS(cp1ops);
switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
case s_fmt:{ /* 0 */
union {
Expand Down Expand Up @@ -1240,7 +1243,7 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
prevepc = xcp->cp0_epc;

if (get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}
if (insn == 0)
Expand Down Expand Up @@ -1276,33 +1279,50 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
}

#ifdef CONFIG_DEBUG_FS

static int fpuemu_stat_get(void *data, u64 *val)
{
int cpu;
unsigned long sum = 0;
for_each_online_cpu(cpu) {
struct mips_fpu_emulator_stats *ps;
local_t *pv;
ps = &per_cpu(fpuemustats, cpu);
pv = (void *)ps + (unsigned long)data;
sum += local_read(pv);
}
*val = sum;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_fpuemu_stat, fpuemu_stat_get, NULL, "%llu\n");

extern struct dentry *mips_debugfs_dir;
static int __init debugfs_fpuemu(void)
{
struct dentry *d, *dir;
int i;
static struct {
const char *name;
unsigned int *v;
} vars[] __initdata = {
{ "emulated", &fpuemustats.emulated },
{ "loads", &fpuemustats.loads },
{ "stores", &fpuemustats.stores },
{ "cp1ops", &fpuemustats.cp1ops },
{ "cp1xops", &fpuemustats.cp1xops },
{ "errors", &fpuemustats.errors },
};

if (!mips_debugfs_dir)
return -ENODEV;
dir = debugfs_create_dir("fpuemustats", mips_debugfs_dir);
if (!dir)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(vars); i++) {
d = debugfs_create_u32(vars[i].name, S_IRUGO, dir, vars[i].v);
if (!d)
return -ENOMEM;
}

#define FPU_STAT_CREATE(M) \
do { \
d = debugfs_create_file(#M , S_IRUGO, dir, \
(void *)offsetof(struct mips_fpu_emulator_stats, M), \
&fops_fpuemu_stat); \
if (!d) \
return -ENOMEM; \
} while (0)

FPU_STAT_CREATE(emulated);
FPU_STAT_CREATE(loads);
FPU_STAT_CREATE(stores);
FPU_STAT_CREATE(cp1ops);
FPU_STAT_CREATE(cp1xops);
FPU_STAT_CREATE(errors);

return 0;
}
__initcall(debugfs_fpuemu);
Expand Down
4 changes: 2 additions & 2 deletions arch/mips/math-emu/dsemul.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
err |= __put_user(cpc, &fr->epc);

if (unlikely(err)) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return SIGBUS;
}

Expand Down Expand Up @@ -136,7 +136,7 @@ int do_dsemulret(struct pt_regs *xcp)
err |= __get_user(cookie, &fr->cookie);

if (unlikely(err || (insn != BREAK_MATH) || (cookie != BD_COOKIE))) {
fpuemustats.errors++;
MIPS_FPU_EMU_INC_STATS(errors);
return 0;
}

Expand Down

0 comments on commit b6ee75e

Please sign in to comment.