diff options
Diffstat (limited to 'arch/ia64/kernel/crash.c')
| -rw-r--r-- | arch/ia64/kernel/crash.c | 173 |
1 files changed, 107 insertions, 66 deletions
diff --git a/arch/ia64/kernel/crash.c b/arch/ia64/kernel/crash.c index 0aabedf95da..2955f359e2a 100644 --- a/arch/ia64/kernel/crash.c +++ b/arch/ia64/kernel/crash.c @@ -16,32 +16,16 @@ #include <linux/elfcore.h> #include <linux/sysctl.h> #include <linux/init.h> +#include <linux/kdebug.h> -#include <asm/kdebug.h> #include <asm/mca.h> -#include <asm/uaccess.h> int kdump_status[NR_CPUS]; -atomic_t kdump_cpu_freezed; +static atomic_t kdump_cpu_frozen; atomic_t kdump_in_progress; -int kdump_on_init = 1; -ssize_t -copy_oldmem_page(unsigned long pfn, char *buf, - size_t csize, unsigned long offset, int userbuf) -{ - void *vaddr; - - if (!csize) - return 0; - vaddr = __va(pfn<<PAGE_SHIFT); - if (userbuf) { - if (copy_to_user(buf, (vaddr + offset), csize)) { - return -EFAULT; - } - } else - memcpy(buf, (vaddr + offset), csize); - return csize; -} +static int kdump_freeze_monarch; +static int kdump_on_init = 1; +static int kdump_on_fatal_mca = 1; static inline Elf64_Word *append_elf_note(Elf64_Word *buf, char *name, unsigned type, void *data, @@ -70,7 +54,7 @@ extern void ia64_dump_cpu_regs(void *); static DEFINE_PER_CPU(struct elf_prstatus, elf_prstatus); void -crash_save_this_cpu() +crash_save_this_cpu(void) { void *buf; unsigned long cfm, sof, sol; @@ -92,23 +76,25 @@ crash_save_this_cpu() buf = (u64 *) per_cpu_ptr(crash_notes, cpu); if (!buf) return; - buf = append_elf_note(buf, "CORE", NT_PRSTATUS, prstatus, + buf = append_elf_note(buf, KEXEC_CORE_NOTE_NAME, NT_PRSTATUS, prstatus, sizeof(*prstatus)); final_note(buf); } +#ifdef CONFIG_SMP static int kdump_wait_cpu_freeze(void) { int cpu_num = num_online_cpus() - 1; int timeout = 1000; while(timeout-- > 0) { - if (atomic_read(&kdump_cpu_freezed) == cpu_num) + if (atomic_read(&kdump_cpu_frozen) == cpu_num) return 0; udelay(1000); } return 1; } +#endif void machine_crash_shutdown(struct pt_regs *pt) @@ -123,10 +109,38 @@ machine_crash_shutdown(struct pt_regs *pt) */ kexec_disable_iosapic(); #ifdef CONFIG_SMP + /* + * 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, + * all slaves will be frozen soon since kdump_in_progress will let + * them into DIE_INIT_SLAVE_LEAVE. + * - 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 + * be frozen soon with no wait since the INIT rendezvous is skipped + * by kdump_in_progress. + */ kdump_smp_send_stop(); - if (kdump_wait_cpu_freeze() && kdump_on_init) { - //not all cpu response to IPI, send INIT to freeze them + /* not all cpu response to IPI, send INIT to freeze them */ + if (kdump_wait_cpu_freeze()) { kdump_smp_send_init(); + /* wait again, don't go ahead if possible */ + kdump_wait_cpu_freeze(); } #endif } @@ -134,6 +148,7 @@ machine_crash_shutdown(struct pt_regs *pt) static void machine_kdump_on_init(void) { + crash_save_vmcoreinfo(); local_irq_disable(); kexec_disable_iosapic(); machine_kexec(ia64_kimage); @@ -143,18 +158,19 @@ void kdump_cpu_freeze(struct unw_frame_info *info, void *arg) { int cpuid; + local_irq_disable(); cpuid = smp_processor_id(); crash_save_this_cpu(); current->thread.ksp = (__u64)info->sw - 16; - atomic_inc(&kdump_cpu_freezed); + + ia64_set_psr_mc(); /* mask MCA/INIT and stop reentrance */ + + atomic_inc(&kdump_cpu_frozen); kdump_status[cpuid] = 1; mb(); - if (cpuid == 0) { - for (;;) - cpu_relax(); - } else - ia64_jump_to_sal(&sal_boot_rendez_state[cpuid]); + for (;;) + cpu_relax(); } static int @@ -163,80 +179,105 @@ kdump_init_notifier(struct notifier_block *self, unsigned long val, void *data) struct ia64_mca_notify_die *nd; struct die_args *args = data; - if (!kdump_on_init) + 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: + case DIE_INIT_MONARCH_ENTER: + case DIE_MCA_RENDZVOUS_LEAVE: + unw_init_running(kdump_cpu_freeze, NULL); + break; + } + } + + if (!kdump_on_init && !kdump_on_fatal_mca) return NOTIFY_DONE; - if (val != DIE_INIT_MONARCH_ENTER && - val != DIE_INIT_SLAVE_ENTER && - val != DIE_MCA_RENDZVOUS_LEAVE && + if (!ia64_kimage) { + if (val == DIE_INIT_MONARCH_LEAVE) + ia64_mca_printk(KERN_NOTICE + "%s: kdump not configured\n", + __func__); + return NOTIFY_DONE; + } + + if (val != DIE_INIT_MONARCH_LEAVE && + val != DIE_INIT_MONARCH_PROCESS && val != DIE_MCA_MONARCH_LEAVE) return NOTIFY_DONE; nd = (struct ia64_mca_notify_die *)args->err; - /* Reason code 1 means machine check rendezous*/ - if ((val == DIE_INIT_MONARCH_ENTER || DIE_INIT_SLAVE_ENTER) && - nd->sos->rv_rc == 1) - return NOTIFY_DONE; switch (val) { - case DIE_INIT_MONARCH_ENTER: + case DIE_INIT_MONARCH_PROCESS: + /* Reason code 1 means machine check rendezvous*/ + if (kdump_on_init && (nd->sos->rv_rc != 1)) { + if (atomic_inc_return(&kdump_in_progress) != 1) + kdump_freeze_monarch = 1; + } + break; + case DIE_INIT_MONARCH_LEAVE: + /* Reason code 1 means machine check rendezvous*/ + if (kdump_on_init && (nd->sos->rv_rc != 1)) machine_kdump_on_init(); - break; - case DIE_INIT_SLAVE_ENTER: - unw_init_running(kdump_cpu_freeze, NULL); - break; - case DIE_MCA_RENDZVOUS_LEAVE: - if (atomic_read(&kdump_in_progress)) - unw_init_running(kdump_cpu_freeze, NULL); - break; - case DIE_MCA_MONARCH_LEAVE: - /* die_register->signr indicate if MCA is recoverable */ - if (!args->signr) + break; + case DIE_MCA_MONARCH_LEAVE: + /* *(nd->data) indicate if MCA is recoverable */ + if (kdump_on_fatal_mca && !(*(nd->data))) { + if (atomic_inc_return(&kdump_in_progress) == 1) machine_kdump_on_init(); - break; + /* We got fatal MCA while kdump!? No way!! */ + } + break; } return NOTIFY_DONE; } #ifdef CONFIG_SYSCTL -static ctl_table kdump_on_init_table[] = { +static struct ctl_table kdump_ctl_table[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "kdump_on_init", .data = &kdump_on_init, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, + }, + { + .procname = "kdump_on_fatal_mca", + .data = &kdump_on_fatal_mca, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, }, - { .ctl_name = 0 } + { } }; -static ctl_table sys_table[] = { +static struct ctl_table sys_table[] = { { - .ctl_name = CTL_KERN, .procname = "kernel", .mode = 0555, - .child = kdump_on_init_table, + .child = kdump_ctl_table, }, - { .ctl_name = 0 } + { } }; #endif static int machine_crash_setup(void) { - char *from = strstr(saved_command_line, "elfcorehdr="); + /* be notified before default_monarch_init_process */ static struct notifier_block kdump_init_notifier_nb = { .notifier_call = kdump_init_notifier, + .priority = 1, }; int ret; - if (from) - elfcorehdr_addr = memparse(from+11, &from); - saved_max_pfn = (unsigned long)-1; if((ret = register_die_notifier(&kdump_init_notifier_nb)) != 0) return ret; #ifdef CONFIG_SYSCTL - register_sysctl_table(sys_table, 0); + register_sysctl_table(sys_table); #endif return 0; } |
