[S390] smp: always reboot on cpu 0
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Fri, 26 Feb 2010 21:37:34 +0000 (22:37 +0100)
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>
Fri, 26 Feb 2010 21:37:30 +0000 (22:37 +0100)
Always reboot on logical cpu 0. This makes sure that the IPL cpu is
always the same and usually avoids strange numbering schemes between
physical and logical cpus.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/smp.h
arch/s390/kernel/Makefile
arch/s390/kernel/ipl.c
arch/s390/kernel/machine_kexec.c
arch/s390/kernel/smp.c
arch/s390/kernel/switch_cpu.S [new file with mode: 0644]
arch/s390/kernel/switch_cpu64.S [new file with mode: 0644]

index 9d2acb0..c2d0e63 100644 (file)
@@ -31,6 +31,18 @@ extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
 
 extern struct save_area *zfcpdump_save_areas[NR_CPUS + 1];
 
+extern void smp_switch_to_ipl_cpu(void (*func)(void *), void *);
+extern void smp_switch_to_cpu(void (*)(void *), void *, unsigned long sp,
+                             int from, int to);
+extern void smp_restart_cpu(void);
+
+#else /* CONFIG_SMP */
+
+static inline void smp_switch_to_ipl_cpu(void (*func)(void *), void *data)
+{
+       func(data);
+}
+
 #endif /* CONFIG_SMP */
 
 #ifdef CONFIG_HOTPLUG_CPU
index 683f638..20f8612 100644 (file)
@@ -32,6 +32,8 @@ extra-y                               += head.o init_task.o vmlinux.lds
 
 obj-$(CONFIG_MODULES)          += s390_ksyms.o module.o
 obj-$(CONFIG_SMP)              += smp.o topology.o
+obj-$(CONFIG_SMP)              += $(if $(CONFIG_64BIT),switch_cpu64.o, \
+                                                       switch_cpu.o)
 obj-$(CONFIG_HIBERNATION)      += suspend.o swsusp_asm64.o
 obj-$(CONFIG_AUDIT)            += audit.o
 compat-obj-$(CONFIG_AUDIT)     += compat_audit.o
index 4d73296..0a7c39d 100644 (file)
@@ -553,7 +553,7 @@ out:
        return rc;
 }
 
-static void ipl_run(struct shutdown_trigger *trigger)
+static void __ipl_run(void *unused)
 {
        diag308(DIAG308_IPL, NULL);
        if (MACHINE_IS_VM)
@@ -562,6 +562,11 @@ static void ipl_run(struct shutdown_trigger *trigger)
                reipl_ccw_dev(&ipl_info.data.ccw.dev_id);
 }
 
+static void ipl_run(struct shutdown_trigger *trigger)
+{
+       smp_switch_to_ipl_cpu(__ipl_run, NULL);
+}
+
 static int __init ipl_init(void)
 {
        int rc;
@@ -1039,7 +1044,7 @@ static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb,
                sprintf(dst + pos, " PARM %s", vmparm);
 }
 
-static void reipl_run(struct shutdown_trigger *trigger)
+static void __reipl_run(void *unused)
 {
        struct ccw_dev_id devid;
        static char buf[128];
@@ -1087,6 +1092,11 @@ static void reipl_run(struct shutdown_trigger *trigger)
        disabled_wait((unsigned long) __builtin_return_address(0));
 }
 
+static void reipl_run(struct shutdown_trigger *trigger)
+{
+       smp_switch_to_ipl_cpu(__reipl_run, NULL);
+}
+
 static void reipl_block_ccw_init(struct ipl_parameter_block *ipb)
 {
        ipb->hdr.len = IPL_PARM_BLK_CCW_LEN;
@@ -1369,20 +1379,18 @@ static struct kobj_attribute dump_type_attr =
 
 static struct kset *dump_kset;
 
-static void dump_run(struct shutdown_trigger *trigger)
+static void __dump_run(void *unused)
 {
        struct ccw_dev_id devid;
        static char buf[100];
 
        switch (dump_method) {
        case DUMP_METHOD_CCW_CIO:
-               smp_send_stop();
                devid.devno = dump_block_ccw->ipl_info.ccw.devno;
                devid.ssid  = 0;
                reipl_ccw_dev(&devid);
                break;
        case DUMP_METHOD_CCW_VM:
-               smp_send_stop();
                sprintf(buf, "STORE STATUS");
                __cpcmd(buf, NULL, 0, NULL);
                sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
@@ -1396,10 +1404,17 @@ static void dump_run(struct shutdown_trigger *trigger)
                diag308(DIAG308_SET, dump_block_fcp);
                diag308(DIAG308_DUMP, NULL);
                break;
-       case DUMP_METHOD_NONE:
-               return;
+       default:
+               break;
        }
-       printk(KERN_EMERG "Dump failed!\n");
+}
+
+static void dump_run(struct shutdown_trigger *trigger)
+{
+       if (dump_method == DUMP_METHOD_NONE)
+               return;
+       smp_send_stop();
+       smp_switch_to_ipl_cpu(__dump_run, NULL);
 }
 
 static int __init dump_ccw_init(void)
index 131d7ee..a922d51 100644 (file)
@@ -54,11 +54,11 @@ void machine_shutdown(void)
 {
 }
 
-void machine_kexec(struct kimage *image)
+static void __machine_kexec(void *data)
 {
        relocate_kernel_t data_mover;
+       struct kimage *image = data;
 
-       smp_send_stop();
        pfault_fini();
        s390_reset_system();
 
@@ -68,3 +68,9 @@ void machine_kexec(struct kimage *image)
        (*data_mover)(&image->head, image->start);
        for (;;);
 }
+
+void machine_kexec(struct kimage *image)
+{
+       smp_send_stop();
+       smp_switch_to_ipl_cpu(__machine_kexec, image);
+}
index 384a684..b39f596 100644 (file)
@@ -90,6 +90,39 @@ static int cpu_stopped(int cpu)
        return 0;
 }
 
+void smp_switch_to_ipl_cpu(void (*func)(void *), void *data)
+{
+       struct _lowcore *lc, *current_lc;
+       struct stack_frame *sf;
+       struct pt_regs *regs;
+       unsigned long sp;
+
+       if (smp_processor_id() == 0)
+               func(data);
+       __load_psw_mask(PSW_BASE_BITS | PSW_DEFAULT_KEY);
+       /* Disable lowcore protection */
+       __ctl_clear_bit(0, 28);
+       current_lc = lowcore_ptr[smp_processor_id()];
+       lc = lowcore_ptr[0];
+       if (!lc)
+               lc = current_lc;
+       lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
+       lc->restart_psw.addr = PSW_ADDR_AMODE | (unsigned long) smp_restart_cpu;
+       if (!cpu_online(0))
+               smp_switch_to_cpu(func, data, 0, stap(), __cpu_logical_map[0]);
+       while (signal_processor(0, sigp_stop_and_store_status) == sigp_busy)
+               cpu_relax();
+       sp = lc->panic_stack;
+       sp -= sizeof(struct pt_regs);
+       regs = (struct pt_regs *) sp;
+       memcpy(&regs->gprs, &current_lc->gpregs_save_area, sizeof(regs->gprs));
+       memcpy(&regs->psw, &current_lc->st_status_fixed_logout, sizeof(psw_t));
+       sp -= STACK_FRAME_OVERHEAD;
+       sf = (struct stack_frame *) sp;
+       sf->back_chain = regs->gprs[15];
+       smp_switch_to_cpu(func, data, sp, stap(), __cpu_logical_map[0]);
+}
+
 void smp_send_stop(void)
 {
        int cpu, rc;
@@ -752,7 +785,8 @@ static ssize_t cpu_configure_store(struct sys_device *dev,
        get_online_cpus();
        mutex_lock(&smp_cpu_state_mutex);
        rc = -EBUSY;
-       if (cpu_online(cpu))
+       /* disallow configuration changes of online cpus and cpu 0 */
+       if (cpu_online(cpu) || cpu == 0)
                goto out;
        rc = 0;
        switch (val) {
diff --git a/arch/s390/kernel/switch_cpu.S b/arch/s390/kernel/switch_cpu.S
new file mode 100644 (file)
index 0000000..c05ee6c
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 31-bit switch cpu code
+ *
+ * Copyright IBM Corp. 2009
+ *
+ */
+
+#include <asm/asm-offsets.h>
+#include <asm/lowcore.h>
+#include <asm/ptrace.h>
+
+# smp_switch_to_cpu switches to destination cpu and executes the passed function
+# Parameter: %r2 - function to call
+#           %r3 - function parameter
+#           %r4 - stack poiner
+#           %r5 - current cpu
+#           %r6 - destination cpu
+
+       .section .text
+       .align 4
+       .globl smp_switch_to_cpu
+smp_switch_to_cpu:
+       stm     %r6,%r15,__SF_GPRS(%r15)
+       lr      %r1,%r15
+       ahi     %r15,-STACK_FRAME_OVERHEAD
+       st      %r1,__SF_BACKCHAIN(%r15)
+       basr    %r13,0
+0:     la      %r1,.gprregs_addr-0b(%r13)
+       l       %r1,0(%r1)
+       stm     %r0,%r15,0(%r1)
+1:     sigp    %r0,%r6,__SIGP_RESTART  /* start destination CPU */
+       brc     2,1b                    /* busy, try again */
+2:     sigp    %r0,%r5,__SIGP_STOP     /* stop current CPU */
+       brc     2,2b                    /* busy, try again */
+3:     j       3b
+
+       .globl  smp_restart_cpu
+smp_restart_cpu:
+       basr    %r13,0
+0:     la      %r1,.gprregs_addr-0b(%r13)
+       l       %r1,0(%r1)
+       lm      %r0,%r15,0(%r1)
+1:     sigp    %r0,%r5,__SIGP_SENSE    /* Wait for calling CPU */
+       brc     10,1b                   /* busy, accepted (status 0), running */
+       tmll    %r0,0x40                /* Test if calling CPU is stopped */
+       jz      1b
+       ltr     %r4,%r4                 /* New stack ? */
+       jz      1f
+       lr      %r15,%r4
+1:     basr    %r14,%r2
+
+.gprregs_addr:
+       .long   .gprregs
+
+       .section .data,"aw",@progbits
+.gprregs:
+       .rept   16
+       .long   0
+       .endr
diff --git a/arch/s390/kernel/switch_cpu64.S b/arch/s390/kernel/switch_cpu64.S
new file mode 100644 (file)
index 0000000..c73ede3
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 64-bit switch cpu code
+ *
+ * Copyright IBM Corp. 2009
+ *
+ */
+
+#include <asm/asm-offsets.h>
+#include <asm/lowcore.h>
+#include <asm/ptrace.h>
+
+# smp_switch_to_cpu switches to destination cpu and executes the passed function
+# Parameter: %r2 - function to call
+#           %r3 - function parameter
+#           %r4 - stack poiner
+#           %r5 - current cpu
+#           %r6 - destination cpu
+
+       .section .text
+       .align 4
+       .globl smp_switch_to_cpu
+smp_switch_to_cpu:
+       stmg    %r6,%r15,__SF_GPRS(%r15)
+       lgr     %r1,%r15
+       aghi    %r15,-STACK_FRAME_OVERHEAD
+       stg     %r1,__SF_BACKCHAIN(%r15)
+       larl    %r1,.gprregs
+       stmg    %r0,%r15,0(%r1)
+1:     sigp    %r0,%r6,__SIGP_RESTART  /* start destination CPU */
+       brc     2,1b                    /* busy, try again */
+2:     sigp    %r0,%r5,__SIGP_STOP     /* stop current CPU */
+       brc     2,2b                    /* busy, try again */
+3:     j       3b
+
+       .globl  smp_restart_cpu
+smp_restart_cpu:
+       larl    %r1,.gprregs
+       lmg     %r0,%r15,0(%r1)
+1:     sigp    %r0,%r5,__SIGP_SENSE    /* Wait for calling CPU */
+       brc     10,1b                   /* busy, accepted (status 0), running */
+       tmll    %r0,0x40                /* Test if calling CPU is stopped */
+       jz      1b
+       ltgr    %r4,%r4                 /* New stack ? */
+       jz      1f
+       lgr     %r15,%r4
+1:     basr    %r14,%r2
+
+       .section .data,"aw",@progbits
+.gprregs:
+       .rept   16
+       .quad   0
+       .endr