Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 267079
b: refs/heads/master
c: 85055dd
h: refs/heads/master
i:
  267077: e10c5de
  267075: 85bcac4
  267071: bc4d8db
v: v3
  • Loading branch information
Martin Schwidefsky authored and Rafael J. Wysocki committed Oct 16, 2011
1 parent c5747fb commit af28ad1
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: ca123102f69fb260221502ade9bbc069290fae84
refs/heads/master: 85055dd805f0822f13f736bee2a521e222c38293
1 change: 1 addition & 0 deletions trunk/arch/s390/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ config S390
select HAVE_ARCH_MUTEX_CPU_RELAX
select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
select HAVE_RCU_TABLE_FREE if SMP
select ARCH_SAVE_PAGE_KEYS if HIBERNATION
select ARCH_INLINE_SPIN_TRYLOCK
select ARCH_INLINE_SPIN_TRYLOCK_BH
select ARCH_INLINE_SPIN_LOCK
Expand Down
118 changes: 118 additions & 0 deletions trunk/arch/s390/kernel/suspend.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,131 @@
*/

#include <linux/pfn.h>
#include <linux/mm.h>
#include <asm/system.h>

/*
* References to section boundaries
*/
extern const void __nosave_begin, __nosave_end;

/*
* The restore of the saved pages in an hibernation image will set
* the change and referenced bits in the storage key for each page.
* Overindication of the referenced bits after an hibernation cycle
* does not cause any harm but the overindication of the change bits
* would cause trouble.
* Use the ARCH_SAVE_PAGE_KEYS hooks to save the storage key of each
* page to the most significant byte of the associated page frame
* number in the hibernation image.
*/

/*
* Key storage is allocated as a linked list of pages.
* The size of the keys array is (PAGE_SIZE - sizeof(long))
*/
struct page_key_data {
struct page_key_data *next;
unsigned char data[];
};

#define PAGE_KEY_DATA_SIZE (PAGE_SIZE - sizeof(struct page_key_data *))

static struct page_key_data *page_key_data;
static struct page_key_data *page_key_rp, *page_key_wp;
static unsigned long page_key_rx, page_key_wx;

/*
* For each page in the hibernation image one additional byte is
* stored in the most significant byte of the page frame number.
* On suspend no additional memory is required but on resume the
* keys need to be memorized until the page data has been restored.
* Only then can the storage keys be set to their old state.
*/
unsigned long page_key_additional_pages(unsigned long pages)
{
return DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE);
}

/*
* Free page_key_data list of arrays.
*/
void page_key_free(void)
{
struct page_key_data *pkd;

while (page_key_data) {
pkd = page_key_data;
page_key_data = pkd->next;
free_page((unsigned long) pkd);
}
}

/*
* Allocate page_key_data list of arrays with enough room to store
* one byte for each page in the hibernation image.
*/
int page_key_alloc(unsigned long pages)
{
struct page_key_data *pk;
unsigned long size;

size = DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE);
while (size--) {
pk = (struct page_key_data *) get_zeroed_page(GFP_KERNEL);
if (!pk) {
page_key_free();
return -ENOMEM;
}
pk->next = page_key_data;
page_key_data = pk;
}
page_key_rp = page_key_wp = page_key_data;
page_key_rx = page_key_wx = 0;
return 0;
}

/*
* Save the storage key into the upper 8 bits of the page frame number.
*/
void page_key_read(unsigned long *pfn)
{
unsigned long addr;

addr = (unsigned long) page_address(pfn_to_page(*pfn));
*(unsigned char *) pfn = (unsigned char) page_get_storage_key(addr);
}

/*
* Extract the storage key from the upper 8 bits of the page frame number
* and store it in the page_key_data list of arrays.
*/
void page_key_memorize(unsigned long *pfn)
{
page_key_wp->data[page_key_wx] = *(unsigned char *) pfn;
*(unsigned char *) pfn = 0;
if (++page_key_wx < PAGE_KEY_DATA_SIZE)
return;
page_key_wp = page_key_wp->next;
page_key_wx = 0;
}

/*
* Get the next key from the page_key_data list of arrays and set the
* storage key of the page referred by @address. If @address refers to
* a "safe" page the swsusp_arch_resume code will transfer the storage
* key from the buffer page to the original page.
*/
void page_key_write(void *address)
{
page_set_storage_key((unsigned long) address,
page_key_rp->data[page_key_rx], 0);
if (++page_key_rx >= PAGE_KEY_DATA_SIZE)
return;
page_key_rp = page_key_rp->next;
page_key_rx = 0;
}

int pfn_is_nosave(unsigned long pfn)
{
unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin));
Expand Down
3 changes: 3 additions & 0 deletions trunk/arch/s390/kernel/swsusp_asm64.S
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,14 @@ ENTRY(swsusp_arch_resume)
0:
lg %r2,8(%r1)
lg %r4,0(%r1)
iske %r0,%r4
lghi %r3,PAGE_SIZE
lghi %r5,PAGE_SIZE
1:
mvcle %r2,%r4,0
jo 1b
lg %r2,8(%r1)
sske %r0,%r2
lg %r1,16(%r1)
ltgr %r1,%r1
jnz 0b
Expand Down
34 changes: 34 additions & 0 deletions trunk/include/linux/suspend.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,4 +386,38 @@ static inline void unlock_system_sleep(void)
}
#endif

#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS
/*
* The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
* to save/restore additional information to/from the array of page
* frame numbers in the hibernation image. For s390 this is used to
* save and restore the storage key for each page that is included
* in the hibernation image.
*/
unsigned long page_key_additional_pages(unsigned long pages);
int page_key_alloc(unsigned long pages);
void page_key_free(void);
void page_key_read(unsigned long *pfn);
void page_key_memorize(unsigned long *pfn);
void page_key_write(void *address);

#else /* !CONFIG_ARCH_SAVE_PAGE_KEYS */

static inline unsigned long page_key_additional_pages(unsigned long pages)
{
return 0;
}

static inline int page_key_alloc(unsigned long pages)
{
return 0;
}

static inline void page_key_free(void) {}
static inline void page_key_read(unsigned long *pfn) {}
static inline void page_key_memorize(unsigned long *pfn) {}
static inline void page_key_write(void *address) {}

#endif /* !CONFIG_ARCH_SAVE_PAGE_KEYS */

#endif /* _LINUX_SUSPEND_H */
3 changes: 3 additions & 0 deletions trunk/kernel/power/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ config HIBERNATION

For more information take a look at <file:Documentation/power/swsusp.txt>.

config ARCH_SAVE_PAGE_KEYS
bool

config PM_STD_PARTITION
string "Default resume partition"
depends on HIBERNATION
Expand Down
18 changes: 18 additions & 0 deletions trunk/kernel/power/snapshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,9 @@ int hibernate_preallocate_memory(void)
count += highmem;
count -= totalreserve_pages;

/* Add number of pages required for page keys (s390 only). */
size += page_key_additional_pages(saveable);

/* Compute the maximum number of saveable pages to leave in memory. */
max_size = (count - (size + PAGES_FOR_IO)) / 2
- 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE);
Expand Down Expand Up @@ -1662,6 +1665,8 @@ pack_pfns(unsigned long *buf, struct memory_bitmap *bm)
buf[j] = memory_bm_next_pfn(bm);
if (unlikely(buf[j] == BM_END_OF_MAP))
break;
/* Save page key for data page (s390 only). */
page_key_read(buf + j);
}
}

Expand Down Expand Up @@ -1821,6 +1826,9 @@ static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
if (unlikely(buf[j] == BM_END_OF_MAP))
break;

/* Extract and buffer page key for data page (s390 only). */
page_key_memorize(buf + j);

if (memory_bm_pfn_present(bm, buf[j]))
memory_bm_set_bit(bm, buf[j]);
else
Expand Down Expand Up @@ -2223,6 +2231,11 @@ int snapshot_write_next(struct snapshot_handle *handle)
if (error)
return error;

/* Allocate buffer for page keys. */
error = page_key_alloc(nr_copy_pages);
if (error)
return error;

} else if (handle->cur <= nr_meta_pages + 1) {
error = unpack_orig_pfns(buffer, &copy_bm);
if (error)
Expand All @@ -2243,6 +2256,8 @@ int snapshot_write_next(struct snapshot_handle *handle)
}
} else {
copy_last_highmem_page();
/* Restore page key for data page (s390 only). */
page_key_write(handle->buffer);
handle->buffer = get_buffer(&orig_bm, &ca);
if (IS_ERR(handle->buffer))
return PTR_ERR(handle->buffer);
Expand All @@ -2264,6 +2279,9 @@ int snapshot_write_next(struct snapshot_handle *handle)
void snapshot_write_finalize(struct snapshot_handle *handle)
{
copy_last_highmem_page();
/* Restore page key for data page (s390 only). */
page_key_write(handle->buffer);
page_key_free();
/* Free only if we have loaded the image entirely */
if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) {
memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR);
Expand Down

0 comments on commit af28ad1

Please sign in to comment.