From: Michael Ellerman This patch adds support for stopping, and restarting, spus from xmon. We use the spu master runcntl bit to stop execution, this is apparently the "right" way to control spu execution and spufs will be changed in the future to use this bit. Testing has shown that to restart execution we have to turn the master runcntl bit on and also rewrite the spu runcntl bit, even if it is already set to 1 (running). Stopping spus is triggered by the xmon command 'ss' - "spus stop" perhaps. Restarting them is triggered via 'sr'. Restart doesn't start execution on spus unless they were running prior to being stopped by xmon. Walking the spu->full_list in xmon after a panic, would mean corruption of any spu struct would make all the others inaccessible. To avoid this, and also to make the next patch easier, we cache pointers to all spus during boot. We attempt to catch and recover from errors while stopping and restarting the spus, but as with most xmon functionality there are no guarantees that performing these operations won't crash xmon itself. Signed-off-by: Michael Ellerman Signed-off-by: Arnd Bergmann --- arch/powerpc/platforms/cell/spu_base.c | 4 arch/powerpc/xmon/xmon.c | 142 ++++++++++++++++++++++++++++++++- include/asm-powerpc/xmon.h | 2 3 files changed, 146 insertions(+), 2 deletions(-) Index: linux-2.6/arch/powerpc/platforms/cell/spu_base.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spu_base.c +++ linux-2.6/arch/powerpc/platforms/cell/spu_base.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "interrupt.h" @@ -943,6 +944,9 @@ static int __init init_spu_base(void) break; } } + + xmon_register_spus(&spu_full_list); + return ret; } module_init(init_spu_base); Index: linux-2.6/arch/powerpc/xmon/xmon.c =================================================================== --- linux-2.6.orig/arch/powerpc/xmon/xmon.c +++ linux-2.6/arch/powerpc/xmon/xmon.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #ifdef CONFIG_PPC64 #include @@ -147,6 +149,8 @@ static void xmon_print_symbol(unsigned l const char *after); static const char *getvecname(unsigned long vec); +static int do_spu_cmd(void); + int xmon_no_auto_backtrace; extern int print_insn_powerpc(unsigned long, unsigned long, int); @@ -209,8 +213,12 @@ Commands:\n\ mi show information about memory allocation\n\ p call a procedure\n\ r print registers\n\ - s single step\n\ - S print special registers\n\ + s single step\n" +#ifdef CONFIG_PPC_CELL +" ss stop execution on all spus\n\ + sr restore execution on stopped spus\n" +#endif +" S print special registers\n\ t print backtrace\n\ x exit monitor and recover\n\ X exit monitor and dont recover\n" @@ -518,6 +526,7 @@ int xmon(struct pt_regs *excp) xmon_save_regs(®s); excp = ®s; } + return xmon_core(excp, 0); } EXPORT_SYMBOL(xmon); @@ -809,6 +818,8 @@ cmds(struct pt_regs *excp) cacheflush(); break; case 's': + if (do_spu_cmd() == 0) + break; if (do_step(excp)) return cmd; break; @@ -2630,3 +2641,130 @@ void __init xmon_setup(void) if (xmon_early) debugger(NULL); } + +#ifdef CONFIG_PPC_CELL + +struct spu_info { + struct spu *spu; + u64 saved_mfc_sr1_RW; + u32 saved_spu_runcntl_RW; + u8 stopped_ok; +}; + +#define XMON_NUM_SPUS 16 /* Enough for current hardware */ + +static struct spu_info spu_info[XMON_NUM_SPUS]; + +void xmon_register_spus(struct list_head *list) +{ + struct spu *spu; + + list_for_each_entry(spu, list, full_list) { + if (spu->number >= XMON_NUM_SPUS) { + WARN_ON(1); + continue; + } + + spu_info[spu->number].spu = spu; + spu_info[spu->number].stopped_ok = 0; + } +} + +static void stop_spus(void) +{ + struct spu *spu; + int i; + u64 tmp; + + for (i = 0; i < XMON_NUM_SPUS; i++) { + if (!spu_info[i].spu) + continue; + + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + + spu = spu_info[i].spu; + + spu_info[i].saved_spu_runcntl_RW = + in_be32(&spu->problem->spu_runcntl_RW); + + tmp = spu_mfc_sr1_get(spu); + spu_info[i].saved_mfc_sr1_RW = tmp; + + tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK; + spu_mfc_sr1_set(spu, tmp); + + sync(); + __delay(200); + + spu_info[i].stopped_ok = 1; + printf("Stopped spu %.2d\n", i); + } else { + catch_memory_errors = 0; + printf("*** Error stopping spu %.2d\n", i); + } + catch_memory_errors = 0; + } +} + +static void restart_spus(void) +{ + struct spu *spu; + int i; + + for (i = 0; i < XMON_NUM_SPUS; i++) { + if (!spu_info[i].spu) + continue; + + if (!spu_info[i].stopped_ok) { + printf("*** Error, spu %d was not successfully stopped" + ", not restarting\n", i); + continue; + } + + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + + spu = spu_info[i].spu; + spu_mfc_sr1_set(spu, spu_info[i].saved_mfc_sr1_RW); + out_be32(&spu->problem->spu_runcntl_RW, + spu_info[i].saved_spu_runcntl_RW); + + sync(); + __delay(200); + + printf("Restarted spu %.2d\n", i); + } else { + catch_memory_errors = 0; + printf("*** Error restarting spu %.2d\n", i); + } + catch_memory_errors = 0; + } +} + +static int do_spu_cmd(void) +{ + int cmd; + + cmd = inchar(); + switch (cmd) { + case 's': + stop_spus(); + break; + case 'r': + restart_spus(); + break; + default: + return -1; + } + + return 0; +} +#else /* ! CONFIG_PPC_CELL */ +static int do_spu_cmd(void) +{ + return -1; +} +#endif Index: linux-2.6/include/asm-powerpc/xmon.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/xmon.h +++ linux-2.6/include/asm-powerpc/xmon.h @@ -14,8 +14,10 @@ #ifdef CONFIG_XMON extern void xmon_setup(void); +extern void xmon_register_spus(struct list_head *list); #else static inline void xmon_setup(void) { }; +static inline void xmon_register_spus(struct list_head *list) { }; #endif #endif /* __KERNEL __ */ -- - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/