#include <asm/io.h>
#include "power.h"
+#include "tuxonice_builtin.h"
+#include "tuxonice_pagedir.h"
static int swsusp_page_is_free(struct page *);
static void swsusp_set_page_forbidden(struct page *);
* directly to their "original" page frames.
*/
struct pbe *restore_pblist;
+EXPORT_SYMBOL_GPL(restore_pblist);
+
+int resume_attempted;
+EXPORT_SYMBOL_GPL(resume_attempted);
/* Pointer to an auxiliary buffer (1 page) */
static void *buffer;
unsigned long get_safe_page(gfp_t gfp_mask)
{
+ if (toi_running)
+ return toi_get_nonconflicting_page();
+
return (unsigned long)get_image_page(gfp_mask, PG_SAFE);
}
* the represented memory area.
*/
-#define BM_END_OF_MAP (~0UL)
-
-#define BM_BITS_PER_BLOCK (PAGE_SIZE * BITS_PER_BYTE)
-
-struct bm_block {
- struct list_head hook; /* hook into a list of bitmap blocks */
- unsigned long start_pfn; /* pfn represented by the first bit */
- unsigned long end_pfn; /* pfn represented by the last bit plus 1 */
- unsigned long *data; /* bitmap representing pages */
-};
-
static inline unsigned long bm_block_bits(struct bm_block *bb)
{
return bb->end_pfn - bb->start_pfn;
}
-/* strcut bm_position is used for browsing memory bitmaps */
+/* Functions that operate on memory bitmaps */
-struct bm_position {
- struct bm_block *block;
- int bit;
-};
+void memory_bm_position_reset_index(struct memory_bitmap *bm, int index)
+{
+ bm->states[index].block = list_entry(bm->blocks.next,
+ struct bm_block, hook);
+ bm->states[index].bit = 0;
+}
+EXPORT_SYMBOL_GPL(memory_bm_position_reset_index);
-struct memory_bitmap {
- struct list_head blocks; /* list of bitmap blocks */
- struct linked_page *p_list; /* list of pages used to store zone
- * bitmap objects and bitmap block
- * objects
- */
- struct bm_position cur; /* most recently used bit position */
-};
+void memory_bm_position_reset(struct memory_bitmap *bm)
+{
+ int i;
-/* Functions that operate on memory bitmaps */
+ for (i = 0; i < bm->num_states; i++) {
+ bm->states[i].block = list_entry(bm->blocks.next,
+ struct bm_block, hook);
+ bm->states[i].bit = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(memory_bm_position_reset);
-static void memory_bm_position_reset(struct memory_bitmap *bm)
+int memory_bm_set_iterators(struct memory_bitmap *bm, int number)
{
- bm->cur.block = list_entry(bm->blocks.next, struct bm_block, hook);
- bm->cur.bit = 0;
-}
+ int bytes = number * sizeof(struct bm_position);
+ struct bm_position *new_states;
+
+ if (number < bm->num_states)
+ return 0;
-static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free);
+ new_states = kmalloc(bytes, GFP_KERNEL);
+ if (!new_states)
+ return -ENOMEM;
+
+ if (bm->states)
+ kfree(bm->states);
+
+ bm->states = new_states;
+ bm->num_states = number;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(memory_bm_set_iterators);
/**
* create_bm_block_list - create a list of block bitmap objects
/**
* memory_bm_create - allocate memory for a memory bitmap
*/
-static int
-memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask, int safe_needed)
+int memory_bm_create_index(struct memory_bitmap *bm, gfp_t gfp_mask,
+ int safe_needed, int states)
{
struct chain_allocator ca;
struct list_head mem_extents;
}
}
+ if (!error)
+ error = memory_bm_set_iterators(bm, states);
+
bm->p_list = ca.chain;
memory_bm_position_reset(bm);
Exit:
memory_bm_free(bm, PG_UNSAFE_CLEAR);
goto Exit;
}
+EXPORT_SYMBOL_GPL(memory_bm_create_index);
+
+int memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask, int safe_needed)
+{
+ return memory_bm_create_index(bm, gfp_mask, safe_needed, 1);
+}
+EXPORT_SYMBOL_GPL(memory_bm_create);
/**
* memory_bm_free - free memory occupied by the memory bitmap @bm
*/
-static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free)
+void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free)
{
struct bm_block *bb;
free_list_of_pages(bm->p_list, clear_nosave_free);
INIT_LIST_HEAD(&bm->blocks);
+
+ if (bm->states) {
+ kfree(bm->states);
+ bm->states = NULL;
+ bm->num_states = 0;
+ }
}
+EXPORT_SYMBOL_GPL(memory_bm_free);
/**
* memory_bm_find_bit - find the bit in the bitmap @bm that corresponds
* to given pfn. The cur_zone_bm member of @bm and the cur_block member
- * of @bm->cur_zone_bm are updated.
+ * of @bm->states[i]_zone_bm are updated.
*/
-static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
- void **addr, unsigned int *bit_nr)
+static int memory_bm_find_bit_index(struct memory_bitmap *bm, unsigned long pfn,
+ void **addr, unsigned int *bit_nr, int state)
{
struct bm_block *bb;
* Check if the pfn corresponds to the current bitmap block and find
* the block where it fits if this is not the case.
*/
- bb = bm->cur.block;
+ bb = bm->states[state].block;
if (pfn < bb->start_pfn)
list_for_each_entry_continue_reverse(bb, &bm->blocks, hook)
if (pfn >= bb->start_pfn)
return -EFAULT;
/* The block has been found */
- bm->cur.block = bb;
+ bm->states[state].block = bb;
pfn -= bb->start_pfn;
- bm->cur.bit = pfn + 1;
+ bm->states[state].bit = pfn + 1;
*bit_nr = pfn;
*addr = bb->data;
return 0;
}
-static void memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn)
+static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
+ void **addr, unsigned int *bit_nr)
+{
+ return memory_bm_find_bit_index(bm, pfn, addr, bit_nr, 0);
+}
+
+void memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn)
{
void *addr;
unsigned int bit;
BUG_ON(error);
set_bit(bit, addr);
}
+EXPORT_SYMBOL_GPL(memory_bm_set_bit);
static int mem_bm_set_bit_check(struct memory_bitmap *bm, unsigned long pfn)
{
return error;
}
-static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn)
+void memory_bm_clear_bit_index(struct memory_bitmap *bm, unsigned long pfn,
+ int index)
{
void *addr;
unsigned int bit;
int error;
- error = memory_bm_find_bit(bm, pfn, &addr, &bit);
+ error = memory_bm_find_bit_index(bm, pfn, &addr, &bit, index);
BUG_ON(error);
clear_bit(bit, addr);
}
+EXPORT_SYMBOL_GPL(memory_bm_clear_bit_index);
+
+void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn)
+{
+ memory_bm_clear_bit_index(bm, pfn, 0);
+}
+EXPORT_SYMBOL_GPL(memory_bm_clear_bit);
-static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
+int memory_bm_test_bit_index(struct memory_bitmap *bm, unsigned long pfn,
+ int index)
{
void *addr;
unsigned int bit;
int error;
- error = memory_bm_find_bit(bm, pfn, &addr, &bit);
+ error = memory_bm_find_bit_index(bm, pfn, &addr, &bit, index);
BUG_ON(error);
return test_bit(bit, addr);
}
+EXPORT_SYMBOL_GPL(memory_bm_test_bit_index);
+
+int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
+{
+ return memory_bm_test_bit_index(bm, pfn, 0);
+}
+EXPORT_SYMBOL_GPL(memory_bm_test_bit);
static bool memory_bm_pfn_present(struct memory_bitmap *bm, unsigned long pfn)
{
* this function.
*/
-static unsigned long memory_bm_next_pfn(struct memory_bitmap *bm)
+unsigned long memory_bm_next_pfn_index(struct memory_bitmap *bm, int index)
{
struct bm_block *bb;
int bit;
- bb = bm->cur.block;
+ bb = bm->states[index].block;
do {
- bit = bm->cur.bit;
+ bit = bm->states[index].bit;
bit = find_next_bit(bb->data, bm_block_bits(bb), bit);
if (bit < bm_block_bits(bb))
goto Return_pfn;
bb = list_entry(bb->hook.next, struct bm_block, hook);
- bm->cur.block = bb;
- bm->cur.bit = 0;
+ bm->states[index].block = bb;
+ bm->states[index].bit = 0;
} while (&bb->hook != &bm->blocks);
- memory_bm_position_reset(bm);
+ memory_bm_position_reset_index(bm, index);
return BM_END_OF_MAP;
Return_pfn:
- bm->cur.bit = bit + 1;
+ bm->states[index].bit = bit + 1;
return bb->start_pfn + bit;
}
+EXPORT_SYMBOL_GPL(memory_bm_next_pfn_index);
-/**
- * This structure represents a range of page frames the contents of which
- * should not be saved during the suspend.
- */
+unsigned long memory_bm_next_pfn(struct memory_bitmap *bm)
+{
+ return memory_bm_next_pfn_index(bm, 0);
+}
+EXPORT_SYMBOL_GPL(memory_bm_next_pfn);
-struct nosave_region {
- struct list_head list;
- unsigned long start_pfn;
- unsigned long end_pfn;
-};
+void memory_bm_clear(struct memory_bitmap *bm)
+{
+ unsigned long pfn;
+
+ memory_bm_position_reset(bm);
+ pfn = memory_bm_next_pfn(bm);
+ while (pfn != BM_END_OF_MAP) {
+ memory_bm_clear_bit(bm, pfn);
+ pfn = memory_bm_next_pfn(bm);
+ }
+}
+EXPORT_SYMBOL_GPL(memory_bm_clear);
+
+void memory_bm_copy(struct memory_bitmap *source, struct memory_bitmap *dest)
+{
+ unsigned long pfn;
+
+ memory_bm_position_reset(source);
+ pfn = memory_bm_next_pfn(source);
+ while (pfn != BM_END_OF_MAP) {
+ memory_bm_set_bit(dest, pfn);
+ pfn = memory_bm_next_pfn(source);
+ }
+}
+EXPORT_SYMBOL_GPL(memory_bm_copy);
+
+void memory_bm_dup(struct memory_bitmap *source, struct memory_bitmap *dest)
+{
+ memory_bm_clear(dest);
+ memory_bm_copy(source, dest);
+}
+EXPORT_SYMBOL_GPL(memory_bm_dup);
+
+#ifdef CONFIG_TOI
+#define DEFINE_MEMORY_BITMAP(name) \
+struct memory_bitmap *name; \
+EXPORT_SYMBOL_GPL(name)
+
+DEFINE_MEMORY_BITMAP(pageset1_map);
+DEFINE_MEMORY_BITMAP(pageset1_copy_map);
+DEFINE_MEMORY_BITMAP(pageset2_map);
+DEFINE_MEMORY_BITMAP(page_resave_map);
+DEFINE_MEMORY_BITMAP(io_map);
+DEFINE_MEMORY_BITMAP(nosave_map);
+DEFINE_MEMORY_BITMAP(free_map);
+
+int memory_bm_write(struct memory_bitmap *bm, int (*rw_chunk)
+ (int rw, struct toi_module_ops *owner, char *buffer, int buffer_size))
+{
+ int result = 0;
+ unsigned int nr = 0;
+ struct bm_block *bb;
+
+ if (!bm)
+ return result;
+
+ list_for_each_entry(bb, &bm->blocks, hook)
+ nr++;
+
+ result = (*rw_chunk)(WRITE, NULL, (char *) &nr, sizeof(unsigned int));
+ if (result)
+ return result;
+
+ list_for_each_entry(bb, &bm->blocks, hook) {
+ result = (*rw_chunk)(WRITE, NULL, (char *) &bb->start_pfn,
+ 2 * sizeof(unsigned long));
+ if (result)
+ return result;
+
+ result = (*rw_chunk)(WRITE, NULL, (char *) bb->data, PAGE_SIZE);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(memory_bm_write);
+
+int memory_bm_read(struct memory_bitmap *bm, int (*rw_chunk)
+ (int rw, struct toi_module_ops *owner, char *buffer, int buffer_size))
+{
+ int result = 0;
+ unsigned int nr, i;
+ struct bm_block *bb;
+
+ if (!bm)
+ return result;
+
+ result = memory_bm_create(bm, GFP_KERNEL, 0);
-static LIST_HEAD(nosave_regions);
+ if (result)
+ return result;
+
+ result = (*rw_chunk)(READ, NULL, (char *) &nr, sizeof(unsigned int));
+ if (result)
+ goto Free;
+
+ for (i = 0; i < nr; i++) {
+ unsigned long pfn;
+
+ result = (*rw_chunk)(READ, NULL, (char *) &pfn,
+ sizeof(unsigned long));
+ if (result)
+ goto Free;
+
+ list_for_each_entry(bb, &bm->blocks, hook)
+ if (bb->start_pfn == pfn)
+ break;
+
+ if (&bb->hook == &bm->blocks) {
+ printk(KERN_ERR
+ "TuxOnIce: Failed to load memory bitmap.\n");
+ result = -EINVAL;
+ goto Free;
+ }
+
+ result = (*rw_chunk)(READ, NULL, (char *) &pfn,
+ sizeof(unsigned long));
+ if (result)
+ goto Free;
+
+ if (pfn != bb->end_pfn) {
+ printk(KERN_ERR
+ "TuxOnIce: Failed to load memory bitmap. "
+ "End PFN doesn't match what was saved.\n");
+ result = -EINVAL;
+ goto Free;
+ }
+
+ result = (*rw_chunk)(READ, NULL, (char *) bb->data, PAGE_SIZE);
+
+ if (result)
+ goto Free;
+ }
+
+ return 0;
+
+Free:
+ memory_bm_free(bm, PG_ANY);
+ return result;
+}
+EXPORT_SYMBOL_GPL(memory_bm_read);
+#endif
+
+LIST_HEAD(nosave_regions);
+EXPORT_SYMBOL_GPL(nosave_regions);
/**
* register_nosave_region - register a range of page frames the contents
* We should save the page if it isn't Nosave or NosaveFree, or Reserved,
* and it isn't a part of a free chunk of pages.
*/
-static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn)
+struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn)
{
struct page *page;
return page;
}
+EXPORT_SYMBOL_GPL(saveable_highmem_page);
/**
* count_highmem_pages - compute the total number of saveable highmem
}
return n;
}
-#else
-static inline void *saveable_highmem_page(struct zone *z, unsigned long p)
-{
- return NULL;
-}
#endif /* CONFIG_HIGHMEM */
/**
* of pages statically defined as 'unsaveable', and it isn't a part of
* a free chunk of pages.
*/
-static struct page *saveable_page(struct zone *zone, unsigned long pfn)
+struct page *saveable_page(struct zone *zone, unsigned long pfn)
{
struct page *page;
return page;
}
+EXPORT_SYMBOL_GPL(saveable_page);
/**
* count_data_pages - compute the total number of saveable non-highmem
* highmem and non-highmem zones separately.
*/
pages_highmem = preallocate_image_highmem(highmem / 2);
- alloc = (count - max_size) - pages_highmem;
+ alloc = count - max_size;
+ if (alloc > pages_highmem)
+ alloc -= pages_highmem;
+ else
+ alloc = 0;
pages = preallocate_image_memory(alloc, avail_normal);
if (pages < alloc) {
/* We have exhausted non-highmem pages, try highmem. */
{
unsigned int nr_pages, nr_highmem;
+ if (toi_running)
+ return toi_post_context_save();
+
printk(KERN_INFO "PM: Creating hibernation image:\n");
drain_local_pages(NULL);
}
#ifndef CONFIG_ARCH_HIBERNATION_HEADER
-static int init_header_complete(struct swsusp_info *info)
+int init_header_complete(struct swsusp_info *info)
{
memcpy(&info->uts, init_utsname(), sizeof(struct new_utsname));
info->version_code = LINUX_VERSION_CODE;
return 0;
}
-static char *check_image_kernel(struct swsusp_info *info)
+char *check_image_kernel(struct swsusp_info *info)
{
if (info->version_code != LINUX_VERSION_CODE)
return "kernel version";
return "machine";
return NULL;
}
+EXPORT_SYMBOL_GPL(check_image_kernel);
#endif /* CONFIG_ARCH_HIBERNATION_HEADER */
unsigned long snapshot_get_image_size(void)
return nr_copy_pages + nr_meta_pages + 1;
}
-static int init_header(struct swsusp_info *info)
+int init_header(struct swsusp_info *info)
{
memset(info, 0, sizeof(struct swsusp_info));
info->num_physpages = num_physpages;
info->size <<= PAGE_SHIFT;
return init_header_complete(info);
}
+EXPORT_SYMBOL_GPL(init_header);
/**
* pack_pfns - pfns corresponding to the set bits found in the bitmap @bm