crash.c 6.9 KB
Newer Older
Z
Zou Nan hai 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * arch/ia64/kernel/crash.c
 *
 * Architecture specific (ia64) functions for kexec based crash dumps.
 *
 * Created by: Khalid Aziz <khalid.aziz@hp.com>
 * Copyright (C) 2005 Hewlett-Packard Development Company, L.P.
 * Copyright (C) 2005 Intel Corp	Zou Nan hai <nanhai.zou@intel.com>
 *
 */
#include <linux/smp.h>
#include <linux/delay.h>
#include <linux/crash_dump.h>
#include <linux/bootmem.h>
#include <linux/kexec.h>
#include <linux/elfcore.h>
#include <linux/sysctl.h>
#include <linux/init.h>
19
#include <linux/kdebug.h>
Z
Zou Nan hai 已提交
20 21 22 23

#include <asm/mca.h>

int kdump_status[NR_CPUS];
S
Simon Horman 已提交
24
static atomic_t kdump_cpu_frozen;
Z
Zou Nan hai 已提交
25
atomic_t kdump_in_progress;
26
static int kdump_freeze_monarch;
S
Simon Horman 已提交
27
static int kdump_on_init = 1;
28
static int kdump_on_fatal_mca = 1;
Z
Zou Nan hai 已提交
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

static inline Elf64_Word
*append_elf_note(Elf64_Word *buf, char *name, unsigned type, void *data,
		size_t data_len)
{
	struct elf_note *note = (struct elf_note *)buf;
	note->n_namesz = strlen(name) + 1;
	note->n_descsz = data_len;
	note->n_type   = type;
	buf += (sizeof(*note) + 3)/4;
	memcpy(buf, name, note->n_namesz);
	buf += (note->n_namesz + 3)/4;
	memcpy(buf, data, data_len);
	buf += (data_len + 3)/4;
	return buf;
}

static void
final_note(void *buf)
{
	memset(buf, 0, sizeof(struct elf_note));
}

extern void ia64_dump_cpu_regs(void *);

static DEFINE_PER_CPU(struct elf_prstatus, elf_prstatus);

void
A
Al Viro 已提交
57
crash_save_this_cpu(void)
Z
Zou Nan hai 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
{
	void *buf;
	unsigned long cfm, sof, sol;

	int cpu = smp_processor_id();
	struct elf_prstatus *prstatus = &per_cpu(elf_prstatus, cpu);

	elf_greg_t *dst = (elf_greg_t *)&(prstatus->pr_reg);
	memset(prstatus, 0, sizeof(*prstatus));
	prstatus->pr_pid = current->pid;

	ia64_dump_cpu_regs(dst);
	cfm = dst[43];
	sol = (cfm >> 7) & 0x7f;
	sof = cfm & 0x7f;
	dst[46] = (unsigned long)ia64_rse_skip_regs((unsigned long *)dst[46],
			sof - sol);

	buf = (u64 *) per_cpu_ptr(crash_notes, cpu);
	if (!buf)
		return;
79
	buf = append_elf_note(buf, KEXEC_CORE_NOTE_NAME, NT_PRSTATUS, prstatus,
Z
Zou Nan hai 已提交
80 81 82 83
			sizeof(*prstatus));
	final_note(buf);
}

84
#ifdef CONFIG_SMP
Z
Zou Nan hai 已提交
85 86 87 88 89 90
static int
kdump_wait_cpu_freeze(void)
{
	int cpu_num = num_online_cpus() - 1;
	int timeout = 1000;
	while(timeout-- > 0) {
S
Simon Horman 已提交
91
		if (atomic_read(&kdump_cpu_frozen) == cpu_num)
Z
Zou Nan hai 已提交
92 93 94 95 96
			return 0;
		udelay(1000);
	}
	return 1;
}
97
#endif
Z
Zou Nan hai 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111

void
machine_crash_shutdown(struct pt_regs *pt)
{
	/* This function is only called after the system
	 * has paniced or is otherwise in a critical state.
	 * The minimum amount of code to allow a kexec'd kernel
	 * to run successfully needs to happen here.
	 *
	 * In practice this means shooting down the other cpus in
	 * an SMP system.
	 */
	kexec_disable_iosapic();
#ifdef CONFIG_SMP
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
	/*
	 * If kdump_on_init is set and an INIT is asserted here, kdump will
	 * be started again via INIT monarch.
	 */
	local_irq_disable();
	ia64_set_psr_mc();	/* mask MCA/INIT */
	if (atomic_inc_return(&kdump_in_progress) != 1)
		unw_init_running(kdump_cpu_freeze, NULL);

	/*
	 * Now this cpu is ready for kdump.
	 * Stop all others by IPI or INIT.  They could receive INIT from
	 * outside and might be INIT monarch, but only thing they have to
	 * do is falling into kdump_cpu_freeze().
	 *
	 * If an INIT is asserted here:
	 * - All receivers might be slaves, since some of cpus could already
	 *   be frozen and INIT might be masked on monarch.  In this case,
130 131
	 *   all slaves will be frozen soon since kdump_in_progress will let
	 *   them into DIE_INIT_SLAVE_LEAVE.
132 133 134
	 * - One might be a monarch, but INIT rendezvous will fail since
	 *   at least this cpu already have INIT masked so it never join
	 *   to the rendezvous.  In this case, all slaves and monarch will
135 136
	 *   be frozen soon with no wait since the INIT rendezvous is skipped
	 *   by kdump_in_progress.
137
	 */
Z
Zou Nan hai 已提交
138
	kdump_smp_send_stop();
S
Simon Horman 已提交
139
	/* not all cpu response to IPI, send INIT to freeze them */
140
	if (kdump_wait_cpu_freeze()) {
Z
Zou Nan hai 已提交
141
		kdump_smp_send_init();
142 143
		/* wait again, don't go ahead if possible */
		kdump_wait_cpu_freeze();
Z
Zou Nan hai 已提交
144 145 146 147 148 149 150
	}
#endif
}

static void
machine_kdump_on_init(void)
{
151
	crash_save_vmcoreinfo();
Z
Zou Nan hai 已提交
152 153 154 155 156 157 158 159 160
	local_irq_disable();
	kexec_disable_iosapic();
	machine_kexec(ia64_kimage);
}

void
kdump_cpu_freeze(struct unw_frame_info *info, void *arg)
{
	int cpuid;
161

Z
Zou Nan hai 已提交
162 163 164 165
	local_irq_disable();
	cpuid = smp_processor_id();
	crash_save_this_cpu();
	current->thread.ksp = (__u64)info->sw - 16;
166 167 168

	ia64_set_psr_mc();	/* mask MCA/INIT and stop reentrance */

S
Simon Horman 已提交
169
	atomic_inc(&kdump_cpu_frozen);
Z
Zou Nan hai 已提交
170 171
	kdump_status[cpuid] = 1;
	mb();
172 173
	for (;;)
		cpu_relax();
Z
Zou Nan hai 已提交
174 175 176 177 178 179 180 181
}

static int
kdump_init_notifier(struct notifier_block *self, unsigned long val, void *data)
{
	struct ia64_mca_notify_die *nd;
	struct die_args *args = data;

182 183 184 185 186 187 188
	if (atomic_read(&kdump_in_progress)) {
		switch (val) {
		case DIE_INIT_MONARCH_LEAVE:
			if (!kdump_freeze_monarch)
				break;
			/* fall through */
		case DIE_INIT_SLAVE_LEAVE:
189
		case DIE_INIT_MONARCH_ENTER:
190 191 192 193 194 195
		case DIE_MCA_RENDZVOUS_LEAVE:
			unw_init_running(kdump_cpu_freeze, NULL);
			break;
		}
	}

196
	if (!kdump_on_init && !kdump_on_fatal_mca)
Z
Zou Nan hai 已提交
197 198
		return NOTIFY_DONE;

199 200 201 202
	if (!ia64_kimage) {
		if (val == DIE_INIT_MONARCH_LEAVE)
			ia64_mca_printk(KERN_NOTICE
					"%s: kdump not configured\n",
203
					__func__);
204 205 206
		return NOTIFY_DONE;
	}

207 208
	if (val != DIE_INIT_MONARCH_LEAVE &&
	    val != DIE_INIT_MONARCH_PROCESS &&
Z
Zou Nan hai 已提交
209 210 211 212 213 214
	    val != DIE_MCA_MONARCH_LEAVE)
		return NOTIFY_DONE;

	nd = (struct ia64_mca_notify_die *)args->err;

	switch (val) {
215
	case DIE_INIT_MONARCH_PROCESS:
216 217
		/* Reason code 1 means machine check rendezvous*/
		if (kdump_on_init && (nd->sos->rv_rc != 1)) {
218 219
			if (atomic_inc_return(&kdump_in_progress) != 1)
				kdump_freeze_monarch = 1;
220 221 222
		}
		break;
	case DIE_INIT_MONARCH_LEAVE:
223 224 225
		/* Reason code 1 means machine check rendezvous*/
		if (kdump_on_init && (nd->sos->rv_rc != 1))
			machine_kdump_on_init();
226 227
		break;
	case DIE_MCA_MONARCH_LEAVE:
228 229
		/* *(nd->data) indicate if MCA is recoverable */
		if (kdump_on_fatal_mca && !(*(nd->data))) {
230
			if (atomic_inc_return(&kdump_in_progress) == 1)
231 232
				machine_kdump_on_init();
			/* We got fatal MCA while kdump!? No way!! */
233 234
		}
		break;
Z
Zou Nan hai 已提交
235 236 237 238 239
	}
	return NOTIFY_DONE;
}

#ifdef CONFIG_SYSCTL
240
static ctl_table kdump_ctl_table[] = {
Z
Zou Nan hai 已提交
241 242 243 244 245 246 247 248
	{
		.ctl_name = CTL_UNNUMBERED,
		.procname = "kdump_on_init",
		.data = &kdump_on_init,
		.maxlen = sizeof(int),
		.mode = 0644,
		.proc_handler = &proc_dointvec,
	},
249 250 251 252 253 254 255 256
	{
		.ctl_name = CTL_UNNUMBERED,
		.procname = "kdump_on_fatal_mca",
		.data = &kdump_on_fatal_mca,
		.maxlen = sizeof(int),
		.mode = 0644,
		.proc_handler = &proc_dointvec,
	},
Z
Zou Nan hai 已提交
257 258 259 260 261 262 263 264
	{ .ctl_name = 0 }
};

static ctl_table sys_table[] = {
	{
	  .ctl_name = CTL_KERN,
	  .procname = "kernel",
	  .mode = 0555,
265
	  .child = kdump_ctl_table,
Z
Zou Nan hai 已提交
266 267 268 269 270 271 272 273
	},
	{ .ctl_name = 0 }
};
#endif

static int
machine_crash_setup(void)
{
274
	/* be notified before default_monarch_init_process */
Z
Zou Nan hai 已提交
275 276
	static struct notifier_block kdump_init_notifier_nb = {
		.notifier_call = kdump_init_notifier,
277
		.priority = 1,
Z
Zou Nan hai 已提交
278 279 280 281 282
	};
	int ret;
	if((ret = register_die_notifier(&kdump_init_notifier_nb)) != 0)
		return ret;
#ifdef CONFIG_SYSCTL
283
	register_sysctl_table(sys_table);
Z
Zou Nan hai 已提交
284 285 286 287 288 289
#endif
	return 0;
}

__initcall(machine_crash_setup);