crash.c 6.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * Architecture specific (PPC64) functions for kexec based crash dumps.
 *
 * Copyright (C) 2005, IBM Corp.
 *
 * Created by: Haren Myneni
 *
 * This source code is licensed under the GNU General Public License,
 * Version 2.  See the file COPYING for more details.
 *
 */

#undef DEBUG

#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/reboot.h>
#include <linux/kexec.h>
#include <linux/bootmem.h>
#include <linux/crash_dump.h>
#include <linux/delay.h>
#include <linux/elf.h>
#include <linux/elfcore.h>
#include <linux/init.h>
25
#include <linux/irq.h>
26
#include <linux/types.h>
27
#include <linux/irq.h>
28 29 30

#include <asm/processor.h>
#include <asm/machdep.h>
31
#include <asm/kexec.h>
32 33 34
#include <asm/kdump.h>
#include <asm/lmb.h>
#include <asm/firmware.h>
35
#include <asm/smp.h>
36 37 38 39 40 41 42 43 44 45

#ifdef DEBUG
#include <asm/udbg.h>
#define DBG(fmt...) udbg_printf(fmt)
#else
#define DBG(fmt...)
#endif

/* This keeps a track of which one is crashing cpu. */
int crashing_cpu = -1;
46
static cpumask_t cpus_in_crash = CPU_MASK_NONE;
47
cpumask_t cpus_in_sr = CPU_MASK_NONE;
48 49

#ifdef CONFIG_SMP
50
static atomic_t enter_on_soft_reset = ATOMIC_INIT(0);
51 52 53 54 55 56 57 58

void crash_ipi_callback(struct pt_regs *regs)
{
	int cpu = smp_processor_id();

	if (!cpu_online(cpu))
		return;

59
	hard_irq_disable();
60
	if (!cpu_isset(cpu, cpus_in_crash))
61
		crash_save_cpu(regs, cpu);
62
	cpu_set(cpu, cpus_in_crash);
63

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
	/*
	 * Entered via soft-reset - could be the kdump
	 * process is invoked using soft-reset or user activated
	 * it if some CPU did not respond to an IPI.
	 * For soft-reset, the secondary CPU can enter this func
	 * twice. 1 - using IPI, and 2. soft-reset.
	 * Tell the kexec CPU that entered via soft-reset and ready
	 * to go down.
	 */
	if (cpu_isset(cpu, cpus_in_sr)) {
		cpu_clear(cpu, cpus_in_sr);
		atomic_inc(&enter_on_soft_reset);
	}

	/*
	 * Starting the kdump boot.
	 * This barrier is needed to make sure that all CPUs are stopped.
	 * If not, soft-reset will be invoked to bring other CPUs.
	 */
	while (!cpu_isset(crashing_cpu, cpus_in_crash))
		cpu_relax();

	if (ppc_md.kexec_cpu_down)
		ppc_md.kexec_cpu_down(1, 1);
88 89

#ifdef CONFIG_PPC64
90
	kexec_smp_wait();
91 92 93 94
#else
	for (;;);	/* FIXME */
#endif

95 96 97
	/* NOTREACHED */
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111
/*
 * Wait until all CPUs are entered via soft-reset.
 */
static void crash_soft_reset_check(int cpu)
{
	unsigned int ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */

	cpu_clear(cpu, cpus_in_sr);
	while (atomic_read(&enter_on_soft_reset) != ncpus)
		cpu_relax();
}


static void crash_kexec_prepare_cpus(int cpu)
112 113 114
{
	unsigned int msecs;

115
	unsigned int ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */
116 117 118 119 120 121 122

	crash_send_ipi(crash_ipi_callback);
	smp_wmb();

	/*
	 * FIXME: Until we will have the way to stop other CPUSs reliabally,
	 * the crash CPU will send an IPI and wait for other CPUs to
123
	 * respond.
124
	 * Delay of at least 10 seconds.
125
	 */
126
	printk(KERN_EMERG "Sending IPI to other cpus...\n");
127
	msecs = 10000;
128 129
	while ((cpus_weight(cpus_in_crash) < ncpus) && (--msecs > 0)) {
		cpu_relax();
130 131 132 133 134 135 136 137
		mdelay(1);
	}

	/* Would it be better to replace the trap vector here? */

	/*
	 * FIXME: In case if we do not get all CPUs, one possibility: ask the
	 * user to do soft reset such that we get all.
138 139 140 141 142 143 144 145 146 147 148 149 150 151
	 * Soft-reset will be used until better mechanism is implemented.
	 */
	if (cpus_weight(cpus_in_crash) < ncpus) {
		printk(KERN_EMERG "done waiting: %d cpu(s) not responding\n",
			ncpus - cpus_weight(cpus_in_crash));
		printk(KERN_EMERG "Activate soft-reset to stop other cpu(s)\n");
		cpus_in_sr = CPU_MASK_NONE;
		atomic_set(&enter_on_soft_reset, 0);
		while (cpus_weight(cpus_in_crash) < ncpus)
			cpu_relax();
	}
	/*
	 * Make sure all CPUs are entered via soft-reset if the kdump is
	 * invoked using soft-reset.
152
	 */
153 154
	if (cpu_isset(cpu, cpus_in_sr))
		crash_soft_reset_check(cpu);
155 156
	/* Leave the IPI callback set */
}
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

/*
 * This function will be called by secondary cpus or by kexec cpu
 * if soft-reset is activated to stop some CPUs.
 */
void crash_kexec_secondary(struct pt_regs *regs)
{
	int cpu = smp_processor_id();
	unsigned long flags;
	int msecs = 5;

	local_irq_save(flags);
	/* Wait 5ms if the kexec CPU is not entered yet. */
	while (crashing_cpu < 0) {
		if (--msecs < 0) {
			/*
			 * Either kdump image is not loaded or
			 * kdump process is not started - Probably xmon
			 * exited using 'x'(exit and recover) or
			 * kexec_should_crash() failed for all running tasks.
			 */
			cpu_clear(cpu, cpus_in_sr);
			local_irq_restore(flags);
			return;
		}
		mdelay(1);
		cpu_relax();
	}
	if (cpu == crashing_cpu) {
		/*
		 * Panic CPU will enter this func only via soft-reset.
		 * Wait until all secondary CPUs entered and
		 * then start kexec boot.
		 */
		crash_soft_reset_check(cpu);
		cpu_set(crashing_cpu, cpus_in_crash);
		if (ppc_md.kexec_cpu_down)
			ppc_md.kexec_cpu_down(1, 0);
		machine_kexec(kexec_crash_image);
		/* NOTREACHED */
	}
	crash_ipi_callback(regs);
}

201
#else
202
static void crash_kexec_prepare_cpus(int cpu)
203 204 205 206 207 208 209
{
	/*
	 * move the secondarys to us so that we can copy
	 * the new kernel 0-0x100 safely
	 *
	 * do this if kexec in setup.c ?
	 */
210
#ifdef CONFIG_PPC64
211
	smp_release_cpus();
212 213 214
#else
	/* FIXME */
#endif
215 216
}

217 218 219 220
void crash_kexec_secondary(struct pt_regs *regs)
{
	cpus_in_sr = CPU_MASK_NONE;
}
221 222 223 224
#endif

void default_machine_crash_shutdown(struct pt_regs *regs)
{
225 226
	unsigned int irq;

227 228
	/*
	 * This function is only called after the system
L
Lee Revell 已提交
229
	 * has panicked or is otherwise in a critical state.
230 231 232 233 234 235 236
	 * The minimum amount of code to allow a kexec'd kernel
	 * to run successfully needs to happen here.
	 *
	 * In practice this means stopping other cpus in
	 * an SMP system.
	 * The kernel is broken so disable interrupts.
	 */
237
	hard_irq_disable();
238

239
	for_each_irq(irq) {
240
		struct irq_desc *desc = irq_desc + irq;
241 242

		if (desc->status & IRQ_INPROGRESS)
243
			desc->chip->eoi(irq);
244 245

		if (!(desc->status & IRQ_DISABLED))
246
			desc->chip->disable(irq);
247 248
	}

249 250 251 252 253
	/*
	 * Make a note of crashing cpu. Will be used in machine_kexec
	 * such that another IPI will not be sent.
	 */
	crashing_cpu = smp_processor_id();
254
	crash_save_cpu(regs, crashing_cpu);
255 256 257 258
	crash_kexec_prepare_cpus(crashing_cpu);
	cpu_set(crashing_cpu, cpus_in_crash);
	if (ppc_md.kexec_cpu_down)
		ppc_md.kexec_cpu_down(1, 0);
259
}