Commit ff87eef9 authored by Pavel Machek's avatar Pavel Machek Committed by Linus Torvalds

[PATCH] swsusp: use non-contiguous memory on resume

The following patch is designed to fix a problem in the current implementation
of swsusp in mainline kernels.  Namely, swsusp uses an array of page backup
entries (aka pagedir) to store pointers to memory pages that must be saved
during suspend and restored during resume.

Unfortunately, the pagedir has to be located in a contiguous chunk of memory
and it sometimes turns out that an 8-order or even 9-order allocation is
needed for this purpose.  It sometimes is impossible to get such an allocation
and swsusp may fail during either suspend or resume due to the lack of memory,
although theoretically there is enough free memory for it to succeed.

Moreover, swsusp is more likely to fail for this reason during resume, which
means that it may fail during resume after a successful suspend (this actually
has happened for some people, including me :-)) and this, potentially, may
lead to the loss of data.

The problem is fixed by replacing the pagedir with a linklist so that
high-order memory allocations are avoided (the patches make swsusp use only
0-order allocations).  Unfortunately this means that it's necessary to change
assembly routines used to restore the image after it's been loaded from swap
so that they walk the list instead of walking the array.

This patch makes swsusp allocate only individual pages during resume.  it
contains the necessary changes to the assembly routines etc.  for i386 and
x86-64.
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarPavel Machek <pavel@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent f656b523
......@@ -7,6 +7,7 @@
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/personality.h>
#include <linux/suspend.h>
#include <asm/ucontext.h>
#include "sigframe.h"
#include <asm/fixmap.h>
......@@ -56,6 +57,11 @@ void foo(void)
OFFSET(EXEC_DOMAIN_handler, exec_domain, handler);
OFFSET(RT_SIGFRAME_sigcontext, rt_sigframe, uc.uc_mcontext);
BLANK();
OFFSET(pbe_address, pbe, address);
OFFSET(pbe_orig_address, pbe, orig_address);
OFFSET(pbe_next, pbe, next);
/* Offset from the sysenter stack to tss.esp0 */
DEFINE(TSS_sysenter_esp0, offsetof(struct tss_struct, esp0) -
......
......@@ -12,6 +12,7 @@
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/asm_offsets.h>
.text
......@@ -28,28 +29,28 @@ ENTRY(swsusp_arch_suspend)
ret
ENTRY(swsusp_arch_resume)
movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx
movl %ecx,%cr3
movl $swsusp_pg_dir-__PAGE_OFFSET, %ecx
movl %ecx, %cr3
movl pagedir_nosave, %ebx
xorl %eax, %eax
xorl %edx, %edx
movl pagedir_nosave, %edx
.p2align 4,,7
copy_loop:
movl 4(%ebx,%edx),%edi
movl (%ebx,%edx),%esi
testl %edx, %edx
jz done
movl pbe_address(%edx), %esi
movl pbe_orig_address(%edx), %edi
movl $1024, %ecx
rep
movsl
incl %eax
addl $16, %edx
cmpl nr_copy_pages,%eax
jb copy_loop
movl pbe_next(%edx), %edx
jmp copy_loop
.p2align 4,,7
done:
movl saved_context_esp, %esp
movl saved_context_ebp, %ebp
movl saved_context_ebx, %ebx
......
......@@ -62,8 +62,8 @@ int main(void)
offsetof (struct rt_sigframe32, uc.uc_mcontext));
BLANK();
#endif
DEFINE(SIZEOF_PBE, sizeof(struct pbe));
DEFINE(pbe_address, offsetof(struct pbe, address));
DEFINE(pbe_orig_address, offsetof(struct pbe, orig_address));
DEFINE(pbe_next, offsetof(struct pbe, next));
return 0;
}
......@@ -54,16 +54,10 @@ ENTRY(swsusp_arch_resume)
movq %rax, %cr4; # turn PGE back on
movq pagedir_nosave(%rip), %rdx
/* compute the limit */
movl nr_copy_pages(%rip), %eax
testl %eax, %eax
jz done
movq %rdx,%r8
movl $SIZEOF_PBE,%r9d
mul %r9 # with rax, clobbers rdx
movq %r8, %rdx
addq %r8, %rax
loop:
testq %rdx, %rdx
jz done
/* get addresses from the pbe and copy the page */
movq pbe_address(%rdx), %rsi
movq pbe_orig_address(%rdx), %rdi
......@@ -72,9 +66,8 @@ loop:
movsq
/* progress to the next pbe */
addq $SIZEOF_PBE, %rdx
cmpq %rax, %rdx
jb loop
movq pbe_next(%rdx), %rdx
jmp loop
done:
movl $24, %eax
movl %eax, %ds
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment