crash.c 7.4 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 50 51 52 53 54 55 56 57 58 59 60 61 62 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 88 89 90 91

static u32 *append_elf_note(u32 *buf, char *name, unsigned type, void *data,
							       size_t data_len)
{
	struct elf_note note;

	note.n_namesz = strlen(name) + 1;
	note.n_descsz = data_len;
	note.n_type   = type;
	memcpy(buf, &note, sizeof(note));
	buf += (sizeof(note) +3)/4;
	memcpy(buf, name, note.n_namesz);
	buf += (note.n_namesz + 3)/4;
	memcpy(buf, data, note.n_descsz);
	buf += (note.n_descsz + 3)/4;

	return buf;
}

static void final_note(u32 *buf)
{
	struct elf_note note;

	note.n_namesz = 0;
	note.n_descsz = 0;
	note.n_type   = 0;
	memcpy(buf, &note, sizeof(note));
}

static void crash_save_this_cpu(struct pt_regs *regs, int cpu)
{
	struct elf_prstatus prstatus;
	u32 *buf;

	if ((cpu < 0) || (cpu >= NR_CPUS))
		return;

	/* Using ELF notes here is opportunistic.
	 * I need a well defined structure format
	 * for the data I pass, and I need tags
	 * on the data to indicate what information I have
	 * squirrelled away.  ELF notes happen to provide
	 * all of that that no need to invent something new.
	 */
92 93 94 95
	buf = (u32*)per_cpu_ptr(crash_notes, cpu);
	if (!buf) 
		return;

96 97 98 99 100 101 102 103 104
	memset(&prstatus, 0, sizeof(prstatus));
	prstatus.pr_pid = current->pid;
	elf_core_copy_regs(&prstatus.pr_reg, regs);
	buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus,
			sizeof(prstatus));
	final_note(buf);
}

#ifdef CONFIG_SMP
105
static atomic_t enter_on_soft_reset = ATOMIC_INIT(0);
106 107 108 109 110 111 112 113 114

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

	if (!cpu_online(cpu))
		return;

	local_irq_disable();
115 116 117
	if (!cpu_isset(cpu, cpus_in_crash))
		crash_save_this_cpu(regs, cpu);
	cpu_set(cpu, cpus_in_crash);
118

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
	/*
	 * 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);
143 144

#ifdef CONFIG_PPC64
145
	kexec_smp_wait();
146 147 148 149
#else
	for (;;);	/* FIXME */
#endif

150 151 152
	/* NOTREACHED */
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166
/*
 * 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)
167 168 169
{
	unsigned int msecs;

170
	unsigned int ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */
171 172 173 174 175 176 177

	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
178
	 * respond.
179
	 * Delay of at least 10 seconds.
180
	 */
181
	printk(KERN_EMERG "Sending IPI to other cpus...\n");
182
	msecs = 10000;
183 184
	while ((cpus_weight(cpus_in_crash) < ncpus) && (--msecs > 0)) {
		cpu_relax();
185 186 187 188 189 190 191 192
		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.
193 194 195 196 197 198 199 200 201 202 203 204 205 206
	 * 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.
207
	 */
208 209
	if (cpu_isset(cpu, cpus_in_sr))
		crash_soft_reset_check(cpu);
210 211
	/* Leave the IPI callback set */
}
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255

/*
 * 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);
}

256
#else
257
static void crash_kexec_prepare_cpus(int cpu)
258 259 260 261 262 263 264
{
	/*
	 * move the secondarys to us so that we can copy
	 * the new kernel 0-0x100 safely
	 *
	 * do this if kexec in setup.c ?
	 */
265
#ifdef CONFIG_PPC64
266
	smp_release_cpus();
267 268 269
#else
	/* FIXME */
#endif
270 271
}

272 273 274 275
void crash_kexec_secondary(struct pt_regs *regs)
{
	cpus_in_sr = CPU_MASK_NONE;
}
276 277 278 279
#endif

void default_machine_crash_shutdown(struct pt_regs *regs)
{
280 281
	unsigned int irq;

282 283
	/*
	 * This function is only called after the system
L
Lee Revell 已提交
284
	 * has panicked or is otherwise in a critical state.
285 286 287 288 289 290 291 292 293
	 * 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.
	 */
	local_irq_disable();

294
	for_each_irq(irq) {
295
		struct irq_desc *desc = irq_desc + irq;
296 297

		if (desc->status & IRQ_INPROGRESS)
298
			desc->chip->eoi(irq);
299 300

		if (!(desc->status & IRQ_DISABLED))
301
			desc->chip->disable(irq);
302 303
	}

304 305 306 307 308
	/*
	 * 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();
309
	crash_save_this_cpu(regs, crashing_cpu);
310 311 312 313
	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);
314
}