aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2011-08-17 14:42:24 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2011-10-16 17:27:46 -0400
commit85055dd805f0822f13f736bee2a521e222c38293 (patch)
treeedbad90395fc7471732edfb5372adcd7e55e791b
parentca123102f69fb260221502ade9bbc069290fae84 (diff)
PM / Hibernate: Include storage keys in hibernation image on s390
For s390 there is one additional byte associated with each page, the storage key. This byte contains the referenced and changed bits and needs to be included into the hibernation image. If the storage keys are not restored to their previous state all original pages would appear to be dirty. This can cause inconsistencies e.g. with read-only filesystems. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r--arch/s390/Kconfig1
-rw-r--r--arch/s390/kernel/suspend.c118
-rw-r--r--arch/s390/kernel/swsusp_asm64.S3
-rw-r--r--include/linux/suspend.h34
-rw-r--r--kernel/power/Kconfig3
-rw-r--r--kernel/power/snapshot.c18
6 files changed, 177 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
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 76f42e49b72d..46f3548aef2d 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -386,4 +386,38 @@ static inline void unlock_system_sleep(void)
386} 386}
387#endif 387#endif
388 388
389#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS
390/*
391 * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
392 * to save/restore additional information to/from the array of page
393 * frame numbers in the hibernation image. For s390 this is used to
394 * save and restore the storage key for each page that is included
395 * in the hibernation image.
396 */
397unsigned long page_key_additional_pages(unsigned long pages);
398int page_key_alloc(unsigned long pages);
399void page_key_free(void);
400void page_key_read(unsigned long *pfn);
401void page_key_memorize(unsigned long *pfn);
402void page_key_write(void *address);
403
404#else /* !CONFIG_ARCH_SAVE_PAGE_KEYS */
405
406static inline unsigned long page_key_additional_pages(unsigned long pages)
407{
408 return 0;
409}
410
411static inline int page_key_alloc(unsigned long pages)
412{
413 return 0;
414}
415
416static inline void page_key_free(void) {}
417static inline void page_key_read(unsigned long *pfn) {}
418static inline void page_key_memorize(unsigned long *pfn) {}
419static inline void page_key_write(void *address) {}
420
421#endif /* !CONFIG_ARCH_SAVE_PAGE_KEYS */
422
389#endif /* _LINUX_SUSPEND_H */ 423#endif /* _LINUX_SUSPEND_H */
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 3744c594b19b..e01e6899592c 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -65,6 +65,9 @@ config HIBERNATION
65 65
66 For more information take a look at <file:Documentation/power/swsusp.txt>. 66 For more information take a look at <file:Documentation/power/swsusp.txt>.
67 67
68config ARCH_SAVE_PAGE_KEYS
69 bool
70
68config PM_STD_PARTITION 71config PM_STD_PARTITION
69 string "Default resume partition" 72 string "Default resume partition"
70 depends on HIBERNATION 73 depends on HIBERNATION
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index 06efa54f93d6..cbe2c1441392 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -1339,6 +1339,9 @@ int hibernate_preallocate_memory(void)
1339 count += highmem; 1339 count += highmem;
1340 count -= totalreserve_pages; 1340 count -= totalreserve_pages;
1341 1341
1342 /* Add number of pages required for page keys (s390 only). */
1343 size += page_key_additional_pages(saveable);
1344
1342 /* Compute the maximum number of saveable pages to leave in memory. */ 1345 /* Compute the maximum number of saveable pages to leave in memory. */
1343 max_size = (count - (size + PAGES_FOR_IO)) / 2 1346 max_size = (count - (size + PAGES_FOR_IO)) / 2
1344 - 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE); 1347 - 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE);
@@ -1662,6 +1665,8 @@ pack_pfns(unsigned long *buf, struct memory_bitmap *bm)
1662 buf[j] = memory_bm_next_pfn(bm); 1665 buf[j] = memory_bm_next_pfn(bm);
1663 if (unlikely(buf[j] == BM_END_OF_MAP)) 1666 if (unlikely(buf[j] == BM_END_OF_MAP))
1664 break; 1667 break;
1668 /* Save page key for data page (s390 only). */
1669 page_key_read(buf + j);
1665 } 1670 }
1666} 1671}
1667 1672
@@ -1821,6 +1826,9 @@ static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
1821 if (unlikely(buf[j] == BM_END_OF_MAP)) 1826 if (unlikely(buf[j] == BM_END_OF_MAP))
1822 break; 1827 break;
1823 1828
1829 /* Extract and buffer page key for data page (s390 only). */
1830 page_key_memorize(buf + j);
1831
1824 if (memory_bm_pfn_present(bm, buf[j])) 1832 if (memory_bm_pfn_present(bm, buf[j]))
1825 memory_bm_set_bit(bm, buf[j]); 1833 memory_bm_set_bit(bm, buf[j]);
1826 else 1834 else
@@ -2223,6 +2231,11 @@ int snapshot_write_next(struct snapshot_handle *handle)
2223 if (error) 2231 if (error)
2224 return error; 2232 return error;
2225 2233
2234 /* Allocate buffer for page keys. */
2235 error = page_key_alloc(nr_copy_pages);
2236 if (error)
2237 return error;
2238
2226 } else if (handle->cur <= nr_meta_pages + 1) { 2239 } else if (handle->cur <= nr_meta_pages + 1) {
2227 error = unpack_orig_pfns(buffer, &copy_bm); 2240 error = unpack_orig_pfns(buffer, &copy_bm);
2228 if (error) 2241 if (error)
@@ -2243,6 +2256,8 @@ int snapshot_write_next(struct snapshot_handle *handle)
2243 } 2256 }
2244 } else { 2257 } else {
2245 copy_last_highmem_page(); 2258 copy_last_highmem_page();
2259 /* Restore page key for data page (s390 only). */
2260 page_key_write(handle->buffer);
2246 handle->buffer = get_buffer(&orig_bm, &ca); 2261 handle->buffer = get_buffer(&orig_bm, &ca);
2247 if (IS_ERR(handle->buffer)) 2262 if (IS_ERR(handle->buffer))
2248 return PTR_ERR(handle->buffer); 2263 return PTR_ERR(handle->buffer);
@@ -2264,6 +2279,9 @@ int snapshot_write_next(struct snapshot_handle *handle)
2264void snapshot_write_finalize(struct snapshot_handle *handle) 2279void snapshot_write_finalize(struct snapshot_handle *handle)
2265{ 2280{
2266 copy_last_highmem_page(); 2281 copy_last_highmem_page();
2282 /* Restore page key for data page (s390 only). */
2283 page_key_write(handle->buffer);
2284 page_key_free();
2267 /* Free only if we have loaded the image entirely */ 2285 /* Free only if we have loaded the image entirely */
2268 if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) { 2286 if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) {
2269 memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR); 2287 memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR);