aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel
diff options
context:
space:
mode:
authorMichael Holzheu <michael.holzheu@linux.vnet.ibm.com>2009-09-22 16:58:53 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-09-22 16:58:46 -0400
commit1aaf179d043856d80bbb354f9feaf706b9cfbcd3 (patch)
treeb477b19f229869f45fc9f331f308dcdb5e3b01a0 /arch/s390/kernel
parent68d1e5f08b13132504752cad54169376739753db (diff)
[S390] hibernate: Do real CPU swap at resume time
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>
Diffstat (limited to 'arch/s390/kernel')
-rw-r--r--arch/s390/kernel/asm-offsets.c7
-rw-r--r--arch/s390/kernel/sclp.S5
-rw-r--r--arch/s390/kernel/smp.c36
-rw-r--r--arch/s390/kernel/swsusp_asm64.S91
4 files changed, 70 insertions, 69 deletions
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index fa9905ce7d0b..63e46433e81d 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -7,6 +7,7 @@
7#include <linux/sched.h> 7#include <linux/sched.h>
8#include <linux/kbuild.h> 8#include <linux/kbuild.h>
9#include <asm/vdso.h> 9#include <asm/vdso.h>
10#include <asm/sigp.h>
10 11
11int main(void) 12int main(void)
12{ 13{
@@ -59,6 +60,10 @@ int main(void)
59 DEFINE(CLOCK_REALTIME, CLOCK_REALTIME); 60 DEFINE(CLOCK_REALTIME, CLOCK_REALTIME);
60 DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC); 61 DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC);
61 DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); 62 DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC);
62 63 /* constants for SIGP */
64 DEFINE(__SIGP_STOP, sigp_stop);
65 DEFINE(__SIGP_RESTART, sigp_restart);
66 DEFINE(__SIGP_SENSE, sigp_sense);
67 DEFINE(__SIGP_INITIAL_CPU_RESET, sigp_initial_cpu_reset);
63 return 0; 68 return 0;
64} 69}
diff --git a/arch/s390/kernel/sclp.S b/arch/s390/kernel/sclp.S
index 20639dfe0c42..e27ca63076d1 100644
--- a/arch/s390/kernel/sclp.S
+++ b/arch/s390/kernel/sclp.S
@@ -24,8 +24,6 @@ LC_EXT_INT_CODE = 0x86 # addr of ext int code
24# R3 = external interruption parameter if R2=0 24# R3 = external interruption parameter if R2=0
25# 25#
26 26
27.section ".init.text","ax"
28
29_sclp_wait_int: 27_sclp_wait_int:
30 stm %r6,%r15,24(%r15) # save registers 28 stm %r6,%r15,24(%r15) # save registers
31 basr %r13,0 # get base register 29 basr %r13,0 # get base register
@@ -318,9 +316,8 @@ _sclp_print_early:
318 .long _sclp_work_area 316 .long _sclp_work_area
319.Lascebc: 317.Lascebc:
320 .long _ascebc 318 .long _ascebc
321.previous
322 319
323.section ".init.data","a" 320.section .data,"aw",@progbits
324 .balign 4096 321 .balign 4096
325_sclp_work_area: 322_sclp_work_area:
326 .fill 4096 323 .fill 4096
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 6f14734abe72..b4b6396e6cf0 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -1041,42 +1041,6 @@ out:
1041static SYSDEV_CLASS_ATTR(dispatching, 0644, dispatching_show, 1041static SYSDEV_CLASS_ATTR(dispatching, 0644, dispatching_show,
1042 dispatching_store); 1042 dispatching_store);
1043 1043
1044/*
1045 * If the resume kernel runs on another cpu than the suspended kernel,
1046 * we have to switch the cpu IDs in the logical map.
1047 */
1048void smp_switch_boot_cpu_in_resume(u32 resume_phys_cpu_id,
1049 struct _lowcore *suspend_lowcore)
1050{
1051 int cpu, suspend_cpu_id, resume_cpu_id;
1052 u32 suspend_phys_cpu_id;
1053
1054 suspend_phys_cpu_id = __cpu_logical_map[suspend_lowcore->cpu_nr];
1055 suspend_cpu_id = suspend_lowcore->cpu_nr;
1056
1057 for_each_present_cpu(cpu) {
1058 if (__cpu_logical_map[cpu] == resume_phys_cpu_id) {
1059 resume_cpu_id = cpu;
1060 goto found;
1061 }
1062 }
1063 panic("Could not find resume cpu in logical map.\n");
1064
1065found:
1066 printk("Resume cpu ID: %i/%i\n", resume_phys_cpu_id, resume_cpu_id);
1067 printk("Suspend cpu ID: %i/%i\n", suspend_phys_cpu_id, suspend_cpu_id);
1068
1069 __cpu_logical_map[resume_cpu_id] = suspend_phys_cpu_id;
1070 __cpu_logical_map[suspend_cpu_id] = resume_phys_cpu_id;
1071
1072 lowcore_ptr[suspend_cpu_id]->cpu_addr = resume_phys_cpu_id;
1073}
1074
1075u32 smp_get_phys_cpu_id(void)
1076{
1077 return __cpu_logical_map[smp_processor_id()];
1078}
1079
1080static int __init topology_init(void) 1044static int __init topology_init(void)
1081{ 1045{
1082 int cpu; 1046 int cpu;
diff --git a/arch/s390/kernel/swsusp_asm64.S b/arch/s390/kernel/swsusp_asm64.S
index fc056810a017..fe927d0bc20b 100644
--- a/arch/s390/kernel/swsusp_asm64.S
+++ b/arch/s390/kernel/swsusp_asm64.S
@@ -9,6 +9,7 @@
9 9
10#include <asm/page.h> 10#include <asm/page.h>
11#include <asm/ptrace.h> 11#include <asm/ptrace.h>
12#include <asm/thread_info.h>
12#include <asm/asm-offsets.h> 13#include <asm/asm-offsets.h>
13 14
14/* 15/*
@@ -41,6 +42,9 @@ swsusp_arch_suspend:
41 /* Get pointer to save area */ 42 /* Get pointer to save area */
42 lghi %r1,0x1000 43 lghi %r1,0x1000
43 44
45 /* Save CPU address */
46 stap __LC_CPU_ADDRESS(%r1)
47
44 /* Store registers */ 48 /* Store registers */
45 mvc 0x318(4,%r1),__SF_EMPTY(%r15) /* move prefix to lowcore */ 49 mvc 0x318(4,%r1),__SF_EMPTY(%r15) /* move prefix to lowcore */
46 stfpc 0x31c(%r1) /* store fpu control */ 50 stfpc 0x31c(%r1) /* store fpu control */
@@ -105,12 +109,7 @@ swsusp_arch_resume:
105 /* Make all free pages stable */ 109 /* Make all free pages stable */
106 lghi %r2,1 110 lghi %r2,1
107 brasl %r14,arch_set_page_states 111 brasl %r14,arch_set_page_states
108#ifdef CONFIG_SMP 112
109 /* Save boot cpu number */
110 brasl %r14,smp_get_phys_cpu_id
111 larl %r1,saved_cpu_id
112 st %r2,0(%r1)
113#endif
114 /* Deactivate DAT */ 113 /* Deactivate DAT */
115 stnsm __SF_EMPTY(%r15),0xfb 114 stnsm __SF_EMPTY(%r15),0xfb
116 115
@@ -139,12 +138,10 @@ swsusp_arch_resume:
139 138
140 /* Reset System */ 139 /* Reset System */
141 larl %r1,restart_entry 140 larl %r1,restart_entry
142 larl %r2,restart_psw 141 larl %r2,.Lrestart_diag308_psw
143 og %r1,0(%r2) 142 og %r1,0(%r2)
144 stg %r1,0(%r0) 143 stg %r1,0(%r0)
145 larl %r1,saved_pgm_check_psw 144 larl %r1,.Lnew_pgm_check_psw
146 mvc 0(16,%r1),__LC_PGM_NEW_PSW(%r0)
147 larl %r1,new_pgm_check_psw
148 epsw %r2,%r3 145 epsw %r2,%r3
149 stm %r2,%r3,0(%r1) 146 stm %r2,%r3,0(%r1)
150 mvc __LC_PGM_NEW_PSW(16,%r0),0(%r1) 147 mvc __LC_PGM_NEW_PSW(16,%r0),0(%r1)
@@ -154,12 +151,54 @@ restart_entry:
154 lhi %r1,1 151 lhi %r1,1
155 sigp %r1,%r0,0x12 152 sigp %r1,%r0,0x12
156 sam64 153 sam64
157 larl %r1,new_pgm_check_psw 154 larl %r1,.Lnew_pgm_check_psw
158 lpswe 0(%r1) 155 lpswe 0(%r1)
159pgm_check_entry: 156pgm_check_entry:
160 larl %r1,saved_pgm_check_psw
161 mvc __LC_PGM_NEW_PSW(16,%r0),0(%r1)
162 157
158 /* Switch to original suspend CPU */
159 larl %r1,.Lresume_cpu /* Resume CPU address: r2 */
160 stap 0(%r1)
161 llgh %r2,0(%r1)
162 lghi %r3,0x1000
163 llgh %r1,__LC_CPU_ADDRESS(%r3) /* Suspend CPU address: r1 */
164 cgr %r1,%r2
165 je restore_registers /* r1 = r2 -> nothing to do */
166 larl %r4,.Lrestart_suspend_psw /* Set new restart PSW */
167 mvc __LC_RESTART_PSW(16,%r0),0(%r4)
1683:
169 sigp %r9,%r1,__SIGP_INITIAL_CPU_RESET
170 brc 8,4f /* accepted */
171 brc 2,3b /* busy, try again */
172
173 /* Suspend CPU not available -> panic */
174 larl %r15,init_thread_union
175 ahi %r15,1<<(PAGE_SHIFT+THREAD_ORDER)
176 larl %r2,.Lpanic_string
177 larl %r3,_sclp_print_early
178 lghi %r1,0
179 sam31
180 sigp %r1,%r0,0x12
181 basr %r14,%r3
182 larl %r3,.Ldisabled_wait_31
183 lpsw 0(%r3)
1844:
185 /* Switch to suspend CPU */
186 sigp %r9,%r1,__SIGP_RESTART /* start suspend CPU */
187 brc 2,4b /* busy, try again */
1885:
189 sigp %r9,%r2,__SIGP_STOP /* stop resume (current) CPU */
1906: j 6b
191
192restart_suspend:
193 larl %r1,.Lresume_cpu
194 llgh %r2,0(%r1)
1957:
196 sigp %r9,%r2,__SIGP_SENSE /* Wait for resume CPU */
197 brc 2,7b /* busy, try again */
198 tmll %r9,0x40 /* Test if resume CPU is stopped */
199 jz 7b
200
201restore_registers:
163 /* Restore registers */ 202 /* Restore registers */
164 lghi %r13,0x1000 /* %r1 = pointer to save arae */ 203 lghi %r13,0x1000 /* %r1 = pointer to save arae */
165 204
@@ -193,13 +232,6 @@ pgm_check_entry:
193 /* Pointer to save area */ 232 /* Pointer to save area */
194 lghi %r13,0x1000 233 lghi %r13,0x1000
195 234
196#ifdef CONFIG_SMP
197 /* Switch CPUs */
198 larl %r1,saved_cpu_id
199 llgf %r2,0(%r1)
200 llgf %r3,0x318(%r13)
201 brasl %r14,smp_switch_boot_cpu_in_resume
202#endif
203 /* Restore prefix register */ 235 /* Restore prefix register */
204 spx 0x318(%r13) 236 spx 0x318(%r13)
205 237
@@ -217,13 +249,16 @@ pgm_check_entry:
217 249
218 .section .data.nosave,"aw",@progbits 250 .section .data.nosave,"aw",@progbits
219 .align 8 251 .align 8
220restart_psw: 252.Ldisabled_wait_31:
253 .long 0x000a0000,0x00000000
254.Lpanic_string:
255 .asciz "Resume not possible because suspend CPU is no longer available"
256 .align 8
257.Lrestart_diag308_psw:
221 .long 0x00080000,0x80000000 258 .long 0x00080000,0x80000000
222new_pgm_check_psw: 259.Lrestart_suspend_psw:
260 .quad 0x0000000180000000,restart_suspend
261.Lnew_pgm_check_psw:
223 .quad 0,pgm_check_entry 262 .quad 0,pgm_check_entry
224saved_pgm_check_psw: 263.Lresume_cpu:
225 .quad 0,0 264 .byte 0,0
226#ifdef CONFIG_SMP
227saved_cpu_id:
228 .long 0
229#endif