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