Re: [patch 1/1] [PATCH] include storage keys in hibernation image.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tuesday, August 16, 2011, Martin Schwidefsky wrote:
> From: Martin Schwidefsky <schwidefsky@xxxxxxxxxx>
> 
> 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.
> 
> Cc: Pavel Machek <pavel@xxxxxx>
> Cc: Rafael J. Wysocki <rjw@xxxxxxx>
> Cc: Jiri Slaby <jslaby@xxxxxxx>
> Cc: linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx
> Cc: linux-kernel@xxxxxxxxxxxxxxx
> Cc: linux-s390@xxxxxxxxxxxxxxx
> Signed-off-by: Martin Schwidefsky <schwidefsky@xxxxxxxxxx>

OK, I don't have any complaints.  Do you want me to take this
patch or do you want to push it through the s390 tree?

Rafael


> ---
> 
>  arch/s390/Kconfig               |    1 
>  arch/s390/kernel/suspend.c      |  118 ++++++++++++++++++++++++++++++++++++++++
>  arch/s390/kernel/swsusp_asm64.S |    3 +
>  include/linux/suspend.h         |   34 +++++++++++
>  kernel/power/Kconfig            |    3 +
>  kernel/power/snapshot.c         |   18 ++++++
>  6 files changed, 177 insertions(+)
> 
> Index: hibernate-2.6/arch/s390/Kconfig
> ===================================================================
> --- hibernate-2.6.orig/arch/s390/Kconfig	2011-08-09 17:40:09.220010373 +0200
> +++ hibernate-2.6/arch/s390/Kconfig	2011-08-16 16:06:09.845540931 +0200
> @@ -91,6 +91,7 @@
>  	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
> Index: hibernate-2.6/arch/s390/kernel/suspend.c
> ===================================================================
> --- hibernate-2.6.orig/arch/s390/kernel/suspend.c	2009-09-24 09:06:44.000000000 +0200
> +++ hibernate-2.6/arch/s390/kernel/suspend.c	2011-08-16 16:06:09.845540931 +0200
> @@ -7,6 +7,7 @@
>   */
>  
>  #include <linux/pfn.h>
> +#include <linux/mm.h>
>  #include <asm/system.h>
>  
>  /*
> @@ -14,6 +15,123 @@
>   */
>  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));
> Index: hibernate-2.6/arch/s390/kernel/swsusp_asm64.S
> ===================================================================
> --- hibernate-2.6.orig/arch/s390/kernel/swsusp_asm64.S	2011-08-09 17:40:09.228010544 +0200
> +++ hibernate-2.6/arch/s390/kernel/swsusp_asm64.S	2011-08-16 16:06:09.845540931 +0200
> @@ -136,11 +136,14 @@
>  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
> Index: hibernate-2.6/include/linux/suspend.h
> ===================================================================
> --- hibernate-2.6.orig/include/linux/suspend.h	2011-08-09 17:40:10.164030551 +0200
> +++ hibernate-2.6/include/linux/suspend.h	2011-08-16 16:06:09.845540931 +0200
> @@ -334,4 +334,38 @@
>  }
>  #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 */
> Index: hibernate-2.6/kernel/power/Kconfig
> ===================================================================
> --- hibernate-2.6.orig/kernel/power/Kconfig	2011-08-09 17:40:10.192031153 +0200
> +++ hibernate-2.6/kernel/power/Kconfig	2011-08-16 16:06:09.845540931 +0200
> @@ -65,6 +65,9 @@
>  
>  	  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
> Index: hibernate-2.6/kernel/power/snapshot.c
> ===================================================================
> --- hibernate-2.6.orig/kernel/power/snapshot.c	2011-07-08 11:25:06.293203483 +0200
> +++ hibernate-2.6/kernel/power/snapshot.c	2011-08-16 16:06:09.845540931 +0200
> @@ -1339,6 +1339,9 @@
>  	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);
> @@ -1662,6 +1665,8 @@
>  		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);
>  	}
>  }
>  
> @@ -1821,6 +1826,9 @@
>  		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
> @@ -2223,6 +2231,11 @@
>  		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)
> @@ -2243,6 +2256,8 @@
>  		}
>  	} 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);
> @@ -2264,6 +2279,9 @@
>  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);
> 
> 
> 

_______________________________________________
linux-pm mailing list
linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/linux-pm


[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux