diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2007-05-06 17:50:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-07 15:12:59 -0400 |
commit | d1d241cc2c5feec057c370aa71637380b1b945d5 (patch) | |
tree | 69c07d4c7a8b52b9ee6efba1511f3b4f8f79e6bb /kernel/power/swsusp.c | |
parent | 726162b5dad154a90dad51c0185b891312de5757 (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>
Diffstat (limited to 'kernel/power/swsusp.c')
-rw-r--r-- | kernel/power/swsusp.c | 137 |
1 files changed, 73 insertions, 64 deletions
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 |