* [PATCH -mm 0/5] Cleanups and improvements related to swsusp
@ 2006-08-10 13:10 Rafael J. Wysocki
2006-08-10 13:12 ` [PATCH -mm 1/5] Clean up suspend header Rafael J. Wysocki
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Rafael J. Wysocki @ 2006-08-10 13:10 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, Pavel Machek
Hi,
The following series of patches contains some swsusp-related cleanups and
improvements.
Greetings,
Rafael
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH -mm 1/5] Clean up suspend header
2006-08-10 13:10 [PATCH -mm 0/5] Cleanups and improvements related to swsusp Rafael J. Wysocki
@ 2006-08-10 13:12 ` Rafael J. Wysocki
2006-08-10 13:16 ` [PATCH -mm 2/5] Change the name of pagedir_nosave Rafael J. Wysocki
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Rafael J. Wysocki @ 2006-08-10 13:12 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, Pavel Machek
Remove some things that are no longer used or defined elsewhere from suspend.h
and make the inline version of software_suspend() return the right error code.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
include/linux/suspend.h | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
Index: linux-2.6.18-rc3-mm2/include/linux/suspend.h
===================================================================
--- linux-2.6.18-rc3-mm2.orig/include/linux/suspend.h 2006-08-10 00:08:08.000000000 +0200
+++ linux-2.6.18-rc3-mm2/include/linux/suspend.h 2006-08-10 07:43:53.000000000 +0200
@@ -10,11 +10,11 @@
#include <linux/pm.h>
/* page backup entry */
-typedef struct pbe {
+struct pbe {
unsigned long address; /* address of the copy */
unsigned long orig_address; /* original address of page */
struct pbe *next;
-} suspend_pagedir_t;
+};
#define for_each_pbe(pbe, pblist) \
for (pbe = pblist ; pbe ; pbe = pbe->next)
@@ -25,15 +25,6 @@ typedef struct pbe {
#define for_each_pb_page(pbe, pblist) \
for (pbe = pblist ; pbe ; pbe = (pbe+PB_PAGE_SKIP)->next)
-
-#define SWAP_FILENAME_MAXLENGTH 32
-
-
-extern dev_t swsusp_resume_device;
-
-/* mm/vmscan.c */
-extern int shrink_mem(void);
-
/* mm/page_alloc.c */
extern void drain_local_pages(void);
extern void mark_free_pages(struct zone *zone);
@@ -53,7 +44,7 @@ static inline void pm_restore_console(vo
static inline int software_suspend(void)
{
printk("Warning: fake suspend called\n");
- return -EPERM;
+ return -ENOSYS;
}
#endif /* CONFIG_PM */
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH -mm 2/5] Change the name of pagedir_nosave
2006-08-10 13:10 [PATCH -mm 0/5] Cleanups and improvements related to swsusp Rafael J. Wysocki
2006-08-10 13:12 ` [PATCH -mm 1/5] Clean up suspend header Rafael J. Wysocki
@ 2006-08-10 13:16 ` Rafael J. Wysocki
2006-08-10 13:18 ` [PATCH -mm 3/5] swsusp: Introduce some helpful constants Rafael J. Wysocki
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Rafael J. Wysocki @ 2006-08-10 13:16 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, Pavel Machek
The name of the pagedir_nosave variable does not make sense any more, so it
seems reasonable to change it to something more meaningful.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
arch/i386/power/swsusp.S | 2 +-
arch/powerpc/kernel/swsusp_32.S | 4 ++--
arch/x86_64/kernel/suspend_asm.S | 2 +-
kernel/power/power.h | 2 --
kernel/power/snapshot.c | 26 ++++++++++++++------------
5 files changed, 18 insertions(+), 18 deletions(-)
Index: linux-2.6.18-rc3-mm2/arch/i386/power/swsusp.S
===================================================================
--- linux-2.6.18-rc3-mm2.orig/arch/i386/power/swsusp.S 2006-08-10 08:17:13.000000000 +0200
+++ linux-2.6.18-rc3-mm2/arch/i386/power/swsusp.S 2006-08-10 08:17:31.000000000 +0200
@@ -32,7 +32,7 @@ ENTRY(swsusp_arch_resume)
movl $swsusp_pg_dir-__PAGE_OFFSET, %ecx
movl %ecx, %cr3
- movl pagedir_nosave, %edx
+ movl restore_pblist, %edx
.p2align 4,,7
copy_loop:
Index: linux-2.6.18-rc3-mm2/arch/powerpc/kernel/swsusp_32.S
===================================================================
--- linux-2.6.18-rc3-mm2.orig/arch/powerpc/kernel/swsusp_32.S 2006-08-10 08:17:13.000000000 +0200
+++ linux-2.6.18-rc3-mm2/arch/powerpc/kernel/swsusp_32.S 2006-08-10 08:17:31.000000000 +0200
@@ -159,8 +159,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
isync
/* Load ptr the list of pages to copy in r3 */
- lis r11,(pagedir_nosave - KERNELBASE)@h
- ori r11,r11,pagedir_nosave@l
+ lis r11,(restore_pblist - KERNELBASE)@h
+ ori r11,r11,restore_pblist@l
lwz r10,0(r11)
/* Copy the pages. This is a very basic implementation, to
Index: linux-2.6.18-rc3-mm2/arch/x86_64/kernel/suspend_asm.S
===================================================================
--- linux-2.6.18-rc3-mm2.orig/arch/x86_64/kernel/suspend_asm.S 2006-08-10 08:17:13.000000000 +0200
+++ linux-2.6.18-rc3-mm2/arch/x86_64/kernel/suspend_asm.S 2006-08-10 08:17:31.000000000 +0200
@@ -54,7 +54,7 @@ ENTRY(restore_image)
movq %rcx, %cr3;
movq %rax, %cr4; # turn PGE back on
- movq pagedir_nosave(%rip), %rdx
+ movq restore_pblist(%rip), %rdx
loop:
testq %rdx, %rdx
jz done
Index: linux-2.6.18-rc3-mm2/kernel/power/power.h
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/power.h 2006-08-10 08:17:13.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/power.h 2006-08-10 08:17:31.000000000 +0200
@@ -38,8 +38,6 @@ extern struct subsystem power_subsys;
/* References to section boundaries */
extern const void __nosave_begin, __nosave_end;
-extern struct pbe *pagedir_nosave;
-
/* Preferred image size in bytes (default 500 MB) */
extern unsigned long image_size;
extern int in_suspend;
Index: linux-2.6.18-rc3-mm2/kernel/power/snapshot.c
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/snapshot.c 2006-08-10 08:17:21.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/snapshot.c 2006-08-10 08:17:44.000000000 +0200
@@ -34,7 +34,9 @@
#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;
@@ -415,7 +417,7 @@ void swsusp_free(void)
}
nr_copy_pages = 0;
nr_meta_pages = 0;
- pagedir_nosave = NULL;
+ restore_pblist = NULL;
buffer = NULL;
}
@@ -490,15 +492,15 @@ asmlinkage int swsusp_save(void)
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,
@@ -580,13 +582,13 @@ int snapshot_read_next(struct snapshot_h
if (!handle->offset) {
init_header((struct swsusp_info *)buffer);
handle->buffer = buffer;
- handle->pbe = pagedir_nosave;
+ handle->pbe = restore_pblist;
}
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;
@@ -689,7 +691,7 @@ static int load_header(struct snapshot_h
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;
@@ -716,7 +718,7 @@ static inline struct pbe *unpack_orig_ad
/**
* 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
@@ -741,7 +743,7 @@ static int prepare_image(struct snapshot
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);
@@ -773,7 +775,7 @@ static int prepare_image(struct snapshot
}
}
if (!error) {
- pagedir_nosave = pblist;
+ restore_pblist = pblist;
} else {
handle->pbe = NULL;
swsusp_free();
@@ -858,7 +860,7 @@ int snapshot_write_next(struct snapshot_
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;
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH -mm 3/5] swsusp: Introduce some helpful constants
2006-08-10 13:10 [PATCH -mm 0/5] Cleanups and improvements related to swsusp Rafael J. Wysocki
2006-08-10 13:12 ` [PATCH -mm 1/5] Clean up suspend header Rafael J. Wysocki
2006-08-10 13:16 ` [PATCH -mm 2/5] Change the name of pagedir_nosave Rafael J. Wysocki
@ 2006-08-10 13:18 ` Rafael J. Wysocki
2006-08-10 13:21 ` [PATCH -mm 4/5] swsusp: Introduce memory bitmaps Rafael J. Wysocki
2006-08-10 13:23 ` [PATCH -mm 5/5] swsusp: Use memory bitmaps during resume Rafael J. Wysocki
4 siblings, 0 replies; 8+ messages in thread
From: Rafael J. Wysocki @ 2006-08-10 13:18 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, Pavel Machek
Introduce some constants that hopefully will help improve the readability of
code in kernel/power/snapshot.c.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
kernel/power/snapshot.c | 24 +++++++++++++++---------
1 file changed, 15 insertions(+), 9 deletions(-)
Index: linux-2.6.18-rc3-mm2/kernel/power/snapshot.c
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/snapshot.c 2006-08-10 08:09:26.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/snapshot.c 2006-08-10 08:09:56.000000000 +0200
@@ -167,6 +167,11 @@ static inline int restore_highmem(void)
* and we count them using unsafe_pages
*/
+#define PG_ANY 0
+#define PG_SAFE 1
+#define PG_UNSAFE_CLEAR 1
+#define PG_UNSAFE_KEEP 0
+
static unsigned int unsafe_pages;
static void *alloc_image_page(gfp_t gfp_mask, int safe_needed)
@@ -190,7 +195,7 @@ static void *alloc_image_page(gfp_t gfp_
unsigned long get_safe_page(gfp_t gfp_mask)
{
- return (unsigned long)alloc_image_page(gfp_mask, 1);
+ return (unsigned long)alloc_image_page(gfp_mask, PG_SAFE);
}
/**
@@ -381,7 +386,7 @@ static struct pbe *alloc_pagedir(unsigne
pbe = pblist;
for (num = PBES_PER_PAGE; num < nr_pages; num += PBES_PER_PAGE) {
if (!pbe) {
- free_pagedir(pblist, 1);
+ free_pagedir(pblist, PG_UNSAFE_CLEAR);
return NULL;
}
pbe += PB_PAGE_SKIP;
@@ -458,12 +463,13 @@ static struct pbe *swsusp_alloc(unsigned
{
struct pbe *pblist;
- if (!(pblist = alloc_pagedir(nr_pages, GFP_ATOMIC | __GFP_COLD, 0))) {
+ pblist = alloc_pagedir(nr_pages, GFP_ATOMIC | __GFP_COLD, PG_ANY);
+ if (!pblist) {
printk(KERN_ERR "suspend: Allocating pagedir failed.\n");
return NULL;
}
- if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, 0)) {
+ if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, PG_ANY)) {
printk(KERN_ERR "suspend: Allocating image pages failed.\n");
swsusp_free();
return NULL;
@@ -575,7 +581,7 @@ int snapshot_read_next(struct snapshot_h
return 0;
if (!buffer) {
/* This makes the buffer be freed by swsusp_free() */
- buffer = alloc_image_page(GFP_ATOMIC, 0);
+ buffer = alloc_image_page(GFP_ATOMIC, PG_ANY);
if (!buffer)
return -ENOMEM;
}
@@ -688,7 +694,7 @@ static int load_header(struct snapshot_h
error = check_header(info);
if (!error) {
- pblist = alloc_pagedir(info->image_pages, GFP_ATOMIC, 0);
+ pblist = alloc_pagedir(info->image_pages, GFP_ATOMIC, PG_ANY);
if (!pblist)
return -ENOMEM;
restore_pblist = pblist;
@@ -746,10 +752,10 @@ static int prepare_image(struct snapshot
p = restore_pblist;
error = mark_unsafe_pages(p);
if (!error) {
- pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, 1);
+ pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, PG_SAFE);
if (pblist)
copy_page_backup_list(pblist, p);
- free_pagedir(p, 0);
+ free_pagedir(p, PG_UNSAFE_KEEP);
if (!pblist)
error = -ENOMEM;
}
@@ -840,7 +846,7 @@ int snapshot_write_next(struct snapshot_
return 0;
if (!buffer) {
/* This makes the buffer be freed by swsusp_free() */
- buffer = alloc_image_page(GFP_ATOMIC, 0);
+ buffer = alloc_image_page(GFP_ATOMIC, PG_ANY);
if (!buffer)
return -ENOMEM;
}
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH -mm 4/5] swsusp: Introduce memory bitmaps
2006-08-10 13:10 [PATCH -mm 0/5] Cleanups and improvements related to swsusp Rafael J. Wysocki
` (2 preceding siblings ...)
2006-08-10 13:18 ` [PATCH -mm 3/5] swsusp: Introduce some helpful constants Rafael J. Wysocki
@ 2006-08-10 13:21 ` Rafael J. Wysocki
2006-08-10 13:23 ` [PATCH -mm 5/5] swsusp: Use memory bitmaps during resume Rafael J. Wysocki
4 siblings, 0 replies; 8+ messages in thread
From: Rafael J. Wysocki @ 2006-08-10 13:21 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, Pavel Machek
Introduce the memory bitmap data structure and make swsusp use in the suspend
phase.
The current swsusp's internal data structure is not very efficient from the
memory usage point of view, so it seems reasonable to replace it with a data
structure that will require less memory, such as a pair of bitmaps.
The idea is to use bitmaps that may be allocated as sets of individual pages,
so that we can avoid making allocations of order greater than 0. For this
reason the memory bitmap structure consists of several linked lists of objects
that contain pointers to memory pages with the actual bitmap data. Still, for
a typical system all of these lists fit in a single page, so it's reasonable
to introduce an additional mechanism allowing us to allocate all of them
efficiently without sacrificing the generality of the design. This is done
with the help of the chain_allocator structure and associated functions.
We need to use two memory bitmaps during the suspend phase of the
suspend-resume cycle. One of them is necessary for marking the saveable
pages, and the second is used to mark the pages in which to store the copies
of them (aka image pages).
First, the bitmaps are created and we allocate as many image pages as needed
(the corresponding bits in the second bitmap are set as soon as the pages are
allocated). Second, the bits corresponding to the saveable pages are set in
the first bitmap and the saveable pages are copied to the image pages.
Finally, the first bitmap is used to save the kernel virtual addresses of the
saveable pages and the second one is used to save the contents of the image
pages.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
kernel/power/power.h | 3
kernel/power/snapshot.c | 606 ++++++++++++++++++++++++++++++++++++++++++------
kernel/power/swsusp.c | 5
3 files changed, 542 insertions(+), 72 deletions(-)
Index: linux-2.6.18-rc3-mm2/kernel/power/snapshot.c
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/snapshot.c 2006-08-10 08:59:34.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/snapshot.c 2006-08-10 09:30:14.000000000 +0200
@@ -211,6 +211,467 @@ static inline void free_image_page(void
free_page((unsigned long)addr);
}
+/* struct linked_page is used to build chains of pages */
+
+#define LINKED_PAGE_DATA_SIZE (PAGE_SIZE - sizeof(void *))
+
+struct linked_page {
+ struct linked_page *next;
+ char data[LINKED_PAGE_DATA_SIZE];
+} __attribute__((packed));
+
+static inline void
+free_list_of_pages(struct linked_page *list, int clear_page_nosave)
+{
+ while (list) {
+ struct linked_page *lp = list->next;
+
+ free_image_page(list, clear_page_nosave);
+ list = lp;
+ }
+}
+
+/**
+ * struct chain_allocator is used for allocating small objects out of
+ * a linked list of pages called 'the chain'.
+ *
+ * The chain grows each time when there is no room for a new object in
+ * the current page. The allocated objects cannot be freed individually.
+ * It is only possible to free them all at once, by freeing the entire
+ * chain.
+ *
+ * NOTE: The chain allocator may be inefficient if the allocated objects
+ * are not much smaller than PAGE_SIZE.
+ */
+
+struct chain_allocator {
+ struct linked_page *chain; /* the chain */
+ unsigned int used_space; /* total size of objects allocated out
+ * of the current page
+ */
+ gfp_t gfp_mask; /* mask for allocating pages */
+ int safe_needed; /* if set, only "safe" pages are allocated */
+};
+
+static void
+chain_init(struct chain_allocator *ca, gfp_t gfp_mask, int safe_needed)
+{
+ ca->chain = NULL;
+ ca->used_space = LINKED_PAGE_DATA_SIZE;
+ ca->gfp_mask = gfp_mask;
+ ca->safe_needed = safe_needed;
+}
+
+static void *chain_alloc(struct chain_allocator *ca, unsigned int size)
+{
+ void *ret;
+
+ if (LINKED_PAGE_DATA_SIZE - ca->used_space < size) {
+ struct linked_page *lp;
+
+ lp = alloc_image_page(ca->gfp_mask, ca->safe_needed);
+ if (!lp)
+ return NULL;
+
+ lp->next = ca->chain;
+ ca->chain = lp;
+ ca->used_space = 0;
+ }
+ ret = ca->chain->data + ca->used_space;
+ ca->used_space += size;
+ return ret;
+}
+
+static void chain_free(struct chain_allocator *ca, int clear_page_nosave)
+{
+ free_list_of_pages(ca->chain, clear_page_nosave);
+ memset(ca, 0, sizeof(struct chain_allocator));
+}
+
+/**
+ * Data types related to memory bitmaps.
+ *
+ * Memory bitmap is a structure consiting of many linked lists of
+ * objects. The main list's elements are of type struct zone_bitmap
+ * and each of them corresonds to one zone. For each zone bitmap
+ * object there is a list of objects of type struct bm_block that
+ * represent each blocks of bit chunks in which information is
+ * stored.
+ *
+ * struct memory_bitmap contains a pointer to the main list of zone
+ * bitmap objects, a struct bm_position used for browsing the bitmap,
+ * and a pointer to the list of pages used for allocating all of the
+ * zone bitmap objects and bitmap block objects.
+ *
+ * NOTE: It has to be possible to lay out the bitmap in memory
+ * using only allocations of order 0. Additionally, the bitmap is
+ * designed to work with arbitrary number of zones (this is over the
+ * top for now, but let's avoid making unnecessary assumptions ;-).
+ *
+ * struct zone_bitmap contains a pointer to a list of bitmap block
+ * objects and a pointer to the bitmap block object that has been
+ * most recently used for setting bits. Additionally, it contains the
+ * pfns that correspond to the start and end of the represented zone.
+ *
+ * struct bm_block contains a pointer to the memory page in which
+ * information is stored (in the form of a block of bit chunks
+ * of type unsigned long each). It also contains the pfns that
+ * correspond to the start and end of the represented memory area and
+ * the number of bit chunks in the block.
+ *
+ * NOTE: Memory bitmaps are used for two types of operations only:
+ * "set a bit" and "find the next bit set". Moreover, the searching
+ * is always carried out after all of the "set a bit" operations
+ * on given bitmap.
+ */
+
+#define BM_END_OF_MAP (~0UL)
+
+#define BM_CHUNKS_PER_BLOCK (PAGE_SIZE / sizeof(long))
+#define BM_BITS_PER_CHUNK (sizeof(long) << 3)
+#define BM_BITS_PER_BLOCK (PAGE_SIZE << 3)
+
+struct bm_block {
+ struct bm_block *next; /* next element of the list */
+ unsigned long start_pfn; /* pfn represented by the first bit */
+ unsigned long end_pfn; /* pfn represented by the last bit plus 1 */
+ unsigned int size; /* number of bit chunks */
+ unsigned long *data; /* chunks of bits representing pages */
+};
+
+struct zone_bitmap {
+ struct zone_bitmap *next; /* next element of the list */
+ unsigned long start_pfn; /* minimal pfn in this zone */
+ unsigned long end_pfn; /* maximal pfn in this zone plus 1 */
+ struct bm_block *bm_blocks; /* list of bitmap blocks */
+ struct bm_block *cur_block; /* recently used bitmap block */
+};
+
+/* strcut bm_position is used for browsing memory bitmaps */
+
+struct bm_position {
+ struct zone_bitmap *zone_bm;
+ struct bm_block *block;
+ int chunk;
+ int bit;
+};
+
+struct memory_bitmap {
+ struct zone_bitmap *zone_bm_list; /* list of zone bitmaps */
+ 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 */
+};
+
+/* Functions that operate on memory bitmaps */
+
+static inline void memory_bm_reset_chunk(struct memory_bitmap *bm)
+{
+ bm->cur.chunk = 0;
+ bm->cur.bit = -1;
+}
+
+static void memory_bm_position_reset(struct memory_bitmap *bm)
+{
+ struct zone_bitmap *zone_bm;
+
+ zone_bm = bm->zone_bm_list;
+ bm->cur.zone_bm = zone_bm;
+ bm->cur.block = zone_bm->bm_blocks;
+ memory_bm_reset_chunk(bm);
+}
+
+static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free);
+
+/**
+ * create_bm_block_list - create a list of block bitmap objects
+ */
+
+static inline struct bm_block *
+create_bm_block_list(unsigned int nr_blocks, struct chain_allocator *ca)
+{
+ struct bm_block *bblist = NULL;
+
+ while (nr_blocks-- > 0) {
+ struct bm_block *bb;
+
+ bb = chain_alloc(ca, sizeof(struct bm_block));
+ if (!bb)
+ return NULL;
+
+ bb->next = bblist;
+ bblist = bb;
+ }
+ return bblist;
+}
+
+/**
+ * create_zone_bm_list - create a list of zone bitmap objects
+ */
+
+static inline struct zone_bitmap *
+create_zone_bm_list(unsigned int nr_zones, struct chain_allocator *ca)
+{
+ struct zone_bitmap *zbmlist = NULL;
+
+ while (nr_zones-- > 0) {
+ struct zone_bitmap *zbm;
+
+ zbm = chain_alloc(ca, sizeof(struct zone_bitmap));
+ if (!zbm)
+ return NULL;
+
+ zbm->next = zbmlist;
+ zbmlist = zbm;
+ }
+ return zbmlist;
+}
+
+/**
+ * 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)
+{
+ struct chain_allocator ca;
+ struct zone *zone;
+ struct zone_bitmap *zone_bm;
+ struct bm_block *bb;
+ unsigned int nr;
+
+ chain_init(&ca, gfp_mask, safe_needed);
+
+ /* Compute the number of zones */
+ nr = 0;
+ for_each_zone (zone)
+ if (populated_zone(zone) && !is_highmem(zone))
+ nr++;
+
+ /* Allocate the list of zones bitmap objects */
+ zone_bm = create_zone_bm_list(nr, &ca);
+ bm->zone_bm_list = zone_bm;
+ if (!zone_bm) {
+ chain_free(&ca, PG_UNSAFE_CLEAR);
+ return -ENOMEM;
+ }
+
+ /* Initialize the zone bitmap objects */
+ for_each_zone (zone) {
+ unsigned long pfn;
+
+ if (!populated_zone(zone) || is_highmem(zone))
+ continue;
+
+ zone_bm->start_pfn = zone->zone_start_pfn;
+ zone_bm->end_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ /* Allocate the list of bitmap block objects */
+ nr = DIV_ROUND_UP(zone->spanned_pages, BM_BITS_PER_BLOCK);
+ bb = create_bm_block_list(nr, &ca);
+ zone_bm->bm_blocks = bb;
+ zone_bm->cur_block = bb;
+ if (!bb)
+ goto Free;
+
+ nr = zone->spanned_pages;
+ pfn = zone->zone_start_pfn;
+ /* Initialize the bitmap block objects */
+ while (bb) {
+ unsigned long *ptr;
+
+ ptr = alloc_image_page(gfp_mask, safe_needed);
+ bb->data = ptr;
+ if (!ptr)
+ goto Free;
+
+ bb->start_pfn = pfn;
+ if (nr >= BM_BITS_PER_BLOCK) {
+ pfn += BM_BITS_PER_BLOCK;
+ bb->size = BM_CHUNKS_PER_BLOCK;
+ nr -= BM_BITS_PER_BLOCK;
+ } else {
+ /* This is executed only once in the loop */
+ pfn += nr;
+ bb->size = DIV_ROUND_UP(nr, BM_BITS_PER_CHUNK);
+ }
+ bb->end_pfn = pfn;
+ bb = bb->next;
+ }
+ zone_bm = zone_bm->next;
+ }
+ bm->p_list = ca.chain;
+ memory_bm_position_reset(bm);
+ return 0;
+
+Free:
+ bm->p_list = ca.chain;
+ memory_bm_free(bm, PG_UNSAFE_CLEAR);
+ return -ENOMEM;
+}
+
+/**
+ * memory_bm_free - free memory occupied by the memory bitmap @bm
+ */
+
+static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free)
+{
+ struct zone_bitmap *zone_bm;
+
+ /* Free the list of bit blocks for each zone_bitmap object */
+ zone_bm = bm->zone_bm_list;
+ while (zone_bm) {
+ struct bm_block *bb;
+
+ bb = zone_bm->bm_blocks;
+ while (bb) {
+ if (bb->data)
+ free_image_page(bb->data, clear_nosave_free);
+ bb = bb->next;
+ }
+ zone_bm = zone_bm->next;
+ }
+ free_list_of_pages(bm->p_list, clear_nosave_free);
+ bm->zone_bm_list = NULL;
+}
+
+/**
+ * memory_bm_set_bit - set 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.
+ *
+ * If the bit cannot be set, the function returns -EINVAL .
+ */
+
+static int
+memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn)
+{
+ struct zone_bitmap *zone_bm;
+ struct bm_block *bb;
+
+ /* Check if the pfn is from the current zone */
+ zone_bm = bm->cur.zone_bm;
+ if (pfn < zone_bm->start_pfn || pfn >= zone_bm->end_pfn) {
+ zone_bm = bm->zone_bm_list;
+ /* We don't assume that the zones are sorted by pfns */
+ while (pfn < zone_bm->start_pfn || pfn >= zone_bm->end_pfn) {
+ zone_bm = zone_bm->next;
+ if (unlikely(!zone_bm))
+ return -EINVAL;
+ }
+ bm->cur.zone_bm = zone_bm;
+ }
+ /* Check if the pfn corresponds to the current bitmap block */
+ bb = zone_bm->cur_block;
+ if (pfn < bb->start_pfn)
+ bb = zone_bm->bm_blocks;
+
+ while (pfn >= bb->end_pfn) {
+ bb = bb->next;
+ if (unlikely(!bb))
+ return -EINVAL;
+ }
+ zone_bm->cur_block = bb;
+ pfn -= bb->start_pfn;
+ set_bit(pfn % BM_BITS_PER_CHUNK, bb->data + pfn / BM_BITS_PER_CHUNK);
+ return 0;
+}
+
+/* Two auxiliary functions for memory_bm_next_pfn */
+
+/* Find the first set bit in the given chunk, if there is one */
+
+static inline int next_bit_in_chunk(int bit, unsigned long *chunk_p)
+{
+ bit++;
+ while (bit < BM_BITS_PER_CHUNK) {
+ if (test_bit(bit, chunk_p))
+ return bit;
+
+ bit++;
+ }
+ return -1;
+}
+
+/* Find a chunk containing some bits set in given block of bits */
+
+static inline int next_chunk_in_block(int n, struct bm_block *bb)
+{
+ n++;
+ while (n < bb->size) {
+ if (bb->data[n])
+ return n;
+
+ n++;
+ }
+ return -1;
+}
+
+/**
+ * memory_bm_next_pfn - find the pfn that corresponds to the next set bit
+ * in the bitmap @bm. If the pfn cannot be found, BM_END_OF_MAP is
+ * returned.
+ *
+ * It is required to run memory_bm_position_reset() before the first call to
+ * this function.
+ */
+
+static unsigned long memory_bm_next_pfn(struct memory_bitmap *bm)
+{
+ struct zone_bitmap *zone_bm;
+ struct bm_block *bb;
+ int chunk;
+ int bit;
+
+ do {
+ bb = bm->cur.block;
+ do {
+ chunk = bm->cur.chunk;
+ bit = bm->cur.bit;
+ do {
+ bit = next_bit_in_chunk(bit, bb->data + chunk);
+ if (bit >= 0)
+ goto Return_pfn;
+
+ chunk = next_chunk_in_block(chunk, bb);
+ bit = -1;
+ } while (chunk >= 0);
+ bb = bb->next;
+ bm->cur.block = bb;
+ memory_bm_reset_chunk(bm);
+ } while (bb);
+ zone_bm = bm->cur.zone_bm->next;
+ if (zone_bm) {
+ bm->cur.zone_bm = zone_bm;
+ bm->cur.block = zone_bm->bm_blocks;
+ memory_bm_reset_chunk(bm);
+ }
+ } while (zone_bm);
+ memory_bm_position_reset(bm);
+ return BM_END_OF_MAP;
+
+Return_pfn:
+ bm->cur.chunk = chunk;
+ bm->cur.bit = bit;
+ return bb->start_pfn + chunk * BM_BITS_PER_CHUNK + bit;
+}
+
+/**
+ * snapshot_additional_pages - estimate the number of additional pages
+ * be needed for setting up the suspend image data structures for given
+ * zone (usually the returned value is greater than the exact number)
+ */
+
+unsigned int snapshot_additional_pages(struct zone *zone)
+{
+ unsigned int res;
+
+ res = DIV_ROUND_UP(zone->spanned_pages, BM_BITS_PER_BLOCK);
+ res += DIV_ROUND_UP(res * sizeof(struct bm_block), PAGE_SIZE);
+ return res;
+}
+
/**
* pfn_is_nosave - check if given pfn is in the 'nosave' section
*/
@@ -276,32 +737,38 @@ static inline void copy_data_page(long *
*dst++ = *src++;
}
-static void copy_data_pages(struct pbe *pblist)
+static void
+copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
{
struct zone *zone;
- unsigned long pfn, max_zone_pfn;
- struct pbe *pbe;
+ unsigned long pfn;
- pbe = pblist;
for_each_zone (zone) {
+ unsigned long max_zone_pfn;
+
if (is_highmem(zone))
continue;
+
mark_free_pages(zone);
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);
-
- BUG_ON(!pbe);
- copy_data_page((void *)pbe->address, ptr);
- pbe->orig_address = (unsigned long)ptr;
- pbe = pbe->next;
- }
- }
+ for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
+ if (saveable_page(pfn))
+ memory_bm_set_bit(orig_bm, pfn);
}
- BUG_ON(pbe);
+ memory_bm_position_reset(orig_bm);
+ memory_bm_position_reset(copy_bm);
+ do {
+ pfn = memory_bm_next_pfn(orig_bm);
+ if (likely(pfn != BM_END_OF_MAP)) {
+ struct page *page;
+ void *src;
+
+ page = pfn_to_page(pfn);
+ src = page_address(page);
+ page = pfn_to_page(memory_bm_next_pfn(copy_bm));
+ copy_data_page(page_address(page), src);
+ }
+ } while (pfn != BM_END_OF_MAP);
}
/**
@@ -447,37 +914,43 @@ static int enough_free_mem(unsigned int
(nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
}
-static int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed)
+static int
+swsusp_alloc(struct memory_bitmap *orig_bm, struct memory_bitmap *copy_bm,
+ unsigned int nr_pages)
{
- struct pbe *p;
+ int error;
- for_each_pbe (p, pblist) {
- p->address = (unsigned long)alloc_image_page(gfp_mask, safe_needed);
- if (!p->address)
- return -ENOMEM;
+ error = memory_bm_create(orig_bm, GFP_ATOMIC | __GFP_COLD, PG_ANY);
+ if (error)
+ goto Free;
+
+ error = memory_bm_create(copy_bm, GFP_ATOMIC | __GFP_COLD, PG_ANY);
+ if (error)
+ goto Free;
+
+ while (nr_pages-- > 0) {
+ struct page *page = alloc_page(GFP_ATOMIC | __GFP_COLD);
+ if (!page)
+ goto Free;
+
+ SetPageNosave(page);
+ SetPageNosaveFree(page);
+ memory_bm_set_bit(copy_bm, page_to_pfn(page));
}
return 0;
-}
-
-static struct pbe *swsusp_alloc(unsigned int nr_pages)
-{
- struct pbe *pblist;
-
- pblist = alloc_pagedir(nr_pages, GFP_ATOMIC | __GFP_COLD, PG_ANY);
- if (!pblist) {
- printk(KERN_ERR "suspend: Allocating pagedir failed.\n");
- return NULL;
- }
-
- if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, PG_ANY)) {
- printk(KERN_ERR "suspend: Allocating image pages failed.\n");
- swsusp_free();
- return NULL;
- }
- return pblist;
+Free:
+ swsusp_free();
+ return -ENOMEM;
}
+/* Memory bitmap used for marking saveable pages */
+static struct memory_bitmap orig_bm;
+/* Memory bitmap used for marking allocated pages that will contain the copies
+ * of saveable pages
+ */
+static struct memory_bitmap copy_bm;
+
asmlinkage int swsusp_save(void)
{
unsigned int nr_pages;
@@ -498,15 +971,14 @@ asmlinkage int swsusp_save(void)
return -ENOMEM;
}
- restore_pblist = swsusp_alloc(nr_pages);
- if (!restore_pblist)
+ if (swsusp_alloc(&orig_bm, ©_bm, nr_pages))
return -ENOMEM;
/* During allocating of suspend pagedir, new cold pages may appear.
* Kill them.
*/
drain_local_pages();
- copy_data_pages(restore_pblist);
+ copy_data_pages(©_bm, &orig_bm);
/*
* End of critical section. From now on, we can write to memory,
@@ -535,22 +1007,23 @@ static void init_header(struct swsusp_in
}
/**
- * pack_orig_addresses - the .orig_address fields of the PBEs from the
- * list starting at @pbe are stored in the array @buf[] (1 page)
+ * pack_addresses - the addresses corresponding to pfns found in the
+ * bitmap @bm are stored in the array @buf[] (1 page)
*/
-static inline struct pbe *pack_orig_addresses(unsigned long *buf, struct pbe *pbe)
+static inline void
+pack_addresses(unsigned long *buf, struct memory_bitmap *bm)
{
int j;
- for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) {
- buf[j] = pbe->orig_address;
- pbe = pbe->next;
+ for (j = 0; j < PAGE_SIZE / sizeof(long); j++) {
+ unsigned long pfn = memory_bm_next_pfn(bm);
+
+ if (unlikely(pfn == BM_END_OF_MAP))
+ break;
+
+ buf[j] = (unsigned long)page_address(pfn_to_page(pfn));
}
- if (!pbe)
- for (; j < PAGE_SIZE / sizeof(long); j++)
- buf[j] = 0;
- return pbe;
}
/**
@@ -579,6 +1052,7 @@ int snapshot_read_next(struct snapshot_h
{
if (handle->cur > nr_meta_pages + nr_copy_pages)
return 0;
+
if (!buffer) {
/* This makes the buffer be freed by swsusp_free() */
buffer = alloc_image_page(GFP_ATOMIC, PG_ANY);
@@ -588,16 +1062,17 @@ int snapshot_read_next(struct snapshot_h
if (!handle->offset) {
init_header((struct swsusp_info *)buffer);
handle->buffer = buffer;
- handle->pbe = restore_pblist;
+ memory_bm_position_reset(&orig_bm);
+ memory_bm_position_reset(©_bm);
}
if (handle->prev < handle->cur) {
if (handle->cur <= nr_meta_pages) {
- handle->pbe = pack_orig_addresses(buffer, handle->pbe);
- if (!handle->pbe)
- handle->pbe = restore_pblist;
+ memset(buffer, 0, PAGE_SIZE);
+ pack_addresses(buffer, &orig_bm);
} else {
- handle->buffer = (void *)handle->pbe->address;
- handle->pbe = handle->pbe->next;
+ unsigned long pfn = memory_bm_next_pfn(©_bm);
+
+ handle->buffer = page_address(pfn_to_page(pfn));
}
handle->prev = handle->cur;
}
@@ -736,12 +1211,7 @@ static inline struct pbe *unpack_orig_ad
* of "safe" which will be used later
*/
-struct safe_page {
- struct safe_page *next;
- char padding[PAGE_SIZE - sizeof(void *)];
-};
-
-static struct safe_page *safe_pages;
+static struct linked_page *safe_pages;
static int prepare_image(struct snapshot_handle *handle)
{
@@ -763,9 +1233,9 @@ static int prepare_image(struct snapshot
if (!error && nr_pages > unsafe_pages) {
nr_pages -= unsafe_pages;
while (nr_pages--) {
- struct safe_page *ptr;
+ struct linked_page *ptr;
- ptr = (struct safe_page *)get_zeroed_page(GFP_ATOMIC);
+ ptr = (void *)get_zeroed_page(GFP_ATOMIC);
if (!ptr) {
error = -ENOMEM;
break;
Index: linux-2.6.18-rc3-mm2/kernel/power/power.h
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/power.h 2006-08-10 08:59:34.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/power.h 2006-08-10 09:29:29.000000000 +0200
@@ -109,9 +109,10 @@ struct snapshot_handle {
*/
#define data_of(handle) ((handle).buffer + (handle).buf_offset)
+extern unsigned int snapshot_additional_pages(struct zone *zone);
extern int snapshot_read_next(struct snapshot_handle *handle, size_t count);
extern int snapshot_write_next(struct snapshot_handle *handle, size_t count);
-int snapshot_image_loaded(struct snapshot_handle *handle);
+extern int snapshot_image_loaded(struct snapshot_handle *handle);
#define SNAPSHOT_IOC_MAGIC '3'
#define SNAPSHOT_FREEZE _IO(SNAPSHOT_IOC_MAGIC, 1)
Index: linux-2.6.18-rc3-mm2/kernel/power/swsusp.c
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/swsusp.c 2006-08-10 08:59:34.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/swsusp.c 2006-08-10 09:29:27.000000000 +0200
@@ -193,14 +193,13 @@ int swsusp_shrink_memory(void)
printk("Shrinking memory... ");
do {
size = 2 * count_highmem_pages();
- size += size / 50 + count_data_pages();
- size += (size + PBES_PER_PAGE - 1) / PBES_PER_PAGE +
- PAGES_FOR_IO;
+ size += size / 50 + count_data_pages() + PAGES_FOR_IO;
tmp = size;
for_each_zone (zone)
if (!is_highmem(zone) && populated_zone(zone)) {
tmp -= zone->free_pages;
tmp += zone->lowmem_reserve[ZONE_NORMAL];
+ tmp += snapshot_additional_pages(zone);
}
if (tmp > 0) {
tmp = __shrink_memory(tmp);
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH -mm 5/5] swsusp: Use memory bitmaps during resume
2006-08-10 13:10 [PATCH -mm 0/5] Cleanups and improvements related to swsusp Rafael J. Wysocki
` (3 preceding siblings ...)
2006-08-10 13:21 ` [PATCH -mm 4/5] swsusp: Introduce memory bitmaps Rafael J. Wysocki
@ 2006-08-10 13:23 ` Rafael J. Wysocki
2006-08-13 22:04 ` Andrew Morton
4 siblings, 1 reply; 8+ messages in thread
From: Rafael J. Wysocki @ 2006-08-10 13:23 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, Pavel Machek
Make swsusp use memory bitmaps to store its internal information during the
resume phase of the suspend-resume cycle.
If the pfns of saveable pages are saved during the suspend phase instead of
the kernel virtual addresses of these pages, we can use them during the resume
phase directly to set the corresponding bits in a memory bitmap. Then, this
bitmap is used to mark the page frames corresponding to the pages that were
saveable before the suspend (aka "unsafe" page frames).
Next, we allocate as many page frames as needed to store the entire suspend
image and make sure that there will be some extra free "safe" page frames for
the list of PBEs constructed later. Subsequently, the image is loaded and,
if possible, the data loaded from it are written into their "original" page frames
(ie. the ones they had occupied before the suspend). The image data that
cannot be written into their "original" page frames are loaded into "safe" page
frames and their "original" kernel virtual addresses, as well as the addresses
of the "safe" pages containing their copies, are stored in a list of PBEs.
Finally, the list of PBEs is used to copy the remaining image data into their
"original" page frames (this is done atomically, by the architecture-dependent
parts of swsusp).
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
include/linux/suspend.h | 9 -
kernel/power/power.h | 11 -
kernel/power/snapshot.c | 421 ++++++++++++++++++++----------------------------
kernel/power/swap.c | 4
kernel/power/user.c | 1
5 files changed, 188 insertions(+), 258 deletions(-)
Index: linux-2.6.18-rc3-mm2/kernel/power/snapshot.c
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/snapshot.c 2006-08-10 09:20:53.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/snapshot.c 2006-08-10 09:21:34.000000000 +0200
@@ -39,7 +39,7 @@ struct pbe *restore_pblist;
static unsigned int nr_copy_pages;
static unsigned int nr_meta_pages;
-static unsigned long *buffer;
+static void *buffer;
#ifdef CONFIG_HIGHMEM
unsigned int count_highmem_pages(void)
@@ -172,7 +172,7 @@ static inline int restore_highmem(void)
#define PG_UNSAFE_CLEAR 1
#define PG_UNSAFE_KEEP 0
-static unsigned int unsafe_pages;
+static unsigned int allocated_unsafe_pages;
static void *alloc_image_page(gfp_t gfp_mask, int safe_needed)
{
@@ -183,7 +183,7 @@ static void *alloc_image_page(gfp_t gfp_
while (res && PageNosaveFree(virt_to_page(res))) {
/* The page is unsafe, mark it for swsusp_free() */
SetPageNosave(virt_to_page(res));
- unsafe_pages++;
+ allocated_unsafe_pages++;
res = (void *)get_zeroed_page(gfp_mask);
}
if (res) {
@@ -772,101 +772,10 @@ copy_data_pages(struct memory_bitmap *co
}
/**
- * free_pagedir - free pages allocated with alloc_pagedir()
- */
-
-static void free_pagedir(struct pbe *pblist, int clear_nosave_free)
-{
- struct pbe *pbe;
-
- while (pblist) {
- pbe = (pblist + PB_PAGE_SKIP)->next;
- 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, unsigned int n)
-{
- struct pbe *p;
-
- p = pbpage;
- 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;
- unsigned int num = PBES_PER_PAGE;
-
- for_each_pb_page (pbpage, pblist) {
- if (num >= nr_pages)
- break;
-
- fill_pb_page(pbpage, PBES_PER_PAGE);
- num += PBES_PER_PAGE;
- }
- if (pbpage) {
- num -= PBES_PER_PAGE;
- fill_pb_page(pbpage, nr_pages - num);
- }
-}
-
-/**
- * alloc_pagedir - Allocate the page directory.
- *
- * First, determine exactly how many pages we need and
- * allocate them.
+ * swsusp_free - free pages allocated for the suspend.
*
- * We arrange the pages in a chain: each page is an array of PBES_PER_PAGE
- * struct pbe elements (pbes) and the last element in the page points
- * to the next page.
- *
- * On each page we set up a list of struct_pbe elements.
- */
-
-static struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask,
- int safe_needed)
-{
- unsigned int num;
- struct pbe *pblist, *pbe;
-
- if (!nr_pages)
- return NULL;
-
- pblist = alloc_image_page(gfp_mask, safe_needed);
- pbe = pblist;
- for (num = PBES_PER_PAGE; num < nr_pages; num += PBES_PER_PAGE) {
- if (!pbe) {
- free_pagedir(pblist, PG_UNSAFE_CLEAR);
- return NULL;
- }
- pbe += PB_PAGE_SKIP;
- pbe->next = alloc_image_page(gfp_mask, safe_needed);
- pbe = pbe->next;
- }
- create_pbe_list(pblist, nr_pages);
- return pblist;
-}
-
-/**
- * Free pages we allocated for suspend. Suspend pages are alocated
- * before atomic copy, so we need to free them after resume.
+ * Suspend pages are alocated before the atomic copy is made, so we
+ * need to release them after the resume.
*/
void swsusp_free(void)
@@ -904,14 +813,18 @@ void swsusp_free(void)
static int enough_free_mem(unsigned int nr_pages)
{
struct zone *zone;
- unsigned int n = 0;
+ unsigned int free = 0, meta = 0;
for_each_zone (zone)
- if (!is_highmem(zone))
- n += zone->free_pages;
- pr_debug("swsusp: available memory: %u pages\n", n);
- return n > (nr_pages + PAGES_FOR_IO +
- (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
+ if (!is_highmem(zone)) {
+ free += zone->free_pages;
+ meta += snapshot_additional_pages(zone);
+ }
+
+ pr_debug("swsusp: pages needed: %u + %u + %u, available pages: %u\n",
+ nr_pages, PAGES_FOR_IO, meta, free);
+
+ return free > nr_pages + PAGES_FOR_IO + meta;
}
static int
@@ -961,11 +874,6 @@ asmlinkage int swsusp_save(void)
nr_pages = count_data_pages();
printk("swsusp: Need to copy %u pages\n", nr_pages);
- pr_debug("swsusp: pages needed: %u + %lu + %u, free: %u\n",
- nr_pages,
- (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE,
- PAGES_FOR_IO, nr_free_pages());
-
if (!enough_free_mem(nr_pages)) {
printk(KERN_ERR "swsusp: Not enough free memory\n");
return -ENOMEM;
@@ -1007,22 +915,19 @@ static void init_header(struct swsusp_in
}
/**
- * pack_addresses - the addresses corresponding to pfns found in the
- * bitmap @bm are stored in the array @buf[] (1 page)
+ * pack_pfns - pfns corresponding to the set bits found in the bitmap @bm
+ * are stored in the array @buf[] (1 page at a time)
*/
static inline void
-pack_addresses(unsigned long *buf, struct memory_bitmap *bm)
+pack_pfns(unsigned long *buf, struct memory_bitmap *bm)
{
int j;
for (j = 0; j < PAGE_SIZE / sizeof(long); j++) {
- unsigned long pfn = memory_bm_next_pfn(bm);
-
- if (unlikely(pfn == BM_END_OF_MAP))
+ buf[j] = memory_bm_next_pfn(bm);
+ if (unlikely(buf[j] == BM_END_OF_MAP))
break;
-
- buf[j] = (unsigned long)page_address(pfn_to_page(pfn));
}
}
@@ -1068,7 +973,7 @@ int snapshot_read_next(struct snapshot_h
if (handle->prev < handle->cur) {
if (handle->cur <= nr_meta_pages) {
memset(buffer, 0, PAGE_SIZE);
- pack_addresses(buffer, &orig_bm);
+ pack_pfns(buffer, &orig_bm);
} else {
unsigned long pfn = memory_bm_next_pfn(©_bm);
@@ -1094,14 +999,10 @@ int snapshot_read_next(struct snapshot_h
* had been used before suspend
*/
-static int mark_unsafe_pages(struct pbe *pblist)
+static int mark_unsafe_pages(struct memory_bitmap *bm)
{
struct zone *zone;
unsigned long pfn, max_zone_pfn;
- struct pbe *p;
-
- if (!pblist) /* a sanity check */
- return -EINVAL;
/* Clear page flags */
for_each_zone (zone) {
@@ -1111,30 +1012,37 @@ static int mark_unsafe_pages(struct pbe
ClearPageNosaveFree(pfn_to_page(pfn));
}
- /* Mark orig addresses */
- for_each_pbe (p, pblist) {
- if (virt_addr_valid(p->orig_address))
- SetPageNosaveFree(virt_to_page(p->orig_address));
- else
- return -EFAULT;
- }
+ /* Mark pages that correspond to the "original" pfns as "unsafe" */
+ memory_bm_position_reset(bm);
+ do {
+ pfn = memory_bm_next_pfn(bm);
+ if (likely(pfn != BM_END_OF_MAP)) {
+ if (likely(pfn_valid(pfn)))
+ SetPageNosaveFree(pfn_to_page(pfn));
+ else
+ return -EFAULT;
+ }
+ } while (pfn != BM_END_OF_MAP);
- unsafe_pages = 0;
+ allocated_unsafe_pages = 0;
return 0;
}
-static void copy_page_backup_list(struct pbe *dst, struct pbe *src)
+static void
+duplicate_memory_bitmap(struct memory_bitmap *dst, struct memory_bitmap *src)
{
- /* We assume both lists contain the same number of elements */
- while (src) {
- dst->orig_address = src->orig_address;
- dst = dst->next;
- src = src->next;
+ unsigned long pfn;
+
+ memory_bm_position_reset(src);
+ pfn = memory_bm_next_pfn(src);
+ while (pfn != BM_END_OF_MAP) {
+ memory_bm_set_bit(dst, pfn);
+ pfn = memory_bm_next_pfn(src);
}
}
-static int check_header(struct swsusp_info *info)
+static inline int check_header(struct swsusp_info *info)
{
char *reason = NULL;
@@ -1161,19 +1069,14 @@ static int check_header(struct swsusp_in
* load header - check the image header and copy data from it
*/
-static int load_header(struct snapshot_handle *handle,
- struct swsusp_info *info)
+static int
+load_header(struct swsusp_info *info)
{
int error;
- struct pbe *pblist;
+ restore_pblist = NULL;
error = check_header(info);
if (!error) {
- pblist = alloc_pagedir(info->image_pages, GFP_ATOMIC, PG_ANY);
- if (!pblist)
- return -ENOMEM;
- restore_pblist = pblist;
- handle->pbe = pblist;
nr_copy_pages = info->image_pages;
nr_meta_pages = info->pages - info->image_pages - 1;
}
@@ -1181,108 +1084,137 @@ static int load_header(struct snapshot_h
}
/**
- * unpack_orig_addresses - copy the elements of @buf[] (1 page) to
- * the PBEs in the list starting at @pbe
+ * unpack_orig_pfns - for each element of @buf[] (1 page at a time) set
+ * the corresponding bit in the memory bitmap @bm
*/
-static inline struct pbe *unpack_orig_addresses(unsigned long *buf,
- struct pbe *pbe)
+static inline void
+unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
{
int j;
- for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) {
- pbe->orig_address = buf[j];
- pbe = pbe->next;
+ for (j = 0; j < PAGE_SIZE / sizeof(long); j++) {
+ if (unlikely(buf[j] == BM_END_OF_MAP))
+ break;
+
+ memory_bm_set_bit(bm, buf[j]);
}
- return pbe;
}
/**
- * prepare_image - use metadata contained in the PBE list
- * 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
+ * prepare_image - use the memory bitmap @bm to mark the pages that will
+ * be overwritten in the process of restoring the system memory state
+ * from the suspend 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
+ * The idea is to allocate a new memory bitmap first and then allocate
+ * as many pages as needed for the image data, but not to assign these
+ * pages to specific tasks initially. Instead, we just mark them as
+ * allocated and create a list of "safe" pages that will be used later.
*/
-static struct linked_page *safe_pages;
+#define PBES_PER_LINKED_PAGE (LINKED_PAGE_DATA_SIZE / sizeof(struct pbe))
-static int prepare_image(struct snapshot_handle *handle)
+static struct linked_page *safe_pages_list;
+
+static int
+prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
{
- int error = 0;
- unsigned int nr_pages = nr_copy_pages;
- struct pbe *p, *pblist = NULL;
+ unsigned int nr_pages;
+ struct linked_page *sp_list, *lp;
+ int error;
- p = restore_pblist;
- error = mark_unsafe_pages(p);
- if (!error) {
- pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, PG_SAFE);
- if (pblist)
- copy_page_backup_list(pblist, p);
- free_pagedir(p, PG_UNSAFE_KEEP);
- if (!pblist)
+ error = mark_unsafe_pages(bm);
+ if (error)
+ goto Free;
+
+ error = memory_bm_create(new_bm, GFP_ATOMIC, PG_SAFE);
+ if (error)
+ goto Free;
+
+ duplicate_memory_bitmap(new_bm, bm);
+ memory_bm_free(bm, PG_UNSAFE_KEEP);
+ /* Reserve some safe pages for potential later use.
+ *
+ * NOTE: This way we make sure there will be enough safe pages for the
+ * chain_alloc() in get_buffer(). It is a bit wasteful, but
+ * nr_copy_pages cannot be greater than 50% of the memory anyway.
+ */
+ sp_list = NULL;
+ /* nr_copy_pages cannot be lesser than allocated_unsafe_pages */
+ nr_pages = nr_copy_pages - allocated_unsafe_pages;
+ nr_pages = DIV_ROUND_UP(nr_pages, PBES_PER_LINKED_PAGE);
+ while (nr_pages > 0) {
+ lp = alloc_image_page(GFP_ATOMIC, PG_SAFE);
+ if (!lp) {
error = -ENOMEM;
- }
- safe_pages = NULL;
- if (!error && nr_pages > unsafe_pages) {
- nr_pages -= unsafe_pages;
- while (nr_pages--) {
- struct linked_page *ptr;
-
- ptr = (void *)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));
+ goto Free;
}
+ lp->next = sp_list;
+ sp_list = lp;
+ nr_pages--;
+ }
+ /* Preallocate memory for the image */
+ safe_pages_list = NULL;
+ nr_pages = nr_copy_pages - allocated_unsafe_pages;
+ while (nr_pages > 0) {
+ lp = (struct linked_page *)get_zeroed_page(GFP_ATOMIC);
+ if (!lp) {
+ error = -ENOMEM;
+ goto Free;
+ }
+ if (!PageNosaveFree(virt_to_page(lp))) {
+ /* The page is "safe", add it to the list */
+ lp->next = safe_pages_list;
+ safe_pages_list = lp;
+ }
+ /* Mark the page as allocated */
+ SetPageNosave(virt_to_page(lp));
+ SetPageNosaveFree(virt_to_page(lp));
+ nr_pages--;
+ }
+ /* Free the reserved safe pages so that chain_alloc() can use them */
+ while (sp_list) {
+ lp = sp_list->next;
+ free_image_page(sp_list, PG_UNSAFE_CLEAR);
+ sp_list = lp;
}
- if (!error) {
- restore_pblist = pblist;
- } else {
- handle->pbe = NULL;
- swsusp_free();
- }
+ return 0;
+
+Free:
+ swsusp_free();
return error;
}
-static void *get_buffer(struct snapshot_handle *handle)
+/**
+ * get_buffer - compute the address that snapshot_write_next() should
+ * set for its caller to write to.
+ */
+
+static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
{
- struct pbe *pbe = handle->pbe, *last = handle->last_pbe;
- struct page *page = virt_to_page(pbe->orig_address);
+ struct pbe *pbe;
+ struct page *page = pfn_to_page(memory_bm_next_pfn(bm));
- if (PageNosave(page) && PageNosaveFree(page)) {
- /*
- * We have allocated the "original" page frame and we can
- * use it directly to store the read page
+ if (PageNosave(page) && PageNosaveFree(page))
+ /* We have allocated the "original" page frame and we can
+ * use it directly to store the loaded 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
+ return page_address(page);
+
+ /* The "original" page frame has not been allocated and we have to
+ * use a "safe" page frame to store the loaded page.
*/
- pbe->address = (unsigned long)safe_pages;
- safe_pages = safe_pages->next;
- if (last)
- last->next = pbe;
- handle->last_pbe = pbe;
+ pbe = chain_alloc(ca, sizeof(struct pbe));
+ if (!pbe) {
+ swsusp_free();
+ return NULL;
+ }
+ pbe->orig_address = (unsigned long)page_address(page);
+ pbe->address = (unsigned long)safe_pages_list;
+ safe_pages_list = safe_pages_list->next;
+ pbe->next = restore_pblist;
+ restore_pblist = pbe;
return (void *)pbe->address;
}
@@ -1310,10 +1242,13 @@ static void *get_buffer(struct snapshot_
int snapshot_write_next(struct snapshot_handle *handle, size_t count)
{
+ static struct chain_allocator ca;
int error = 0;
+ /* Check if we have already loaded the entire image */
if (handle->prev && handle->cur > nr_meta_pages + nr_copy_pages)
return 0;
+
if (!buffer) {
/* This makes the buffer be freed by swsusp_free() */
buffer = alloc_image_page(GFP_ATOMIC, PG_ANY);
@@ -1324,27 +1259,32 @@ int snapshot_write_next(struct snapshot_
handle->buffer = buffer;
handle->sync_read = 1;
if (handle->prev < handle->cur) {
- if (!handle->prev) {
- error = load_header(handle,
- (struct swsusp_info *)buffer);
+ if (handle->prev == 0) {
+ error = load_header(buffer);
+ if (error)
+ return error;
+
+ error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY);
if (error)
return error;
+
} else if (handle->prev <= nr_meta_pages) {
- handle->pbe = unpack_orig_addresses(buffer,
- handle->pbe);
- if (!handle->pbe) {
- error = prepare_image(handle);
+ unpack_orig_pfns(buffer, ©_bm);
+ if (handle->prev == nr_meta_pages) {
+ error = prepare_image(&orig_bm, ©_bm);
if (error)
return error;
- handle->pbe = restore_pblist;
- handle->last_pbe = NULL;
- handle->buffer = get_buffer(handle);
+
+ chain_init(&ca, GFP_ATOMIC, PG_SAFE);
+ memory_bm_position_reset(&orig_bm);
+ restore_pblist = NULL;
handle->sync_read = 0;
+ handle->buffer = get_buffer(&orig_bm, &ca);
+ if (!handle->buffer)
+ return -ENOMEM;
}
} else {
- handle->pbe = handle->pbe->next;
- handle->buffer = get_buffer(handle);
- handle->sync_read = 0;
+ handle->buffer = get_buffer(&orig_bm, &ca);
}
handle->prev = handle->cur;
}
@@ -1362,6 +1302,13 @@ int snapshot_write_next(struct snapshot_
int snapshot_image_loaded(struct snapshot_handle *handle)
{
- return !(!handle->pbe || handle->pbe->next || !nr_copy_pages ||
- handle->cur <= nr_meta_pages + nr_copy_pages);
+ return !(!nr_copy_pages ||
+ handle->cur <= nr_meta_pages + nr_copy_pages);
+}
+
+void snapshot_free_unused_memory(struct snapshot_handle *handle)
+{
+ /* Free only if we have loaded the image entirely */
+ if (handle->prev && handle->cur > nr_meta_pages + nr_copy_pages)
+ memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR);
}
Index: linux-2.6.18-rc3-mm2/kernel/power/power.h
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/power.h 2006-08-10 09:20:53.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/power.h 2006-08-10 09:21:18.000000000 +0200
@@ -81,16 +81,6 @@ struct snapshot_handle {
unsigned int prev; /* number of the block of PAGE_SIZE bytes that
* was the current one previously
*/
- struct pbe *pbe; /* PBE that corresponds to 'buffer' */
- struct pbe *last_pbe; /* When the image is restored (eg. read
- * from disk) we can store some image
- * data directly in the page frames
- * in which they were before suspend.
- * In such a case the PBEs that
- * correspond to them will be unused.
- * This is the last PBE, so far, that
- * does not correspond to such data.
- */
void *buffer; /* address of the block to read from
* or write to
*/
@@ -113,6 +103,7 @@ extern unsigned int snapshot_additional_
extern int snapshot_read_next(struct snapshot_handle *handle, size_t count);
extern int snapshot_write_next(struct snapshot_handle *handle, size_t count);
extern int snapshot_image_loaded(struct snapshot_handle *handle);
+extern void snapshot_free_unused_memory(struct snapshot_handle *handle);
#define SNAPSHOT_IOC_MAGIC '3'
#define SNAPSHOT_FREEZE _IO(SNAPSHOT_IOC_MAGIC, 1)
Index: linux-2.6.18-rc3-mm2/kernel/power/swap.c
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/swap.c 2006-08-10 09:20:53.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/swap.c 2006-08-10 09:21:18.000000000 +0200
@@ -332,8 +332,7 @@ static int enough_swap(unsigned int nr_p
unsigned int free_swap = count_swap_pages(root_swap, 1);
pr_debug("swsusp: free swap pages: %u\n", free_swap);
- return free_swap > (nr_pages + PAGES_FOR_IO +
- (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
+ return free_swap > nr_pages + PAGES_FOR_IO;
}
/**
@@ -548,6 +547,7 @@ static int load_image(struct swap_map_ha
error = err2;
if (!error) {
printk("\b\b\b\bdone\n");
+ snapshot_free_unused_memory(snapshot);
if (!snapshot_image_loaded(snapshot))
error = -ENODATA;
}
Index: linux-2.6.18-rc3-mm2/kernel/power/user.c
===================================================================
--- linux-2.6.18-rc3-mm2.orig/kernel/power/user.c 2006-08-10 09:20:53.000000000 +0200
+++ linux-2.6.18-rc3-mm2/kernel/power/user.c 2006-08-10 09:21:18.000000000 +0200
@@ -193,6 +193,7 @@ static int snapshot_ioctl(struct inode *
error = -EPERM;
break;
}
+ snapshot_free_unused_memory(&data->handle);
down(&pm_sem);
pm_prepare_console();
error = device_suspend(PMSG_PRETHAW);
Index: linux-2.6.18-rc3-mm2/include/linux/suspend.h
===================================================================
--- linux-2.6.18-rc3-mm2.orig/include/linux/suspend.h 2006-08-10 09:20:53.000000000 +0200
+++ linux-2.6.18-rc3-mm2/include/linux/suspend.h 2006-08-10 09:21:18.000000000 +0200
@@ -16,15 +16,6 @@ struct pbe {
struct pbe *next;
};
-#define for_each_pbe(pbe, pblist) \
- for (pbe = pblist ; pbe ; pbe = pbe->next)
-
-#define PBES_PER_PAGE (PAGE_SIZE/sizeof(struct pbe))
-#define PB_PAGE_SKIP (PBES_PER_PAGE-1)
-
-#define for_each_pb_page(pbe, pblist) \
- for (pbe = pblist ; pbe ; pbe = (pbe+PB_PAGE_SKIP)->next)
-
/* mm/page_alloc.c */
extern void drain_local_pages(void);
extern void mark_free_pages(struct zone *zone);
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH -mm 5/5] swsusp: Use memory bitmaps during resume
2006-08-10 13:23 ` [PATCH -mm 5/5] swsusp: Use memory bitmaps during resume Rafael J. Wysocki
@ 2006-08-13 22:04 ` Andrew Morton
2006-08-14 9:00 ` Rafael J. Wysocki
0 siblings, 1 reply; 8+ messages in thread
From: Andrew Morton @ 2006-08-13 22:04 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: LKML, Pavel Machek
On Thu, 10 Aug 2006 15:23:41 +0200
"Rafael J. Wysocki" <rjw@sisk.pl> wrote:
> Make swsusp use memory bitmaps to store its internal information during the
> resume phase of the suspend-resume cycle.
This patch makes the resume-time disk IO go all slow again.
Time to read 80k pages:
2.6.18-rc4: 24 seconds
2.6.18-rc4+akpm-speedups: 10 seconds
2.6.18-rc4+akpm-speedups+this-patch: 24 seconds
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH -mm 5/5] swsusp: Use memory bitmaps during resume
2006-08-13 22:04 ` Andrew Morton
@ 2006-08-14 9:00 ` Rafael J. Wysocki
0 siblings, 0 replies; 8+ messages in thread
From: Rafael J. Wysocki @ 2006-08-14 9:00 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, Pavel Machek
On Monday 14 August 2006 00:04, Andrew Morton wrote:
> On Thu, 10 Aug 2006 15:23:41 +0200
> "Rafael J. Wysocki" <rjw@sisk.pl> wrote:
>
> > Make swsusp use memory bitmaps to store its internal information during the
> > resume phase of the suspend-resume cycle.
>
> This patch makes the resume-time disk IO go all slow again.
>
> Time to read 80k pages:
>
> 2.6.18-rc4: 24 seconds
> 2.6.18-rc4+akpm-speedups: 10 seconds
> 2.6.18-rc4+akpm-speedups+this-patch: 24 seconds
Well, I removed one line too many, sorry.
kernel/power/snapshot.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletion(-)
Index: linux-2.6.18-rc4-mm1/kernel/power/snapshot.c
===================================================================
--- linux-2.6.18-rc4-mm1.orig/kernel/power/snapshot.c
+++ linux-2.6.18-rc4-mm1/kernel/power/snapshot.c
@@ -1278,13 +1278,14 @@ int snapshot_write_next(struct snapshot_
chain_init(&ca, GFP_ATOMIC, PG_SAFE);
memory_bm_position_reset(&orig_bm);
restore_pblist = NULL;
- handle->sync_read = 0;
handle->buffer = get_buffer(&orig_bm, &ca);
+ handle->sync_read = 0;
if (!handle->buffer)
return -ENOMEM;
}
} else {
handle->buffer = get_buffer(&orig_bm, &ca);
+ handle->sync_read = 0;
}
handle->prev = handle->cur;
}
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2006-08-14 9:02 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-08-10 13:10 [PATCH -mm 0/5] Cleanups and improvements related to swsusp Rafael J. Wysocki
2006-08-10 13:12 ` [PATCH -mm 1/5] Clean up suspend header Rafael J. Wysocki
2006-08-10 13:16 ` [PATCH -mm 2/5] Change the name of pagedir_nosave Rafael J. Wysocki
2006-08-10 13:18 ` [PATCH -mm 3/5] swsusp: Introduce some helpful constants Rafael J. Wysocki
2006-08-10 13:21 ` [PATCH -mm 4/5] swsusp: Introduce memory bitmaps Rafael J. Wysocki
2006-08-10 13:23 ` [PATCH -mm 5/5] swsusp: Use memory bitmaps during resume Rafael J. Wysocki
2006-08-13 22:04 ` Andrew Morton
2006-08-14 9:00 ` Rafael J. Wysocki
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®