powerpc/xmon: Add support for running a command on all cpus in xmon
authorNaveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Tue, 1 Jun 2021 07:48:01 +0000 (13:18 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 25 Jun 2021 04:47:19 +0000 (14:47 +1000)
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
arch/powerpc/xmon/xmon.c

index dbc3848..12dcfb5 100644 (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;
+
+       return xmon_switch_cpu(cpu);
+}
 #else
+static int cpu_cmd(void)
+{
        return 0;
-#endif /* CONFIG_SMP */
 }
+#endif /* CONFIG_SMP */
 
 static unsigned short fcstab[256] = {
        0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,