powerpc/xmon: Add support for running a command on all cpus in xmon
It is sometimes desirable to run a command on all cpus in xmon. A typical scenario is to obtain the backtrace from all cpus in xmon if there is a soft lockup. Add rudimentary support for the same. The command to be run on all cpus should be prefixed with 'c#'. As an example, 'c#t' will run 't' command and produce a backtrace on all cpus in xmon. Since many xmon commands are not sensible for running in this manner, we only allow a predefined list of commands -- 'r', 'S' and 't' for now. Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20210601074801.617363-1-naveen.n.rao@linux.vnet.ibm.com
This commit is contained in:
parent
dcf57af201
commit
b8ee3e6d6c
@ -70,6 +70,9 @@ static cpumask_t cpus_in_xmon = CPU_MASK_NONE;
|
||||
static unsigned long xmon_taken = 1;
|
||||
static int xmon_owner;
|
||||
static int xmon_gate;
|
||||
static int xmon_batch;
|
||||
static unsigned long xmon_batch_start_cpu;
|
||||
static cpumask_t xmon_batch_cpus = CPU_MASK_NONE;
|
||||
#else
|
||||
#define xmon_owner 0
|
||||
#endif /* CONFIG_SMP */
|
||||
@ -133,6 +136,12 @@ static void prdump(unsigned long, long);
|
||||
static int ppc_inst_dump(unsigned long, long, int);
|
||||
static void dump_log_buf(void);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static int xmon_switch_cpu(unsigned long);
|
||||
static int xmon_batch_next_cpu(void);
|
||||
static int batch_cmds(struct pt_regs *);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PPC_POWERNV
|
||||
static void dump_opal_msglog(void);
|
||||
#else
|
||||
@ -216,7 +225,8 @@ Commands:\n\
|
||||
#ifdef CONFIG_SMP
|
||||
"\
|
||||
c print cpus stopped in xmon\n\
|
||||
c# try to switch to cpu number h (in hex)\n"
|
||||
c# try to switch to cpu number h (in hex)\n\
|
||||
c# $ run command '$' (one of 'r','S' or 't') on all cpus in xmon\n"
|
||||
#endif
|
||||
"\
|
||||
C checksum\n\
|
||||
@ -644,7 +654,12 @@ static int xmon_core(struct pt_regs *regs, int fromipi)
|
||||
spin_cpu_relax();
|
||||
touch_nmi_watchdog();
|
||||
} else {
|
||||
if (!locked_down)
|
||||
cmd = 1;
|
||||
#ifdef CONFIG_SMP
|
||||
if (xmon_batch)
|
||||
cmd = batch_cmds(regs);
|
||||
#endif
|
||||
if (!locked_down && cmd)
|
||||
cmd = cmds(regs);
|
||||
if (locked_down || cmd != 0) {
|
||||
/* exiting xmon */
|
||||
@ -1243,11 +1258,112 @@ static void bootcmds(void)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static int xmon_switch_cpu(unsigned long cpu)
|
||||
{
|
||||
int timeout;
|
||||
|
||||
xmon_taken = 0;
|
||||
mb();
|
||||
xmon_owner = cpu;
|
||||
timeout = 10000000;
|
||||
while (!xmon_taken) {
|
||||
if (--timeout == 0) {
|
||||
if (test_and_set_bit(0, &xmon_taken))
|
||||
break;
|
||||
/* take control back */
|
||||
mb();
|
||||
xmon_owner = smp_processor_id();
|
||||
printf("cpu 0x%lx didn't take control\n", cpu);
|
||||
return 0;
|
||||
}
|
||||
barrier();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int xmon_batch_next_cpu(void)
|
||||
{
|
||||
unsigned long cpu;
|
||||
|
||||
while (!cpumask_empty(&xmon_batch_cpus)) {
|
||||
cpu = cpumask_next_wrap(smp_processor_id(), &xmon_batch_cpus,
|
||||
xmon_batch_start_cpu, true);
|
||||
if (cpu == nr_cpumask_bits)
|
||||
break;
|
||||
if (xmon_batch_start_cpu == -1)
|
||||
xmon_batch_start_cpu = cpu;
|
||||
if (xmon_switch_cpu(cpu))
|
||||
return 0;
|
||||
cpumask_clear_cpu(cpu, &xmon_batch_cpus);
|
||||
}
|
||||
|
||||
xmon_batch = 0;
|
||||
printf("%x:mon> \n", smp_processor_id());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int batch_cmds(struct pt_regs *excp)
|
||||
{
|
||||
int cmd;
|
||||
|
||||
/* simulate command entry */
|
||||
cmd = xmon_batch;
|
||||
termch = '\n';
|
||||
|
||||
last_cmd = NULL;
|
||||
xmon_regs = excp;
|
||||
|
||||
printf("%x:", smp_processor_id());
|
||||
printf("mon> ");
|
||||
printf("%c\n", (char)cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case 'r':
|
||||
prregs(excp); /* print regs */
|
||||
break;
|
||||
case 'S':
|
||||
super_regs();
|
||||
break;
|
||||
case 't':
|
||||
backtrace(excp);
|
||||
break;
|
||||
}
|
||||
|
||||
cpumask_clear_cpu(smp_processor_id(), &xmon_batch_cpus);
|
||||
|
||||
return xmon_batch_next_cpu();
|
||||
}
|
||||
|
||||
static int cpu_cmd(void)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
unsigned long cpu, first_cpu, last_cpu;
|
||||
int timeout;
|
||||
|
||||
cpu = skipbl();
|
||||
if (cpu == '#') {
|
||||
xmon_batch = skipbl();
|
||||
if (xmon_batch) {
|
||||
switch (xmon_batch) {
|
||||
case 'r':
|
||||
case 'S':
|
||||
case 't':
|
||||
cpumask_copy(&xmon_batch_cpus, &cpus_in_xmon);
|
||||
if (cpumask_weight(&xmon_batch_cpus) <= 1) {
|
||||
printf("There are no other cpus in xmon\n");
|
||||
break;
|
||||
}
|
||||
xmon_batch_start_cpu = -1;
|
||||
if (!xmon_batch_next_cpu())
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
printf("c# only supports 'r', 'S' and 't' commands\n");
|
||||
}
|
||||
xmon_batch = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
termch = cpu;
|
||||
|
||||
if (!scanhex(&cpu)) {
|
||||
/* print cpus waiting or in xmon */
|
||||
@ -1279,27 +1395,15 @@ static int cpu_cmd(void)
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
xmon_taken = 0;
|
||||
mb();
|
||||
xmon_owner = cpu;
|
||||
timeout = 10000000;
|
||||
while (!xmon_taken) {
|
||||
if (--timeout == 0) {
|
||||
if (test_and_set_bit(0, &xmon_taken))
|
||||
break;
|
||||
/* take control back */
|
||||
mb();
|
||||
xmon_owner = smp_processor_id();
|
||||
printf("cpu 0x%lx didn't take control\n", cpu);
|
||||
return 0;
|
||||
}
|
||||
barrier();
|
||||
}
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
return xmon_switch_cpu(cpu);
|
||||
}
|
||||
#else
|
||||
static int cpu_cmd(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
static unsigned short fcstab[256] = {
|
||||
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
|
||||
|
Loading…
Reference in New Issue
Block a user