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

[PATCH] suspend-to-disk: cleanup printks(), rearrange reading

I'd like Florent credited -- he is maintaining 2.4.X version and
helping with development. Kill warnings by rearranging code / adding
prototypes. Enable using separate console (so user sees progress and X
suspend/resume works properly), forward-port of updates from Florent
and stop using own PRINTK stuff (mostly). Reading now primarily uses
block_device(), this should enable more cleanups. Fixed double free on
error path.
parent e80e317b
...@@ -502,6 +502,14 @@ S: 48287 Sawleaf ...@@ -502,6 +502,14 @@ S: 48287 Sawleaf
S: Fremont, California 94539 S: Fremont, California 94539
S: USA S: USA
N: Florent Chabaud
E: florent.chabaud@polytechnique.org
D: software suspend
S: SGDN/DCSSI/SDS/LTI
S: 58, Bd Latour-Maubourg
S: 75700 Paris 07 SP
S: France
N: Gordon Chaffee N: Gordon Chaffee
E: chaffee@cs.berkeley.edu E: chaffee@cs.berkeley.edu
W: http://bmrc.berkeley.edu/people/chaffee/ W: http://bmrc.berkeley.edu/people/chaffee/
...@@ -1708,7 +1716,7 @@ S: Germany ...@@ -1708,7 +1716,7 @@ S: Germany
N: Gabor Kuti N: Gabor Kuti
M: seasons@falcon.sch.bme.hu M: seasons@falcon.sch.bme.hu
M: seasons@makosteszta.sote.hu M: seasons@makosteszta.sote.hu
D: Software suspend D: Original author of software suspend
N: Jaroslav Kysela N: Jaroslav Kysela
E: perex@suse.cz E: perex@suse.cz
......
...@@ -97,6 +97,14 @@ static inline void save_processor_context (void) ...@@ -97,6 +97,14 @@ static inline void save_processor_context (void)
asm volatile ("pushfl ; popl (%0)" : "=m" (saved_context.eflags)); asm volatile ("pushfl ; popl (%0)" : "=m" (saved_context.eflags));
} }
static void
do_fpu_end(void)
{
/* restore FPU regs if necessary */
/* Do it out of line so that gcc does not move cr0 load to some stupid place */
kernel_fpu_end();
}
/* /*
* restore_processor_context * restore_processor_context
* *
...@@ -220,13 +228,6 @@ void fix_processor_context(void) ...@@ -220,13 +228,6 @@ void fix_processor_context(void)
} }
static void
do_fpu_end(void)
{
/* restore FPU regs if necessary */
/* Do it out of line so that gcc does not move cr0 load to some stupid place */
kernel_fpu_end();
}
#ifdef CONFIG_SOFTWARE_SUSPEND #ifdef CONFIG_SOFTWARE_SUSPEND
/* Local variables for do_magic */ /* Local variables for do_magic */
......
...@@ -38,7 +38,6 @@ struct saved_context { ...@@ -38,7 +38,6 @@ struct saved_context {
: /* no output */ \ : /* no output */ \
:"r" ((thread)->debugreg[register])) :"r" ((thread)->debugreg[register]))
extern void do_fpu_end(void);
extern void fix_processor_context(void); extern void fix_processor_context(void);
extern void do_magic(int resume); extern void do_magic(int resume);
......
...@@ -56,9 +56,22 @@ extern int register_suspend_notifier(struct notifier_block *); ...@@ -56,9 +56,22 @@ extern int register_suspend_notifier(struct notifier_block *);
extern int unregister_suspend_notifier(struct notifier_block *); extern int unregister_suspend_notifier(struct notifier_block *);
extern void refrigerator(unsigned long); extern void refrigerator(unsigned long);
extern int freeze_processes(void);
extern void thaw_processes(void);
extern unsigned int nr_copy_pages __nosavedata; extern unsigned int nr_copy_pages __nosavedata;
extern suspend_pagedir_t *pagedir_nosave __nosavedata; extern suspend_pagedir_t *pagedir_nosave __nosavedata;
/* Communication between kernel/suspend.c and arch/i386/suspend.c */
extern void do_magic_resume_1(void);
extern void do_magic_resume_2(void);
extern void do_magic_suspend_1(void);
extern void do_magic_suspend_2(void);
/* Communication between acpi and arch/i386/suspend.c */
extern void do_suspend_lowlevel(int resume);
#else #else
#define software_suspend() do { } while(0) #define software_suspend() do { } while(0)
......
...@@ -69,7 +69,7 @@ extern void signal_wake_up(struct task_struct *t); ...@@ -69,7 +69,7 @@ extern void signal_wake_up(struct task_struct *t);
unsigned char software_suspend_enabled = 0; unsigned char software_suspend_enabled = 0;
/* #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1) */ #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
/* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but /* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but
we probably do not take enough locks for switching consoles, etc, we probably do not take enough locks for switching consoles, etc,
so bad things might happen. so bad things might happen.
...@@ -97,7 +97,7 @@ spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED; ...@@ -97,7 +97,7 @@ spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED;
/* Variables to be preserved over suspend */ /* Variables to be preserved over suspend */
static int new_loglevel = 7; static int new_loglevel = 7;
static int orig_loglevel = 0; static int orig_loglevel = 0;
static int orig_fgconsole; static int orig_fgconsole, orig_kmsg;
static int pagedir_order_check; static int pagedir_order_check;
static int nr_copy_pages_check; static int nr_copy_pages_check;
...@@ -139,32 +139,23 @@ union diskpage { ...@@ -139,32 +139,23 @@ union diskpage {
*/ */
#define PAGES_FOR_IO 512 #define PAGES_FOR_IO 512
static const char *name_suspend = "Suspend Machine: "; static const char name_suspend[] = "Suspend Machine: ";
static const char *name_resume = "Resume Machine: "; static const char name_resume[] = "Resume Machine: ";
/* /*
* Debug * Debug
*/ */
#define DEBUG_DEFAULT 1 #undef DEBUG_DEFAULT
#undef DEBUG_PROCESS #undef DEBUG_PROCESS
#undef DEBUG_SLOW #undef DEBUG_SLOW
#define TEST_SWSUSP 1 /* Set to 1 to reboot instead of halt machine after suspension */ #define TEST_SWSUSP 1 /* Set to 1 to reboot instead of halt machine after suspension */
#ifdef DEBUG_DEFAULT #ifdef DEBUG_DEFAULT
#define PRINTD(func, f, a...) \ # define PRINTK(f, a...) printk(f, ## a)
do { \
printk("%s", func); \
printk(f, ## a); \
} while(0)
#define PRINTS(f, a...) PRINTD(name_suspend, f, ## a)
#define PRINTR(f, a...) PRINTD(name_resume, f, ## a)
#define PRINTK(f, a...) printk(f, ## a)
#else #else
#define PRINTD(func, f, a...) # define PRINTK(f, a...)
#define PRINTS(f, a...)
#define PRINTR(f, a...)
#define PRINTK(f, a...)
#endif #endif
#ifdef DEBUG_SLOW #ifdef DEBUG_SLOW
#define MDELAY(a) mdelay(a) #define MDELAY(a) mdelay(a)
#else #else
...@@ -195,8 +186,8 @@ void refrigerator(unsigned long flag) ...@@ -195,8 +186,8 @@ void refrigerator(unsigned long flag)
long save; long save;
save = current->state; save = current->state;
current->state = TASK_STOPPED; current->state = TASK_STOPPED;
// PRINTK("%s entered refrigerator\n", current->comm); PRINTK("%s entered refrigerator\n", current->comm);
printk(":"); printk("=");
current->flags &= ~PF_FREEZE; current->flags &= ~PF_FREEZE;
if (flag) if (flag)
flush_signals(current); /* We have signaled a kernel thread, which isn't normal behaviour flush_signals(current); /* We have signaled a kernel thread, which isn't normal behaviour
...@@ -205,8 +196,7 @@ void refrigerator(unsigned long flag) ...@@ -205,8 +196,7 @@ void refrigerator(unsigned long flag)
current->flags |= PF_FROZEN; current->flags |= PF_FROZEN;
while (current->flags & PF_FROZEN) while (current->flags & PF_FROZEN)
schedule(); schedule();
// PRINTK("%s left refrigerator\n", current->comm); PRINTK("%s left refrigerator\n", current->comm);
printk(":");
current->state = save; current->state = save;
} }
...@@ -216,8 +206,7 @@ int freeze_processes(void) ...@@ -216,8 +206,7 @@ int freeze_processes(void)
int todo, start_time; int todo, start_time;
struct task_struct *p; struct task_struct *p;
PRINTS( "Waiting for tasks to stop... " ); printk( "Stopping tasks: " );
start_time = jiffies; start_time = jiffies;
do { do {
todo = 0; todo = 0;
...@@ -239,13 +228,13 @@ int freeze_processes(void) ...@@ -239,13 +228,13 @@ int freeze_processes(void)
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
yield(); yield();
if (time_after(jiffies, start_time + TIMEOUT)) { if (time_after(jiffies, start_time + TIMEOUT)) {
PRINTK( "\n" ); printk( "\n" );
printk(KERN_ERR " stopping tasks failed (%d tasks remaining)\n", todo ); printk(KERN_ERR " stopping tasks failed (%d tasks remaining)\n", todo );
return todo; return todo;
} }
} while(todo); } while(todo);
PRINTK( " ok\n" ); printk( "|\n" );
return 0; return 0;
} }
...@@ -253,7 +242,7 @@ void thaw_processes(void) ...@@ -253,7 +242,7 @@ void thaw_processes(void)
{ {
struct task_struct *p; struct task_struct *p;
PRINTR( "Restarting tasks..." ); printk( "Restarting tasks..." );
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
for_each_task(p) { for_each_task(p) {
INTERESTING(p); INTERESTING(p);
...@@ -264,7 +253,7 @@ void thaw_processes(void) ...@@ -264,7 +253,7 @@ void thaw_processes(void)
wake_up_process(p); wake_up_process(p);
} }
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
PRINTK( " done\n" ); printk( " done\n" );
MDELAY(500); MDELAY(500);
} }
...@@ -284,8 +273,7 @@ static __inline__ int fill_suspend_header(struct suspend_header *sh) ...@@ -284,8 +273,7 @@ static __inline__ int fill_suspend_header(struct suspend_header *sh)
sh->num_cpus = num_online_cpus(); sh->num_cpus = num_online_cpus();
sh->page_size = PAGE_SIZE; sh->page_size = PAGE_SIZE;
sh->suspend_pagedir = pagedir_nosave; sh->suspend_pagedir = pagedir_nosave;
if (pagedir_save != pagedir_nosave) BUG_ON (pagedir_save != pagedir_nosave);
panic("Must not happen");
sh->num_pbes = nr_copy_pages; sh->num_pbes = nr_copy_pages;
/* TODO: needed? mounted fs' last mounted date comparison /* TODO: needed? mounted fs' last mounted date comparison
* [so they haven't been mounted since last suspend. * [so they haven't been mounted since last suspend.
...@@ -376,7 +364,7 @@ static void read_swapfiles(void) /* This is called before saving image */ ...@@ -376,7 +364,7 @@ static void read_swapfiles(void) /* This is called before saving image */
root_swap = i; root_swap = i;
} else { } else {
#if 0 #if 0
PRINTS( "device %s (%x != %x) ignored\n", swap_info[i].swap_file->d_name.name, swap_info[i].swap_device, resume_device ); printk( "Resume: device %s (%x != %x) ignored\n", swap_info[i].swap_file->d_name.name, swap_info[i].swap_device, resume_device );
#endif #endif
swapfile_used[i] = SWAPFILE_IGNORED; swapfile_used[i] = SWAPFILE_IGNORED;
} }
...@@ -409,14 +397,14 @@ static int write_suspend_image(void) ...@@ -409,14 +397,14 @@ static int write_suspend_image(void)
unsigned long address; unsigned long address;
struct page *page; struct page *page;
PRINTS( "Writing data to swap (%d pages): ", nr_copy_pages ); printk( "Writing data to swap (%d pages): ", nr_copy_pages );
for (i=0; i<nr_copy_pages; i++) { for (i=0; i<nr_copy_pages; i++) {
if (!(i%100)) if (!(i%100))
PRINTK( "." ); printk( "." );
if (!(entry = get_swap_page()).val) if (!(entry = get_swap_page()).val)
panic("\nNot enough swapspace when writing data" ); panic("\nNot enough swapspace when writing data" );
if(swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND) if (swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND)
panic("\nPage %d: not enough swapspace on suspend device", i ); panic("\nPage %d: not enough swapspace on suspend device", i );
address = (pagedir_nosave+i)->address; address = (pagedir_nosave+i)->address;
...@@ -424,13 +412,12 @@ static int write_suspend_image(void) ...@@ -424,13 +412,12 @@ static int write_suspend_image(void)
rw_swap_page_sync(WRITE, entry, page); rw_swap_page_sync(WRITE, entry, page);
(pagedir_nosave+i)->swap_address = entry; (pagedir_nosave+i)->swap_address = entry;
} }
PRINTK(" done\n"); printk( "|\n" );
PRINTS( "Writing pagedir (%d pages): ", nr_pgdir_pages); printk( "Writing pagedir (%d pages): ", nr_pgdir_pages);
for (i=0; i<nr_pgdir_pages; i++) { for (i=0; i<nr_pgdir_pages; i++) {
cur = (union diskpage *)((char *) pagedir_nosave)+i; cur = (union diskpage *)((char *) pagedir_nosave)+i;
if ((char *) cur != (((char *) pagedir_nosave) + i*PAGE_SIZE)) BUG_ON ((char *) cur != (((char *) pagedir_nosave) + i*PAGE_SIZE));
panic("Something is of wrong size"); printk( "." );
PRINTK( "." );
if (!(entry = get_swap_page()).val) { if (!(entry = get_swap_page()).val) {
printk(KERN_CRIT "Not enough swapspace when writing pgdir\n" ); printk(KERN_CRIT "Not enough swapspace when writing pgdir\n" );
panic("Don't know how to recover"); panic("Don't know how to recover");
...@@ -441,28 +428,24 @@ static int write_suspend_image(void) ...@@ -441,28 +428,24 @@ static int write_suspend_image(void)
if(swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND) if(swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND)
panic("\nNot enough swapspace for pagedir on suspend device" ); panic("\nNot enough swapspace for pagedir on suspend device" );
if (sizeof(swp_entry_t) != sizeof(long)) BUG_ON (sizeof(swp_entry_t) != sizeof(long));
panic("I need swp_entry_t to be sizeof long, otherwise next assignment could damage pagedir"); BUG_ON (PAGE_SIZE % sizeof(struct pbe));
if (PAGE_SIZE % sizeof(struct pbe))
panic("I need PAGE_SIZE to be integer multiple of struct pbe, otherwise next assignment could damage pagedir");
cur->link.next = prev; cur->link.next = prev;
page = virt_to_page((unsigned long)cur); page = virt_to_page((unsigned long)cur);
rw_swap_page_sync(WRITE, entry, page); rw_swap_page_sync(WRITE, entry, page);
prev = entry; prev = entry;
} }
PRINTK(", header"); printk("H");
if (sizeof(struct suspend_header) > PAGE_SIZE-sizeof(swp_entry_t)) BUG_ON (sizeof(struct suspend_header) > PAGE_SIZE-sizeof(swp_entry_t));
panic("sizeof(struct suspend_header) too big: %d", BUG_ON (sizeof(union diskpage) != PAGE_SIZE);
sizeof(struct suspend_header));
if (sizeof(union diskpage) != PAGE_SIZE)
panic("union diskpage has bad size");
if (!(entry = get_swap_page()).val) if (!(entry = get_swap_page()).val)
panic( "\nNot enough swapspace when writing header" ); panic( "\nNot enough swapspace when writing header" );
if(swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND) if (swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND)
panic("\nNot enough swapspace for header on suspend device" ); panic("\nNot enough swapspace for header on suspend device" );
cur = (void *) buffer; cur = (void *) buffer;
if(fill_suspend_header(&cur->sh)) if (fill_suspend_header(&cur->sh))
panic("\nOut of memory while writing header"); panic("\nOut of memory while writing header");
cur->link.next = prev; cur->link.next = prev;
...@@ -471,13 +454,9 @@ static int write_suspend_image(void) ...@@ -471,13 +454,9 @@ static int write_suspend_image(void)
rw_swap_page_sync(WRITE, entry, page); rw_swap_page_sync(WRITE, entry, page);
prev = entry; prev = entry;
PRINTK( ", signature" ); printk( "S" );
#if 0
if (swp_type(entry) != 0)
panic("Need just one swapfile");
#endif
mark_swapfiles(prev, MARK_SWAP_SUSPEND); mark_swapfiles(prev, MARK_SWAP_SUSPEND);
PRINTK( ", done\n" ); printk( "|\n" );
MDELAY(1000); MDELAY(1000);
free_page((unsigned long) buffer); free_page((unsigned long) buffer);
...@@ -493,20 +472,19 @@ static int count_and_copy_data_pages(struct pbe *pagedir_p) ...@@ -493,20 +472,19 @@ static int count_and_copy_data_pages(struct pbe *pagedir_p)
if (max_mapnr != num_physpages) if (max_mapnr != num_physpages)
panic("mapnr is not expected"); panic("mapnr is not expected");
for(loop = 0; loop < max_mapnr; loop++) { for (loop = 0; loop < max_mapnr; loop++) {
if(PageHighMem(mem_map+loop)) if (PageHighMem(mem_map+loop))
panic("No highmem for me, sorry."); panic("Swsusp not supported on highmem boxes. Send 1GB of RAM to <pavel@ucw.cz> and try again ;-).");
if(!PageReserved(mem_map+loop)) { if (!PageReserved(mem_map+loop)) {
if(PageNosave(mem_map+loop)) if (PageNosave(mem_map+loop))
continue; continue;
if((chunk_size=is_head_of_free_region(mem_map+loop))!=0) { if ((chunk_size=is_head_of_free_region(mem_map+loop))!=0) {
loop += chunk_size - 1; loop += chunk_size - 1;
continue; continue;
} }
} else if(PageReserved(mem_map+loop)) { } else if (PageReserved(mem_map+loop)) {
if(PageNosave(mem_map+loop)) BUG_ON (PageNosave(mem_map+loop));
panic("What?");
/* /*
* Just copy whole code segment. Hopefully it is not that big. * Just copy whole code segment. Hopefully it is not that big.
...@@ -514,15 +492,15 @@ static int count_and_copy_data_pages(struct pbe *pagedir_p) ...@@ -514,15 +492,15 @@ static int count_and_copy_data_pages(struct pbe *pagedir_p)
if (ADDRESS(loop) >= (unsigned long) if (ADDRESS(loop) >= (unsigned long)
&__nosave_begin && ADDRESS(loop) < &__nosave_begin && ADDRESS(loop) <
(unsigned long)&__nosave_end) { (unsigned long)&__nosave_end) {
printk("[nosave]"); PRINTK("[nosave %x]", ADDRESS(loop));
continue; continue;
} }
/* Hmm, perhaps copying all reserved pages is not too healthy as they may contain /* Hmm, perhaps copying all reserved pages is not too healthy as they may contain
critical bios data? */ critical bios data? */
} else panic("No third thing should be possible"); } else BUG();
nr_copy_pages++; nr_copy_pages++;
if(pagedir_p) { if (pagedir_p) {
pagedir_p->orig_address = ADDRESS(loop); pagedir_p->orig_address = ADDRESS(loop);
copy_page(pagedir_p->address, pagedir_p->orig_address); copy_page(pagedir_p->address, pagedir_p->orig_address);
pagedir_p++; pagedir_p++;
...@@ -539,10 +517,10 @@ static void free_suspend_pagedir(unsigned long this_pagedir) ...@@ -539,10 +517,10 @@ static void free_suspend_pagedir(unsigned long this_pagedir)
(PAGE_SIZE << pagedir_order); (PAGE_SIZE << pagedir_order);
for(i=0; i < num_physpages; i++, page++) { for(i=0; i < num_physpages; i++, page++) {
if(!TestClearPageNosave(page)) if (!TestClearPageNosave(page))
continue; continue;
if(ADDRESS(i) >= this_pagedir && ADDRESS(i) < this_pagedir_end) if (ADDRESS(i) >= this_pagedir && ADDRESS(i) < this_pagedir_end)
continue; /* old pagedir gets freed in one */ continue; /* old pagedir gets freed in one */
free_page(ADDRESS(i)); free_page(ADDRESS(i));
...@@ -570,7 +548,6 @@ static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages) ...@@ -570,7 +548,6 @@ static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages)
while(nr_copy_pages--) { while(nr_copy_pages--) {
p->address = get_free_page(GFP_ATOMIC); p->address = get_free_page(GFP_ATOMIC);
if(!p->address) { if(!p->address) {
panic("oom");
free_suspend_pagedir((unsigned long) pagedir); free_suspend_pagedir((unsigned long) pagedir);
return NULL; return NULL;
} }
...@@ -596,9 +573,11 @@ static int prepare_suspend_console(void) ...@@ -596,9 +573,11 @@ static int prepare_suspend_console(void)
set_console (SUSPEND_CONSOLE); set_console (SUSPEND_CONSOLE);
if(vt_waitactive(SUSPEND_CONSOLE)) { if(vt_waitactive(SUSPEND_CONSOLE)) {
PRINTS("Bummer. Can't switch VCs."); PRINTK("Bummer. Can't switch VCs.");
return 1; return 1;
} }
orig_kmsg = kmsg_redirect;
kmsg_redirect = SUSPEND_CONSOLE;
#endif #endif
#endif #endif
return 0; return 0;
...@@ -615,14 +594,12 @@ static void restore_console(void) ...@@ -615,14 +594,12 @@ static void restore_console(void)
static int prepare_suspend_processes(void) static int prepare_suspend_processes(void)
{ {
PRINTS( "Stopping processes\n" );
MDELAY(1000);
if (freeze_processes()) { if (freeze_processes()) {
PRINTS( "Not all processes stopped!\n" ); printk( KERN_ERR "Suspend failed: Not all processes stopped!\n" );
thaw_processes(); thaw_processes();
return 1; return 1;
} }
do_suspend_sync(); sys_sync();
return 0; return 0;
} }
...@@ -633,10 +610,10 @@ static int prepare_suspend_processes(void) ...@@ -633,10 +610,10 @@ static int prepare_suspend_processes(void)
*/ */
static void free_some_memory(void) static void free_some_memory(void)
{ {
PRINTS("Freeing memory: "); printk("Freeing memory: ");
while (try_to_free_pages(&contig_page_data.node_zones[ZONE_HIGHMEM], GFP_KSWAPD, 0)) while (try_to_free_pages(&contig_page_data.node_zones[ZONE_HIGHMEM], GFP_KSWAPD, 0))
printk("."); printk(".");
printk("\n"); printk("|\n");
} }
/* Make disk drivers accept operations, again */ /* Make disk drivers accept operations, again */
...@@ -669,9 +646,11 @@ static int drivers_suspend(void) ...@@ -669,9 +646,11 @@ static int drivers_suspend(void)
#define RESUME_ALL_PHASES (RESUME_PHASE1 | RESUME_PHASE2) #define RESUME_ALL_PHASES (RESUME_PHASE1 | RESUME_PHASE2)
static void drivers_resume(int flags) static void drivers_resume(int flags)
{ {
if (flags & RESUME_PHASE1) {
device_resume(RESUME_ENABLE); device_resume(RESUME_ENABLE);
device_resume(RESUME_RESTORE_STATE); device_resume(RESUME_RESTORE_STATE);
if(flags & RESUME_PHASE2) { }
if (flags & RESUME_PHASE2) {
if(pm_suspend_state) { if(pm_suspend_state) {
if(pm_send_all(PM_RESUME,(void *)0)) if(pm_send_all(PM_RESUME,(void *)0))
printk(KERN_WARNING "Problem while sending resume event\n"); printk(KERN_WARNING "Problem while sending resume event\n");
...@@ -691,11 +670,11 @@ static int suspend_save_image(void) ...@@ -691,11 +670,11 @@ static int suspend_save_image(void)
unsigned int nr_needed_pages = 0; unsigned int nr_needed_pages = 0;
pagedir_nosave = NULL; pagedir_nosave = NULL;
PRINTS( "/critical section: Counting pages to copy" ); printk( "/critical section: Counting pages to copy" );
nr_copy_pages = count_and_copy_data_pages(NULL); nr_copy_pages = count_and_copy_data_pages(NULL);
nr_needed_pages = nr_copy_pages + PAGES_FOR_IO; nr_needed_pages = nr_copy_pages + PAGES_FOR_IO;
PRINTK(" (pages needed: %d+%d=%d free: %d)\n",nr_copy_pages,PAGES_FOR_IO,nr_needed_pages,nr_free_pages()); printk(" (pages needed: %d+%d=%d free: %d)\n",nr_copy_pages,PAGES_FOR_IO,nr_needed_pages,nr_free_pages());
if(nr_free_pages() < nr_needed_pages) { if(nr_free_pages() < nr_needed_pages) {
printk(KERN_CRIT "%sCouldn't get enough free pages, on %d pages short\n", printk(KERN_CRIT "%sCouldn't get enough free pages, on %d pages short\n",
name_suspend, nr_needed_pages-nr_free_pages()); name_suspend, nr_needed_pages-nr_free_pages());
...@@ -724,7 +703,7 @@ static int suspend_save_image(void) ...@@ -724,7 +703,7 @@ static int suspend_save_image(void)
pagedir_order_check = pagedir_order; pagedir_order_check = pagedir_order;
if (nr_copy_pages != count_and_copy_data_pages(pagedir_nosave)) /* copy */ if (nr_copy_pages != count_and_copy_data_pages(pagedir_nosave)) /* copy */
panic("Count and copy returned another count than when counting?\n"); BUG();
/* /*
* End of critical section. From now on, we can write to memory, * End of critical section. From now on, we can write to memory,
...@@ -735,14 +714,12 @@ static int suspend_save_image(void) ...@@ -735,14 +714,12 @@ static int suspend_save_image(void)
*/ */
drivers_unsuspend(); drivers_unsuspend();
spin_unlock_irq(&suspend_pagedir_lock); spin_unlock_irq(&suspend_pagedir_lock);
PRINTS( "critical section/: done (%d pages copied)\n", nr_copy_pages ); printk( "critical section/: done (%d pages copied)\n", nr_copy_pages );
lock_swapdevices(); lock_swapdevices();
write_suspend_image(); write_suspend_image();
lock_swapdevices(); /* This will unlock ignored swap devices since writing is finished */ lock_swapdevices(); /* This will unlock ignored swap devices since writing is finished */
/* Image is saved, call sync & restart machine */
PRINTS( "Syncing disks\n" );
/* It is important _NOT_ to umount filesystems at this point. We want /* It is important _NOT_ to umount filesystems at this point. We want
* them synced (in case something goes wrong) but we DO not want to mark * them synced (in case something goes wrong) but we DO not want to mark
* filesystem clean: it is not. (And it does not matter, if we resume * filesystem clean: it is not. (And it does not matter, if we resume
...@@ -754,9 +731,9 @@ static int suspend_save_image(void) ...@@ -754,9 +731,9 @@ static int suspend_save_image(void)
void suspend_power_down(void) void suspend_power_down(void)
{ {
C_A_D = 0; C_A_D = 0;
printk(KERN_EMERG "%sTrying to power down.\n", name_suspend); printk(KERN_EMERG "%s%s Trying to power down.\n", name_suspend, TEST_SWSUSP ? "Disable TEST_SWSUSP. NOT ": "");
#ifdef CONFIG_VT #ifdef CONFIG_VT
printk(KERN_EMERG "shift_state: %04x\n", shift_state); PRINTK(KERN_EMERG "shift_state: %04x\n", shift_state);
mdelay(1000); mdelay(1000);
if (TEST_SWSUSP ^ (!!(shift_state & (1 << KG_CTRL)))) if (TEST_SWSUSP ^ (!!(shift_state & (1 << KG_CTRL))))
machine_restart(NULL); machine_restart(NULL);
...@@ -783,7 +760,7 @@ void do_magic_resume_1(void) ...@@ -783,7 +760,7 @@ void do_magic_resume_1(void)
mb(); mb();
spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */
printk( "Waiting for DMAs to settle down...\n"); PRINTK( "Waiting for DMAs to settle down...\n");
mdelay(1000); /* We do not want some readahead with DMA to corrupt our memory, right? mdelay(1000); /* We do not want some readahead with DMA to corrupt our memory, right?
Do it with disabled interrupts for best effect. That way, if some Do it with disabled interrupts for best effect. That way, if some
driver scheduled DMA, we have good chance for DMA to finish ;-). */ driver scheduled DMA, we have good chance for DMA to finish ;-). */
...@@ -791,12 +768,10 @@ void do_magic_resume_1(void) ...@@ -791,12 +768,10 @@ void do_magic_resume_1(void)
void do_magic_resume_2(void) void do_magic_resume_2(void)
{ {
if (nr_copy_pages_check != nr_copy_pages) BUG_ON (nr_copy_pages_check != nr_copy_pages);
panic("nr_copy_pages changed?!"); BUG_ON (pagedir_order_check != pagedir_order);
if (pagedir_order_check != pagedir_order)
panic("pagedir_order changed?!");
PRINTR( "Freeing prev allocated pagedir\n" ); PRINTK( "Freeing prev allocated pagedir\n" );
free_suspend_pagedir((unsigned long) pagedir_save); free_suspend_pagedir((unsigned long) pagedir_save);
__flush_tlb_global(); /* Even mappings of "global" things (vmalloc) need to be fixed */ __flush_tlb_global(); /* Even mappings of "global" things (vmalloc) need to be fixed */
drivers_resume(RESUME_ALL_PHASES); drivers_resume(RESUME_ALL_PHASES);
...@@ -824,7 +799,7 @@ void do_magic_suspend_2(void) ...@@ -824,7 +799,7 @@ void do_magic_suspend_2(void)
if (!suspend_save_image()) if (!suspend_save_image())
suspend_power_down(); /* FIXME: if suspend_power_down is commented out, console is lost after few suspends ?! */ suspend_power_down(); /* FIXME: if suspend_power_down is commented out, console is lost after few suspends ?! */
printk(KERN_WARNING "%sSuspend failed, trying to recover...\n", name_suspend); printk(KERN_EMERG "%sSuspend failed, trying to recover...\n", name_suspend);
MDELAY(1000); /* So user can wait and report us messages if armageddon comes :-) */ MDELAY(1000); /* So user can wait and report us messages if armageddon comes :-) */
barrier(); barrier();
...@@ -837,7 +812,7 @@ void do_magic_suspend_2(void) ...@@ -837,7 +812,7 @@ void do_magic_suspend_2(void)
drivers_resume(RESUME_PHASE1); drivers_resume(RESUME_PHASE1);
spin_unlock_irq(&suspend_pagedir_lock); spin_unlock_irq(&suspend_pagedir_lock);
mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME); mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
printk(KERN_WARNING "%sLeaving do_magic_suspend_2...\n", name_suspend); PRINTK(KERN_WARNING "%sLeaving do_magic_suspend_2...\n", name_suspend);
} }
/* /*
...@@ -849,7 +824,7 @@ void do_software_suspend(void) ...@@ -849,7 +824,7 @@ void do_software_suspend(void)
{ {
arch_prepare_suspend(); arch_prepare_suspend();
if (prepare_suspend_console()) if (prepare_suspend_console())
printk( "Can't allocate a console... proceeding\n"); printk( "%sCan't allocate a console... proceeding\n", name_suspend);
if (!prepare_suspend_processes()) { if (!prepare_suspend_processes()) {
free_some_memory(); free_some_memory();
...@@ -858,11 +833,11 @@ void do_software_suspend(void) ...@@ -858,11 +833,11 @@ void do_software_suspend(void)
* We sync here -- so you have consistent filesystem state when things go wrong. * We sync here -- so you have consistent filesystem state when things go wrong.
* -- so that noone writes to disk after we do atomic copy of data. * -- so that noone writes to disk after we do atomic copy of data.
*/ */
PRINTS("Syncing disks before copy\n"); PRINTK("Syncing disks before copy\n");
do_suspend_sync(); do_suspend_sync();
if(drivers_suspend()==0) if(drivers_suspend()==0)
do_magic(0); /* This function returns after machine woken up from resume */ do_magic(0); /* This function returns after machine woken up from resume */
PRINTR("Restarting processes...\n"); PRINTK("Restarting processes...\n");
thaw_processes(); thaw_processes();
} }
software_suspend_enabled = 1; software_suspend_enabled = 1;
...@@ -950,6 +925,8 @@ static int relocate_pagedir(void) ...@@ -950,6 +925,8 @@ static int relocate_pagedir(void)
void **eaten_memory = NULL; void **eaten_memory = NULL;
void **c = eaten_memory, *m, *f; void **c = eaten_memory, *m, *f;
printk("Relocating pagedir");
if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) { if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) {
printk("not neccessary\n"); printk("not neccessary\n");
return 0; return 0;
...@@ -979,7 +956,7 @@ static int relocate_pagedir(void) ...@@ -979,7 +956,7 @@ static int relocate_pagedir(void)
if (f) if (f)
free_pages((unsigned long)f, pagedir_order); free_pages((unsigned long)f, pagedir_order);
} }
printk("okay\n"); printk("|\n");
return 0; return 0;
} }
...@@ -1011,19 +988,10 @@ static int sanity_check(struct suspend_header *sh) ...@@ -1011,19 +988,10 @@ static int sanity_check(struct suspend_header *sh)
return 0; return 0;
} }
static int bdev_read_page(kdev_t dev, long pos, void *buf) static int bdev_read_page(struct block_device *bdev, long pos, void *buf)
{ {
struct buffer_head *bh; struct buffer_head *bh;
struct block_device *bdev; BUG_ON (pos%PAGE_SIZE);
if (pos%PAGE_SIZE) panic("Sorry, dave, I can't let you do that!\n");
bdev = bdget(kdev_t_to_nr(dev));
blkdev_get(bdev, FMODE_READ, O_RDONLY, BDEV_RAW);
if (!bdev) {
printk("No block device for %s\n", __bdevname(dev));
BUG();
}
set_blocksize(bdev, PAGE_SIZE);
bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE); bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
if (!bh || (!bh->b_data)) { if (!bh || (!bh->b_data)) {
return -1; return -1;
...@@ -1031,44 +999,48 @@ static int bdev_read_page(kdev_t dev, long pos, void *buf) ...@@ -1031,44 +999,48 @@ static int bdev_read_page(kdev_t dev, long pos, void *buf)
memcpy(buf, bh->b_data, PAGE_SIZE); /* FIXME: may need kmap() */ memcpy(buf, bh->b_data, PAGE_SIZE); /* FIXME: may need kmap() */
BUG_ON(!buffer_uptodate(bh)); BUG_ON(!buffer_uptodate(bh));
brelse(bh); brelse(bh);
blkdev_put(bdev, BDEV_RAW);
return 0; return 0;
} }
static int bdev_write_page(struct block_device *bdev, long pos, void *buf)
{
struct buffer_head *bh;
#if 0
BUG_ON (pos%PAGE_SIZE);
bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
if (!bh || (!bh->b_data)) {
return -1;
}
memcpy(bh->b_data, buf, PAGE_SIZE); /* FIXME: may need kmap() */
BUG_ON(!buffer_uptodate(bh));
generic_make_request(WRITE, bh);
if (!buffer_uptodate(bh))
printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unsuccessful...\n", name_resume, resume_file);
wait_on_buffer(bh);
brelse(bh);
return 0;
#endif
printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unimplemented...\n", name_resume, resume_file);
}
extern kdev_t __init name_to_kdev_t(const char *line); extern kdev_t __init name_to_kdev_t(const char *line);
static int resume_try_to_read(const char * specialfile, int noresume) static int __read_suspend_image(struct block_device *bdev, union diskpage *cur, int noresume)
{ {
union diskpage *cur;
swp_entry_t next; swp_entry_t next;
int i, nr_pgdir_pages, error; int i, nr_pgdir_pages;
int blksize = 0;
resume_device = name_to_kdev_t(specialfile);
cur = (void *) get_free_page(GFP_ATOMIC);
if (!cur) {
printk( "%sNot enough memory?\n", name_resume );
error = -ENOMEM;
goto resume_read_error;
}
printk("Resuming from device %x\n", kdev_t_to_nr(resume_device));
#define READTO(pos, ptr) \
if (bdev_read_page(resume_device, pos, ptr)) { error = -EIO; goto resume_read_error; }
#define PREPARENEXT \ #define PREPARENEXT \
{ next = cur->link.next; \ { next = cur->link.next; \
next.val = swp_offset(next) * PAGE_SIZE; \ next.val = swp_offset(next) * PAGE_SIZE; \
} }
error = -EIO; if (bdev_read_page(bdev, 0, cur)) return -EIO;
READTO(0, cur);
if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) || if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
(!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) { (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
printk(KERN_ERR "%sThis is normal swap space\n", name_resume ); printk(KERN_ERR "%sThis is normal swap space\n", name_resume );
error = -EINVAL; return -EINVAL;
goto resume_read_error;
} }
PREPARENEXT; /* We have to read next position before we overwrite it */ PREPARENEXT; /* We have to read next position before we overwrite it */
...@@ -1080,18 +1052,26 @@ static int resume_try_to_read(const char * specialfile, int noresume) ...@@ -1080,18 +1052,26 @@ static int resume_try_to_read(const char * specialfile, int noresume)
else { else {
panic("%sUnable to find suspended-data signature (%.10s - misspelled?\n", panic("%sUnable to find suspended-data signature (%.10s - misspelled?\n",
name_resume, cur->swh.magic.magic); name_resume, cur->swh.magic.magic);
/* We want to panic even with noresume -- we certainly don't want to add
out signature into your ext2 filesystem ;-) */
}
if(noresume) {
/* We don't do a sanity check here: we want to restore the swap
whatever version of kernel made the suspend image;
We need to write swap, but swap is *not* enabled so
we must write the device directly */
printk("%s: Fixing swap signatures %s...\n", name_resume, resume_file);
bdev_write_page(bdev, 0, cur);
} }
if (prepare_suspend_console())
printk("%sCan't allocate a console... proceeding\n", name_resume);
printk( "%sSignature found, resuming\n", name_resume ); printk( "%sSignature found, resuming\n", name_resume );
MDELAY(1000); MDELAY(1000);
READTO(next.val, cur); if (bdev_read_page(bdev, next.val, cur)) return -EIO;
if (sanity_check(&cur->sh)) /* Is this same machine? */
error = -EPERM; return -EPERM;
if (sanity_check(&cur->sh))
goto resume_read_error;
/* Probably this is the same machine */
PREPARENEXT; PREPARENEXT;
pagedir_save = cur->sh.suspend_pagedir; pagedir_save = cur->sh.suspend_pagedir;
...@@ -1099,64 +1079,82 @@ static int resume_try_to_read(const char * specialfile, int noresume) ...@@ -1099,64 +1079,82 @@ static int resume_try_to_read(const char * specialfile, int noresume)
nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages); nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages);
pagedir_order = get_bitmask_order(nr_pgdir_pages); pagedir_order = get_bitmask_order(nr_pgdir_pages);
error = -ENOMEM;
free_page((unsigned long) cur);
pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order); pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
if(!pagedir_nosave) if (!pagedir_nosave)
goto resume_read_error; return -ENOMEM;
PRINTR( "%sReading pagedir, ", name_resume ); PRINTK( "%sReading pagedir, ", name_resume );
/* We get pages in reverse order of saving! */ /* We get pages in reverse order of saving! */
error=-EIO;
for (i=nr_pgdir_pages-1; i>=0; i--) { for (i=nr_pgdir_pages-1; i>=0; i--) {
if (!next.val) BUG_ON (!next.val);
panic( "Preliminary end of suspended data?" );
cur = (union diskpage *)((char *) pagedir_nosave)+i; cur = (union diskpage *)((char *) pagedir_nosave)+i;
READTO(next.val, cur); if (bdev_read_page(bdev, next.val, cur)) return -EIO;
PREPARENEXT; PREPARENEXT;
} }
if (next.val) BUG_ON (next.val);
panic( "Suspended data too long?" );
printk("Relocating pagedir"); if (relocate_pagedir())
if((error=relocate_pagedir())!=0) return -ENOMEM;
goto resume_read_error; if (check_pagedir())
if((error=check_pagedir())!=0) return -ENOMEM;
goto resume_read_error;
PRINTK( "image data (%d pages): ", nr_copy_pages ); printk( "Reading image data (%d pages): ", nr_copy_pages );
error = -EIO;
for(i=0; i < nr_copy_pages; i++) { for(i=0; i < nr_copy_pages; i++) {
swp_entry_t swap_address = (pagedir_nosave+i)->swap_address; swp_entry_t swap_address = (pagedir_nosave+i)->swap_address;
if (!(i%100)) if (!(i%100))
PRINTK( "." ); printk( "." );
next.val = swp_offset(swap_address) * PAGE_SIZE;
/* You do not need to check for overlaps... /* You do not need to check for overlaps...
... check_pagedir already did this work */ ... check_pagedir already did this work */
READTO(next.val, (char *)((pagedir_nosave+i)->address)); if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address)))
return -EIO;
} }
PRINTK( " done\n" ); printk( "|\n" );
error = 0; return 0;
}
resume_read_error: static int read_suspend_image(const char * specialfile, int noresume)
{
union diskpage *cur;
unsigned long scratch_page = 0;
int error;
resume_device = name_to_kdev_t(specialfile);
scratch_page = get_free_page(GFP_ATOMIC);
cur = (void *) scratch_page;
if (cur) {
struct block_device *bdev;
printk("Resuming from device %s\n", __bdevname(resume_device));
bdev = bdget(kdev_t_to_nr(resume_device));
if (!bdev) {
printk("No such block device ?!\n");
BUG();
}
blkdev_get(bdev, FMODE_READ, O_RDONLY, BDEV_RAW);
set_blocksize(bdev, PAGE_SIZE);
error = __read_suspend_image(bdev, cur, noresume);
blkdev_put(bdev, BDEV_RAW);
} else error = -ENOMEM;
if (scratch_page)
free_page(scratch_page);
switch (error) { switch (error) {
case 0: case 0:
PRINTR("Reading resume file was successful\n"); PRINTK("Reading resume file was successful\n");
break; break;
case -EINVAL: case -EINVAL:
break; break;
case -EIO: case -EIO:
printk( "%sI/O error\n", name_resume); printk( "%sI/O error\n", name_resume);
panic("Wanted to resume but it did not work\n");
break; break;
case -ENOENT: case -ENOENT:
printk( "%s%s: No such file or directory\n", name_resume, specialfile); printk( "%s%s: No such file or directory\n", name_resume, specialfile);
panic("Wanted to resume but it did not work\n"); break;
case -ENOMEM:
printk( "%sNot enough memory\n", name_resume);
break; break;
default: default:
printk( "%sError %d resuming\n", name_resume, error ); printk( "%sError %d resuming\n", name_resume, error );
panic("Wanted to resume but it did not work\n");
} }
MDELAY(1000); MDELAY(1000);
return error; return error;
...@@ -1180,7 +1178,8 @@ void software_resume(void) ...@@ -1180,7 +1178,8 @@ void software_resume(void)
printk( "%s", name_resume ); printk( "%s", name_resume );
if(resume_status == NORESUME) { if(resume_status == NORESUME) {
/* FIXME: Signature should be restored here */ if(resume_file[0])
read_suspend_image(resume_file, 1);
printk( "disabled\n" ); printk( "disabled\n" );
return; return;
} }
...@@ -1190,12 +1189,12 @@ void software_resume(void) ...@@ -1190,12 +1189,12 @@ void software_resume(void)
console_loglevel = new_loglevel; console_loglevel = new_loglevel;
if(!resume_file[0] && resume_status == RESUME_SPECIFIED) { if(!resume_file[0] && resume_status == RESUME_SPECIFIED) {
printk( "nowhere to resume from\n" ); printk( "suspension device unspecified\n" );
return; return;
} }
printk( "resuming from %s\n", resume_file); printk( "resuming from %s\n", resume_file);
if(resume_try_to_read(resume_file, 0)) if(read_suspend_image(resume_file, 0))
goto read_failure; goto read_failure;
do_magic(1); do_magic(1);
panic("This never returns"); panic("This never returns");
......
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