Patchwork sparc64: Hibernation support

login
register
mail settings
Submitter Kirill Tkhai
Date March 8, 2013, 4:19 p.m.
Message ID <1580671362759586@web24e.yandex.ru>
Download mbox | patch
Permalink /patch/226162/
State Superseded
Delegated to: David Miller
Headers show

Comments

Kirill Tkhai - March 8, 2013, 4:19 p.m.
Hi!

This patch is for hibernation support on sparc64.

Two steps to try:
1)"echo disk > /sys/power/state" makes a system sleeping
2)kernel parameter "resume=/dev/[swap_partition]" will say
the system which swap partition to use for restore.

Use MMU bypass to copy data pages. This allowes not to
create temporary page table as on some other architectures.

I changed mm/init_64.c because of restore may be called
from initcall. This moment is before free_initmem(), so
num_physpages must not be changed in it. Otherwise,
restore fails by the reason of mismatched sizes of the loaded
image and the system.

I tested the patch on single-core mcst-r1000 variant of v9.
Everything work, but everybody is welcome to test it on any
more standard v9 :)

Signed-off-by: Kirill Tkhai <tkhai@yandex.ru>
CC: David Miller <davem@davemloft.net>
---
--
To unsubscribe from this list: send the line "unsubscribe sparclinux" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sam Ravnborg - March 19, 2013, 9:37 p.m.
On Fri, Mar 08, 2013 at 08:19:46PM +0400, Kirill Tkhai wrote:
> Hi!
> 
> This patch is for hibernation support on sparc64.

Hi Kirill.
Some late nitpick feedback.

	Sam


> 
> Two steps to try:
> 1)"echo disk > /sys/power/state" makes a system sleeping
> 2)kernel parameter "resume=/dev/[swap_partition]" will say
> the system which swap partition to use for restore.
> 
> Use MMU bypass to copy data pages. This allowes not to
> create temporary page table as on some other architectures.
> 
> I changed mm/init_64.c because of restore may be called
> from initcall. This moment is before free_initmem(), so
> num_physpages must not be changed in it. Otherwise,
> restore fails by the reason of mismatched sizes of the loaded
> image and the system.
> 
> I tested the patch on single-core mcst-r1000 variant of v9.
> Everything work, but everybody is welcome to test it on any
> more standard v9 :)
> 
> Signed-off-by: Kirill Tkhai <tkhai@yandex.ru>
> CC: David Miller <davem@davemloft.net>
> ---
> diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
> index 9bff3db..9e1256f 100644
> --- a/arch/sparc/Kconfig
> +++ b/arch/sparc/Kconfig
> @@ -103,6 +103,9 @@ config HAVE_LATENCYTOP_SUPPORT
>  	bool
>  	default y if SPARC64
>  
> +config ARCH_HIBERNATION_POSSIBLE
> +	def_bool y if SPARC64
> +
>  config AUDIT_ARCH
>  	bool
>  	default y
> @@ -333,6 +336,10 @@ config ARCH_SPARSEMEM_DEFAULT
>  
>  source "mm/Kconfig"
>  
> +if SPARC64
> +source "kernel/power/Kconfig"
> +endif

The if SPARC64 is really not needed. As far as I could see we
do not enable anything uin there for sparc32 anyway.
So the only purpose of adding the "if" thing is for documentation.


> +
>  config SCHED_SMT
>  	bool "SMT (Hyperthreading) scheduler support"
>  	depends on SPARC64 && SMP
> diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
> index 541b8b0..9ff4236 100644
> --- a/arch/sparc/Makefile
> +++ b/arch/sparc/Makefile
> @@ -57,6 +57,7 @@ core-y                 += arch/sparc/
>  libs-y                 += arch/sparc/prom/
>  libs-y                 += arch/sparc/lib/
>  
> +drivers-$(CONFIG_PM) += arch/sparc/power/
>  drivers-$(CONFIG_OPROFILE)	+= arch/sparc/oprofile/
>  
>  boot := arch/sparc/boot

> diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
> index 2feb15c..1a2ce1e 100644
> diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
> index 8410065..d4fc2f0 100644
These looks bogus. Did you touch file permissions?

> diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
> index 5c2c6e6..a40a845 100644
> --- a/arch/sparc/mm/init_64.c
> +++ b/arch/sparc/mm/init_64.c
> @@ -2061,7 +2061,7 @@ void __init mem_init(void)
>  	 * allocated below.
>  	 */
>  	totalram_pages -= 1;
> -	num_physpages = totalram_pages;
> +	num_physpages = memblock_mem_size(last_valid_pfn) >> PAGE_SHIFT;
>  
>  	/*
>  	 * Set up the zero page, mark it reserved, so that page count
> @@ -2125,7 +2125,6 @@ void free_initmem(void)
>  			ClearPageReserved(p);
>  			init_page_count(p);
>  			__free_page(p);
> -			num_physpages++;
>  			totalram_pages++;
>  		}
>  	}
> @@ -2142,7 +2141,6 @@ void free_initrd_mem(unsigned long start, unsigned long end)
>  		ClearPageReserved(p);
>  		init_page_count(p);
>  		__free_page(p);
> -		num_physpages++;
>  		totalram_pages++;
>  	}
>  }
Could you separate this out as an independent change?
I must admit I do not see why what you do here is OK.

> diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
> index 8ad21a2..380e1a1 100644
Looks bogus.

> diff --git a/arch/sparc/power/Makefile b/arch/sparc/power/Makefile
> new file mode 100644
> index 0000000..3201ace
> --- /dev/null
> +++ b/arch/sparc/power/Makefile
> @@ -0,0 +1,3 @@
> +# Makefile for Sparc-specific hibernate files.
> +
> +obj-$(CONFIG_HIBERNATION)	+= hibernate.o hibernate_asm.o
> diff --git a/arch/sparc/power/hibernate.c b/arch/sparc/power/hibernate.c
> new file mode 100644
> index 0000000..89ca059
> --- /dev/null
> +++ b/arch/sparc/power/hibernate.c
> @@ -0,0 +1,65 @@
> +/*
> + * hibernate.c:  Hibernaton support specific for sparc64.
> + *
> + * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
> + */
> +
> +#include <linux/mm.h>
Empty line..

> +#include <asm/page.h>
> +#include <asm/tlb.h>
> +#include <asm/visasm.h>
Inverse christmas tree. Longest lines first.

> +
> +
> +#include "hibernate.h"
> +
> +/* References to section boundaries */
> +extern const void __nosave_begin, __nosave_end;
These belong in asm-generic/sections.h
Consider submitting a separate patch to Arnd.

> +
> +struct saved_context {
> +	unsigned long fp;
> +	unsigned long cwp;
> +	unsigned long wstate;
> +
> +	unsigned long tick;
> +	unsigned long pstate;
> +
> +	unsigned long g4;
> +	unsigned long g5;
> +	unsigned long g6;
> +} saved_context;
> +
> +/*
> + *	pfn_is_nosave - check if given pfn is in the 'nosave' section
> + */
> +
> +int pfn_is_nosave(unsigned long pfn)
> +{
> +	unsigned long nosave_begin_pfn = ((unsigned long)&__nosave_begin) >> PAGE_SHIFT;
> +	unsigned long nosave_end_pfn = ((unsigned long)&__nosave_end) >> PAGE_SHIFT;
Use PFN_DOWN

> +
> +	BUILD_BUG_ON(SC_REG_FP != offsetof(struct saved_context, fp));
> +	BUILD_BUG_ON(SC_REG_CWP != offsetof(struct saved_context, cwp));
> +	BUILD_BUG_ON(SC_REG_WSTATE != offsetof(struct saved_context, wstate));
> +
> +	BUILD_BUG_ON(SC_REG_TICK != offsetof(struct saved_context, tick));
> +	BUILD_BUG_ON(SC_REG_PSTATE != offsetof(struct saved_context, pstate));
> +
> +	BUILD_BUG_ON(SC_REG_G4 != offsetof(struct saved_context, g4));
> +	BUILD_BUG_ON(SC_REG_G5 != offsetof(struct saved_context, g5));
> +	BUILD_BUG_ON(SC_REG_G6 != offsetof(struct saved_context, g6));
Hmmm... See below.


> +
> +	return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
> +}
> +
> +void save_processor_state(void)
> +{
> +	save_and_clear_fpu();
> +}
> +
> +void restore_processor_state(void)
> +{
> +	struct mm_struct *mm = current->active_mm;
> +
> +	load_secondary_context(mm);
> +	tsb_context_switch(mm);
> +}
> diff --git a/arch/sparc/power/hibernate.h b/arch/sparc/power/hibernate.h
> new file mode 100644
> index 0000000..6c002f4
> --- /dev/null
> +++ b/arch/sparc/power/hibernate.h
> @@ -0,0 +1,21 @@
> +/*
> + * hibernate.c:  Hibernaton support specific for sparc64.
> + *
> + * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
> + */
> +
> +#ifndef ___SPARC_HIBERNATE_H
> +#define ___SPARC_HIBERNATE_H
> +
> +#define SC_REG_FP	0
> +#define SC_REG_CWP	8
> +#define SC_REG_WSTATE	16
> +
> +#define SC_REG_TICK	24
> +#define SC_REG_PSTATE	32
> +
> +#define SC_REG_G4	40
> +#define SC_REG_G5	48
> +#define SC_REG_G6	56
Use kernel/asm-offsets.c to generate these constants.
This is relaiable and you can skip the run-time checks above.

> +
> +#endif
> diff --git a/arch/sparc/power/hibernate_asm.S b/arch/sparc/power/hibernate_asm.S
> new file mode 100644
> index 0000000..8cc5c4e
> --- /dev/null
> +++ b/arch/sparc/power/hibernate_asm.S
> @@ -0,0 +1,129 @@
> +/*
> + * hibernate.c:  Hibernaton support specific for sparc64.
> + *
> + * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
> + */
> +
> +#include <linux/linkage.h>
> +#include <asm/page.h>
> +#include <asm/cpudata.h>
> +#include "hibernate.h"

Empty lines between the blocks.
And inverse christmas tree again.


For the asm part I did not look at it carefully.
But a few comments anyway.

> +
> +ENTRY(swsusp_arch_suspend)
> +	save	%sp, -192, %sp
> +	save	%sp, -192, %sp
This looks strange. Why two times?

> +	flushw
> +
> +	setuw	saved_context, %g3
> +	
> +	/* Save window regs */
> +	rdpr	%cwp, %g2
> +	stx	%g2, [%g3 + SC_REG_CWP]
> +	rdpr	%wstate, %g2
> +	stx	%g2, [%g3 + SC_REG_WSTATE]
> +	stx	%fp, [%g3 + SC_REG_FP]
> +
> +	/* Save state regs */
> +	rdpr	%tick, %g2
> +	stx	%g2, [%g3 + SC_REG_TICK]
> +	rdpr	%pstate, %g2
> +	stx	%g2, [%g3 + SC_REG_PSTATE]
> +
> +	/* Save global regs */
> +	stx	%g4, [%g3 + SC_REG_G4]
> +	stx	%g5, [%g3 + SC_REG_G5]
> +	stx	%g6, [%g3 + SC_REG_G6]
> +
> +	call	swsusp_save
> +	 nop
> +
> +	mov	%o0, %i0
> +	restore
> +
> +        mov	%o0, %i0
Uses space for indent => use tab

> +	ret
> +	 restore
> +
> +ENTRY(swsusp_arch_resume)
> +	/* Write restore_pblist to %l0 */
> +	sethi	%hi(restore_pblist), %l0
> +        ldx	[%l0 + %lo(restore_pblist)], %l0
Uses space for indent => use tab

> +
> +	call	__flush_tlb_all
> +	 nop
> +	
> +	/* Write PAGE_OFFSET to %g7 */
> +	sethi	%uhi(PAGE_OFFSET), %g7
> +	sllx	%g7, 32, %g7
> +
> +	setuw	(PAGE_SIZE-8), %g3
> +	
> +	/* Use MMU Bypass */
> +	rd	%asi, %g1
> +	wr	%g0, ASI_PHYS_USE_EC, %asi
> +
> +	/* Fill ITLB */
> +	ba	16f
> +	 nop
> +
> +pbe_loop:
> +	cmp	%l0, %g0
> +	be	restore_ctx
> +	 sub	%l0, %g7, %l0
Add empty line after delay slot

> +	ldxa	[%l0    ] %asi, %l1 /* address */
> +	ldxa	[%l0 + 8] %asi, %l2 /* orig_address */
> +
> +	/* phys addr */
> +	sub	%l1, %g7, %l1
> +	sub	%l2, %g7, %l2
> +
> +	mov	%g3, %l3 /* PAGE_SIZE-8 */
> +copy_loop:
> +	ldxa	[%l1 + %l3] ASI_PHYS_USE_EC, %g2
> +	stxa	%g2, [%l2 + %l3] ASI_PHYS_USE_EC
> +	cmp	%l3, %g0
> +	bne	copy_loop
> +	 sub	%l3, 8, %l3
> +
> +	/* next pbe */
> +	ba	pbe_loop
> +	 ldxa	[%l0 + 16] %asi, %l0
> +
> +restore_ctx:
> +	setuw	saved_context, %g3
> +	wrpr	%g0, 15, %pil
> +
> +	/* Restore window regs */
> +	wrpr    %g0, 0, %canrestore
> +	wrpr    %g0, 0, %otherwin
> +	wrpr	%g0, 6, %cansave
> +	wrpr    %g0, 0, %cleanwin
> +
> +	ldxa	[%g3 + SC_REG_CWP] %asi, %g2
> +	wrpr	%g2, %cwp
> +	ldxa	[%g3 + SC_REG_WSTATE] %asi, %g2
> +	wrpr	%g2, %wstate
> +	ldxa	[%g3 + SC_REG_FP] %asi, %fp
> +
> +	/* Restore state regs */
> +	ldxa	[%g3 + SC_REG_PSTATE] %asi, %g2
> +	wrpr	%g2, %pstate
> +	ldxa	[%g3 + SC_REG_TICK] %asi, %g2
> +	wrpr	%g2, %tick
> +
> +	/* Restore global regs */
> +	ldxa	[%g3 + SC_REG_G4] %asi, %g4
> +	ldxa	[%g3 + SC_REG_G5] %asi, %g5
> +	ldxa	[%g3 + SC_REG_G6] %asi, %g6
> +
> +	wr	%g1, %g0, %asi
> +
> +	restore
> +	restore
> +
> +	wrpr	%g0, 14, %pil
> +
> +	retl
> +	 mov	%g0, %o0
> +
> +16:	ba,a	pbe_loop
All other labels have nice names - and then suddenly a label named "16"?
And you are missing an instruction in the delay slot here.

	Sam
--
To unsubscribe from this list: send the line "unsubscribe sparclinux" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kirill Tkhai - March 20, 2013, 12:32 a.m.
20.03.2013, 01:37, "Sam Ravnborg" <sam@ravnborg.org>:
> On Fri, Mar 08, 2013 at 08:19:46PM +0400, Kirill Tkhai wrote:
>
>>  Hi!
>>
>>  This patch is for hibernation support on sparc64.
>
> Hi Kirill.
> Some late nitpick feedback.
>

Hi, Sam.

>>  Two steps to try:
>>  1)"echo disk > /sys/power/state" makes a system sleeping
>>  2)kernel parameter "resume=/dev/[swap_partition]" will say
>>  the system which swap partition to use for restore.
>>
>>  Use MMU bypass to copy data pages. This allowes not to
>>  create temporary page table as on some other architectures.
>>
>>  I changed mm/init_64.c because of restore may be called
>>  from initcall. This moment is before free_initmem(), so
>>  num_physpages must not be changed in it. Otherwise,
>>  restore fails by the reason of mismatched sizes of the loaded
>>  image and the system.
>>
>>  I tested the patch on single-core mcst-r1000 variant of v9.
>>  Everything work, but everybody is welcome to test it on any
>>  more standard v9 :)
>>
>>  Signed-off-by: Kirill Tkhai <tkhai@yandex.ru>
>>  CC: David Miller <davem@davemloft.net>
>>  ---
>>  diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
>>  index 9bff3db..9e1256f 100644
>>  --- a/arch/sparc/Kconfig
>>  +++ b/arch/sparc/Kconfig
>>  @@ -103,6 +103,9 @@ config HAVE_LATENCYTOP_SUPPORT
>>           bool
>>           default y if SPARC64
>>
>>  +config ARCH_HIBERNATION_POSSIBLE
>>  + def_bool y if SPARC64
>>  +
>>   config AUDIT_ARCH
>>           bool
>>           default y
>>  @@ -333,6 +336,10 @@ config ARCH_SPARSEMEM_DEFAULT
>>
>>   source "mm/Kconfig"
>>
>>  +if SPARC64
>>  +source "kernel/power/Kconfig"
>>  +endif
>
> The if SPARC64 is really not needed. As far as I could see we
> do not enable anything uin there for sparc32 anyway.
> So the only purpose of adding the "if" thing is for documentation.

Otherwise it is shown at xconfig when "Show all options" is turned
on. I copied the way how "drivers/cpufreq/Kconfig" is included.

>
>>  +
>>   config SCHED_SMT
>>           bool "SMT (Hyperthreading) scheduler support"
>>           depends on SPARC64 && SMP
>>  diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
>>  index 541b8b0..9ff4236 100644
>>  --- a/arch/sparc/Makefile
>>  +++ b/arch/sparc/Makefile
>>  @@ -57,6 +57,7 @@ core-y                 += arch/sparc/
>>   libs-y                 += arch/sparc/prom/
>>   libs-y                 += arch/sparc/lib/
>>
>>  +drivers-$(CONFIG_PM) += arch/sparc/power/
>>   drivers-$(CONFIG_OPROFILE) += arch/sparc/oprofile/
>>
>>   boot := arch/sparc/boot
>>  diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
>>  index 2feb15c..1a2ce1e 100644
>>  diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
>>  index 8410065..d4fc2f0 100644
>
> These looks bogus. Did you touch file permissions?

It's a wrong diff metadata. And one more below. Sorry.

>>  diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
>>  index 5c2c6e6..a40a845 100644
>>  --- a/arch/sparc/mm/init_64.c
>>  +++ b/arch/sparc/mm/init_64.c
>>  @@ -2061,7 +2061,7 @@ void __init mem_init(void)
>>            * allocated below.
>>            */
>>           totalram_pages -= 1;
>>  - num_physpages = totalram_pages;
>>  + num_physpages = memblock_mem_size(last_valid_pfn) >> PAGE_SHIFT;
>>
>>           /*
>>            * Set up the zero page, mark it reserved, so that page count
>>  @@ -2125,7 +2125,6 @@ void free_initmem(void)
>>                           ClearPageReserved(p);
>>                           init_page_count(p);
>>                           __free_page(p);
>>  - num_physpages++;
>>                           totalram_pages++;
>>                   }
>>           }
>>  @@ -2142,7 +2141,6 @@ void free_initrd_mem(unsigned long start, unsigned long end)
>>                   ClearPageReserved(p);
>>                   init_page_count(p);
>>                   __free_page(p);
>>  - num_physpages++;
>>                   totalram_pages++;
>>           }
>>   }
>
> Could you separate this out as an independent change?
> I must admit I do not see why what you do here is OK.
>

The arch-independent places where num_physpages
is used are

inet_frags_init()
sanitize_global_limit()
__online_page_set_limits()
and hibernation code.

It looks like everything is OK with all of them.

>>  diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
>>  index 8ad21a2..380e1a1 100644
>
> Looks bogus.
>
>>  diff --git a/arch/sparc/power/Makefile b/arch/sparc/power/Makefile
>>  new file mode 100644
>>  index 0000000..3201ace
>>  --- /dev/null
>>  +++ b/arch/sparc/power/Makefile
>>  @@ -0,0 +1,3 @@
>>  +# Makefile for Sparc-specific hibernate files.
>>  +
>>  +obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate_asm.o
>>  diff --git a/arch/sparc/power/hibernate.c b/arch/sparc/power/hibernate.c
>>  new file mode 100644
>>  index 0000000..89ca059
>>  --- /dev/null
>>  +++ b/arch/sparc/power/hibernate.c
>>  @@ -0,0 +1,65 @@
>>  +/*
>>  + * hibernate.c:  Hibernaton support specific for sparc64.
>>  + *
>>  + * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
>>  + */
>>  +
>>  +#include <linux/mm.h>
>
> Empty line..
>

Yes, thanks for this and for other formatting points.

>>  +#include <asm/page.h>
>>  +#include <asm/tlb.h>
>>  +#include <asm/visasm.h>
>
> Inverse christmas tree. Longest lines first.
>
>>  +
>>  +
>>  +#include "hibernate.h"
>>  +
>>  +/* References to section boundaries */
>>  +extern const void __nosave_begin, __nosave_end;
>
> These belong in asm-generic/sections.h
> Consider submitting a separate patch to Arnd.
>>  +
>>  +struct saved_context {
>>  + unsigned long fp;
>>  + unsigned long cwp;
>>  + unsigned long wstate;
>>  +
>>  + unsigned long tick;
>>  + unsigned long pstate;
>>  +
>>  + unsigned long g4;
>>  + unsigned long g5;
>>  + unsigned long g6;
>>  +} saved_context;
>>  +
>>  +/*
>>  + * pfn_is_nosave - check if given pfn is in the 'nosave' section
>>  + */
>>  +
>>  +int pfn_is_nosave(unsigned long pfn)
>>  +{
>>  + unsigned long nosave_begin_pfn = ((unsigned long)&__nosave_begin) >> PAGE_SHIFT;
>>  + unsigned long nosave_end_pfn = ((unsigned long)&__nosave_end) >> PAGE_SHIFT;
>
> Use PFN_DOWN
>
>>  +
>>  + BUILD_BUG_ON(SC_REG_FP != offsetof(struct saved_context, fp));
>>  + BUILD_BUG_ON(SC_REG_CWP != offsetof(struct saved_context, cwp));
>>  + BUILD_BUG_ON(SC_REG_WSTATE != offsetof(struct saved_context, wstate));
>>  +
>>  + BUILD_BUG_ON(SC_REG_TICK != offsetof(struct saved_context, tick));
>>  + BUILD_BUG_ON(SC_REG_PSTATE != offsetof(struct saved_context, pstate));
>>  +
>>  + BUILD_BUG_ON(SC_REG_G4 != offsetof(struct saved_context, g4));
>>  + BUILD_BUG_ON(SC_REG_G5 != offsetof(struct saved_context, g5));
>>  + BUILD_BUG_ON(SC_REG_G6 != offsetof(struct saved_context, g6));
>
> Hmmm... See below.
>
>>  +
>>  + return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
>>  +}
>>  +
>>  +void save_processor_state(void)
>>  +{
>>  + save_and_clear_fpu();
>>  +}
>>  +
>>  +void restore_processor_state(void)
>>  +{
>>  + struct mm_struct *mm = current->active_mm;
>>  +
>>  + load_secondary_context(mm);
>>  + tsb_context_switch(mm);
>>  +}
>>  diff --git a/arch/sparc/power/hibernate.h b/arch/sparc/power/hibernate.h
>>  new file mode 100644
>>  index 0000000..6c002f4
>>  --- /dev/null
>>  +++ b/arch/sparc/power/hibernate.h
>>  @@ -0,0 +1,21 @@
>>  +/*
>>  + * hibernate.c:  Hibernaton support specific for sparc64.
>>  + *
>>  + * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
>>  + */
>>  +
>>  +#ifndef ___SPARC_HIBERNATE_H
>>  +#define ___SPARC_HIBERNATE_H
>>  +
>>  +#define SC_REG_FP 0
>>  +#define SC_REG_CWP 8
>>  +#define SC_REG_WSTATE 16
>>  +
>>  +#define SC_REG_TICK 24
>>  +#define SC_REG_PSTATE 32
>>  +
>>  +#define SC_REG_G4 40
>>  +#define SC_REG_G5 48
>>  +#define SC_REG_G6 56
>
> Use kernel/asm-offsets.c to generate these constants.
> This is relaiable and you can skip the run-time checks above.
>

Good advice. I'll follow it.

>>  +
>>  +#endif
>>  diff --git a/arch/sparc/power/hibernate_asm.S b/arch/sparc/power/hibernate_asm.S
>>  new file mode 100644
>>  index 0000000..8cc5c4e
>>  --- /dev/null
>>  +++ b/arch/sparc/power/hibernate_asm.S
>>  @@ -0,0 +1,129 @@
>>  +/*
>>  + * hibernate.c:  Hibernaton support specific for sparc64.
>>  + *
>>  + * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
>>  + */
>>  +
>>  +#include <linux/linkage.h>
>>  +#include <asm/page.h>
>>  +#include <asm/cpudata.h>
>>  +#include "hibernate.h"
>
> Empty lines between the blocks.
> And inverse christmas tree again.
>
> For the asm part I did not look at it carefully.
> But a few comments anyway.
>
>>  +
>>  +ENTRY(swsusp_arch_suspend)
>>  + save %sp, -192, %sp
>>  + save %sp, -192, %sp
>
> This looks strange. Why two times?

It's because I want save only
one %o or %i register -- %i6 (fp). Othervise
it's necessary to save at least one more -- %i7.

>
>>  + flushw
>>  +
>>  + setuw saved_context, %g3
>>  +
>>  + /* Save window regs */
>>  + rdpr %cwp, %g2
>>  + stx %g2, [%g3 + SC_REG_CWP]
>>  + rdpr %wstate, %g2
>>  + stx %g2, [%g3 + SC_REG_WSTATE]
>>  + stx %fp, [%g3 + SC_REG_FP]
>>  +
>>  + /* Save state regs */
>>  + rdpr %tick, %g2
>>  + stx %g2, [%g3 + SC_REG_TICK]
>>  + rdpr %pstate, %g2
>>  + stx %g2, [%g3 + SC_REG_PSTATE]
>>  +
>>  + /* Save global regs */
>>  + stx %g4, [%g3 + SC_REG_G4]
>>  + stx %g5, [%g3 + SC_REG_G5]
>>  + stx %g6, [%g3 + SC_REG_G6]
>>  +
>>  + call swsusp_save
>>  + nop
>>  +
>>  + mov %o0, %i0
>>  + restore
>>  +
>>  +        mov %o0, %i0
>
> Uses space for indent => use tab
>
>>  + ret
>>  + restore
>>  +
>>  +ENTRY(swsusp_arch_resume)
>>  + /* Write restore_pblist to %l0 */
>>  + sethi %hi(restore_pblist), %l0
>>  +        ldx [%l0 + %lo(restore_pblist)], %l0
>
> Uses space for indent => use tab
>
>>  +
>>  + call __flush_tlb_all
>>  + nop
>>  +
>>  + /* Write PAGE_OFFSET to %g7 */
>>  + sethi %uhi(PAGE_OFFSET), %g7
>>  + sllx %g7, 32, %g7
>>  +
>>  + setuw (PAGE_SIZE-8), %g3
>>  +
>>  + /* Use MMU Bypass */
>>  + rd %asi, %g1
>>  + wr %g0, ASI_PHYS_USE_EC, %asi
>>  +
>>  + /* Fill ITLB */
>>  + ba 16f
>>  + nop
>>  +
>>  +pbe_loop:
>>  + cmp %l0, %g0
>>  + be restore_ctx
>>  + sub %l0, %g7, %l0
>
> Add empty line after delay slot
>
>>  + ldxa [%l0    ] %asi, %l1 /* address */
>>  + ldxa [%l0 + 8] %asi, %l2 /* orig_address */
>>  +
>>  + /* phys addr */
>>  + sub %l1, %g7, %l1
>>  + sub %l2, %g7, %l2
>>  +
>>  + mov %g3, %l3 /* PAGE_SIZE-8 */
>>  +copy_loop:
>>  + ldxa [%l1 + %l3] ASI_PHYS_USE_EC, %g2
>>  + stxa %g2, [%l2 + %l3] ASI_PHYS_USE_EC
>>  + cmp %l3, %g0
>>  + bne copy_loop
>>  + sub %l3, 8, %l3
>>  +
>>  + /* next pbe */
>>  + ba pbe_loop
>>  + ldxa [%l0 + 16] %asi, %l0
>>  +
>>  +restore_ctx:
>>  + setuw saved_context, %g3
>>  + wrpr %g0, 15, %pil
>>  +
>>  + /* Restore window regs */
>>  + wrpr    %g0, 0, %canrestore
>>  + wrpr    %g0, 0, %otherwin
>>  + wrpr %g0, 6, %cansave
>>  + wrpr    %g0, 0, %cleanwin
>>  +
>>  + ldxa [%g3 + SC_REG_CWP] %asi, %g2
>>  + wrpr %g2, %cwp
>>  + ldxa [%g3 + SC_REG_WSTATE] %asi, %g2
>>  + wrpr %g2, %wstate
>>  + ldxa [%g3 + SC_REG_FP] %asi, %fp
>>  +
>>  + /* Restore state regs */
>>  + ldxa [%g3 + SC_REG_PSTATE] %asi, %g2
>>  + wrpr %g2, %pstate
>>  + ldxa [%g3 + SC_REG_TICK] %asi, %g2
>>  + wrpr %g2, %tick
>>  +
>>  + /* Restore global regs */
>>  + ldxa [%g3 + SC_REG_G4] %asi, %g4
>>  + ldxa [%g3 + SC_REG_G5] %asi, %g5
>>  + ldxa [%g3 + SC_REG_G6] %asi, %g6
>>  +
>>  + wr %g1, %g0, %asi
>>  +
>>  + restore
>>  + restore
>>  +
>>  + wrpr %g0, 14, %pil
>>  +
>>  + retl
>>  + mov %g0, %o0
>>  +
>>  +16: ba,a pbe_loop
>
> All other labels have nice names - and then suddenly a label named "16"?
> And you are missing an instruction in the delay slot here.

Now, I think mask NMI earlier and put this action as delayed instruction
for this for the present unnamed branch. Looks like all copying must
processes with level=15.

Thanks for feedback, I will send new patch a little later.

Kirill
--
To unsubscribe from this list: send the line "unsubscribe sparclinux" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 9bff3db..9e1256f 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -103,6 +103,9 @@  config HAVE_LATENCYTOP_SUPPORT
 	bool
 	default y if SPARC64
 
+config ARCH_HIBERNATION_POSSIBLE
+	def_bool y if SPARC64
+
 config AUDIT_ARCH
 	bool
 	default y
@@ -333,6 +336,10 @@  config ARCH_SPARSEMEM_DEFAULT
 
 source "mm/Kconfig"
 
+if SPARC64
+source "kernel/power/Kconfig"
+endif
+
 config SCHED_SMT
 	bool "SMT (Hyperthreading) scheduler support"
 	depends on SPARC64 && SMP
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
index 541b8b0..9ff4236 100644
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -57,6 +57,7 @@  core-y                 += arch/sparc/
 libs-y                 += arch/sparc/prom/
 libs-y                 += arch/sparc/lib/
 
+drivers-$(CONFIG_PM) += arch/sparc/power/
 drivers-$(CONFIG_OPROFILE)	+= arch/sparc/oprofile/
 
 boot := arch/sparc/boot
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
index 2feb15c..1a2ce1e 100644
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
index 8410065..d4fc2f0 100644
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 5c2c6e6..a40a845 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -2061,7 +2061,7 @@  void __init mem_init(void)
 	 * allocated below.
 	 */
 	totalram_pages -= 1;
-	num_physpages = totalram_pages;
+	num_physpages = memblock_mem_size(last_valid_pfn) >> PAGE_SHIFT;
 
 	/*
 	 * Set up the zero page, mark it reserved, so that page count
@@ -2125,7 +2125,6 @@  void free_initmem(void)
 			ClearPageReserved(p);
 			init_page_count(p);
 			__free_page(p);
-			num_physpages++;
 			totalram_pages++;
 		}
 	}
@@ -2142,7 +2141,6 @@  void free_initrd_mem(unsigned long start, unsigned long end)
 		ClearPageReserved(p);
 		init_page_count(p);
 		__free_page(p);
-		num_physpages++;
 		totalram_pages++;
 	}
 }
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 8ad21a2..380e1a1 100644
diff --git a/arch/sparc/power/Makefile b/arch/sparc/power/Makefile
new file mode 100644
index 0000000..3201ace
--- /dev/null
+++ b/arch/sparc/power/Makefile
@@ -0,0 +1,3 @@ 
+# Makefile for Sparc-specific hibernate files.
+
+obj-$(CONFIG_HIBERNATION)	+= hibernate.o hibernate_asm.o
diff --git a/arch/sparc/power/hibernate.c b/arch/sparc/power/hibernate.c
new file mode 100644
index 0000000..89ca059
--- /dev/null
+++ b/arch/sparc/power/hibernate.c
@@ -0,0 +1,65 @@ 
+/*
+ * hibernate.c:  Hibernaton support specific for sparc64.
+ *
+ * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
+ */
+
+#include <linux/mm.h>
+#include <asm/page.h>
+#include <asm/tlb.h>
+#include <asm/visasm.h>
+
+
+#include "hibernate.h"
+
+/* References to section boundaries */
+extern const void __nosave_begin, __nosave_end;
+
+struct saved_context {
+	unsigned long fp;
+	unsigned long cwp;
+	unsigned long wstate;
+
+	unsigned long tick;
+	unsigned long pstate;
+
+	unsigned long g4;
+	unsigned long g5;
+	unsigned long g6;
+} saved_context;
+
+/*
+ *	pfn_is_nosave - check if given pfn is in the 'nosave' section
+ */
+
+int pfn_is_nosave(unsigned long pfn)
+{
+	unsigned long nosave_begin_pfn = ((unsigned long)&__nosave_begin) >> PAGE_SHIFT;
+	unsigned long nosave_end_pfn = ((unsigned long)&__nosave_end) >> PAGE_SHIFT;
+
+	BUILD_BUG_ON(SC_REG_FP != offsetof(struct saved_context, fp));
+	BUILD_BUG_ON(SC_REG_CWP != offsetof(struct saved_context, cwp));
+	BUILD_BUG_ON(SC_REG_WSTATE != offsetof(struct saved_context, wstate));
+
+	BUILD_BUG_ON(SC_REG_TICK != offsetof(struct saved_context, tick));
+	BUILD_BUG_ON(SC_REG_PSTATE != offsetof(struct saved_context, pstate));
+
+	BUILD_BUG_ON(SC_REG_G4 != offsetof(struct saved_context, g4));
+	BUILD_BUG_ON(SC_REG_G5 != offsetof(struct saved_context, g5));
+	BUILD_BUG_ON(SC_REG_G6 != offsetof(struct saved_context, g6));
+
+	return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
+}
+
+void save_processor_state(void)
+{
+	save_and_clear_fpu();
+}
+
+void restore_processor_state(void)
+{
+	struct mm_struct *mm = current->active_mm;
+
+	load_secondary_context(mm);
+	tsb_context_switch(mm);
+}
diff --git a/arch/sparc/power/hibernate.h b/arch/sparc/power/hibernate.h
new file mode 100644
index 0000000..6c002f4
--- /dev/null
+++ b/arch/sparc/power/hibernate.h
@@ -0,0 +1,21 @@ 
+/*
+ * hibernate.c:  Hibernaton support specific for sparc64.
+ *
+ * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
+ */
+
+#ifndef ___SPARC_HIBERNATE_H
+#define ___SPARC_HIBERNATE_H
+
+#define SC_REG_FP	0
+#define SC_REG_CWP	8
+#define SC_REG_WSTATE	16
+
+#define SC_REG_TICK	24
+#define SC_REG_PSTATE	32
+
+#define SC_REG_G4	40
+#define SC_REG_G5	48
+#define SC_REG_G6	56
+
+#endif
diff --git a/arch/sparc/power/hibernate_asm.S b/arch/sparc/power/hibernate_asm.S
new file mode 100644
index 0000000..8cc5c4e
--- /dev/null
+++ b/arch/sparc/power/hibernate_asm.S
@@ -0,0 +1,129 @@ 
+/*
+ * hibernate.c:  Hibernaton support specific for sparc64.
+ *
+ * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
+ */
+
+#include <linux/linkage.h>
+#include <asm/page.h>
+#include <asm/cpudata.h>
+#include "hibernate.h"
+
+ENTRY(swsusp_arch_suspend)
+	save	%sp, -192, %sp
+	save	%sp, -192, %sp
+	flushw
+
+	setuw	saved_context, %g3
+	
+	/* Save window regs */
+	rdpr	%cwp, %g2
+	stx	%g2, [%g3 + SC_REG_CWP]
+	rdpr	%wstate, %g2
+	stx	%g2, [%g3 + SC_REG_WSTATE]
+	stx	%fp, [%g3 + SC_REG_FP]
+
+	/* Save state regs */
+	rdpr	%tick, %g2
+	stx	%g2, [%g3 + SC_REG_TICK]
+	rdpr	%pstate, %g2
+	stx	%g2, [%g3 + SC_REG_PSTATE]
+
+	/* Save global regs */
+	stx	%g4, [%g3 + SC_REG_G4]
+	stx	%g5, [%g3 + SC_REG_G5]
+	stx	%g6, [%g3 + SC_REG_G6]
+
+	call	swsusp_save
+	 nop
+
+	mov	%o0, %i0
+	restore
+
+        mov	%o0, %i0
+	ret
+	 restore
+
+ENTRY(swsusp_arch_resume)
+	/* Write restore_pblist to %l0 */
+	sethi	%hi(restore_pblist), %l0
+        ldx	[%l0 + %lo(restore_pblist)], %l0
+
+	call	__flush_tlb_all
+	 nop
+	
+	/* Write PAGE_OFFSET to %g7 */
+	sethi	%uhi(PAGE_OFFSET), %g7
+	sllx	%g7, 32, %g7
+
+	setuw	(PAGE_SIZE-8), %g3
+	
+	/* Use MMU Bypass */
+	rd	%asi, %g1
+	wr	%g0, ASI_PHYS_USE_EC, %asi
+
+	/* Fill ITLB */
+	ba	16f
+	 nop
+
+pbe_loop:
+	cmp	%l0, %g0
+	be	restore_ctx
+	 sub	%l0, %g7, %l0
+	ldxa	[%l0    ] %asi, %l1 /* address */
+	ldxa	[%l0 + 8] %asi, %l2 /* orig_address */
+
+	/* phys addr */
+	sub	%l1, %g7, %l1
+	sub	%l2, %g7, %l2
+
+	mov	%g3, %l3 /* PAGE_SIZE-8 */
+copy_loop:
+	ldxa	[%l1 + %l3] ASI_PHYS_USE_EC, %g2
+	stxa	%g2, [%l2 + %l3] ASI_PHYS_USE_EC
+	cmp	%l3, %g0
+	bne	copy_loop
+	 sub	%l3, 8, %l3
+
+	/* next pbe */
+	ba	pbe_loop
+	 ldxa	[%l0 + 16] %asi, %l0
+
+restore_ctx:
+	setuw	saved_context, %g3
+	wrpr	%g0, 15, %pil
+
+	/* Restore window regs */
+	wrpr    %g0, 0, %canrestore
+	wrpr    %g0, 0, %otherwin
+	wrpr	%g0, 6, %cansave
+	wrpr    %g0, 0, %cleanwin
+
+	ldxa	[%g3 + SC_REG_CWP] %asi, %g2
+	wrpr	%g2, %cwp
+	ldxa	[%g3 + SC_REG_WSTATE] %asi, %g2
+	wrpr	%g2, %wstate
+	ldxa	[%g3 + SC_REG_FP] %asi, %fp
+
+	/* Restore state regs */
+	ldxa	[%g3 + SC_REG_PSTATE] %asi, %g2
+	wrpr	%g2, %pstate
+	ldxa	[%g3 + SC_REG_TICK] %asi, %g2
+	wrpr	%g2, %tick
+
+	/* Restore global regs */
+	ldxa	[%g3 + SC_REG_G4] %asi, %g4
+	ldxa	[%g3 + SC_REG_G5] %asi, %g5
+	ldxa	[%g3 + SC_REG_G6] %asi, %g6
+
+	wr	%g1, %g0, %asi
+
+	restore
+	restore
+
+	wrpr	%g0, 14, %pil
+
+	retl
+	 mov	%g0, %o0
+
+16:	ba,a	pbe_loop