Add the ia64 specific crash_stop code. This contains routines that are called from the common crash_stop code and from the ia64 notify_die chain.
Signed-off-by: Keith Owens <[EMAIL PROTECTED]> --- arch/ia64/kernel/Makefile | 1 arch/ia64/kernel/crash_stop.c | 237 ++++++++++++++++++++++++++++++++++++++++++ include/asm-ia64/crash_stop.h | 11 + 3 files changed, 249 insertions(+) Index: linux/arch/ia64/kernel/Makefile =================================================================== --- linux.orig/arch/ia64/kernel/Makefile +++ linux/arch/ia64/kernel/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_KPROBES) += kprobes.o jpro obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o obj-$(CONFIG_AUDIT) += audit.o obj-$(CONFIG_PCI_MSI) += msi_ia64.o +obj-$(CONFIG_CRASH_STOP_SUPPORTED) += crash_stop.o mca_recovery-y += mca_drv.o mca_drv_asm.o obj-$(CONFIG_IA64_ESI) += esi.o Index: linux/arch/ia64/kernel/crash_stop.c =================================================================== --- /dev/null +++ linux/arch/ia64/kernel/crash_stop.c @@ -0,0 +1,237 @@ +/* + * linux/arch/ia64/crash_stop.c + * + * Copyright (C) 2006 Keith Owens <[EMAIL PROTECTED]> + * + * Most of the IA64 specific bits of the crash_stop code. There is a little + * bit of crash_stop code in arch/ia64/kernel/smp.c to handle IPI_CRASH_STOP, + * everything else is in this file. + * + * IA64 is more complicated than the other architectures (isn't it always?). + * An MCA will force the entire machine into an MCA rendezvous state, using + * normal interrupts and/or selective INIT. The NMI command/button will send + * INIT to all cpus at the "same" time. For both these cases, one cpu is the + * monarch and all others are slaves[1]. An INIT can also be generated by + * crash_stop, to interrupt any cpus that are spinning disabled. In the latter + * case, mca.c does not know about the monarch that called crash_stop(). + * + * The code in arch/ia64/kernel/mca.c only handles the first two cases, i.e. + * the ones that are defined by the SAL specification. To handle the Linux + * specific crash_stop case, we have to fool mca.c into thinking that the + * monarch cpu has already been defined, then clear the monarch cpu to allow + * the INIT slaves to resume. The notify_die callbacks are passed a data + * pointer, for MCA/INIT events, data->err is a pointer to a struct + * ia64_mca_notify_die. The data in that structure lets crash_stop decide + * which cpu is the monarch and which is the slave, as well as override the + * 'wait for slaves' logic in mca.c. + * + * [1] Ignoring broken proms which assign the wrong values to the monarch flag. + * + * Another IA64 complication is that struct pt_regs only contains part of the + * system state. IA64 also needs a struct switch_stack in order to give the + * unwinder all the state information. Tasks that have been scheduled off a + * cpu already have a switch_stack, but the running tasks do not. Create a + * switch_stack for each running task and store the address of that structure + * in the arch specific area of crash_stop_running_process. + */ + +#include <linux/crash_stop.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <asm/kdebug.h> +#include <asm/mca.h> + +int cs_arch_monarch_cpu; + +/* cs_arch_cpu() -> unw_init_running() -> cs_ca_switch_stack(). Save + * the address of the switch_stack created by unw_init_running() in the arch + * specific area of crash_stop_running_process then cs_common_cpu() to + * do the rest of the per cpu setup for a crash stop. + */ + +struct cs_ca_data { + int monarch; + struct crash_stop_running_process *r; +}; + +static +void cs_ca_switch_stack(struct unw_frame_info *info, void *vdata) +{ + struct cs_ca_data *data = vdata; + int monarch = data->monarch; + struct crash_stop_running_process *r = data->r; + struct switch_stack *sw; + sw = (struct switch_stack *)(info+1); + /* padding from unw_init_running */ + sw = (struct switch_stack *)(((unsigned long)sw + 15) & ~15); + r->arch.sw = sw; + cs_common_cpu(monarch); +} + +void +cs_arch_cpu(int monarch, struct crash_stop_running_process *r) +{ + struct cs_ca_data data = { + .monarch = monarch, + .r = r, + }; + unw_init_running(cs_ca_switch_stack, &data); +} + +/* Called at the start of a notify_chain. */ +static int +cs_arch_notify_start(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args = data; + switch(val) { + case DIE_OOPS: + case DIE_MCA_MONARCH_ENTER: + case DIE_INIT_MONARCH_ENTER: + cs_notify_chain_start(args->regs); + break; + } + return NOTIFY_OK; +} + +/* Called at the end of a notify_chain. */ +static int +cs_arch_notify_end(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args = data; + switch(val) { + case DIE_OOPS: + case DIE_INIT_MONARCH_LEAVE: + cs_notify_chain_end(); + break; + case DIE_MCA_MONARCH_LEAVE: + cs_notify_chain_end(); + /* mca.c passes the recover flag as signr */ + if (args->signr) + crash_stop_recovered(); + break; + } + return NOTIFY_OK; +} + +static int +cs_arch_notify_nmi(struct notifier_block *self, + unsigned long val, void *data) +{ + struct ia64_mca_notify_die *nd; + struct pt_regs *old_regs; + struct die_args *args = data; + static cpumask_t cs_INIT; + int cpu = smp_processor_id(); + nd = (struct ia64_mca_notify_die *)(args->err); + + switch(val) { + /* FIXME: if the MCA rendezvous timeout is increased (case + * DIE_MCA_NEW_TIMEOUT), does crash_stop care about the new limit? It + * might affect wait_secs in crash_stop() - KAO. + */ + case DIE_MCA_RENDZVOUS_PROCESS: + /* The MCA monarch event has woken up the slaves that were + * suspended via the MCA rendezvous interrupt. Tell + * crash_stop() that this slave cpu is ready and waiting to be + * debugged. + */ + old_regs = set_irq_regs(args->regs); + crash_stop_slave(); + set_irq_regs(old_regs); + break; + case DIE_INIT_ENTER: + /* INIT that is sent to all cpus is correctly handled by mca.c. + * If cs_arch_send_nmi() was invoked on IA64 because a cpu was + * spinning disabled then we get a lone INIT event with no + * monarch, or at least not a monarch that mca.c knows about. + * Tell mca.c that we already have a monarch. Also clear the + * sos->monarch flag, some broken proms incorrectly mark + * individual INIT events as a monarch event. + */ + oops_in_progress = 1; + if (crash_stop_sent_nmi()) { + cpu_set(cpu, cs_INIT); + *(nd->monarch_cpu) = cs_arch_monarch_cpu; + nd->sos->monarch = 0; + } + break; + case DIE_INIT_SLAVE_ENTER: + /* This slave INIT event could have come from crash_stop(), it + * could also have come from a global INIT event. In either + * case, drop into the crash_stop() slave processing. + */ + old_regs = set_irq_regs(args->regs); + crash_stop_slave(); + set_irq_regs(old_regs); + break; + case DIE_INIT_SLAVE_PROCESS: + /* Reverse the processing for DIE_INIT_ENTER. Normal mca.c + * processing waits for the MCA (monarch) to release any INIT + * slaves, but we may not have an MCA monarch. Pretend that + * each slave that was hit with INIT by crash_stop() is a + * monarch, to avoid complicating mca.c any more than it + * already is. + */ + if (cpu_isset(cpu, cs_INIT)) { + cpu_clear(cpu, cs_INIT); + *(nd->monarch_cpu) = -1; + nd->sos->monarch = 1; + } + break; + } + return NOTIFY_OK; +} + + +static struct notifier_block cs_arch_nb_start = { + .notifier_call = cs_arch_notify_start, + .priority = ~0U >> 1, +}; + +static struct notifier_block cs_arch_nb_end = { + .notifier_call = cs_arch_notify_end, + .priority = 1, +}; + +static struct notifier_block cs_arch_nb_nmi = { + .notifier_call = cs_arch_notify_nmi, + .priority = 10, +}; + +static int __init +cs_arch_init(void) +{ + int err; + const char *nb_name; + nb_name = "cs_arch_nb_start"; + if ((err = register_die_notifier(&cs_arch_nb_start))) + goto error; + nb_name = "cs_arch_nb_end"; + if ((err = register_die_notifier(&cs_arch_nb_end))) + goto error; + nb_name = "cs_arch_nb_nmi"; + if ((err = register_die_notifier(&cs_arch_nb_nmi))) + goto error; + return 0; +error: + printk(KERN_ERR "Failed to register %s\n", nb_name); + unregister_die_notifier(&cs_arch_nb_start); + unregister_die_notifier(&cs_arch_nb_end); + unregister_die_notifier(&cs_arch_nb_nmi); + return err; +} + +static void __exit +cs_arch_exit(void) +{ + unregister_die_notifier(&cs_arch_nb_nmi); + unregister_die_notifier(&cs_arch_nb_start); + unregister_die_notifier(&cs_arch_nb_end); + return; +} + +module_init(cs_arch_init); +module_exit(cs_arch_exit); Index: linux/include/asm-ia64/crash_stop.h =================================================================== --- /dev/null +++ linux/include/asm-ia64/crash_stop.h @@ -0,0 +1,11 @@ +#ifndef _ASM_CRASH_STOP_H +#define _ASM_CRASH_STOP_H + +struct crash_stop_running_process_arch +{ + struct switch_stack *sw; +}; + +extern int cs_arch_monarch_cpu; + +#endif /* _ASM_CRASH_STOP_H */ - To unsubscribe from this list: send the line "unsubscribe linux-arch" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html