-
-/* More restore stuff */
-
-/*
- * Returns true if given address/order collides with any orig_address
- */
-static int __init does_collide_order(unsigned long addr, int order)
-{
- int i;
-
- for (i=0; i < (1<<order); i++)
- if (!PageNosaveFree(virt_to_page(addr + i * PAGE_SIZE)))
- 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 __init 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_order(addr, 0));
-
- (pagedir_nosave+i)->address = addr;
- }
- return 0;
-}
-
-static int __init swsusp_pagedir_relocate(void)
-{
- /*
- * We have to avoid recursion (not to overflow kernel stack),
- * and that's why code looks pretty cryptic
- */
- suspend_pagedir_t *old_pagedir = pagedir_nosave;
- void **eaten_memory = NULL;
- void **c = eaten_memory, *m, *f;
- int ret = 0;
- struct zone *zone;
- int i;
- struct pbe *p;
- unsigned long zone_pfn;
-
- printk("Relocating pagedir ");
-
- /* Set page flags */
-
- for_each_zone(zone) {
- for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
- SetPageNosaveFree(pfn_to_page(zone_pfn +
- zone->zone_start_pfn));
- }
-
- /* Clear orig address */
-
- for(i = 0, p = pagedir_nosave; i < nr_copy_pages; i++, p++) {
- ClearPageNosaveFree(virt_to_page(p->orig_address));
- }
-
- if (!does_collide_order((unsigned long)old_pagedir, pagedir_order)) {
- printk("not necessary\n");
- return check_pagedir();
- }
-
- while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order)) != NULL) {
- if (!does_collide_order((unsigned long)m, pagedir_order))
- break;
- eaten_memory = m;
- printk( "." );
- *eaten_memory = c;
- c = eaten_memory;
- }
-
- if (!m) {
- printk("out of memory\n");
- ret = -ENOMEM;
- } else {
- pagedir_nosave =
- memcpy(m, old_pagedir, PAGE_SIZE << pagedir_order);
- }
-
- c = eaten_memory;
- while (c) {
- printk(":");
- f = c;
- c = *c;
- free_pages((unsigned long)f, pagedir_order);
- }
- if (ret)
- return ret;
- printk("|\n");
- return check_pagedir();
-}
-
-/**
- * Using bio to read from swap.
- * This code requires a bit more work than just using buffer heads
- * but, it is the recommended way for 2.5/2.6.
- * The following are to signal the beginning and end of I/O. Bios
- * finish asynchronously, while we want them to happen synchronously.
- * A simple atomic_t, and a wait loop take care of this problem.
- */
-
-static atomic_t io_done = ATOMIC_INIT(0);
-
-static int end_io(struct bio * bio, unsigned int num, int err)
-{
- if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
- panic("I/O error reading memory image");
- atomic_set(&io_done, 0);
- return 0;
-}
-
-static struct block_device * resume_bdev;
-
-/**
- * submit - submit BIO request.
- * @rw: READ or WRITE.
- * @off physical offset of page.
- * @page: page we're reading or writing.
- *
- * Straight from the textbook - allocate and initialize the bio.
- * If we're writing, make sure the page is marked as dirty.
- * Then submit it and wait.
- */
-
-static int submit(int rw, pgoff_t page_off, void * page)
-{
- int error = 0;
- struct bio * bio;
-
- bio = bio_alloc(GFP_ATOMIC, 1);
- if (!bio)
- return -ENOMEM;
- bio->bi_sector = page_off * (PAGE_SIZE >> 9);
- bio_get(bio);
- bio->bi_bdev = resume_bdev;
- bio->bi_end_io = end_io;
-
- if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
- printk("swsusp: ERROR: adding page to bio at %ld\n",page_off);
- error = -EFAULT;
- goto Done;
- }
-
- if (rw == WRITE)
- bio_set_pages_dirty(bio);
-
- atomic_set(&io_done, 1);
- submit_bio(rw | (1 << BIO_RW_SYNC), bio);
- while (atomic_read(&io_done))
- yield();
-
- Done:
- bio_put(bio);
- return error;
-}
-
-static int bio_read_page(pgoff_t page_off, void * page)
-{
- return submit(READ, page_off, page);
-}
-
-static int bio_write_page(pgoff_t page_off, void * page)
-{
- return submit(WRITE, page_off, page);
-}
-
-/*
- * 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 const char * __init sanity_check(void)
-{
- dump_info();
- if(swsusp_info.version_code != LINUX_VERSION_CODE)
- return "kernel version";
- if(swsusp_info.num_physpages != num_physpages)
- return "memory size";
- if (strcmp(swsusp_info.uts.sysname,system_utsname.sysname))
- return "system type";
- if (strcmp(swsusp_info.uts.release,system_utsname.release))
- return "kernel release";
- if (strcmp(swsusp_info.uts.version,system_utsname.version))
- return "version";
- if (strcmp(swsusp_info.uts.machine,system_utsname.machine))
- return "machine";
- if(swsusp_info.cpus != num_online_cpus())
- return "number of cpus";
- return NULL;
-}
-
-
-static int __init check_header(void)
-{
- const char * reason = NULL;
- int error;
-
- if ((error = bio_read_page(swp_offset(swsusp_header.swsusp_info), &swsusp_info)))
- return error;
-
- /* Is this same machine? */
- if ((reason = sanity_check())) {
- printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason);
- return -EPERM;
- }
- nr_copy_pages = swsusp_info.image_pages;
- pagedir_order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages));
- return error;
-}
-
-static int __init check_sig(void)
-{
- int error;
-
- memset(&swsusp_header, 0, sizeof(swsusp_header));
- if ((error = bio_read_page(0, &swsusp_header)))
- return error;
- if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
- memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
-
- /*
- * Reset swap signature now.
- */
- error = bio_write_page(0, &swsusp_header);
- } else {
- pr_debug(KERN_ERR "swsusp: Suspend partition has wrong signature?\n");
- return -EINVAL;
- }
- if (!error)
- pr_debug("swsusp: Signature found, resuming\n");
- return error;
-}
-
-/**
- * swsusp_read_data - Read image pages from swap.
- *
- * You do not need to check for overlaps, check_pagedir()
- * already did that.
- */
-
-static int __init data_read(void)
-{
- struct pbe * p;
- int error;
- int i;
- int mod = nr_copy_pages / 100;
-
- if (!mod)
- mod = 1;
-
- if ((error = swsusp_pagedir_relocate()))
- return error;
-
- printk( "Reading image data (%d pages): ", nr_copy_pages );
- for(i = 0, p = pagedir_nosave; i < nr_copy_pages && !error; i++, p++) {
- if (!(i%mod))
- printk( "\b\b\b\b%3d%%", i / mod );
- error = bio_read_page(swp_offset(p->swap_address),
- (void *)p->address);
- }
- printk(" %d done.\n",i);
- return error;
-
-}
-
-extern dev_t __init name_to_dev_t(const char *line);
-
-static int __init read_pagedir(void)
-{
- unsigned long addr;
- int i, n = swsusp_info.pagedir_pages;
- int error = 0;
-
- addr = __get_free_pages(GFP_ATOMIC, pagedir_order);
- if (!addr)
- return -ENOMEM;
- pagedir_nosave = (struct pbe *)addr;
-
- pr_debug("swsusp: Reading pagedir (%d Pages)\n",n);
-
- for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) {
- unsigned long offset = swp_offset(swsusp_info.pagedir[i]);
- if (offset)
- error = bio_read_page(offset, (void *)addr);
- else
- error = -EFAULT;
- }
- if (error)
- free_pages((unsigned long)pagedir_nosave, pagedir_order);
- return error;
-}
-
-static int __init read_suspend_image(void)
-{
- int error = 0;
-
- if ((error = check_sig()))
- return error;
- if ((error = check_header()))
- return error;
- if ((error = read_pagedir()))
- return error;
- if ((error = data_read()))
- free_pages((unsigned long)pagedir_nosave, pagedir_order);
- return error;
-}
-
-/**
- * swsusp_read - Read saved image from swap.
- */
-
-int __init swsusp_read(void)
-{
- int error;
-
- if (!strlen(resume_file))
- return -ENOENT;
-
- resume_device = name_to_dev_t(resume_file);
- pr_debug("swsusp: Resume From Partition: %s\n", resume_file);
-
- resume_bdev = open_by_devnum(resume_device, FMODE_READ);
- if (!IS_ERR(resume_bdev)) {
- set_blocksize(resume_bdev, PAGE_SIZE);
- error = read_suspend_image();
- blkdev_put(resume_bdev);
- } else
- error = PTR_ERR(resume_bdev);
-
- if (!error)
- pr_debug("Reading resume file was successful\n");
- else
- pr_debug("swsusp: Error %d resuming\n", error);
- return error;
-}