]> git.hungrycats.org Git - linux/commitdiff
Merge osdl.org:/home/mochel/src/kernel/devel/linux-2.5-virgin
authorPatrick Mochel <mochel@osdl.org>
Mon, 4 Aug 2003 06:55:56 +0000 (23:55 -0700)
committerPatrick Mochel <mochel@osdl.org>
Mon, 4 Aug 2003 06:55:56 +0000 (23:55 -0700)
into osdl.org:/home/mochel/src/kernel/devel/linux-2.5-power

1  2 
arch/i386/Kconfig
drivers/power/swsusp.c
kernel/Makefile

Simple merge
index 9b20131e9498b7f29dd5fa92f6a71828e1a4f0d7,0000000000000000000000000000000000000000..5b3ab78e06f9956ec2713d8eab4f26b71aacc2b8
mode 100644,000000..100644
--- /dev/null
@@@ -1,1179 -1,0 +1,1179 @@@
- static int orig_loglevel = 0;
 +/*
 + * linux/kernel/suspend.c
 + *
 + * This file is to realize architecture-independent
 + * machine suspend feature using pretty near only high-level routines
 + *
 + * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu>
 + * Copyright (C) 1998,2001,2002 Pavel Machek <pavel@suse.cz>
 + *
 + * I'd like to thank the following people for their work:
 + * 
 + * Pavel Machek <pavel@ucw.cz>:
 + * Modifications, defectiveness pointing, being with me at the very beginning,
 + * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17.
 + *
 + * Steve Doddi <dirk@loth.demon.co.uk>: 
 + * Support the possibility of hardware state restoring.
 + *
 + * Raph <grey.havens@earthling.net>:
 + * Support for preserving states of network devices and virtual console
 + * (including X and svgatextmode)
 + *
 + * Kurt Garloff <garloff@suse.de>:
 + * Straightened the critical function in order to prevent compilers from
 + * playing tricks with local variables.
 + *
 + * Andreas Mohr <a.mohr@mailto.de>
 + *
 + * Alex Badea <vampire@go.ro>:
 + * Fixed runaway init
 + *
 + * More state savers are welcome. Especially for the scsi layer...
 + *
 + * For TODOs,FIXMEs also look in Documentation/swsusp.txt
 + */
 +
 +#include <linux/module.h>
 +#include <linux/mm.h>
 +#include <linux/suspend.h>
 +#include <linux/smp_lock.h>
 +#include <linux/file.h>
 +#include <linux/utsname.h>
 +#include <linux/version.h>
 +#include <linux/delay.h>
 +#include <linux/reboot.h>
 +#include <linux/vt_kern.h>
 +#include <linux/bitops.h>
 +#include <linux/kbd_kern.h>
 +#include <linux/keyboard.h>
 +#include <linux/spinlock.h>
 +#include <linux/genhd.h>
 +#include <linux/kernel.h>
 +#include <linux/major.h>
 +#include <linux/swap.h>
 +#include <linux/pm.h>
 +#include <linux/device.h>
 +#include <linux/buffer_head.h>
 +#include <linux/swapops.h>
 +#include <linux/bootmem.h>
 +
 +#include <asm/uaccess.h>
 +#include <asm/mmu_context.h>
 +#include <asm/pgtable.h>
 +#include <asm/io.h>
 +
 +extern long sys_sync(void);
 +
 +unsigned char software_suspend_enabled = 0;
 +
 +#define SUSPEND_CONSOLE       (MAX_NR_CONSOLES-1)
 +/* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but
 +   we probably do not take enough locks for switching consoles, etc,
 +   so bad things might happen.
 +*/
 +#if !defined(CONFIG_VT) || !defined(CONFIG_VT_CONSOLE)
 +#undef SUSPEND_CONSOLE
 +#endif
 +
 +#define __ADDRESS(x)  ((unsigned long) phys_to_virt(x))
 +#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT)
 +#define ADDRESS2(x) __ADDRESS(__pa(x))                /* Needed for x86-64 where some pages are in memory twice */
 +
 +/* References to section boundaries */
 +extern char __nosave_begin, __nosave_end;
 +
 +extern int is_head_of_free_region(struct page *);
 +
 +/* Locks */
 +spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED;
 +
 +/* Variables to be preserved over suspend */
 +static int new_loglevel = 7;
- static int resume_status = 0;
++static int orig_loglevel;
 +static int orig_fgconsole, orig_kmsg;
 +static int pagedir_order_check;
 +static int nr_copy_pages_check;
 +
- static int pm_suspend_state = 0;
++static int resume_status;
 +static char resume_file[256] = "";                    /* For resume= kernel option */
 +static dev_t resume_device;
 +/* Local variables that should not be affected by save */
 +unsigned int nr_copy_pages __nosavedata = 0;
 +
++static int pm_suspend_state;
 +
 +/* Suspend pagedir is allocated before final copy, therefore it
 +   must be freed after resume 
 +
 +   Warning: this is evil. There are actually two pagedirs at time of
 +   resume. One is "pagedir_save", which is empty frame allocated at
 +   time of suspend, that must be freed. Second is "pagedir_nosave", 
 +   allocated at time of resume, that travels through memory not to
 +   collide with anything.
 + */
 +suspend_pagedir_t *pagedir_nosave __nosavedata = NULL;
 +static suspend_pagedir_t *pagedir_save;
 +static int pagedir_order __nosavedata = 0;
 +
 +struct link {
 +      char dummy[PAGE_SIZE - sizeof(swp_entry_t)];
 +      swp_entry_t next;
 +};
 +
 +union diskpage {
 +      union swap_header swh;
 +      struct link link;
 +      struct suspend_header sh;
 +};
 +
 +/*
 + * XXX: We try to keep some more pages free so that I/O operations succeed
 + * without paging. Might this be more?
 + */
 +#define PAGES_FOR_IO  512
 +
 +static const char name_suspend[] = "Suspend Machine: ";
 +static const char name_resume[] = "Resume Machine: ";
 +
 +/*
 + * Debug
 + */
 +#define       DEBUG_DEFAULT
 +#undef        DEBUG_PROCESS
 +#undef        DEBUG_SLOW
 +#define TEST_SWSUSP 0         /* Set to 1 to reboot instead of halt machine after suspension */
 +
 +#ifdef DEBUG_DEFAULT
 +# define PRINTK(f, a...)       printk(f, ## a)
 +#else
 +# define PRINTK(f, a...)
 +#endif
 +
 +#ifdef DEBUG_SLOW
 +#define MDELAY(a) mdelay(a)
 +#else
 +#define MDELAY(a)
 +#endif
 +
 +/*
 + * Saving part...
 + */
 +
 +static __inline__ int fill_suspend_header(struct suspend_header *sh)
 +{
 +      memset((char *)sh, 0, sizeof(*sh));
 +
 +      sh->version_code = LINUX_VERSION_CODE;
 +      sh->num_physpages = num_physpages;
 +      strncpy(sh->machine, system_utsname.machine, 8);
 +      strncpy(sh->version, system_utsname.version, 20);
 +      /* FIXME: Is this bogus? --RR */
 +      sh->num_cpus = num_online_cpus();
 +      sh->page_size = PAGE_SIZE;
 +      sh->suspend_pagedir = pagedir_nosave;
 +      BUG_ON (pagedir_save != pagedir_nosave);
 +      sh->num_pbes = nr_copy_pages;
 +      /* TODO: needed? mounted fs' last mounted date comparison
 +       * [so they haven't been mounted since last suspend.
 +       * Maybe it isn't.] [we'd need to do this for _all_ fs-es]
 +       */
 +      return 0;
 +}
 +
 +/*
 + * This is our sync function. With this solution we probably won't sleep
 + * but that should not be a problem since tasks are stopped..
 + */
 +
 +static inline void do_suspend_sync(void)
 +{
 +      blk_run_queues();
 +#warning This might be broken. We need to somehow wait for data to reach the disk
 +}
 +
 +/* We memorize in swapfile_used what swap devices are used for suspension */
 +#define SWAPFILE_UNUSED    0
 +#define SWAPFILE_SUSPEND   1  /* This is the suspending device */
 +#define SWAPFILE_IGNORED   2  /* Those are other swap devices ignored for suspension */
 +
 +static unsigned short swapfile_used[MAX_SWAPFILES];
 +static unsigned short root_swap;
 +#define MARK_SWAP_SUSPEND 0
 +#define MARK_SWAP_RESUME 2
 +
 +static void mark_swapfiles(swp_entry_t prev, int mode)
 +{
 +      swp_entry_t entry;
 +      union diskpage *cur;
 +      struct page *page;
 +
 +      if (root_swap == 0xFFFF)  /* ignored */
 +              return;
 +
 +      page = alloc_page(GFP_ATOMIC);
 +      if (!page)
 +              panic("Out of memory in mark_swapfiles");
 +      cur = page_address(page);
 +      /* XXX: this is dirty hack to get first page of swap file */
 +      entry = swp_entry(root_swap, 0);
 +      rw_swap_page_sync(READ, entry, page);
 +
 +      if (mode == MARK_SWAP_RESUME) {
 +              if (!memcmp("S1",cur->swh.magic.magic,2))
 +                      memcpy(cur->swh.magic.magic,"SWAP-SPACE",10);
 +              else if (!memcmp("S2",cur->swh.magic.magic,2))
 +                      memcpy(cur->swh.magic.magic,"SWAPSPACE2",10);
 +              else printk("%sUnable to find suspended-data signature (%.10s - misspelled?\n", 
 +                      name_resume, cur->swh.magic.magic);
 +      } else {
 +              if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)))
 +                      memcpy(cur->swh.magic.magic,"S1SUSP....",10);
 +              else if ((!memcmp("SWAPSPACE2",cur->swh.magic.magic,10)))
 +                      memcpy(cur->swh.magic.magic,"S2SUSP....",10);
 +              else panic("\nSwapspace is not swapspace (%.10s)\n", cur->swh.magic.magic);
 +              cur->link.next = prev; /* prev is the first/last swap page of the resume area */
 +              /* link.next lies *no more* in last 4/8 bytes of magic */
 +      }
 +      rw_swap_page_sync(WRITE, entry, page);
 +      __free_page(page);
 +}
 +
 +static void read_swapfiles(void) /* This is called before saving image */
 +{
 +      int i, len;
 +      
 +      len=strlen(resume_file);
 +      root_swap = 0xFFFF;
 +      
 +      swap_list_lock();
 +      for(i=0; i<MAX_SWAPFILES; i++) {
 +              if (swap_info[i].flags == 0) {
 +                      swapfile_used[i]=SWAPFILE_UNUSED;
 +              } else {
 +                      if(!len) {
 +                              printk(KERN_WARNING "resume= option should be used to set suspend device" );
 +                              if(root_swap == 0xFFFF) {
 +                                      swapfile_used[i] = SWAPFILE_SUSPEND;
 +                                      root_swap = i;
 +                              } else
 +                                      swapfile_used[i] = SWAPFILE_IGNORED;                              
 +                      } else {
 +                              /* we ignore all swap devices that are not the resume_file */
 +                              if (1) {
 +// FIXME                              if(resume_device == swap_info[i].swap_device) {
 +                                      swapfile_used[i] = SWAPFILE_SUSPEND;
 +                                      root_swap = i;
 +                              } else {
 +#if 0
 +                                      printk( "Resume: device %s (%x != %x) ignored\n", swap_info[i].swap_file->d_name.name, swap_info[i].swap_device, resume_device );                                 
 +#endif
 +                                      swapfile_used[i] = SWAPFILE_IGNORED;
 +                              }
 +                      }
 +              }
 +      }
 +      swap_list_unlock();
 +}
 +
 +static void lock_swapdevices(void) /* This is called after saving image so modification
 +                                    will be lost after resume... and that's what we want. */
 +{
 +      int i;
 +
 +      swap_list_lock();
 +      for(i = 0; i< MAX_SWAPFILES; i++)
 +              if(swapfile_used[i] == SWAPFILE_IGNORED) {
 +                      swap_info[i].flags ^= 0xFF; /* we make the device unusable. A new call to
 +                                                     lock_swapdevices can unlock the devices. */
 +              }
 +      swap_list_unlock();
 +}
 +
 +static int write_suspend_image(void)
 +{
 +      int i;
 +      swp_entry_t entry, prev = { 0 };
 +      int nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages);
 +      union diskpage *cur,  *buffer = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
 +      unsigned long address;
 +      struct page *page;
 +
 +      printk( "Writing data to swap (%d pages): ", nr_copy_pages );
 +      for (i=0; i<nr_copy_pages; i++) {
 +              if (!(i%100))
 +                      printk( "." );
 +              if (!(entry = get_swap_page()).val)
 +                      panic("\nNot enough swapspace when writing data" );
 +              
 +              if (swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND)
 +                      panic("\nPage %d: not enough swapspace on suspend device", i );
 +          
 +              address = (pagedir_nosave+i)->address;
 +              page = virt_to_page(address);
 +              rw_swap_page_sync(WRITE, entry, page);
 +              (pagedir_nosave+i)->swap_address = entry;
 +      }
 +      printk( "|\n" );
 +      printk( "Writing pagedir (%d pages): ", nr_pgdir_pages);
 +      for (i=0; i<nr_pgdir_pages; i++) {
 +              cur = (union diskpage *)((char *) pagedir_nosave)+i;
 +              BUG_ON ((char *) cur != (((char *) pagedir_nosave) + i*PAGE_SIZE));
 +              printk( "." );
 +              if (!(entry = get_swap_page()).val) {
 +                      printk(KERN_CRIT "Not enough swapspace when writing pgdir\n" );
 +                      panic("Don't know how to recover");
 +                      free_page((unsigned long) buffer);
 +                      return -ENOSPC;
 +              }
 +
 +              if(swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND)
 +                      panic("\nNot enough swapspace for pagedir on suspend device" );
 +
 +              BUG_ON (sizeof(swp_entry_t) != sizeof(long));
 +              BUG_ON (PAGE_SIZE % sizeof(struct pbe));
 +
 +              cur->link.next = prev;                          
 +              page = virt_to_page((unsigned long)cur);
 +              rw_swap_page_sync(WRITE, entry, page);
 +              prev = entry;
 +      }
 +      printk("H");
 +      BUG_ON (sizeof(struct suspend_header) > PAGE_SIZE-sizeof(swp_entry_t));
 +      BUG_ON (sizeof(union diskpage) != PAGE_SIZE);
 +      if (!(entry = get_swap_page()).val)
 +              panic( "\nNot enough swapspace when writing header" );
 +      if (swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND)
 +              panic("\nNot enough swapspace for header on suspend device" );
 +
 +      cur = (void *) buffer;
 +      if (fill_suspend_header(&cur->sh))
 +              panic("\nOut of memory while writing header");
 +              
 +      cur->link.next = prev;
 +
 +      page = virt_to_page((unsigned long)cur);
 +      rw_swap_page_sync(WRITE, entry, page);
 +      prev = entry;
 +
 +      printk( "S" );
 +      mark_swapfiles(prev, MARK_SWAP_SUSPEND);
 +      printk( "|\n" );
 +
 +      MDELAY(1000);
 +      free_page((unsigned long) buffer);
 +      return 0;
 +}
 +
 +/* if pagedir_p != NULL it also copies the counted pages */
 +static int count_and_copy_data_pages(struct pbe *pagedir_p)
 +{
 +      int chunk_size;
 +      int nr_copy_pages = 0;
 +      int pfn;
 +      struct page *page;
 +      
 +#ifdef CONFIG_DISCONTIGMEM
 +      panic("Discontingmem not supported");
 +#else
 +      BUG_ON (max_pfn != num_physpages);
 +#endif
 +      for (pfn = 0; pfn < max_pfn; pfn++) {
 +              page = pfn_to_page(pfn);
 +              if (PageHighMem(page))
 +                      panic("Swsusp not supported on highmem boxes. Send 1GB of RAM to <pavel@ucw.cz> and try again ;-).");
 +
 +              if (!PageReserved(page)) {
 +                      if (PageNosave(page))
 +                              continue;
 +
 +                      if ((chunk_size=is_head_of_free_region(page))!=0) {
 +                              pfn += chunk_size - 1;
 +                              continue;
 +                      }
 +              } else if (PageReserved(page)) {
 +                      BUG_ON (PageNosave(page));
 +
 +                      /*
 +                       * Just copy whole code segment. Hopefully it is not that big.
 +                       */
 +                      if ((ADDRESS(pfn) >= (unsigned long) ADDRESS2(&__nosave_begin)) && 
 +                          (ADDRESS(pfn) <  (unsigned long) ADDRESS2(&__nosave_end))) {
 +                              PRINTK("[nosave %lx]", ADDRESS(pfn));
 +                              continue;
 +                      }
 +                      /* Hmm, perhaps copying all reserved pages is not too healthy as they may contain 
 +                         critical bios data? */
 +              } else  BUG();
 +
 +              nr_copy_pages++;
 +              if (pagedir_p) {
 +                      pagedir_p->orig_address = ADDRESS(pfn);
 +                      copy_page((void *) pagedir_p->address, (void *) pagedir_p->orig_address);
 +                      pagedir_p++;
 +              }
 +      }
 +      return nr_copy_pages;
 +}
 +
 +static void free_suspend_pagedir(unsigned long this_pagedir)
 +{
 +      struct page *page;
 +      int pfn;
 +      unsigned long this_pagedir_end = this_pagedir +
 +              (PAGE_SIZE << pagedir_order);
 +
 +      for(pfn = 0; pfn < num_physpages; pfn++) {
 +              page = pfn_to_page(pfn);
 +              if (!TestClearPageNosave(page))
 +                      continue;
 +
 +              if (ADDRESS(pfn) >= this_pagedir && ADDRESS(pfn) < this_pagedir_end)
 +                      continue; /* old pagedir gets freed in one */
 +              
 +              free_page(ADDRESS(pfn));
 +      }
 +      free_pages(this_pagedir, pagedir_order);
 +}
 +
 +static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages)
 +{
 +      int i;
 +      suspend_pagedir_t *pagedir;
 +      struct pbe *p;
 +      struct page *page;
 +
 +      pagedir_order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages));
 +
 +      p = pagedir = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, pagedir_order);
 +      if(!pagedir)
 +              return NULL;
 +
 +      page = virt_to_page(pagedir);
 +      for(i=0; i < 1<<pagedir_order; i++)
 +              SetPageNosave(page++);
 +              
 +      while(nr_copy_pages--) {
 +              p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD);
 +              if(!p->address) {
 +                      free_suspend_pagedir((unsigned long) pagedir);
 +                      return NULL;
 +              }
 +              printk(".");
 +              SetPageNosave(virt_to_page(p->address));
 +              p->orig_address = 0;
 +              p++;
 +      }
 +      return pagedir;
 +}
 +
 +static int prepare_suspend_console(void)
 +{
 +      orig_loglevel = console_loglevel;
 +      console_loglevel = new_loglevel;
 +
 +#ifdef CONFIG_VT
 +      orig_fgconsole = fg_console;
 +#ifdef SUSPEND_CONSOLE
 +      if(vc_allocate(SUSPEND_CONSOLE))
 +        /* we can't have a free VC for now. Too bad,
 +         * we don't want to mess the screen for now. */
 +              return 1;
 +
 +      set_console (SUSPEND_CONSOLE);
 +      if(vt_waitactive(SUSPEND_CONSOLE)) {
 +              PRINTK("Bummer. Can't switch VCs.");
 +              return 1;
 +      }
 +      orig_kmsg = kmsg_redirect;
 +      kmsg_redirect = SUSPEND_CONSOLE;
 +#endif
 +#endif
 +      return 0;
 +}
 +
 +static void restore_console(void)
 +{
 +      console_loglevel = orig_loglevel;
 +#ifdef SUSPEND_CONSOLE
 +      set_console (orig_fgconsole);
 +#endif
 +      return;
 +}
 +
 +static int prepare_suspend_processes(void)
 +{
 +      sys_sync();     /* Syncing needs pdflushd, so do it before stopping processes */
 +      if (freeze_processes()) {
 +              printk( KERN_ERR "Suspend failed: Not all processes stopped!\n" );
 +              thaw_processes();
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +/*
 + * Try to free as much memory as possible, but do not OOM-kill anyone
 + *
 + * Notice: all userland should be stopped at this point, or livelock is possible.
 + */
 +static void free_some_memory(void)
 +{
 +      printk("Freeing memory: ");
 +      while (shrink_all_memory(10000))
 +              printk(".");
 +      printk("|\n");
 +}
 +
 +/* Make disk drivers accept operations, again */
 +static void drivers_unsuspend(void)
 +{
 +      device_resume(RESUME_RESTORE_STATE);
 +      device_resume(RESUME_ENABLE);
 +}
 +
 +/* Called from process context */
 +static int drivers_suspend(void)
 +{
 +      device_suspend(4, SUSPEND_NOTIFY);
 +      device_suspend(4, SUSPEND_SAVE_STATE);
 +      device_suspend(4, SUSPEND_DISABLE);
 +      if(!pm_suspend_state) {
 +              if(pm_send_all(PM_SUSPEND,(void *)3)) {
 +                      printk(KERN_WARNING "Problem while sending suspend event\n");
 +                      return(1);
 +              }
 +              pm_suspend_state=1;
 +      } else
 +              printk(KERN_WARNING "PM suspend state already raised\n");
 +        
 +      return(0);
 +}
 +
 +#define RESUME_PHASE1 1 /* Called from interrupts disabled */
 +#define RESUME_PHASE2 2 /* Called with interrupts enabled */
 +#define RESUME_ALL_PHASES (RESUME_PHASE1 | RESUME_PHASE2)
 +static void drivers_resume(int flags)
 +{
 +      if (flags & RESUME_PHASE1) {
 +              device_resume(RESUME_RESTORE_STATE);
 +              device_resume(RESUME_ENABLE);
 +      }
 +      if (flags & RESUME_PHASE2) {
 +              if(pm_suspend_state) {
 +                      if(pm_send_all(PM_RESUME,(void *)0))
 +                              printk(KERN_WARNING "Problem while sending resume event\n");
 +                      pm_suspend_state=0;
 +              } else
 +                      printk(KERN_WARNING "PM suspend state wasn't raised\n");
 +
 +#ifdef SUSPEND_CONSOLE
 +              update_screen(fg_console);      /* Hmm, is this the problem? */
 +#endif
 +      }
 +}
 +
 +static int suspend_prepare_image(void)
 +{
 +      struct sysinfo i;
 +      unsigned int nr_needed_pages = 0;
 +
 +      drain_local_pages();
 +
 +      pagedir_nosave = NULL;
 +      printk( "/critical section: Counting pages to copy" );
 +      nr_copy_pages = count_and_copy_data_pages(NULL);
 +      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());
 +      if(nr_free_pages() < nr_needed_pages) {
 +              printk(KERN_CRIT "%sCouldn't get enough free pages, on %d pages short\n",
 +                     name_suspend, nr_needed_pages-nr_free_pages());
 +              root_swap = 0xFFFF;
 +              return 1;
 +      }
 +      si_swapinfo(&i);        /* FIXME: si_swapinfo(&i) returns all swap devices information.
 +                                 We should only consider resume_device. */
 +      if (i.freeswap < nr_needed_pages)  {
 +              printk(KERN_CRIT "%sThere's not enough swap space available, on %ld pages short\n",
 +                     name_suspend, nr_needed_pages-i.freeswap);
 +              return 1;
 +      }
 +
 +      PRINTK( "Alloc pagedir\n" ); 
 +      pagedir_save = pagedir_nosave = create_suspend_pagedir(nr_copy_pages);
 +      if(!pagedir_nosave) {
 +              /* Shouldn't happen */
 +              printk(KERN_CRIT "%sCouldn't allocate enough pages\n",name_suspend);
 +              panic("Really should not happen");
 +              return 1;
 +      }
 +      nr_copy_pages_check = nr_copy_pages;
 +      pagedir_order_check = pagedir_order;
 +
 +      drain_local_pages();    /* During allocating of suspend pagedir, new cold pages may appear. Kill them */
 +      if (nr_copy_pages != count_and_copy_data_pages(pagedir_nosave)) /* copy */
 +              BUG();
 +
 +      /*
 +       * End of critical section. From now on, we can write to memory,
 +       * but we should not touch disk. This specially means we must _not_
 +       * touch swap space! Except we must write out our image of course.
 +       */
 +
 +      printk( "critical section/: done (%d pages copied)\n", nr_copy_pages );
 +      return 0;
 +}
 +
 +static void suspend_save_image(void)
 +{
 +      drivers_unsuspend();
 +
 +      lock_swapdevices();
 +      write_suspend_image();
 +      lock_swapdevices();     /* This will unlock ignored swap devices since writing is finished */
 +
 +      /* 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
 +       * filesystem clean: it is not. (And it does not matter, if we resume
 +       * correctly, we'll mark system clean, anyway.)
 +       */
 +}
 +
 +static void suspend_power_down(void)
 +{
 +      extern int C_A_D;
 +      C_A_D = 0;
 +      printk(KERN_EMERG "%s%s Trying to power down.\n", name_suspend, TEST_SWSUSP ? "Disable TEST_SWSUSP. NOT ": "");
 +#ifdef CONFIG_VT
 +      PRINTK(KERN_EMERG "shift_state: %04x\n", shift_state);
 +      mdelay(1000);
 +      if (TEST_SWSUSP ^ (!!(shift_state & (1 << KG_CTRL))))
 +              machine_restart(NULL);
 +      else
 +#endif
 +      {
 +              device_shutdown();
 +              machine_power_off();
 +      }
 +
 +      printk(KERN_EMERG "%sProbably not capable for powerdown. System halted.\n", name_suspend);
 +      machine_halt();
 +      while (1);
 +      /* NOTREACHED */
 +}
 +
 +/*
 + * Magic happens here
 + */
 +
 +void do_magic_resume_1(void)
 +{
 +      barrier();
 +      mb();
 +      spin_lock_irq(&suspend_pagedir_lock);   /* Done to disable interrupts */ 
 +
 +      PRINTK( "Waiting for DMAs to settle down...\n");
 +      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
 +                         driver scheduled DMA, we have good chance for DMA to finish ;-). */
 +}
 +
 +void do_magic_resume_2(void)
 +{
 +      BUG_ON (nr_copy_pages_check != nr_copy_pages);
 +      BUG_ON (pagedir_order_check != pagedir_order);
 +
 +      __flush_tlb_global();           /* Even mappings of "global" things (vmalloc) need to be fixed */
 +
 +      PRINTK( "Freeing prev allocated pagedir\n" );
 +      free_suspend_pagedir((unsigned long) pagedir_save);
 +      spin_unlock_irq(&suspend_pagedir_lock);
 +      drivers_resume(RESUME_ALL_PHASES);
 +
 +      PRINTK( "Fixing swap signatures... " );
 +      mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
 +      PRINTK( "ok\n" );
 +
 +#ifdef SUSPEND_CONSOLE
 +      update_screen(fg_console);      /* Hmm, is this the problem? */
 +#endif
 +}
 +
 +/* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does:
 +
 +      if (!resume) {
 +              do_magic_suspend_1();
 +              save_processor_state();
 +              SAVE_REGISTERS
 +              do_magic_suspend_2();
 +              return;
 +      }
 +      GO_TO_SWAPPER_PAGE_TABLES
 +      do_magic_resume_1();
 +      COPY_PAGES_BACK
 +      RESTORE_REGISTERS
 +      restore_processor_state();
 +      do_magic_resume_2();
 +
 + */
 +
 +void do_magic_suspend_1(void)
 +{
 +      mb();
 +      barrier();
 +      BUG_ON(in_atomic());
 +      spin_lock_irq(&suspend_pagedir_lock);
 +}
 +
 +void do_magic_suspend_2(void)
 +{
 +      int is_problem;
 +      read_swapfiles();
 +      is_problem = suspend_prepare_image();
 +      spin_unlock_irq(&suspend_pagedir_lock);
 +      if (!is_problem) {
 +              kernel_fpu_end();       /* save_processor_state() does kernel_fpu_begin, and we need to revert it in order to pass in_atomic() checks */
 +              BUG_ON(in_atomic());
 +              suspend_save_image();
 +              suspend_power_down();   /* FIXME: if suspend_power_down is commented out, console is lost after few suspends ?! */
 +      }
 +
 +      printk(KERN_EMERG "%sSuspend failed, trying to recover...\n", name_suspend);
 +      MDELAY(1000); /* So user can wait and report us messages if armageddon comes :-) */
 +
 +      barrier();
 +      mb();
 +      spin_lock_irq(&suspend_pagedir_lock);   /* Done to disable interrupts */ 
 +      mdelay(1000);
 +
 +      free_pages((unsigned long) pagedir_nosave, pagedir_order);
 +      spin_unlock_irq(&suspend_pagedir_lock);
 +      mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
 +      PRINTK(KERN_WARNING "%sLeaving do_magic_suspend_2...\n", name_suspend); 
 +}
 +
 +static void do_software_suspend(void)
 +{
 +      arch_prepare_suspend();
 +      if (prepare_suspend_console())
 +              printk( "%sCan't allocate a console... proceeding\n", name_suspend);
 +      if (!prepare_suspend_processes()) {
 +
 +              /* At this point, all user processes and "dangerous"
 +                   kernel threads are stopped. Free some memory, as we
 +                   need half of memory free. */
 +
 +              free_some_memory();
 +              
 +              /* No need to invalidate any vfsmnt list -- they will be valid after resume, anyway.
 +               *
 +               * 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.
 +               */
 +              PRINTK("Syncing disks before copy\n");
 +              do_suspend_sync();
 +
 +              /* Save state of all device drivers, and stop them. */             
 +              if(drivers_suspend()==0)
 +                      /* If stopping device drivers worked, we proceed basically into
 +                       * suspend_save_image.
 +                       *
 +                       * do_magic(0) returns after system is resumed.
 +                       *
 +                       * do_magic() copies all "used" memory to "free" memory, then
 +                       * unsuspends all device drivers, and writes memory to disk
 +                       * using normal kernel mechanism.
 +                       */
 +                      do_magic(0);
 +              PRINTK("Restarting processes...\n");
 +              thaw_processes();
 +      }
 +      software_suspend_enabled = 1;
 +      MDELAY(1000);
 +      restore_console ();
 +}
 +
 +/*
 + * This is main interface to the outside world. It needs to be
 + * called from process context.
 + */
 +void software_suspend(void)
 +{
 +      if(!software_suspend_enabled)
 +              return;
 +
 +      software_suspend_enabled = 0;
 +      BUG_ON(in_interrupt());
 +      do_software_suspend();
 +}
 +
 +/* More restore stuff */
 +
 +/* FIXME: Why not memcpy(to, from, 1<<pagedir_order*PAGE_SIZE)? */
 +static void copy_pagedir(suspend_pagedir_t *to, suspend_pagedir_t *from)
 +{
 +      int i;
 +      char *topointer=(char *)to, *frompointer=(char *)from;
 +
 +      for(i=0; i < 1 << pagedir_order; i++) {
 +              copy_page(topointer, frompointer);
 +              topointer += PAGE_SIZE;
 +              frompointer += PAGE_SIZE;
 +      }
 +}
 +
 +#define does_collide(addr) does_collide_order(pagedir_nosave, addr, 0)
 +
 +/*
 + * Returns true if given address/order collides with any orig_address 
 + */
 +static int does_collide_order(suspend_pagedir_t *pagedir, unsigned long addr,
 +              int order)
 +{
 +      int i;
 +      unsigned long addre = addr + (PAGE_SIZE<<order);
 +      
 +      for(i=0; i < nr_copy_pages; i++)
 +              if((pagedir+i)->orig_address >= addr &&
 +                      (pagedir+i)->orig_address < addre)
 +                      return 1;
 +
 +      return 0;
 +}
 +
 +/*
 + * We check here that pagedir & pages it points to won't collide with pages
 + * where we're going to restore from the loaded pages later
 + */
 +static int check_pagedir(void)
 +{
 +      int i;
 +
 +      for(i=0; i < nr_copy_pages; i++) {
 +              unsigned long addr;
 +
 +              do {
 +                      addr = get_zeroed_page(GFP_ATOMIC);
 +                      if(!addr)
 +                              return -ENOMEM;
 +              } while (does_collide(addr));
 +
 +              (pagedir_nosave+i)->address = addr;
 +      }
 +      return 0;
 +}
 +
 +static int relocate_pagedir(void)
 +{
 +      /*
 +       * We have to avoid recursion (not to overflow kernel stack),
 +       * and that's why code looks pretty cryptic 
 +       */
 +      suspend_pagedir_t *new_pagedir, *old_pagedir = pagedir_nosave;
 +      void **eaten_memory = NULL;
 +      void **c = eaten_memory, *m, *f;
 +
 +      printk("Relocating pagedir");
 +
 +      if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) {
 +              printk("not necessary\n");
 +              return 0;
 +      }
 +
 +      while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
 +              memset(m, 0, PAGE_SIZE);
 +              if (!does_collide_order(old_pagedir, (unsigned long)m, pagedir_order))
 +                      break;
 +              eaten_memory = m;
 +              printk( "." ); 
 +              *eaten_memory = c;
 +              c = eaten_memory;
 +      }
 +
 +      if (!m)
 +              return -ENOMEM;
 +
 +      pagedir_nosave = new_pagedir = m;
 +      copy_pagedir(new_pagedir, old_pagedir);
 +
 +      c = eaten_memory;
 +      while(c) {
 +              printk(":");
 +              f = *c;
 +              c = *c;
 +              if (f)
 +                      free_pages((unsigned long)f, pagedir_order);
 +      }
 +      printk("|\n");
 +      return 0;
 +}
 +
 +/*
 + * Sanity check if this image makes sense with this kernel/swap context
 + * I really don't think that it's foolproof but more than nothing..
 + */
 +
 +static int sanity_check_failed(char *reason)
 +{
 +      printk(KERN_ERR "%s%s\n",name_resume,reason);
 +      return -EPERM;
 +}
 +
 +static int sanity_check(struct suspend_header *sh)
 +{
 +      if(sh->version_code != LINUX_VERSION_CODE)
 +              return sanity_check_failed("Incorrect kernel version");
 +      if(sh->num_physpages != num_physpages)
 +              return sanity_check_failed("Incorrect memory size");
 +      if(strncmp(sh->machine, system_utsname.machine, 8))
 +              return sanity_check_failed("Incorrect machine type");
 +      if(strncmp(sh->version, system_utsname.version, 20))
 +              return sanity_check_failed("Incorrect version");
 +      if(sh->num_cpus != num_online_cpus())
 +              return sanity_check_failed("Incorrect number of cpus");
 +      if(sh->page_size != PAGE_SIZE)
 +              return sanity_check_failed("Incorrect PAGE_SIZE");
 +      return 0;
 +}
 +
 +static int bdev_read_page(struct block_device *bdev, long pos, void *buf)
 +{
 +      struct buffer_head *bh;
 +      BUG_ON (pos%PAGE_SIZE);
 +      bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
 +      if (!bh || (!bh->b_data)) {
 +              return -1;
 +      }
 +      memcpy(buf, bh->b_data, PAGE_SIZE);     /* FIXME: may need kmap() */
 +      BUG_ON(!buffer_uptodate(bh));
 +      brelse(bh);
 +      return 0;
 +} 
 +
 +static int bdev_write_page(struct block_device *bdev, long pos, void *buf)
 +{
 +#if 0
 +      struct buffer_head *bh;
 +      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);
 +      return 0;
 +}
 +
 +extern dev_t __init name_to_dev_t(const char *line);
 +
 +static int __read_suspend_image(struct block_device *bdev, union diskpage *cur, int noresume)
 +{
 +      swp_entry_t next;
 +      int i, nr_pgdir_pages;
 +
 +#define PREPARENEXT \
 +      {       next = cur->link.next; \
 +              next.val = swp_offset(next) * PAGE_SIZE; \
 +        }
 +
 +      if (bdev_read_page(bdev, 0, cur)) return -EIO;
 +
 +      if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
 +          (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
 +              printk(KERN_ERR "%sThis is normal swap space\n", name_resume );
 +              return -EINVAL;
 +      }
 +
 +      PREPARENEXT; /* We have to read next position before we overwrite it */
 +
 +      if (!memcmp("S1",cur->swh.magic.magic,2))
 +              memcpy(cur->swh.magic.magic,"SWAP-SPACE",10);
 +      else if (!memcmp("S2",cur->swh.magic.magic,2))
 +              memcpy(cur->swh.magic.magic,"SWAPSPACE2",10);
 +      else {
 +              if (noresume)
 +                      return -EINVAL;
 +              panic("%sUnable to find suspended-data signature (%.10s - misspelled?\n", 
 +                      name_resume, cur->swh.magic.magic);
 +      }
 +      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 );
 +      MDELAY(1000);
 +
 +      if (bdev_read_page(bdev, next.val, cur)) return -EIO;
 +      if (sanity_check(&cur->sh))     /* Is this same machine? */     
 +              return -EPERM;
 +      PREPARENEXT;
 +
 +      pagedir_save = cur->sh.suspend_pagedir;
 +      nr_copy_pages = cur->sh.num_pbes;
 +      nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages);
 +      pagedir_order = get_bitmask_order(nr_pgdir_pages);
 +
 +      pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
 +      if (!pagedir_nosave)
 +              return -ENOMEM;
 +
 +      PRINTK( "%sReading pagedir, ", name_resume );
 +
 +      /* We get pages in reverse order of saving! */
 +      for (i=nr_pgdir_pages-1; i>=0; i--) {
 +              BUG_ON (!next.val);
 +              cur = (union diskpage *)((char *) pagedir_nosave)+i;
 +              if (bdev_read_page(bdev, next.val, cur)) return -EIO;
 +              PREPARENEXT;
 +      }
 +      BUG_ON (next.val);
 +
 +      if (relocate_pagedir())
 +              return -ENOMEM;
 +      if (check_pagedir())
 +              return -ENOMEM;
 +
 +      printk( "Reading image data (%d pages): ", nr_copy_pages );
 +      for(i=0; i < nr_copy_pages; i++) {
 +              swp_entry_t swap_address = (pagedir_nosave+i)->swap_address;
 +              if (!(i%100))
 +                      printk( "." );
 +              /* You do not need to check for overlaps...
 +                 ... check_pagedir already did this work */
 +              if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address)))
 +                      return -EIO;
 +      }
 +      printk( "|\n" );
 +      return 0;
 +}
 +
 +static int read_suspend_image(const char * specialfile, int noresume)
 +{
 +      union diskpage *cur;
 +      unsigned long scratch_page = 0;
 +      int error;
 +      char b[BDEVNAME_SIZE];
 +
 +      resume_device = name_to_dev_t(specialfile);
 +      scratch_page = get_zeroed_page(GFP_ATOMIC);
 +      cur = (void *) scratch_page;
 +      if (cur) {
 +              struct block_device *bdev;
 +              printk("Resuming from device %s\n",
 +                              __bdevname(resume_device, b));
 +              bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW);
 +              if (IS_ERR(bdev)) {
 +                      error = PTR_ERR(bdev);
 +              } else {
 +                      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) {
 +              case 0:
 +                      PRINTK("Reading resume file was successful\n");
 +                      break;
 +              case -EINVAL:
 +                      break;
 +              case -EIO:
 +                      printk( "%sI/O error\n", name_resume);
 +                      break;
 +              case -ENOENT:
 +                      printk( "%s%s: No such file or directory\n", name_resume, specialfile);
 +                      break;
 +              case -ENOMEM:
 +                      printk( "%sNot enough memory\n", name_resume);
 +                      break;
 +              default:
 +                      printk( "%sError %d resuming\n", name_resume, error );
 +      }
 +      MDELAY(1000);
 +      return error;
 +}
 +
 +/*
 + * Called from init kernel_thread.
 + * We check if we have an image and if so we try to resume
 + */
 +
 +void software_resume(void)
 +{
 +      if (num_online_cpus() > 1) {
 +              printk(KERN_WARNING "Software Suspend has malfunctioning SMP support. Disabled :(\n");  
 +              return;
 +      }
 +      /* We enable the possibility of machine suspend */
 +      software_suspend_enabled = 1;
 +      if (!resume_status)
 +              return;
 +
 +      printk( "%s", name_resume );
 +      if (resume_status == NORESUME) {
 +              if(resume_file[0])
 +                      read_suspend_image(resume_file, 1);
 +              printk( "disabled\n" );
 +              return;
 +      }
 +      MDELAY(1000);
 +
 +      orig_loglevel = console_loglevel;
 +      console_loglevel = new_loglevel;
 +
 +      if (!resume_file[0] && resume_status == RESUME_SPECIFIED) {
 +              printk( "suspension device unspecified\n" );
 +              return;
 +      }
 +
 +      printk( "resuming from %s\n", resume_file);
 +      if (read_suspend_image(resume_file, 0))
 +              goto read_failure;
 +      do_magic(1);
 +      panic("This never returns");
 +
 +read_failure:
 +      console_loglevel = orig_loglevel;
 +      return;
 +}
 +
 +static int __init resume_setup(char *str)
 +{
 +      if (resume_status == NORESUME)
 +              return 1;
 +
 +      strncpy( resume_file, str, 255 );
 +      resume_status = RESUME_SPECIFIED;
 +
 +      return 1;
 +}
 +
 +static int __init noresume_setup(char *str)
 +{
 +      resume_status = NORESUME;
 +      return 1;
 +}
 +
 +__setup("noresume", noresume_setup);
 +__setup("resume=", resume_setup);
 +
 +EXPORT_SYMBOL(software_suspend);
 +EXPORT_SYMBOL(software_suspend_enabled);
diff --cc kernel/Makefile
index 80a1148cf989b449e8f413f6c1e28ef21c5a5420,d5c2684a6c76d73e89020b7aeb9adf77883e23e0..9be7ecd310f75d213269c5aee4640815caef828b
@@@ -17,7 -17,12 +17,11 @@@ obj-$(CONFIG_KALLSYMS) += kallsyms.
  obj-$(CONFIG_PM) += pm.o
  obj-$(CONFIG_CPU_FREQ) += cpufreq.o
  obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 -obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o
  obj-$(CONFIG_COMPAT) += compat.o
+ obj-$(CONFIG_IKCONFIG) += configs.o
+ 
+ # files to be removed upon make clean
+ clean-files := ikconfig.h
  
  ifneq ($(CONFIG_IA64),y)
  # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is