Commit b8ee3e6d authored by Naveen N. Rao's avatar Naveen N. Rao Committed by Michael Ellerman
Browse files

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: default avatarNaveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210601074801.617363-1-naveen.n.rao@linux.vnet.ibm.com
parent dcf57af2
Loading
Loading
Loading
Loading
+126 −22
Original line number Diff line number Diff line
@@ -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 xmon_switch_cpu(cpu);
}
	return 1;
#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,