]> git.hungrycats.org Git - linux/blob
1003993
[linux] /
1 /* 
2  * kernel/power/tuxonice_extent.c
3  * 
4  * Copyright (C) 2003-2007 Nigel Cunningham (nigel at tuxonice net)
5  *
6  * Distributed under GPLv2.
7  * 
8  * These functions encapsulate the manipulation of storage metadata. For
9  * pageflags, we use dynamically allocated bitmaps.
10  */
11
12 #include <linux/module.h>
13 #include <linux/suspend.h>
14 #include "tuxonice_modules.h"
15 #include "tuxonice_extent.h"
16 #include "tuxonice_ui.h"
17 #include "tuxonice.h"
18
19 /* toi_get_extent
20  *
21  * Returns a free extent. May fail, returning NULL instead.
22  */
23 static struct extent *toi_get_extent(void)
24 {
25         struct extent *result;
26         
27         if (!(result = toi_kmalloc(2, sizeof(struct extent), TOI_ATOMIC_GFP)))
28                 return NULL;
29
30         result->minimum = result->maximum = 0;
31         result->next = NULL;
32
33         return result;
34 }
35
36 /* toi_put_extent_chain.
37  *
38  * Frees a whole chain of extents.
39  */
40 void toi_put_extent_chain(struct extent_chain *chain)
41 {
42         struct extent *this;
43
44         this = chain->first;
45
46         while(this) {
47                 struct extent *next = this->next;
48                 kfree(this);
49                 chain->num_extents--;
50                 this = next;
51         }
52         
53         chain->first = chain->last_touched = NULL;
54         chain->size = 0;
55 }
56
57 /* 
58  * toi_add_to_extent_chain
59  *
60  * Add an extent to an existing chain.
61  */
62 int toi_add_to_extent_chain(struct extent_chain *chain, 
63                 unsigned long minimum, unsigned long maximum)
64 {
65         struct extent *new_extent = NULL, *start_at;
66
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;
71
72         if (!start_at && chain->first && chain->first->minimum < minimum)
73                 start_at = chain->first;
74
75         while (start_at && start_at->next && start_at->next->minimum < minimum)
76                 start_at = start_at->next;
77
78         if (start_at && start_at->maximum == (minimum - 1)) {
79                 start_at->maximum = maximum;
80
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);
89                 }
90
91                 chain->last_touched = start_at;
92                 chain->size+= (maximum - minimum + 1);
93
94                 return 0;
95         }
96
97         new_extent = toi_get_extent();
98         if (!new_extent) {
99                 printk("Error unable to append a new extent to the chain.\n");
100                 return 2;
101         }
102
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;
108
109         chain->last_touched = new_extent;
110
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;
119         }
120
121         return 0;
122 }
123
124 /* toi_serialise_extent_chain
125  *
126  * Write a chain in the image.
127  */
128 int toi_serialise_extent_chain(struct toi_module_ops *owner,
129                 struct extent_chain *chain)
130 {
131         struct extent *this;
132         int ret, i = 0;
133         
134         if ((ret = toiActiveAllocator->rw_header_chunk(WRITE, owner,
135                 (char *) chain,
136                 2 * sizeof(int))))
137                 return ret;
138
139         this = chain->first;
140         while (this) {
141                 if ((ret = toiActiveAllocator->rw_header_chunk(WRITE, owner,
142                                 (char *) this,
143                                 2 * sizeof(unsigned long))))
144                         return ret;
145                 this = this->next;
146                 i++;
147         }
148
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;
153         }
154
155         return ret;
156 }
157
158 /* toi_load_extent_chain
159  *
160  * Read back a chain saved in the image.
161  */
162 int toi_load_extent_chain(struct extent_chain *chain)
163 {
164         struct extent *this, *last = NULL;
165         int i, ret;
166
167         if ((ret = toiActiveAllocator->rw_header_chunk(READ, NULL,
168                 (char *) chain, 2 * sizeof(int)))) {
169                 printk("Failed to read size of extent chain.\n");
170                 return 1;
171         }
172
173         for (i = 0; i < chain->num_extents; i++) {
174                 this = toi_kmalloc(3, sizeof(struct extent), TOI_ATOMIC_GFP);
175                 if (!this) {
176                         printk("Failed to allocate a new extent.\n");
177                         return -ENOMEM;
178                 }
179                 this->next = NULL;
180                 if ((ret = toiActiveAllocator->rw_header_chunk(READ, NULL,
181                                 (char *) this, 2 * sizeof(unsigned long)))) {
182                         printk("Failed to an extent.\n");
183                         return 1;
184                 }
185                 if (last)
186                         last->next = this;
187                 else
188                         chain->first = this;
189                 last = this;
190         }
191         return 0;
192 }
193
194 /* toi_extent_state_next
195  *
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.
198  *
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.
201  */
202 unsigned long toi_extent_state_next(struct extent_iterate_state *state)
203 {
204         if (state->current_chain == state->num_chains)
205                 return 0;
206
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;
215                         }
216                 } else
217                         state->current_offset++;
218         }
219
220         while(!state->current_extent) {
221                 int chain_num = ++(state->current_chain);
222
223                 if (chain_num == state->num_chains)
224                         return 0;
225
226                 state->current_extent = (state->chains + chain_num)->first;
227
228                 if (!state->current_extent)
229                         continue;
230
231                 state->current_offset = state->current_extent->minimum;
232         }
233
234         return state->current_offset;
235 }
236
237 /* toi_extent_state_goto_start
238  *
239  * Find the first valid value in a group of chains.
240  */
241 void toi_extent_state_goto_start(struct extent_iterate_state *state)
242 {
243         state->current_chain = -1;
244         state->current_extent = NULL;
245         state->current_offset = 0;
246 }
247
248 /* toi_extent_start_save
249  *
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).
253  */
254 void toi_extent_state_save(struct extent_iterate_state *state,
255                 struct extent_iterate_saved_state *saved_state)
256 {
257         struct extent *extent;
258
259         saved_state->chain_num = state->current_chain;
260         saved_state->extent_num = 0;
261         saved_state->offset = state->current_offset;
262
263         if (saved_state->chain_num == -1)
264                 return;
265         
266         extent = (state->chains + state->current_chain)->first;
267
268         while (extent != state->current_extent) {
269                 saved_state->extent_num++;
270                 extent = extent->next;
271         }
272 }
273
274 /* toi_extent_start_restore
275  *
276  * Restore the position saved by extent_state_save.
277  */
278 void toi_extent_state_restore(struct extent_iterate_state *state,
279                 struct extent_iterate_saved_state *saved_state)
280 {
281         int posn = saved_state->extent_num;
282
283         if (saved_state->chain_num == -1) {
284                 toi_extent_state_goto_start(state);
285                 return;
286         }
287
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;
291
292         while (posn--)
293                 state->current_extent = state->current_extent->next;
294 }
295
296 #ifdef CONFIG_TOI_EXPORTS
297 EXPORT_SYMBOL_GPL(toi_add_to_extent_chain);
298 EXPORT_SYMBOL_GPL(toi_put_extent_chain);
299 EXPORT_SYMBOL_GPL(toi_load_extent_chain);
300 EXPORT_SYMBOL_GPL(toi_serialise_extent_chain);
301 EXPORT_SYMBOL_GPL(toi_extent_state_save);
302 EXPORT_SYMBOL_GPL(toi_extent_state_restore);
303 EXPORT_SYMBOL_GPL(toi_extent_state_goto_start);
304 EXPORT_SYMBOL_GPL(toi_extent_state_next);
305 #endif