[S390] hibernate: Do real CPU swap at resume time
authorMichael Holzheu <michael.holzheu@linux.vnet.ibm.com>
Tue, 22 Sep 2009 20:58:53 +0000 (22:58 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Tue, 22 Sep 2009 20:58:46 +0000 (22:58 +0200)
Currently, when the physical resume CPU is not equal to the physical suspend
CPU, we swap the CPUs logically, by modifying the logical/physical CPU mapping.
This has two major drawbacks: First the change is visible from user space (e.g.
CPU sysfs files) and second it is hard to ensure that nowhere in the kernel
the physical CPU ID is stored before suspend.
To fix this, we now really swap the physical CPUs, if the resume CPU is not
the pysical suspend CPU. We restart the suspend CPU and stop the resume CPU
using SIGP restart and SIGP stop. If the suspend CPU is no longer available,
we write a message and load a disabled wait PSW.

Signed-off-by: Michael Holzheu <michael.holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/lowcore.h
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/sclp.S
arch/s390/kernel/smp.c
arch/s390/kernel/swsusp_asm64.S

index a3ff9b01bf7d8636e64da98f9e80c6c777a416a8..f2ef4b619ce1a7415966169b89b5cb3779b198ab 100644 (file)
@@ -86,6 +86,7 @@
 #define __LC_PGM_OLD_PSW               0x0150
 #define __LC_MCK_OLD_PSW               0x0160
 #define __LC_IO_OLD_PSW                        0x0170
+#define __LC_RESTART_PSW               0x01a0
 #define __LC_EXT_NEW_PSW               0x01b0
 #define __LC_SVC_NEW_PSW               0x01c0
 #define __LC_PGM_NEW_PSW               0x01d0
index fa9905ce7d0b088bb5e0c1143d30bce03038af75..63e46433e81d2637d9c36670d023b9e2c95817be 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/sched.h>
 #include <linux/kbuild.h>
 #include <asm/vdso.h>
+#include <asm/sigp.h>
 
 int main(void)
 {
@@ -59,6 +60,10 @@ int main(void)
        DEFINE(CLOCK_REALTIME, CLOCK_REALTIME);
        DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC);
        DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC);
-
+       /* constants for SIGP */
+       DEFINE(__SIGP_STOP, sigp_stop);
+       DEFINE(__SIGP_RESTART, sigp_restart);
+       DEFINE(__SIGP_SENSE, sigp_sense);
+       DEFINE(__SIGP_INITIAL_CPU_RESET, sigp_initial_cpu_reset);
        return 0;
 }
index 20639dfe0c42217c31fd360610bdec512cd5eb24..e27ca63076d1d1946ee5c6518d074b1cce6d0f9f 100644 (file)
@@ -24,8 +24,6 @@ LC_EXT_INT_CODE               = 0x86                  # addr of ext int code
 #   R3 = external interruption parameter if R2=0
 #
 
-.section ".init.text","ax"
-
 _sclp_wait_int:
        stm     %r6,%r15,24(%r15)               # save registers
        basr    %r13,0                          # get base register
@@ -318,9 +316,8 @@ _sclp_print_early:
        .long   _sclp_work_area
 .Lascebc:
        .long   _ascebc
-.previous
 
-.section ".init.data","a"
+.section .data,"aw",@progbits
        .balign 4096
 _sclp_work_area:
        .fill   4096
index 6f14734abe72f59d6e8133ee2e33665abd6cac0a..b4b6396e6cf00fcc97347362eb882ae15d7d386f 100644 (file)
@@ -1041,42 +1041,6 @@ out:
 static SYSDEV_CLASS_ATTR(dispatching, 0644, dispatching_show,
                         dispatching_store);
 
-/*
- * If the resume kernel runs on another cpu than the suspended kernel,
- * we have to switch the cpu IDs in the logical map.
- */
-void smp_switch_boot_cpu_in_resume(u32 resume_phys_cpu_id,
-                                  struct _lowcore *suspend_lowcore)
-{
-       int cpu, suspend_cpu_id, resume_cpu_id;
-       u32 suspend_phys_cpu_id;
-
-       suspend_phys_cpu_id = __cpu_logical_map[suspend_lowcore->cpu_nr];
-       suspend_cpu_id = suspend_lowcore->cpu_nr;
-
-       for_each_present_cpu(cpu) {
-               if (__cpu_logical_map[cpu] == resume_phys_cpu_id) {
-                       resume_cpu_id = cpu;
-                       goto found;
-               }
-       }
-       panic("Could not find resume cpu in logical map.\n");
-
-found:
-       printk("Resume  cpu ID: %i/%i\n", resume_phys_cpu_id, resume_cpu_id);
-       printk("Suspend cpu ID: %i/%i\n", suspend_phys_cpu_id, suspend_cpu_id);
-
-       __cpu_logical_map[resume_cpu_id] = suspend_phys_cpu_id;
-       __cpu_logical_map[suspend_cpu_id] = resume_phys_cpu_id;
-
-       lowcore_ptr[suspend_cpu_id]->cpu_addr = resume_phys_cpu_id;
-}
-
-u32 smp_get_phys_cpu_id(void)
-{
-       return __cpu_logical_map[smp_processor_id()];
-}
-
 static int __init topology_init(void)
 {
        int cpu;
index fc056810a01759e0b735b5a1cf9c8e4465bebf7a..fe927d0bc20b73b6bf1a8a0c5355de6f440404e1 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <asm/page.h>
 #include <asm/ptrace.h>
+#include <asm/thread_info.h>
 #include <asm/asm-offsets.h>
 
 /*
@@ -41,6 +42,9 @@ swsusp_arch_suspend:
        /* Get pointer to save area */
        lghi    %r1,0x1000
 
+       /* Save CPU address */
+       stap    __LC_CPU_ADDRESS(%r1)
+
        /* Store registers */
        mvc     0x318(4,%r1),__SF_EMPTY(%r15)   /* move prefix to lowcore */
        stfpc   0x31c(%r1)                      /* store fpu control */
@@ -105,12 +109,7 @@ swsusp_arch_resume:
        /* Make all free pages stable */
        lghi    %r2,1
        brasl   %r14,arch_set_page_states
-#ifdef CONFIG_SMP
-       /* Save boot cpu number */
-       brasl   %r14,smp_get_phys_cpu_id
-       larl    %r1,saved_cpu_id
-       st      %r2,0(%r1)
-#endif
+
        /* Deactivate DAT */
        stnsm   __SF_EMPTY(%r15),0xfb
 
@@ -139,12 +138,10 @@ swsusp_arch_resume:
 
        /* Reset System */
        larl    %r1,restart_entry
-       larl    %r2,restart_psw
+       larl    %r2,.Lrestart_diag308_psw
        og      %r1,0(%r2)
        stg     %r1,0(%r0)
-       larl    %r1,saved_pgm_check_psw
-       mvc     0(16,%r1),__LC_PGM_NEW_PSW(%r0)
-       larl    %r1,new_pgm_check_psw
+       larl    %r1,.Lnew_pgm_check_psw
        epsw    %r2,%r3
        stm     %r2,%r3,0(%r1)
        mvc     __LC_PGM_NEW_PSW(16,%r0),0(%r1)
@@ -154,12 +151,54 @@ restart_entry:
        lhi     %r1,1
        sigp    %r1,%r0,0x12
        sam64
-       larl    %r1,new_pgm_check_psw
+       larl    %r1,.Lnew_pgm_check_psw
        lpswe   0(%r1)
 pgm_check_entry:
-       larl    %r1,saved_pgm_check_psw
-       mvc     __LC_PGM_NEW_PSW(16,%r0),0(%r1)
 
+       /* Switch to original suspend CPU */
+       larl    %r1,.Lresume_cpu                /* Resume CPU address: r2 */
+       stap    0(%r1)
+       llgh    %r2,0(%r1)
+       lghi    %r3,0x1000
+       llgh    %r1,__LC_CPU_ADDRESS(%r3)       /* Suspend CPU address: r1 */
+       cgr     %r1,%r2
+       je      restore_registers               /* r1 = r2 -> nothing to do */
+       larl    %r4,.Lrestart_suspend_psw       /* Set new restart PSW */
+       mvc     __LC_RESTART_PSW(16,%r0),0(%r4)
+3:
+       sigp    %r9,%r1,__SIGP_INITIAL_CPU_RESET
+       brc     8,4f    /* accepted */
+       brc     2,3b    /* busy, try again */
+
+       /* Suspend CPU not available -> panic */
+       larl    %r15,init_thread_union
+       ahi     %r15,1<<(PAGE_SHIFT+THREAD_ORDER)
+       larl    %r2,.Lpanic_string
+       larl    %r3,_sclp_print_early
+       lghi    %r1,0
+       sam31
+       sigp    %r1,%r0,0x12
+       basr    %r14,%r3
+       larl    %r3,.Ldisabled_wait_31
+       lpsw    0(%r3)
+4:
+       /* Switch to suspend CPU */
+       sigp    %r9,%r1,__SIGP_RESTART  /* start suspend CPU */
+       brc     2,4b                    /* busy, try again */
+5:
+       sigp    %r9,%r2,__SIGP_STOP     /* stop resume (current) CPU */
+6:     j       6b
+
+restart_suspend:
+       larl    %r1,.Lresume_cpu
+       llgh    %r2,0(%r1)
+7:
+       sigp    %r9,%r2,__SIGP_SENSE    /* Wait for resume CPU */
+       brc     2,7b                    /* busy, try again */
+       tmll    %r9,0x40                /* Test if resume CPU is stopped */
+       jz      7b
+
+restore_registers:
        /* Restore registers */
        lghi    %r13,0x1000             /* %r1 = pointer to save arae */
 
@@ -193,13 +232,6 @@ pgm_check_entry:
        /* Pointer to save area */
        lghi    %r13,0x1000
 
-#ifdef CONFIG_SMP
-       /* Switch CPUs */
-       larl    %r1,saved_cpu_id
-       llgf    %r2,0(%r1)
-       llgf    %r3,0x318(%r13)
-       brasl   %r14,smp_switch_boot_cpu_in_resume
-#endif
        /* Restore prefix register */
        spx     0x318(%r13)
 
@@ -217,13 +249,16 @@ pgm_check_entry:
 
        .section .data.nosave,"aw",@progbits
        .align  8
-restart_psw:
+.Ldisabled_wait_31:
+       .long  0x000a0000,0x00000000
+.Lpanic_string:
+       .asciz  "Resume not possible because suspend CPU is no longer available"
+       .align  8
+.Lrestart_diag308_psw:
        .long   0x00080000,0x80000000
-new_pgm_check_psw:
+.Lrestart_suspend_psw:
+       .quad   0x0000000180000000,restart_suspend
+.Lnew_pgm_check_psw:
        .quad   0,pgm_check_entry
-saved_pgm_check_psw:
-       .quad   0,0
-#ifdef CONFIG_SMP
-saved_cpu_id:
-       .long   0
-#endif
+.Lresume_cpu:
+       .byte   0,0