aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2007-05-06 17:50:47 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-07 15:12:59 -0400
commitd1d241cc2c5feec057c370aa71637380b1b945d5 (patch)
tree69c07d4c7a8b52b9ee6efba1511f3b4f8f79e6bb
parent726162b5dad154a90dad51c0185b891312de5757 (diff)
swsusp: use rbtree for tracking allocated swap
Make swsusp use extents instead of a bitmap to trace swap pages allocated for saving the image (the tracking is only needed in case there's an error, so that the allocated swap pages can be released). This should allow us to reduce the memory usage, practically always, and improve performance. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Cc: Nigel Cunningham <nigel@nigel.suspend2.net> Cc: Pavel Machek <pavel@ucw.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--kernel/power/power.h27
-rw-r--r--kernel/power/swap.c18
-rw-r--r--kernel/power/swsusp.c137
-rw-r--r--kernel/power/user.c22
4 files changed, 86 insertions, 118 deletions
diff --git a/kernel/power/power.h b/kernel/power/power.h
index a64d3f22de97..a3e47cbdaf31 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -143,30 +143,9 @@ struct resume_swap_area {
143/* If unset, the snapshot device cannot be open. */ 143/* If unset, the snapshot device cannot be open. */
144extern atomic_t snapshot_device_available; 144extern atomic_t snapshot_device_available;
145 145
146/** 146extern sector_t alloc_swapdev_block(int swap);
147 * The bitmap is used for tracing allocated swap pages 147extern void free_all_swap_pages(int swap);
148 * 148extern int swsusp_swap_in_use(void);
149 * The entire bitmap consists of a number of bitmap_page
150 * structures linked with the help of the .next member.
151 * Thus each page can be allocated individually, so we only
152 * need to make 0-order memory allocations to create
153 * the bitmap.
154 */
155
156#define BITMAP_PAGE_SIZE (PAGE_SIZE - sizeof(void *))
157#define BITMAP_PAGE_CHUNKS (BITMAP_PAGE_SIZE / sizeof(long))
158#define BITS_PER_CHUNK (sizeof(long) * 8)
159#define BITMAP_PAGE_BITS (BITMAP_PAGE_CHUNKS * BITS_PER_CHUNK)
160
161struct bitmap_page {
162 unsigned long chunks[BITMAP_PAGE_CHUNKS];
163 struct bitmap_page *next;
164};
165
166extern void free_bitmap(struct bitmap_page *bitmap);
167extern struct bitmap_page *alloc_bitmap(unsigned int nr_bits);
168extern sector_t alloc_swapdev_block(int swap, struct bitmap_page *bitmap);
169extern void free_all_swap_pages(int swap, struct bitmap_page *bitmap);
170 149
171extern int swsusp_check(void); 150extern int swsusp_check(void);
172extern int swsusp_shrink_memory(void); 151extern int swsusp_shrink_memory(void);
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index b18c155cbb60..e83ed9945a80 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -243,7 +243,6 @@ struct swap_map_page {
243struct swap_map_handle { 243struct swap_map_handle {
244 struct swap_map_page *cur; 244 struct swap_map_page *cur;
245 sector_t cur_swap; 245 sector_t cur_swap;
246 struct bitmap_page *bitmap;
247 unsigned int k; 246 unsigned int k;
248}; 247};
249 248
@@ -252,9 +251,6 @@ static void release_swap_writer(struct swap_map_handle *handle)
252 if (handle->cur) 251 if (handle->cur)
253 free_page((unsigned long)handle->cur); 252 free_page((unsigned long)handle->cur);
254 handle->cur = NULL; 253 handle->cur = NULL;
255 if (handle->bitmap)
256 free_bitmap(handle->bitmap);
257 handle->bitmap = NULL;
258} 254}
259 255
260static int get_swap_writer(struct swap_map_handle *handle) 256static int get_swap_writer(struct swap_map_handle *handle)
@@ -262,12 +258,7 @@ static int get_swap_writer(struct swap_map_handle *handle)
262 handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL); 258 handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
263 if (!handle->cur) 259 if (!handle->cur)
264 return -ENOMEM; 260 return -ENOMEM;
265 handle->bitmap = alloc_bitmap(count_swap_pages(root_swap, 0)); 261 handle->cur_swap = alloc_swapdev_block(root_swap);
266 if (!handle->bitmap) {
267 release_swap_writer(handle);
268 return -ENOMEM;
269 }
270 handle->cur_swap = alloc_swapdev_block(root_swap, handle->bitmap);
271 if (!handle->cur_swap) { 262 if (!handle->cur_swap) {
272 release_swap_writer(handle); 263 release_swap_writer(handle);
273 return -ENOSPC; 264 return -ENOSPC;
@@ -284,7 +275,7 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf,
284 275
285 if (!handle->cur) 276 if (!handle->cur)
286 return -EINVAL; 277 return -EINVAL;
287 offset = alloc_swapdev_block(root_swap, handle->bitmap); 278 offset = alloc_swapdev_block(root_swap);
288 error = write_page(buf, offset, bio_chain); 279 error = write_page(buf, offset, bio_chain);
289 if (error) 280 if (error)
290 return error; 281 return error;
@@ -293,7 +284,7 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf,
293 error = wait_on_bio_chain(bio_chain); 284 error = wait_on_bio_chain(bio_chain);
294 if (error) 285 if (error)
295 goto out; 286 goto out;
296 offset = alloc_swapdev_block(root_swap, handle->bitmap); 287 offset = alloc_swapdev_block(root_swap);
297 if (!offset) 288 if (!offset)
298 return -ENOSPC; 289 return -ENOSPC;
299 handle->cur->next_swap = offset; 290 handle->cur->next_swap = offset;
@@ -430,7 +421,8 @@ int swsusp_write(void)
430 } 421 }
431 } 422 }
432 if (error) 423 if (error)
433 free_all_swap_pages(root_swap, handle.bitmap); 424 free_all_swap_pages(root_swap);
425
434 release_swap_writer(&handle); 426 release_swap_writer(&handle);
435 out: 427 out:
436 swsusp_close(); 428 swsusp_close();
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c
index 175370824f37..1109023d8358 100644
--- a/kernel/power/swsusp.c
+++ b/kernel/power/swsusp.c
@@ -50,6 +50,7 @@
50#include <linux/syscalls.h> 50#include <linux/syscalls.h>
51#include <linux/highmem.h> 51#include <linux/highmem.h>
52#include <linux/time.h> 52#include <linux/time.h>
53#include <linux/rbtree.h>
53 54
54#include "power.h" 55#include "power.h"
55 56
@@ -74,72 +75,69 @@ static inline unsigned int count_highmem_pages(void) { return 0; }
74/** 75/**
75 * The following functions are used for tracing the allocated 76 * The following functions are used for tracing the allocated
76 * swap pages, so that they can be freed in case of an error. 77 * swap pages, so that they can be freed in case of an error.
77 *
78 * The functions operate on a linked bitmap structure defined
79 * in power.h
80 */ 78 */
81 79
82void free_bitmap(struct bitmap_page *bitmap) 80struct swsusp_extent {
83{ 81 struct rb_node node;
84 struct bitmap_page *bp; 82 unsigned long start;
83 unsigned long end;
84};
85 85
86 while (bitmap) { 86static struct rb_root swsusp_extents = RB_ROOT;
87 bp = bitmap->next;
88 free_page((unsigned long)bitmap);
89 bitmap = bp;
90 }
91}
92 87
93struct bitmap_page *alloc_bitmap(unsigned int nr_bits) 88static int swsusp_extents_insert(unsigned long swap_offset)
94{ 89{
95 struct bitmap_page *bitmap, *bp; 90 struct rb_node **new = &(swsusp_extents.rb_node);
96 unsigned int n; 91 struct rb_node *parent = NULL;
97 92 struct swsusp_extent *ext;
98 if (!nr_bits) 93
99 return NULL; 94 /* Figure out where to put the new node */
100 95 while (*new) {
101 bitmap = (struct bitmap_page *)get_zeroed_page(GFP_KERNEL); 96 ext = container_of(*new, struct swsusp_extent, node);
102 bp = bitmap; 97 parent = *new;
103 for (n = BITMAP_PAGE_BITS; n < nr_bits; n += BITMAP_PAGE_BITS) { 98 if (swap_offset < ext->start) {
104 bp->next = (struct bitmap_page *)get_zeroed_page(GFP_KERNEL); 99 /* Try to merge */
105 bp = bp->next; 100 if (swap_offset == ext->start - 1) {
106 if (!bp) { 101 ext->start--;
107 free_bitmap(bitmap); 102 return 0;
108 return NULL; 103 }
104 new = &((*new)->rb_left);
105 } else if (swap_offset > ext->end) {
106 /* Try to merge */
107 if (swap_offset == ext->end + 1) {
108 ext->end++;
109 return 0;
110 }
111 new = &((*new)->rb_right);
112 } else {
113 /* It already is in the tree */
114 return -EINVAL;
109 } 115 }
110 } 116 }
111 return bitmap; 117 /* Add the new node and rebalance the tree. */
112} 118 ext = kzalloc(sizeof(struct swsusp_extent), GFP_KERNEL);
113 119 if (!ext)
114static int bitmap_set(struct bitmap_page *bitmap, unsigned long bit) 120 return -ENOMEM;
115{ 121
116 unsigned int n; 122 ext->start = swap_offset;
117 123 ext->end = swap_offset;
118 n = BITMAP_PAGE_BITS; 124 rb_link_node(&ext->node, parent, new);
119 while (bitmap && n <= bit) { 125 rb_insert_color(&ext->node, &swsusp_extents);
120 n += BITMAP_PAGE_BITS;
121 bitmap = bitmap->next;
122 }
123 if (!bitmap)
124 return -EINVAL;
125 n -= BITMAP_PAGE_BITS;
126 bit -= n;
127 n = 0;
128 while (bit >= BITS_PER_CHUNK) {
129 bit -= BITS_PER_CHUNK;
130 n++;
131 }
132 bitmap->chunks[n] |= (1UL << bit);
133 return 0; 126 return 0;
134} 127}
135 128
136sector_t alloc_swapdev_block(int swap, struct bitmap_page *bitmap) 129/**
130 * alloc_swapdev_block - allocate a swap page and register that it has
131 * been allocated, so that it can be freed in case of an error.
132 */
133
134sector_t alloc_swapdev_block(int swap)
137{ 135{
138 unsigned long offset; 136 unsigned long offset;
139 137
140 offset = swp_offset(get_swap_page_of_type(swap)); 138 offset = swp_offset(get_swap_page_of_type(swap));
141 if (offset) { 139 if (offset) {
142 if (bitmap_set(bitmap, offset)) 140 if (swsusp_extents_insert(offset))
143 swap_free(swp_entry(swap, offset)); 141 swap_free(swp_entry(swap, offset));
144 else 142 else
145 return swapdev_block(swap, offset); 143 return swapdev_block(swap, offset);
@@ -147,23 +145,34 @@ sector_t alloc_swapdev_block(int swap, struct bitmap_page *bitmap)
147 return 0; 145 return 0;
148} 146}
149 147
150void free_all_swap_pages(int swap, struct bitmap_page *bitmap) 148/**
149 * free_all_swap_pages - free swap pages allocated for saving image data.
150 * It also frees the extents used to register which swap entres had been
151 * allocated.
152 */
153
154void free_all_swap_pages(int swap)
151{ 155{
152 unsigned int bit, n; 156 struct rb_node *node;
153 unsigned long test; 157
154 158 while ((node = swsusp_extents.rb_node)) {
155 bit = 0; 159 struct swsusp_extent *ext;
156 while (bitmap) { 160 unsigned long offset;
157 for (n = 0; n < BITMAP_PAGE_CHUNKS; n++) 161
158 for (test = 1UL; test; test <<= 1) { 162 ext = container_of(node, struct swsusp_extent, node);
159 if (bitmap->chunks[n] & test) 163 rb_erase(node, &swsusp_extents);
160 swap_free(swp_entry(swap, bit)); 164 for (offset = ext->start; offset <= ext->end; offset++)
161 bit++; 165 swap_free(swp_entry(swap, offset));
162 } 166
163 bitmap = bitmap->next; 167 kfree(ext);
164 } 168 }
165} 169}
166 170
171int swsusp_swap_in_use(void)
172{
173 return (swsusp_extents.rb_node != NULL);
174}
175
167/** 176/**
168 * swsusp_show_speed - print the time elapsed between two events represented by 177 * swsusp_show_speed - print the time elapsed between two events represented by
169 * @start and @stop 178 * @start and @stop
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 72dbfd01408e..ad4e10208cde 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -33,7 +33,6 @@
33static struct snapshot_data { 33static struct snapshot_data {
34 struct snapshot_handle handle; 34 struct snapshot_handle handle;
35 int swap; 35 int swap;
36 struct bitmap_page *bitmap;
37 int mode; 36 int mode;
38 char frozen; 37 char frozen;
39 char ready; 38 char ready;
@@ -69,7 +68,6 @@ static int snapshot_open(struct inode *inode, struct file *filp)
69 data->swap = -1; 68 data->swap = -1;
70 data->mode = O_WRONLY; 69 data->mode = O_WRONLY;
71 } 70 }
72 data->bitmap = NULL;
73 data->frozen = 0; 71 data->frozen = 0;
74 data->ready = 0; 72 data->ready = 0;
75 data->platform_suspend = 0; 73 data->platform_suspend = 0;
@@ -84,8 +82,7 @@ static int snapshot_release(struct inode *inode, struct file *filp)
84 swsusp_free(); 82 swsusp_free();
85 free_basic_memory_bitmaps(); 83 free_basic_memory_bitmaps();
86 data = filp->private_data; 84 data = filp->private_data;
87 free_all_swap_pages(data->swap, data->bitmap); 85 free_all_swap_pages(data->swap);
88 free_bitmap(data->bitmap);
89 if (data->frozen) { 86 if (data->frozen) {
90 mutex_lock(&pm_mutex); 87 mutex_lock(&pm_mutex);
91 thaw_processes(); 88 thaw_processes();
@@ -300,14 +297,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
300 error = -ENODEV; 297 error = -ENODEV;
301 break; 298 break;
302 } 299 }
303 if (!data->bitmap) { 300 offset = alloc_swapdev_block(data->swap);
304 data->bitmap = alloc_bitmap(count_swap_pages(data->swap, 0));
305 if (!data->bitmap) {
306 error = -ENOMEM;
307 break;
308 }
309 }
310 offset = alloc_swapdev_block(data->swap, data->bitmap);
311 if (offset) { 301 if (offset) {
312 offset <<= PAGE_SHIFT; 302 offset <<= PAGE_SHIFT;
313 error = put_user(offset, (sector_t __user *)arg); 303 error = put_user(offset, (sector_t __user *)arg);
@@ -321,13 +311,11 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
321 error = -ENODEV; 311 error = -ENODEV;
322 break; 312 break;
323 } 313 }
324 free_all_swap_pages(data->swap, data->bitmap); 314 free_all_swap_pages(data->swap);
325 free_bitmap(data->bitmap);
326 data->bitmap = NULL;
327 break; 315 break;
328 316
329 case SNAPSHOT_SET_SWAP_FILE: 317 case SNAPSHOT_SET_SWAP_FILE:
330 if (!data->bitmap) { 318 if (!swsusp_swap_in_use()) {
331 /* 319 /*
332 * User space encodes device types as two-byte values, 320 * User space encodes device types as two-byte values,
333 * so we need to recode them 321 * so we need to recode them
@@ -426,7 +414,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
426 break; 414 break;
427 415
428 case SNAPSHOT_SET_SWAP_AREA: 416 case SNAPSHOT_SET_SWAP_AREA:
429 if (data->bitmap) { 417 if (swsusp_swap_in_use()) {
430 error = -EPERM; 418 error = -EPERM;
431 } else { 419 } else {
432 struct resume_swap_area swap_area; 420 struct resume_swap_area swap_area;