aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-10-25 09:18:39 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-10-25 09:18:39 -0400
commit7e0bb71e75020348bee523720a0c2f04cc72f540 (patch)
tree1a22d65bbce34e8cc0f82c543c9486ffb58332f7 /arch/s390
parentb9e2780d576a010d4aba1e69f247170bf3718d6b (diff)
parent0ab1e79b825a5cd8aeb3b34d89c9a89dea900056 (diff)
Merge branch 'pm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
* 'pm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (63 commits) PM / Clocks: Remove redundant NULL checks before kfree() PM / Documentation: Update docs about suspend and CPU hotplug ACPI / PM: Add Sony VGN-FW21E to nonvs blacklist. ARM: mach-shmobile: sh7372 A4R support (v4) ARM: mach-shmobile: sh7372 A3SP support (v4) PM / Sleep: Mark devices involved in wakeup signaling during suspend PM / Hibernate: Improve performance of LZO/plain hibernation, checksum image PM / Hibernate: Do not initialize static and extern variables to 0 PM / Freezer: Make fake_signal_wake_up() wake TASK_KILLABLE tasks too PM / Hibernate: Add resumedelay kernel param in addition to resumewait MAINTAINERS: Update linux-pm list address PM / ACPI: Blacklist Vaio VGN-FW520F machine known to require acpi_sleep=nonvs PM / ACPI: Blacklist Sony Vaio known to require acpi_sleep=nonvs PM / Hibernate: Add resumewait param to support MMC-like devices as resume file PM / Hibernate: Fix typo in a kerneldoc comment PM / Hibernate: Freeze kernel threads after preallocating memory PM: Update the policy on default wakeup settings PM / VT: Cleanup #if defined uglyness and fix compile error PM / Suspend: Off by one in pm_suspend() PM / Hibernate: Include storage keys in hibernation image on s390 ...
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/Kconfig1
-rw-r--r--arch/s390/kernel/suspend.c118
-rw-r--r--arch/s390/kernel/swsusp_asm64.S3
3 files changed, 122 insertions, 0 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index ed5cb5af5281..6b99fc3f9b63 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -91,6 +91,7 @@ config S390
91 select HAVE_ARCH_MUTEX_CPU_RELAX 91 select HAVE_ARCH_MUTEX_CPU_RELAX
92 select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 92 select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
93 select HAVE_RCU_TABLE_FREE if SMP 93 select HAVE_RCU_TABLE_FREE if SMP
94 select ARCH_SAVE_PAGE_KEYS if HIBERNATION
94 select ARCH_INLINE_SPIN_TRYLOCK 95 select ARCH_INLINE_SPIN_TRYLOCK
95 select ARCH_INLINE_SPIN_TRYLOCK_BH 96 select ARCH_INLINE_SPIN_TRYLOCK_BH
96 select ARCH_INLINE_SPIN_LOCK 97 select ARCH_INLINE_SPIN_LOCK
diff --git a/arch/s390/kernel/suspend.c b/arch/s390/kernel/suspend.c
index cf9e5c6d5527..b6f9afed74ec 100644
--- a/arch/s390/kernel/suspend.c
+++ b/arch/s390/kernel/suspend.c
@@ -7,6 +7,7 @@
7 */ 7 */
8 8
9#include <linux/pfn.h> 9#include <linux/pfn.h>
10#include <linux/mm.h>
10#include <asm/system.h> 11#include <asm/system.h>
11 12
12/* 13/*
@@ -14,6 +15,123 @@
14 */ 15 */
15extern const void __nosave_begin, __nosave_end; 16extern const void __nosave_begin, __nosave_end;
16 17
18/*
19 * The restore of the saved pages in an hibernation image will set
20 * the change and referenced bits in the storage key for each page.
21 * Overindication of the referenced bits after an hibernation cycle
22 * does not cause any harm but the overindication of the change bits
23 * would cause trouble.
24 * Use the ARCH_SAVE_PAGE_KEYS hooks to save the storage key of each
25 * page to the most significant byte of the associated page frame
26 * number in the hibernation image.
27 */
28
29/*
30 * Key storage is allocated as a linked list of pages.
31 * The size of the keys array is (PAGE_SIZE - sizeof(long))
32 */
33struct page_key_data {
34 struct page_key_data *next;
35 unsigned char data[];
36};
37
38#define PAGE_KEY_DATA_SIZE (PAGE_SIZE - sizeof(struct page_key_data *))
39
40static struct page_key_data *page_key_data;
41static struct page_key_data *page_key_rp, *page_key_wp;
42static unsigned long page_key_rx, page_key_wx;
43
44/*
45 * For each page in the hibernation image one additional byte is
46 * stored in the most significant byte of the page frame number.
47 * On suspend no additional memory is required but on resume the
48 * keys need to be memorized until the page data has been restored.
49 * Only then can the storage keys be set to their old state.
50 */
51unsigned long page_key_additional_pages(unsigned long pages)
52{
53 return DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE);
54}
55
56/*
57 * Free page_key_data list of arrays.
58 */
59void page_key_free(void)
60{
61 struct page_key_data *pkd;
62
63 while (page_key_data) {
64 pkd = page_key_data;
65 page_key_data = pkd->next;
66 free_page((unsigned long) pkd);
67 }
68}
69
70/*
71 * Allocate page_key_data list of arrays with enough room to store
72 * one byte for each page in the hibernation image.
73 */
74int page_key_alloc(unsigned long pages)
75{
76 struct page_key_data *pk;
77 unsigned long size;
78
79 size = DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE);
80 while (size--) {
81 pk = (struct page_key_data *) get_zeroed_page(GFP_KERNEL);
82 if (!pk) {
83 page_key_free();
84 return -ENOMEM;
85 }
86 pk->next = page_key_data;
87 page_key_data = pk;
88 }
89 page_key_rp = page_key_wp = page_key_data;
90 page_key_rx = page_key_wx = 0;
91 return 0;
92}
93
94/*
95 * Save the storage key into the upper 8 bits of the page frame number.
96 */
97void page_key_read(unsigned long *pfn)
98{
99 unsigned long addr;
100
101 addr = (unsigned long) page_address(pfn_to_page(*pfn));
102 *(unsigned char *) pfn = (unsigned char) page_get_storage_key(addr);
103}
104
105/*
106 * Extract the storage key from the upper 8 bits of the page frame number
107 * and store it in the page_key_data list of arrays.
108 */
109void page_key_memorize(unsigned long *pfn)
110{
111 page_key_wp->data[page_key_wx] = *(unsigned char *) pfn;
112 *(unsigned char *) pfn = 0;
113 if (++page_key_wx < PAGE_KEY_DATA_SIZE)
114 return;
115 page_key_wp = page_key_wp->next;
116 page_key_wx = 0;
117}
118
119/*
120 * Get the next key from the page_key_data list of arrays and set the
121 * storage key of the page referred by @address. If @address refers to
122 * a "safe" page the swsusp_arch_resume code will transfer the storage
123 * key from the buffer page to the original page.
124 */
125void page_key_write(void *address)
126{
127 page_set_storage_key((unsigned long) address,
128 page_key_rp->data[page_key_rx], 0);
129 if (++page_key_rx >= PAGE_KEY_DATA_SIZE)
130 return;
131 page_key_rp = page_key_rp->next;
132 page_key_rx = 0;
133}
134
17int pfn_is_nosave(unsigned long pfn) 135int pfn_is_nosave(unsigned long pfn)
18{ 136{
19 unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin)); 137 unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin));
diff --git a/arch/s390/kernel/swsusp_asm64.S b/arch/s390/kernel/swsusp_asm64.S
index 51bcdb50a230..acb78cdee896 100644
--- a/arch/s390/kernel/swsusp_asm64.S
+++ b/arch/s390/kernel/swsusp_asm64.S
@@ -136,11 +136,14 @@ ENTRY(swsusp_arch_resume)
1360: 1360:
137 lg %r2,8(%r1) 137 lg %r2,8(%r1)
138 lg %r4,0(%r1) 138 lg %r4,0(%r1)
139 iske %r0,%r4
139 lghi %r3,PAGE_SIZE 140 lghi %r3,PAGE_SIZE
140 lghi %r5,PAGE_SIZE 141 lghi %r5,PAGE_SIZE
1411: 1421:
142 mvcle %r2,%r4,0 143 mvcle %r2,%r4,0
143 jo 1b 144 jo 1b
145 lg %r2,8(%r1)
146 sske %r0,%r2
144 lg %r1,16(%r1) 147 lg %r1,16(%r1)
145 ltgr %r1,%r1 148 ltgr %r1,%r1
146 jnz 0b 149 jnz 0b