Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 29005
b: refs/heads/master
c: 968808b
h: refs/heads/master
i:
  29003: 49f252e
v: v3
  • Loading branch information
Rafael J. Wysocki authored and Linus Torvalds committed Jun 23, 2006
1 parent 13925c2 commit a95f625
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 59 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 7bff24e255ee11ecbc304315a252fcbd84f9ffce
refs/heads/master: 968808b8956e332e556b1eae9b4f7df77518f53b
2 changes: 1 addition & 1 deletion trunk/kernel/power/power.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ struct snapshot_handle {
unsigned int page;
unsigned int page_offset;
unsigned int prev;
struct pbe *pbe;
struct pbe *pbe, *last_pbe;
void *buffer;
unsigned int buf_offset;
};
Expand Down
141 changes: 84 additions & 57 deletions trunk/kernel/power/snapshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,62 +401,29 @@ static inline void create_pbe_list(struct pbe *pblist, unsigned int nr_pages)
}
}

/**
* On resume it is necessary to trace and eventually free the unsafe
* pages that have been allocated, because they are needed for I/O
* (on x86-64 we likely will "eat" these pages once again while
* creating the temporary page translation tables)
*/

struct eaten_page {
struct eaten_page *next;
char padding[PAGE_SIZE - sizeof(void *)];
};

static struct eaten_page *eaten_pages = NULL;

static void release_eaten_pages(void)
{
struct eaten_page *p, *q;

p = eaten_pages;
while (p) {
q = p->next;
/* We don't want swsusp_free() to free this page again */
ClearPageNosave(virt_to_page(p));
free_page((unsigned long)p);
p = q;
}
eaten_pages = 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
* which had been used before suspend.
* used before suspend.
*
* The unsafe pages are marked with the PG_nosave_free flag
*
* Allocated but unusable (ie eaten) memory pages should be marked
* so that swsusp_free() can release them
* 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)
do {
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 && PageNosaveFree(virt_to_page(res))) {
/* This is for swsusp_free() */
SetPageNosave(virt_to_page(res));
((struct eaten_page *)res)->next = eaten_pages;
eaten_pages = res;
}
} while (res && PageNosaveFree(virt_to_page(res)));
else
res = (void *)get_zeroed_page(gfp_mask);
}
if (res) {
SetPageNosave(virt_to_page(res));
SetPageNosaveFree(virt_to_page(res));
Expand Down Expand Up @@ -751,6 +718,8 @@ static int mark_unsafe_pages(struct pbe *pblist)
return -EFAULT;
}

unsafe_pages = 0;

return 0;
}

Expand Down Expand Up @@ -828,42 +797,99 @@ static inline struct pbe *unpack_orig_addresses(unsigned long *buf,
}

/**
* create_image - use metadata contained in the PBE list
* prepare_image - use metadata contained in the PBE list
* pointed to by pagedir_nosave to mark the pages that will
* be overwritten in the process of restoring the system
* memory state from the image and allocate memory for
* the image avoiding these pages
* memory state from the image ("unsafe" pages) and allocate
* memory for the image
*
* The idea is to allocate the PBE list first and then
* allocate as many pages as it's needed for the image data,
* but not to assign these pages to the PBEs initially.
* Instead, we just mark them as allocated and create a list
* of "safe" which will be used later
*/

static int create_image(struct snapshot_handle *handle)
struct safe_page {
struct safe_page *next;
char padding[PAGE_SIZE - sizeof(void *)];
};

static struct safe_page *safe_pages;

static int prepare_image(struct snapshot_handle *handle)
{
int error = 0;
struct pbe *p, *pblist;
unsigned int nr_pages = nr_copy_pages;
struct pbe *p, *pblist = NULL;

p = pagedir_nosave;
error = mark_unsafe_pages(p);
if (!error) {
pblist = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 1);
pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, 1);
if (pblist)
copy_page_backup_list(pblist, p);
free_pagedir(p, 0);
if (!pblist)
error = -ENOMEM;
}
if (!error)
error = alloc_data_pages(pblist, GFP_ATOMIC, 1);
safe_pages = NULL;
if (!error && nr_pages > unsafe_pages) {
nr_pages -= unsafe_pages;
while (nr_pages--) {
struct safe_page *ptr;

ptr = (struct safe_page *)get_zeroed_page(GFP_ATOMIC);
if (!ptr) {
error = -ENOMEM;
break;
}
if (!PageNosaveFree(virt_to_page(ptr))) {
/* The page is "safe", add it to the list */
ptr->next = safe_pages;
safe_pages = ptr;
}
/* Mark the page as allocated */
SetPageNosave(virt_to_page(ptr));
SetPageNosaveFree(virt_to_page(ptr));
}
}
if (!error) {
release_eaten_pages();
pagedir_nosave = pblist;
} else {
pagedir_nosave = NULL;
handle->pbe = NULL;
nr_copy_pages = 0;
nr_meta_pages = 0;
swsusp_free();
}
return error;
}

static void *get_buffer(struct snapshot_handle *handle)
{
struct pbe *pbe = handle->pbe, *last = handle->last_pbe;
struct page *page = virt_to_page(pbe->orig_address);

if (PageNosave(page) && PageNosaveFree(page)) {
/*
* We have allocated the "original" page frame and we can
* use it directly to store the read page
*/
pbe->address = 0;
if (last && last->next)
last->next = NULL;
return (void *)pbe->orig_address;
}
/*
* The "original" page frame has not been allocated and we have to
* use a "safe" page frame to store the read page
*/
pbe->address = (unsigned long)safe_pages;
safe_pages = safe_pages->next;
if (last)
last->next = pbe;
handle->last_pbe = pbe;
return (void *)pbe->address;
}

/**
* snapshot_write_next - used for writing the system memory snapshot.
*
Expand Down Expand Up @@ -908,15 +934,16 @@ int snapshot_write_next(struct snapshot_handle *handle, size_t count)
} else if (handle->prev <= nr_meta_pages) {
handle->pbe = unpack_orig_addresses(buffer, handle->pbe);
if (!handle->pbe) {
error = create_image(handle);
error = prepare_image(handle);
if (error)
return error;
handle->pbe = pagedir_nosave;
handle->buffer = (void *)handle->pbe->address;
handle->last_pbe = NULL;
handle->buffer = get_buffer(handle);
}
} else {
handle->pbe = handle->pbe->next;
handle->buffer = (void *)handle->pbe->address;
handle->buffer = get_buffer(handle);
}
handle->prev = handle->page;
}
Expand Down

0 comments on commit a95f625

Please sign in to comment.