2 * kernel/power/extent.c
4 * Copyright (C) 2003-2007 Nigel Cunningham (nigel at suspend2 net)
6 * Distributed under GPLv2.
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>
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
)))
30 result
->minimum
= result
->maximum
= 0;
36 /* suspend_put_extent_chain.
38 * Frees a whole chain of extents.
40 void suspend_put_extent_chain(struct extent_chain
*chain
)
47 struct extent
*next
= this->next
;
53 chain
->first
= chain
->last_touched
= NULL
;
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? */
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
;
91 chain
->last_touched
= start_at
;
92 chain
->size
+= (maximum
- minimum
+ 1);
97 new_extent
= suspend_get_extent();
99 printk("Error unable to append a new extent to the chain.\n");
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
;
112 struct extent
*next
= start_at
->next
;
113 start_at
->next
= new_extent
;
114 new_extent
->next
= next
;
117 new_extent
->next
= chain
->first
;
118 chain
->first
= new_extent
;
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
)
134 if ((ret
= suspendActiveAllocator
->rw_header_chunk(WRITE
, owner
,
141 if ((ret
= suspendActiveAllocator
->rw_header_chunk(WRITE
, owner
,
143 2 * sizeof(unsigned long))))
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
);
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
;
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");
173 for (i
= 0; i
< chain
->num_extents
; i
++) {
174 this = kmalloc(sizeof(struct extent
), GFP_ATOMIC
);
176 printk("Failed to allocate a new extent.\n");
180 if ((ret
= suspendActiveAllocator
->rw_header_chunk(READ
, NULL
,
181 (char *) this, 2 * sizeof(unsigned long)))) {
182 printk("Failed to an extent.\n");
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
)
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
;
213 state
->current_extent
= NULL
;
214 state
->current_offset
= 0;
217 state
->current_offset
++;
220 while(!state
->current_extent
) {
221 int chain_num
= ++(state
->current_chain
);
223 if (chain_num
== state
->num_chains
)
226 state
->current_extent
= (state
->chains
+ chain_num
)->first
;
228 if (!state
->current_extent
)
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
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)
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
);
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
;
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
);