diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/power/power.h | 27 | ||||
-rw-r--r-- | kernel/power/swap.c | 18 | ||||
-rw-r--r-- | kernel/power/swsusp.c | 137 | ||||
-rw-r--r-- | kernel/power/user.c | 22 |
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. */ |
144 | extern atomic_t snapshot_device_available; | 144 | extern atomic_t snapshot_device_available; |
145 | 145 | ||
146 | /** | 146 | extern sector_t alloc_swapdev_block(int swap); |
147 | * The bitmap is used for tracing allocated swap pages | 147 | extern void free_all_swap_pages(int swap); |
148 | * | 148 | extern 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 | |||
161 | struct bitmap_page { | ||
162 | unsigned long chunks[BITMAP_PAGE_CHUNKS]; | ||
163 | struct bitmap_page *next; | ||
164 | }; | ||
165 | |||
166 | extern void free_bitmap(struct bitmap_page *bitmap); | ||
167 | extern struct bitmap_page *alloc_bitmap(unsigned int nr_bits); | ||
168 | extern sector_t alloc_swapdev_block(int swap, struct bitmap_page *bitmap); | ||
169 | extern void free_all_swap_pages(int swap, struct bitmap_page *bitmap); | ||
170 | 149 | ||
171 | extern int swsusp_check(void); | 150 | extern int swsusp_check(void); |
172 | extern int swsusp_shrink_memory(void); | 151 | extern 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 { | |||
243 | struct swap_map_handle { | 243 | struct 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 | ||
260 | static int get_swap_writer(struct swap_map_handle *handle) | 256 | static 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 | ||
82 | void free_bitmap(struct bitmap_page *bitmap) | 80 | struct 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) { | 86 | static struct rb_root swsusp_extents = RB_ROOT; |
87 | bp = bitmap->next; | ||
88 | free_page((unsigned long)bitmap); | ||
89 | bitmap = bp; | ||
90 | } | ||
91 | } | ||
92 | 87 | ||
93 | struct bitmap_page *alloc_bitmap(unsigned int nr_bits) | 88 | static 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) | |
114 | static 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 | ||
136 | sector_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 | |||
134 | sector_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 | ||
150 | void 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 | |||
154 | void 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 | ||
171 | int 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 @@ | |||
33 | static struct snapshot_data { | 33 | static 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; |