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:
Naveen N. Rao 2021-06-01 13:18:01 +05:30 committed by Michael Ellerman
parent dcf57af201
commit b8ee3e6d6c

View file

@ -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,