2 * kernel/power/incremental.c
4 * Copyright (C) 2012 Nigel Cunningham (nigel at tuxonice net)
6 * This file is released under the GPLv2.
8 * This file contains routines related to storing incremental images - that
9 * is, retaining an image after an initial cycle and then storing incremental
10 * changes on subsequent hibernations.
13 #include <linux/suspend.h>
14 #include <linux/highmem.h>
15 #include <linux/vmalloc.h>
16 #include <linux/crypto.h>
17 #include <linux/scatterlist.h>
19 #include "tuxonice_builtin.h"
21 #include "tuxonice_modules.h"
22 #include "tuxonice_sysfs.h"
23 #include "tuxonice_io.h"
24 #include "tuxonice_ui.h"
25 #include "tuxonice_alloc.h"
27 static struct toi_module_ops toi_incremental_ops
;
28 static struct toi_module_ops
*next_driver
;
29 static unsigned long toi_incremental_bytes_in
, toi_incremental_bytes_out
;
31 static char toi_incremental_slow_cmp_name
[32] = "sha1";
32 static int toi_incremental_digestsize
;
34 static DEFINE_MUTEX(stats_lock
);
36 struct toi_cpu_context
{
38 struct hash_desc desc
;
39 struct scatterlist sg
[1];
40 unsigned char *digest
;
43 #define OUT_BUF_SIZE (2 * PAGE_SIZE)
45 static DEFINE_PER_CPU(struct toi_cpu_context
, contexts
);
50 * Prepare to do some work by allocating buffers and transforms.
52 static int toi_incremental_crypto_prepare(void)
54 int cpu
, digestsize
= toi_incremental_digestsize
;
56 if (!*toi_incremental_slow_cmp_name
) {
57 printk(KERN_INFO
"TuxOnIce: Incremental image support enabled but no "
58 "hash algorithm set.\n");
62 for_each_online_cpu(cpu
) {
63 struct toi_cpu_context
*this = &per_cpu(contexts
, cpu
);
64 this->desc
.tfm
= crypto_alloc_hash(toi_incremental_slow_cmp_name
, 0, 0);
65 if (IS_ERR(this->desc
.tfm
)) {
66 printk(KERN_INFO
"TuxOnIce: Failed to initialise the "
67 "%s hashing transform.\n", toi_incremental_slow_cmp_name
);
68 this->desc
.tfm
= NULL
;
73 digestsize
= crypto_hash_digestsize(this->desc
.tfm
);
74 toi_incremental_digestsize
= digestsize
;
77 this->digest
= toi_kzalloc(16, digestsize
, GFP_KERNEL
);
81 this->desc
.flags
= CRYPTO_TFM_REQ_MAY_SLEEP
;
87 static int toi_incremental_rw_cleanup(int writing
)
91 for_each_online_cpu(cpu
) {
92 struct toi_cpu_context
*this = &per_cpu(contexts
, cpu
);
94 crypto_free_hash(this->desc
.tfm
);
95 this->desc
.tfm
= NULL
;
99 toi_kfree(16, this->digest
, toi_incremental_digestsize
);
108 * toi_incremental_init
111 static int toi_incremental_init(int hibernate_or_resume
)
113 if (!hibernate_or_resume
)
116 next_driver
= toi_get_next_filter(&toi_incremental_ops
);
118 return next_driver
? 0 : -ECHILD
;
122 * toi_incremental_rw_init()
125 static int toi_incremental_rw_init(int rw
, int stream_number
)
127 if (rw
== WRITE
&& toi_incremental_crypto_prepare()) {
128 printk(KERN_ERR
"Failed to initialise hashing " "algorithm.\n");
130 printk(KERN_INFO
"Unable to read the image.\n");
133 printk(KERN_INFO
"Continuing without "
134 " calculating an incremental image.\n");
135 toi_incremental_ops
.enabled
= 0;
143 * toi_incremental_write_page()
145 * Decide whether to write a page to the image. Calculate the SHA1 (or something
146 * else if the user changes the hashing algo) of the page and compare it to the
147 * previous value (if any). If there was no previous value or the values are
148 * different, write the page. Otherwise, skip the write.
150 * @TODO: Clear hashes for pages that are no longer in the image!
152 * Buffer_page: Pointer to a buffer of size PAGE_SIZE, containing
153 * data to be written.
155 * Returns: 0 on success. Otherwise the error is that returned by later
156 * modules, -ECHILD if we have a broken pipeline or -EIO if
159 static int toi_incremental_write_page(unsigned long index
, int buf_type
,
160 void *buffer_page
, unsigned int buf_size
)
162 int ret
= 0, cpu
= smp_processor_id();
163 struct toi_cpu_context
*ctx
= &per_cpu(contexts
, cpu
);
167 /* char *old_hash; */
169 ctx
->buffer_start
= TOI_MAP(buf_type
, buffer_page
);
171 sg_init_one(&ctx
->sg
[0], ctx
->buffer_start
, buf_size
);
173 ret
= crypto_hash_digest(&ctx
->desc
, &ctx
->sg
[0], ctx
->sg
[0].length
, ctx
->digest
);
174 /* old_hash = get_old_hash(index); */
176 TOI_UNMAP(buf_type
, buffer_page
);
179 if (!ret
&& new_hash
== old_hash
) {
182 store_hash(ctx
, index
, new_hash
);
186 mutex_lock(&stats_lock
);
188 toi_incremental_bytes_in
+= buf_size
;
190 toi_incremental_bytes_out
+= buf_size
;
192 mutex_unlock(&stats_lock
);
194 if (ret
|| to_write
) {
195 int ret2
= next_driver
->write_page(index
, buf_type
,
196 buffer_page
, buf_size
);
205 * toi_incremental_read_page()
206 * @buffer_page: struct page *. Pointer to a buffer of size PAGE_SIZE.
208 * Nothing extra to do here.
210 static int toi_incremental_read_page(unsigned long *index
, int buf_type
,
211 void *buffer_page
, unsigned int *buf_size
)
213 return next_driver
->read_page(index
, TOI_PAGE
, buffer_page
, buf_size
);
217 * toi_incremental_print_debug_stats
218 * @buffer: Pointer to a buffer into which the debug info will be printed.
219 * @size: Size of the buffer.
221 * Print information to be recorded for debugging purposes into a buffer.
222 * Returns: Number of characters written to the buffer.
225 static int toi_incremental_print_debug_stats(char *buffer
, int size
)
227 unsigned long pages_in
= toi_incremental_bytes_in
>> PAGE_SHIFT
,
228 pages_out
= toi_incremental_bytes_out
>> PAGE_SHIFT
;
231 /* Output the size of the incremental image. */
232 if (*toi_incremental_slow_cmp_name
)
233 len
= scnprintf(buffer
, size
, "- Hash algorithm is '%s'.\n",
234 toi_incremental_slow_cmp_name
);
236 len
= scnprintf(buffer
, size
, "- Hash algorithm is not set.\n");
239 len
+= scnprintf(buffer
+ len
, size
- len
, " Incremental image "
240 "%lu of %lu bytes (%ld percent).\n",
241 toi_incremental_bytes_out
,
242 toi_incremental_bytes_in
, pages_out
* 100 / pages_in
);
247 * toi_incremental_memory_needed
249 * Tell the caller how much memory we need to operate during hibernate/resume.
250 * Returns: Unsigned long. Maximum number of bytes of memory required for
253 static int toi_incremental_memory_needed(void)
255 return 2 * PAGE_SIZE
;
258 static int toi_incremental_storage_needed(void)
260 return 2 * sizeof(unsigned long) + sizeof(int) + strlen(toi_incremental_slow_cmp_name
) + 1;
264 * toi_incremental_save_config_info
265 * @buffer: Pointer to a buffer of size PAGE_SIZE.
267 * Save informaton needed when reloading the image at resume time.
268 * Returns: Number of bytes used for saving our data.
270 static int toi_incremental_save_config_info(char *buffer
)
272 int len
= strlen(toi_incremental_slow_cmp_name
) + 1, offset
= 0;
274 *((unsigned long *)buffer
) = toi_incremental_bytes_in
;
275 offset
+= sizeof(unsigned long);
276 *((unsigned long *)(buffer
+ offset
)) = toi_incremental_bytes_out
;
277 offset
+= sizeof(unsigned long);
278 *((int *)(buffer
+ offset
)) = len
;
279 offset
+= sizeof(int);
280 strncpy(buffer
+ offset
, toi_incremental_slow_cmp_name
, len
);
284 /* toi_incremental_load_config_info
285 * @buffer: Pointer to the start of the data.
286 * @size: Number of bytes that were saved.
288 * Description: Reload information to be retained for debugging info.
290 static void toi_incremental_load_config_info(char *buffer
, int size
)
294 toi_incremental_bytes_in
= *((unsigned long *)buffer
);
295 offset
+= sizeof(unsigned long);
296 toi_incremental_bytes_out
= *((unsigned long *)(buffer
+ offset
));
297 offset
+= sizeof(unsigned long);
298 len
= *((int *)(buffer
+ offset
));
299 offset
+= sizeof(int);
300 strncpy(toi_incremental_slow_cmp_name
, buffer
+ offset
, len
);
303 static void toi_incremental_pre_atomic_restore(struct toi_boot_kernel_data
*bkd
)
305 bkd
->incremental_bytes_in
= toi_incremental_bytes_in
;
306 bkd
->incremental_bytes_out
= toi_incremental_bytes_out
;
309 static void toi_incremental_post_atomic_restore(struct toi_boot_kernel_data
*bkd
)
311 toi_incremental_bytes_in
= bkd
->incremental_bytes_in
;
312 toi_incremental_bytes_out
= bkd
->incremental_bytes_out
;
315 static void toi_incremental_algo_change(void)
317 /* Reset so it's gotten from crypto_hash_digestsize afresh */
318 toi_incremental_digestsize
= 0;
322 * data for our sysfs entries.
324 static struct toi_sysfs_data sysfs_params
[] = {
325 SYSFS_INT("enabled", SYSFS_RW
, &toi_incremental_ops
.enabled
, 0, 1, 0,
327 SYSFS_STRING("algorithm", SYSFS_RW
, toi_incremental_slow_cmp_name
, 31, 0,
328 toi_incremental_algo_change
),
334 static struct toi_module_ops toi_incremental_ops
= {
335 .type
= FILTER_MODULE
,
336 .name
= "incremental",
337 .directory
= "incremental",
338 .module
= THIS_MODULE
,
339 .initialise
= toi_incremental_init
,
340 .memory_needed
= toi_incremental_memory_needed
,
341 .print_debug_info
= toi_incremental_print_debug_stats
,
342 .save_config_info
= toi_incremental_save_config_info
,
343 .load_config_info
= toi_incremental_load_config_info
,
344 .storage_needed
= toi_incremental_storage_needed
,
346 .pre_atomic_restore
= toi_incremental_pre_atomic_restore
,
347 .post_atomic_restore
= toi_incremental_post_atomic_restore
,
349 .rw_init
= toi_incremental_rw_init
,
350 .rw_cleanup
= toi_incremental_rw_cleanup
,
352 .write_page
= toi_incremental_write_page
,
353 .read_page
= toi_incremental_read_page
,
355 .sysfs_data
= sysfs_params
,
356 .num_sysfs_entries
= sizeof(sysfs_params
) / sizeof(struct toi_sysfs_data
),
359 /* ---- Registration ---- */
361 static __init
int toi_incremental_load(void)
363 return toi_register_module(&toi_incremental_ops
);
367 static __exit
void toi_incremental_unload(void)
369 toi_unregister_module(&toi_incremental_ops
);
371 module_init(toi_incremental_load
);
372 module_exit(toi_incremental_unload
);
373 MODULE_LICENSE("GPL");
374 MODULE_AUTHOR("Nigel Cunningham");
375 MODULE_DESCRIPTION("Incremental Image Support for TuxOnIce");
377 late_initcall(toi_incremental_load
);