|
@@ -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>
|
|
@@ -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 */
|
|
|
|
|
@@ -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;
|
|
|
}
|
|
|
|
|
@@ -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 */
|
|
@@ -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));
|
|
@@ -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;
|
|
@@ -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));
|
|
@@ -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;
|
|
@@ -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;
|
|
|
}
|
|
|
|
|
@@ -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 */
|
|
@@ -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));
|
|
@@ -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;
|
|
@@ -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));
|
|
@@ -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;
|
|
@@ -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 {
|
|
@@ -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)
|
|
@@ -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);
|