2.2.9.17 release.
[linux-2.6/suspend2-2.6.18.git] / kernel / power / extent.c
blobcd0ff0ca598cca5e84fbe5f9440c68cbe06ea76a
1 /*
2 * kernel/power/extent.c
3 *
4 * Copyright (C) 2003-2007 Nigel Cunningham (nigel at suspend2 net)
6 * Distributed under GPLv2.
7 *
8 * These functions encapsulate the manipulation of storage metadata. For
9 * pageflags, we use dynamically allocated bitmaps.
12 #include <linux/module.h>
13 #include <linux/suspend.h>
14 #include "modules.h"
15 #include "extent.h"
16 #include "ui.h"
17 #include "suspend.h"
19 /* suspend_get_extent
21 * Returns a free extent. May fail, returning NULL instead.
23 static struct extent *suspend_get_extent(void)
25 struct extent *result;
27 if (!(result = kmalloc(sizeof(struct extent), GFP_ATOMIC)))
28 return NULL;
30 result->minimum = result->maximum = 0;
31 result->next = NULL;
33 return result;
36 /* suspend_put_extent_chain.
38 * Frees a whole chain of extents.
40 void suspend_put_extent_chain(struct extent_chain *chain)
42 struct extent *this;
44 this = chain->first;
46 while(this) {
47 struct extent *next = this->next;
48 kfree(this);
49 chain->num_extents--;
50 this = next;
53 chain->first = chain->last_touched = NULL;
54 chain->size = 0;
57 /*
58 * suspend_add_to_extent_chain
60 * Add an extent to an existing chain.
62 int suspend_add_to_extent_chain(struct extent_chain *chain,
63 unsigned long minimum, unsigned long maximum)
65 struct extent *new_extent = NULL, *start_at;
67 /* Find the right place in the chain */
68 start_at = (chain->last_touched &&
69 (chain->last_touched->minimum < minimum)) ?
70 chain->last_touched : NULL;
72 if (!start_at && chain->first && chain->first->minimum < minimum)
73 start_at = chain->first;
75 while (start_at && start_at->next && start_at->next->minimum < minimum)
76 start_at = start_at->next;
78 if (start_at && start_at->maximum == (minimum - 1)) {
79 start_at->maximum = maximum;
81 /* Merge with the following one? */
82 if (start_at->next &&
83 start_at->maximum + 1 == start_at->next->minimum) {
84 struct extent *to_free = start_at->next;
85 start_at->maximum = start_at->next->maximum;
86 start_at->next = start_at->next->next;
87 chain->num_extents--;
88 kfree(to_free);
91 chain->last_touched = start_at;
92 chain->size+= (maximum - minimum + 1);
94 return 0;
97 new_extent = suspend_get_extent();
98 if (!new_extent) {
99 printk("Error unable to append a new extent to the chain.\n");
100 return 2;
103 chain->num_extents++;
104 chain->size+= (maximum - minimum + 1);
105 new_extent->minimum = minimum;
106 new_extent->maximum = maximum;
107 new_extent->next = NULL;
109 chain->last_touched = new_extent;
111 if (start_at) {
112 struct extent *next = start_at->next;
113 start_at->next = new_extent;
114 new_extent->next = next;
115 } else {
116 if (chain->first)
117 new_extent->next = chain->first;
118 chain->first = new_extent;
121 return 0;
124 /* suspend_serialise_extent_chain
126 * Write a chain in the image.
128 int suspend_serialise_extent_chain(struct suspend_module_ops *owner,
129 struct extent_chain *chain)
131 struct extent *this;
132 int ret, i = 0;
134 if ((ret = suspendActiveAllocator->rw_header_chunk(WRITE, owner,
135 (char *) chain,
136 2 * sizeof(int))))
137 return ret;
139 this = chain->first;
140 while (this) {
141 if ((ret = suspendActiveAllocator->rw_header_chunk(WRITE, owner,
142 (char *) this,
143 2 * sizeof(unsigned long))))
144 return ret;
145 this = this->next;
146 i++;
149 if (i != chain->num_extents) {
150 printk(KERN_EMERG "Saved %d extents but chain metadata says there "
151 "should be %d.\n", i, chain->num_extents);
152 return 1;
155 return ret;
158 /* suspend_load_extent_chain
160 * Read back a chain saved in the image.
162 int suspend_load_extent_chain(struct extent_chain *chain)
164 struct extent *this, *last = NULL;
165 int i, ret;
167 if ((ret = suspendActiveAllocator->rw_header_chunk(READ, NULL,
168 (char *) chain, 2 * sizeof(int)))) {
169 printk("Failed to read size of extent chain.\n");
170 return 1;
173 for (i = 0; i < chain->num_extents; i++) {
174 this = kmalloc(sizeof(struct extent), GFP_ATOMIC);
175 if (!this) {
176 printk("Failed to allocate a new extent.\n");
177 return -ENOMEM;
179 this->next = NULL;
180 if ((ret = suspendActiveAllocator->rw_header_chunk(READ, NULL,
181 (char *) this, 2 * sizeof(unsigned long)))) {
182 printk("Failed to an extent.\n");
183 return 1;
185 if (last)
186 last->next = this;
187 else
188 chain->first = this;
189 last = this;
191 return 0;
194 /* suspend_extent_state_next
196 * Given a state, progress to the next valid entry. We may begin in an
197 * invalid state, as we do when invoked after extent_state_goto_start below.
199 * When using compression and expected_compression > 0, we let the image size
200 * be larger than storage, so we can validly run out of data to return.
202 unsigned long suspend_extent_state_next(struct extent_iterate_state *state)
204 if (state->current_chain == state->num_chains)
205 return 0;
207 if (state->current_extent) {
208 if (state->current_offset == state->current_extent->maximum) {
209 if (state->current_extent->next) {
210 state->current_extent = state->current_extent->next;
211 state->current_offset = state->current_extent->minimum;
212 } else {
213 state->current_extent = NULL;
214 state->current_offset = 0;
216 } else
217 state->current_offset++;
220 while(!state->current_extent) {
221 int chain_num = ++(state->current_chain);
223 if (chain_num == state->num_chains)
224 return 0;
226 state->current_extent = (state->chains + chain_num)->first;
228 if (!state->current_extent)
229 continue;
231 state->current_offset = state->current_extent->minimum;
234 return state->current_offset;
237 /* suspend_extent_state_goto_start
239 * Find the first valid value in a group of chains.
241 void suspend_extent_state_goto_start(struct extent_iterate_state *state)
243 state->current_chain = -1;
244 state->current_extent = NULL;
245 state->current_offset = 0;
248 /* suspend_extent_start_save
250 * Given a state and a struct extent_state_store, save the current
251 * position in a format that can be used with relocated chains (at
252 * resume time).
254 void suspend_extent_state_save(struct extent_iterate_state *state,
255 struct extent_iterate_saved_state *saved_state)
257 struct extent *extent;
259 saved_state->chain_num = state->current_chain;
260 saved_state->extent_num = 0;
261 saved_state->offset = state->current_offset;
263 if (saved_state->chain_num == -1)
264 return;
266 extent = (state->chains + state->current_chain)->first;
268 while (extent != state->current_extent) {
269 saved_state->extent_num++;
270 extent = extent->next;
274 /* suspend_extent_start_restore
276 * Restore the position saved by extent_state_save.
278 void suspend_extent_state_restore(struct extent_iterate_state *state,
279 struct extent_iterate_saved_state *saved_state)
281 int posn = saved_state->extent_num;
283 if (saved_state->chain_num == -1) {
284 suspend_extent_state_goto_start(state);
285 return;
288 state->current_chain = saved_state->chain_num;
289 state->current_extent = (state->chains + state->current_chain)->first;
290 state->current_offset = saved_state->offset;
292 while (posn--)
293 state->current_extent = state->current_extent->next;
296 #ifdef CONFIG_SUSPEND2_EXPORTS
297 EXPORT_SYMBOL_GPL(suspend_add_to_extent_chain);
298 EXPORT_SYMBOL_GPL(suspend_put_extent_chain);
299 EXPORT_SYMBOL_GPL(suspend_load_extent_chain);
300 EXPORT_SYMBOL_GPL(suspend_serialise_extent_chain);
301 EXPORT_SYMBOL_GPL(suspend_extent_state_save);
302 EXPORT_SYMBOL_GPL(suspend_extent_state_restore);
303 EXPORT_SYMBOL_GPL(suspend_extent_state_goto_start);
304 EXPORT_SYMBOL_GPL(suspend_extent_state_next);
305 #endif