#include "power.h"
-struct pbe *pagedir_nosave;
+/* List of PBEs used for creating and restoring the suspend image */
+struct pbe *restore_pblist;
+
static unsigned int nr_copy_pages;
static unsigned int nr_meta_pages;
static unsigned long *buffer;
static inline int restore_highmem(void) {return 0;}
#endif
-static int pfn_is_nosave(unsigned long pfn)
+/**
+ * @safe_needed - on resume, for storing the PBE list and the image,
+ * we can only use memory pages that do not conflict with the pages
+ * used before suspend.
+ *
+ * The unsafe pages are marked with the PG_nosave_free flag
+ * and we count them using unsafe_pages
+ */
+
+static unsigned int unsafe_pages;
+
+static void *alloc_image_page(gfp_t gfp_mask, int safe_needed)
+{
+ void *res;
+
+ res = (void *)get_zeroed_page(gfp_mask);
+ if (safe_needed)
+ while (res && PageNosaveFree(virt_to_page(res))) {
+ /* The page is unsafe, mark it for swsusp_free() */
+ SetPageNosave(virt_to_page(res));
+ unsafe_pages++;
+ res = (void *)get_zeroed_page(gfp_mask);
+ }
+ if (res) {
+ SetPageNosave(virt_to_page(res));
+ SetPageNosaveFree(virt_to_page(res));
+ }
+ return res;
+}
+
+unsigned long get_safe_page(gfp_t gfp_mask)
+{
+ return (unsigned long)alloc_image_page(gfp_mask, 1);
+}
+
+/**
+ * free_image_page - free page represented by @addr, allocated with
+ * alloc_image_page (page flags set by it must be cleared)
+ */
+
+static inline void free_image_page(void *addr, int clear_nosave_free)
+{
+ ClearPageNosave(virt_to_page(addr));
+ if (clear_nosave_free)
+ ClearPageNosaveFree(virt_to_page(addr));
+ free_page((unsigned long)addr);
+}
+
+/**
+ * pfn_is_nosave - check if given pfn is in the 'nosave' section
+ */
+
+static inline int pfn_is_nosave(unsigned long pfn)
{
unsigned long nosave_begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT;
unsigned long nosave_end_pfn = PAGE_ALIGN(__pa(&__nosave_end)) >> PAGE_SHIFT;
* saveable - Determine whether a page should be cloned or not.
* @pfn: The page
*
- * We save a page if it's Reserved, and not in the range of pages
- * statically defined as 'unsaveable', or if it isn't reserved, and
- * isn't part of a free chunk of pages.
+ * We save a page if it isn't Nosave, and is not in the range of pages
+ * statically defined as 'unsaveable', and it
+ * isn't a part of a free chunk of pages.
*/
-static int saveable(struct zone *zone, unsigned long *zone_pfn)
+static struct page *saveable_page(unsigned long pfn)
{
- unsigned long pfn = *zone_pfn + zone->zone_start_pfn;
struct page *page;
if (!pfn_valid(pfn))
- return 0;
+ return NULL;
page = pfn_to_page(pfn);
- BUG_ON(PageReserved(page) && PageNosave(page));
+
if (PageNosave(page))
- return 0;
+ return NULL;
if (PageReserved(page) && pfn_is_nosave(pfn))
- return 0;
+ return NULL;
if (PageNosaveFree(page))
- return 0;
+ return NULL;
- return 1;
+ return page;
}
unsigned int count_data_pages(void)
{
struct zone *zone;
- unsigned long zone_pfn;
+ unsigned long pfn, max_zone_pfn;
unsigned int n = 0;
for_each_zone (zone) {
if (is_highmem(zone))
continue;
mark_free_pages(zone);
- for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
- n += saveable(zone, &zone_pfn);
+ max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
+ n += !!saveable_page(pfn);
}
return n;
}
+static inline void copy_data_page(long *dst, long *src)
+{
+ int n;
+
+ /* copy_page and memcpy are not usable for copying task structs. */
+ for (n = PAGE_SIZE / sizeof(long); n; n--)
+ *dst++ = *src++;
+}
+
static void copy_data_pages(struct pbe *pblist)
{
struct zone *zone;
- unsigned long zone_pfn;
- struct pbe *pbe, *p;
+ unsigned long pfn, max_zone_pfn;
+ struct pbe *pbe;
pbe = pblist;
for_each_zone (zone) {
if (is_highmem(zone))
continue;
mark_free_pages(zone);
- /* This is necessary for swsusp_free() */
- for_each_pb_page (p, pblist)
- SetPageNosaveFree(virt_to_page(p));
- for_each_pbe (p, pblist)
- SetPageNosaveFree(virt_to_page(p->address));
- for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) {
- if (saveable(zone, &zone_pfn)) {
- struct page *page;
- long *src, *dst;
- int n;
+ max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++) {
+ struct page *page = saveable_page(pfn);
+
+ if (page) {
+ void *ptr = page_address(page);
- page = pfn_to_page(zone_pfn + zone->zone_start_pfn);
BUG_ON(!pbe);
- pbe->orig_address = (unsigned long)page_address(page);
- /* copy_page and memcpy are not usable for copying task structs. */
- dst = (long *)pbe->address;
- src = (long *)pbe->orig_address;
- for (n = PAGE_SIZE / sizeof(long); n; n--)
- *dst++ = *src++;
+ copy_data_page((void *)pbe->address, ptr);
+ pbe->orig_address = (unsigned long)ptr;
pbe = pbe->next;
}
}
BUG_ON(pbe);
}
-
/**
* free_pagedir - free pages allocated with alloc_pagedir()
*/
while (pblist) {
pbe = (pblist + PB_PAGE_SKIP)->next;
- ClearPageNosave(virt_to_page(pblist));
- if (clear_nosave_free)
- ClearPageNosaveFree(virt_to_page(pblist));
- free_page((unsigned long)pblist);
+ free_image_page(pblist, clear_nosave_free);
pblist = pbe;
}
}
* fill_pb_page - Create a list of PBEs on a given memory page
*/
-static inline void fill_pb_page(struct pbe *pbpage)
+static inline void fill_pb_page(struct pbe *pbpage, unsigned int n)
{
struct pbe *p;
p = pbpage;
- pbpage += PB_PAGE_SKIP;
+ pbpage += n - 1;
do
p->next = p + 1;
while (++p < pbpage);
/**
* create_pbe_list - Create a list of PBEs on top of a given chain
* of memory pages allocated with alloc_pagedir()
+ *
+ * This function assumes that pages allocated by alloc_image_page() will
+ * always be zeroed.
*/
static inline void create_pbe_list(struct pbe *pblist, unsigned int nr_pages)
{
- struct pbe *pbpage, *p;
+ struct pbe *pbpage;
unsigned int num = PBES_PER_PAGE;
for_each_pb_page (pbpage, pblist) {
if (num >= nr_pages)
break;
- fill_pb_page(pbpage);
+ fill_pb_page(pbpage, PBES_PER_PAGE);
num += PBES_PER_PAGE;
}
if (pbpage) {
- for (num -= PBES_PER_PAGE - 1, p = pbpage; num < nr_pages; p++, num++)
- p->next = p + 1;
- p->next = NULL;
- }
-}
-
-static unsigned int unsafe_pages;
-
-/**
- * @safe_needed - on resume, for storing the PBE list and the image,
- * we can only use memory pages that do not conflict with the pages
- * used before suspend.
- *
- * The unsafe pages are marked with the PG_nosave_free flag
- * and we count them using unsafe_pages
- */
-
-static inline void *alloc_image_page(gfp_t gfp_mask, int safe_needed)
-{
- void *res;
-
- res = (void *)get_zeroed_page(gfp_mask);
- if (safe_needed)
- while (res && PageNosaveFree(virt_to_page(res))) {
- /* The page is unsafe, mark it for swsusp_free() */
- SetPageNosave(virt_to_page(res));
- unsafe_pages++;
- res = (void *)get_zeroed_page(gfp_mask);
- }
- if (res) {
- SetPageNosave(virt_to_page(res));
- SetPageNosaveFree(virt_to_page(res));
+ num -= PBES_PER_PAGE;
+ fill_pb_page(pbpage, nr_pages - num);
}
- return res;
-}
-
-unsigned long get_safe_page(gfp_t gfp_mask)
-{
- return (unsigned long)alloc_image_page(gfp_mask, 1);
}
/**
return NULL;
pblist = alloc_image_page(gfp_mask, safe_needed);
- /* FIXME: rewrite this ugly loop */
- for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages;
- pbe = pbe->next, num += PBES_PER_PAGE) {
+ pbe = pblist;
+ for (num = PBES_PER_PAGE; num < nr_pages; num += PBES_PER_PAGE) {
+ if (!pbe) {
+ free_pagedir(pblist, 1);
+ return NULL;
+ }
pbe += PB_PAGE_SKIP;
pbe->next = alloc_image_page(gfp_mask, safe_needed);
+ pbe = pbe->next;
}
- if (!pbe) { /* get_zeroed_page() failed */
- free_pagedir(pblist, 1);
- pblist = NULL;
- } else
- create_pbe_list(pblist, nr_pages);
+ create_pbe_list(pblist, nr_pages);
return pblist;
}
void swsusp_free(void)
{
struct zone *zone;
- unsigned long zone_pfn;
+ unsigned long pfn, max_zone_pfn;
for_each_zone(zone) {
- for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
- if (pfn_valid(zone_pfn + zone->zone_start_pfn)) {
- struct page *page;
- page = pfn_to_page(zone_pfn + zone->zone_start_pfn);
+ max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
+ if (pfn_valid(pfn)) {
+ struct page *page = pfn_to_page(pfn);
+
if (PageNosave(page) && PageNosaveFree(page)) {
ClearPageNosave(page);
ClearPageNosaveFree(page);
}
nr_copy_pages = 0;
nr_meta_pages = 0;
- pagedir_nosave = NULL;
+ restore_pblist = NULL;
buffer = NULL;
}
return -ENOMEM;
}
- pagedir_nosave = swsusp_alloc(nr_pages);
- if (!pagedir_nosave)
+ restore_pblist = swsusp_alloc(nr_pages);
+ if (!restore_pblist)
return -ENOMEM;
/* During allocating of suspend pagedir, new cold pages may appear.
* Kill them.
*/
drain_local_pages();
- copy_data_pages(pagedir_nosave);
+ copy_data_pages(restore_pblist);
/*
* End of critical section. From now on, we can write to memory,
int snapshot_read_next(struct snapshot_handle *handle, size_t count)
{
- if (handle->page > nr_meta_pages + nr_copy_pages)
+ if (handle->cur > nr_meta_pages + nr_copy_pages)
return 0;
if (!buffer) {
/* This makes the buffer be freed by swsusp_free() */
if (!handle->offset) {
init_header((struct swsusp_info *)buffer);
handle->buffer = buffer;
- handle->pbe = pagedir_nosave;
+ handle->pbe = restore_pblist;
}
- if (handle->prev < handle->page) {
- if (handle->page <= nr_meta_pages) {
+ if (handle->prev < handle->cur) {
+ if (handle->cur <= nr_meta_pages) {
handle->pbe = pack_orig_addresses(buffer, handle->pbe);
if (!handle->pbe)
- handle->pbe = pagedir_nosave;
+ handle->pbe = restore_pblist;
} else {
handle->buffer = (void *)handle->pbe->address;
handle->pbe = handle->pbe->next;
}
- handle->prev = handle->page;
+ handle->prev = handle->cur;
}
- handle->buf_offset = handle->page_offset;
- if (handle->page_offset + count >= PAGE_SIZE) {
- count = PAGE_SIZE - handle->page_offset;
- handle->page_offset = 0;
- handle->page++;
+ handle->buf_offset = handle->cur_offset;
+ if (handle->cur_offset + count >= PAGE_SIZE) {
+ count = PAGE_SIZE - handle->cur_offset;
+ handle->cur_offset = 0;
+ handle->cur++;
} else {
- handle->page_offset += count;
+ handle->cur_offset += count;
}
handle->offset += count;
return count;
static int mark_unsafe_pages(struct pbe *pblist)
{
struct zone *zone;
- unsigned long zone_pfn;
+ unsigned long pfn, max_zone_pfn;
struct pbe *p;
if (!pblist) /* a sanity check */
/* Clear page flags */
for_each_zone (zone) {
- for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
- if (pfn_valid(zone_pfn + zone->zone_start_pfn))
- ClearPageNosaveFree(pfn_to_page(zone_pfn +
- zone->zone_start_pfn));
+ max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
+ if (pfn_valid(pfn))
+ ClearPageNosaveFree(pfn_to_page(pfn));
}
/* Mark orig addresses */
pblist = alloc_pagedir(info->image_pages, GFP_ATOMIC, 0);
if (!pblist)
return -ENOMEM;
- pagedir_nosave = pblist;
+ restore_pblist = pblist;
handle->pbe = pblist;
nr_copy_pages = info->image_pages;
nr_meta_pages = info->pages - info->image_pages - 1;
/**
* prepare_image - use metadata contained in the PBE list
- * pointed to by pagedir_nosave to mark the pages that will
+ * pointed to by restore_pblist to mark the pages that will
* be overwritten in the process of restoring the system
* memory state from the image ("unsafe" pages) and allocate
* memory for the image
unsigned int nr_pages = nr_copy_pages;
struct pbe *p, *pblist = NULL;
- p = pagedir_nosave;
+ p = restore_pblist;
error = mark_unsafe_pages(p);
if (!error) {
pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, 1);
}
}
if (!error) {
- pagedir_nosave = pblist;
+ restore_pblist = pblist;
} else {
handle->pbe = NULL;
swsusp_free();
{
int error = 0;
- if (handle->prev && handle->page > nr_meta_pages + nr_copy_pages)
+ if (handle->prev && handle->cur > nr_meta_pages + nr_copy_pages)
return 0;
if (!buffer) {
/* This makes the buffer be freed by swsusp_free() */
}
if (!handle->offset)
handle->buffer = buffer;
- if (handle->prev < handle->page) {
+ handle->sync_read = 1;
+ if (handle->prev < handle->cur) {
if (!handle->prev) {
- error = load_header(handle, (struct swsusp_info *)buffer);
+ error = load_header(handle,
+ (struct swsusp_info *)buffer);
if (error)
return error;
} else if (handle->prev <= nr_meta_pages) {
- handle->pbe = unpack_orig_addresses(buffer, handle->pbe);
+ handle->pbe = unpack_orig_addresses(buffer,
+ handle->pbe);
if (!handle->pbe) {
error = prepare_image(handle);
if (error)
return error;
- handle->pbe = pagedir_nosave;
+ handle->pbe = restore_pblist;
handle->last_pbe = NULL;
handle->buffer = get_buffer(handle);
+ handle->sync_read = 0;
}
} else {
handle->pbe = handle->pbe->next;
handle->buffer = get_buffer(handle);
+ handle->sync_read = 0;
}
- handle->prev = handle->page;
+ handle->prev = handle->cur;
}
- handle->buf_offset = handle->page_offset;
- if (handle->page_offset + count >= PAGE_SIZE) {
- count = PAGE_SIZE - handle->page_offset;
- handle->page_offset = 0;
- handle->page++;
+ handle->buf_offset = handle->cur_offset;
+ if (handle->cur_offset + count >= PAGE_SIZE) {
+ count = PAGE_SIZE - handle->cur_offset;
+ handle->cur_offset = 0;
+ handle->cur++;
} else {
- handle->page_offset += count;
+ handle->cur_offset += count;
}
handle->offset += count;
return count;
int snapshot_image_loaded(struct snapshot_handle *handle)
{
return !(!handle->pbe || handle->pbe->next || !nr_copy_pages ||
- handle->page <= nr_meta_pages + nr_copy_pages);
+ handle->cur <= nr_meta_pages + nr_copy_pages);
}