2 * kernel/power/tuxonice_prune.c
4 * Copyright (C) 2012 Nigel Cunningham (nigel at tuxonice net)
6 * This file is released under the GPLv2.
8 * This file implements a TuxOnIce module that seeks to prune the
9 * amount of data written to disk. It builds a table of hashes
10 * of the uncompressed data, and writes the pfn of the previous page
11 * with the same contents instead of repeating the data when a match
15 #include <linux/suspend.h>
16 #include <linux/highmem.h>
17 #include <linux/vmalloc.h>
18 #include <linux/crypto.h>
19 #include <linux/scatterlist.h>
20 #include <crypto/hash.h>
22 #include "tuxonice_builtin.h"
24 #include "tuxonice_modules.h"
25 #include "tuxonice_sysfs.h"
26 #include "tuxonice_io.h"
27 #include "tuxonice_ui.h"
28 #include "tuxonice_alloc.h"
31 * We never write a page bigger than PAGE_SIZE, so use a large number
32 * to indicate that data is a PFN.
34 #define PRUNE_DATA_IS_PFN (PAGE_SIZE + 100)
36 static unsigned long toi_pruned_pages
;
38 static struct toi_module_ops toi_prune_ops
;
39 static struct toi_module_ops
*next_driver
;
41 static char toi_prune_hash_algo_name
[32] = "sha1";
43 static DEFINE_MUTEX(stats_lock
);
45 struct toi_cpu_context
{
46 struct shash_desc desc
;
50 #define OUT_BUF_SIZE (2 * PAGE_SIZE)
52 static DEFINE_PER_CPU(struct toi_cpu_context
, contexts
);
57 * Prepare to do some work by allocating buffers and transforms.
59 static int toi_prune_crypto_prepare(void)
61 int cpu
, ret
, digestsize
;
63 if (!*toi_prune_hash_algo_name
) {
64 printk(KERN_INFO
"TuxOnIce: Pruning enabled but no " "hash algorithm set.\n");
68 for_each_online_cpu(cpu
) {
69 struct toi_cpu_context
*this = &per_cpu(contexts
, cpu
);
70 this->desc
.tfm
= crypto_alloc_shash(toi_prune_hash_algo_name
, 0, 0);
71 if (IS_ERR(this->desc
.tfm
)) {
72 printk(KERN_INFO
"TuxOnIce: Failed to allocate the "
73 "%s prune hash algorithm.\n", toi_prune_hash_algo_name
);
74 this->desc
.tfm
= NULL
;
79 digestsize
= crypto_shash_digestsize(this->desc
.tfm
);
81 this->digest
= kmalloc(digestsize
, GFP_KERNEL
);
83 printk(KERN_INFO
"TuxOnIce: Failed to allocate space "
84 "for digest output.\n");
85 crypto_free_shash(this->desc
.tfm
);
86 this->desc
.tfm
= NULL
;
91 ret
= crypto_shash_init(&this->desc
);
93 printk(KERN_INFO
"TuxOnIce: Failed to initialise the "
94 "%s prune hash algorithm.\n", toi_prune_hash_algo_name
);
97 crypto_free_shash(this->desc
.tfm
);
98 this->desc
.tfm
= NULL
;
106 static int toi_prune_rw_cleanup(int writing
)
110 for_each_online_cpu(cpu
) {
111 struct toi_cpu_context
*this = &per_cpu(contexts
, cpu
);
112 if (this->desc
.tfm
) {
113 crypto_free_shash(this->desc
.tfm
);
114 this->desc
.tfm
= NULL
;
130 static int toi_prune_init(int toi_or_resume
)
135 toi_pruned_pages
= 0;
137 next_driver
= toi_get_next_filter(&toi_prune_ops
);
139 return next_driver
? 0 : -ECHILD
;
143 * toi_prune_rw_init()
146 static int toi_prune_rw_init(int rw
, int stream_number
)
148 if (toi_prune_crypto_prepare()) {
149 printk(KERN_ERR
"Failed to initialise prune " "algorithm.\n");
151 printk(KERN_INFO
"Unable to read the image.\n");
154 printk(KERN_INFO
"Continuing without " "pruning the image.\n");
155 toi_prune_ops
.enabled
= 0;
163 * toi_prune_write_page()
165 * Compress a page of data, buffering output and passing on filled
166 * pages to the next module in the pipeline.
168 * Buffer_page: Pointer to a buffer of size PAGE_SIZE, containing
169 * data to be checked.
171 * Returns: 0 on success. Otherwise the error is that returned by later
172 * modules, -ECHILD if we have a broken pipeline or -EIO if
175 static int toi_prune_write_page(unsigned long index
, int buf_type
,
176 void *buffer_page
, unsigned int buf_size
)
178 int ret
= 0, cpu
= smp_processor_id(), write_data
= 1;
179 struct toi_cpu_context
*ctx
= &per_cpu(contexts
, cpu
);
180 u8
*output_buffer
= buffer_page
;
181 int output_len
= buf_size
;
182 int out_buf_type
= buf_type
;
188 buffer_start
= TOI_MAP(buf_type
, buffer_page
);
189 ctx
->len
= OUT_BUF_SIZE
;
191 ret
= crypto_shash_digest(&ctx
->desc
, buffer_start
, buf_size
, &ctx
->digest
);
193 printk(KERN_INFO
"TuxOnIce: Failed to calculate digest (%d).\n", ret
);
195 mutex_lock(&stats_lock
);
199 mutex_unlock(&stats_lock
);
203 TOI_UNMAP(buf_type
, buffer_page
);
207 ret
= next_driver
->write_page(index
, out_buf_type
, output_buffer
, output_len
);
209 ret
= next_driver
->write_page(index
, out_buf_type
, output_buffer
, output_len
);
215 * toi_prune_read_page()
216 * @buffer_page: struct page *. Pointer to a buffer of size PAGE_SIZE.
218 * Retrieve data from later modules or from a previously loaded page and
219 * fill the input buffer.
220 * Zero if successful. Error condition from me or from downstream on failure.
222 static int toi_prune_read_page(unsigned long *index
, int buf_type
,
223 void *buffer_page
, unsigned int *buf_size
)
225 int ret
, cpu
= smp_processor_id();
228 struct toi_cpu_context
*ctx
= &per_cpu(contexts
, cpu
);
231 return next_driver
->read_page(index
, TOI_PAGE
, buffer_page
, buf_size
);
234 * All our reads must be synchronous - we can't handle
235 * data that hasn't been read yet.
238 ret
= next_driver
->read_page(index
, buf_type
, buffer_page
, &len
);
240 if (len
== PRUNE_DATA_IS_PFN
) {
241 buffer_start
= kmap(buffer_page
);
248 * toi_prune_print_debug_stats
249 * @buffer: Pointer to a buffer into which the debug info will be printed.
250 * @size: Size of the buffer.
252 * Print information to be recorded for debugging purposes into a buffer.
253 * Returns: Number of characters written to the buffer.
256 static int toi_prune_print_debug_stats(char *buffer
, int size
)
260 /* Output the number of pages pruned. */
261 if (*toi_prune_hash_algo_name
)
262 len
= scnprintf(buffer
, size
, "- Compressor is '%s'.\n", toi_prune_hash_algo_name
);
264 len
= scnprintf(buffer
, size
, "- Compressor is not set.\n");
266 if (toi_pruned_pages
)
267 len
+= scnprintf(buffer
+ len
, size
- len
, " Pruned "
268 "%lu pages).\n", toi_pruned_pages
);
273 * toi_prune_memory_needed
275 * Tell the caller how much memory we need to operate during hibernate/resume.
276 * Returns: Unsigned long. Maximum number of bytes of memory required for
279 static int toi_prune_memory_needed(void)
281 return 2 * PAGE_SIZE
;
284 static int toi_prune_storage_needed(void)
286 return 2 * sizeof(unsigned long) + 2 * sizeof(int) + strlen(toi_prune_hash_algo_name
) + 1;
290 * toi_prune_save_config_info
291 * @buffer: Pointer to a buffer of size PAGE_SIZE.
293 * Save informaton needed when reloading the image at resume time.
294 * Returns: Number of bytes used for saving our data.
296 static int toi_prune_save_config_info(char *buffer
)
298 int len
= strlen(toi_prune_hash_algo_name
) + 1, offset
= 0;
300 *((unsigned long *)buffer
) = toi_pruned_pages
;
301 offset
+= sizeof(unsigned long);
302 *((int *)(buffer
+ offset
)) = len
;
303 offset
+= sizeof(int);
304 strncpy(buffer
+ offset
, toi_prune_hash_algo_name
, len
);
308 /* toi_prune_load_config_info
309 * @buffer: Pointer to the start of the data.
310 * @size: Number of bytes that were saved.
312 * Description: Reload information needed for passing back to the
315 static void toi_prune_load_config_info(char *buffer
, int size
)
319 toi_pruned_pages
= *((unsigned long *)buffer
);
320 offset
+= sizeof(unsigned long);
321 len
= *((int *)(buffer
+ offset
));
322 offset
+= sizeof(int);
323 strncpy(toi_prune_hash_algo_name
, buffer
+ offset
, len
);
326 static void toi_prune_pre_atomic_restore(struct toi_boot_kernel_data
*bkd
)
328 bkd
->pruned_pages
= toi_pruned_pages
;
331 static void toi_prune_post_atomic_restore(struct toi_boot_kernel_data
*bkd
)
333 toi_pruned_pages
= bkd
->pruned_pages
;
339 * Description: Returns the expected ratio between data passed into this module
340 * and the amount of data output when writing.
341 * Returns: 100 - we have no idea how many pages will be pruned.
344 static int toi_prune_expected_ratio(void)
350 * data for our sysfs entries.
352 static struct toi_sysfs_data sysfs_params
[] = {
353 SYSFS_INT("enabled", SYSFS_RW
, &toi_prune_ops
.enabled
, 0, 1, 0,
355 SYSFS_STRING("algorithm", SYSFS_RW
, toi_prune_hash_algo_name
, 31, 0, NULL
),
361 static struct toi_module_ops toi_prune_ops
= {
362 .type
= FILTER_MODULE
,
364 .directory
= "prune",
365 .module
= THIS_MODULE
,
366 .initialise
= toi_prune_init
,
367 .memory_needed
= toi_prune_memory_needed
,
368 .print_debug_info
= toi_prune_print_debug_stats
,
369 .save_config_info
= toi_prune_save_config_info
,
370 .load_config_info
= toi_prune_load_config_info
,
371 .storage_needed
= toi_prune_storage_needed
,
372 .expected_compression
= toi_prune_expected_ratio
,
374 .pre_atomic_restore
= toi_prune_pre_atomic_restore
,
375 .post_atomic_restore
= toi_prune_post_atomic_restore
,
377 .rw_init
= toi_prune_rw_init
,
378 .rw_cleanup
= toi_prune_rw_cleanup
,
380 .write_page
= toi_prune_write_page
,
381 .read_page
= toi_prune_read_page
,
383 .sysfs_data
= sysfs_params
,
384 .num_sysfs_entries
= sizeof(sysfs_params
) / sizeof(struct toi_sysfs_data
),
387 /* ---- Registration ---- */
389 static __init
int toi_prune_load(void)
391 return toi_register_module(&toi_prune_ops
);
395 static __exit
void toi_prune_unload(void)
397 toi_unregister_module(&toi_prune_ops
);
399 module_init(toi_prune_load
);
400 module_exit(toi_prune_unload
);
401 MODULE_LICENSE("GPL");
402 MODULE_AUTHOR("Nigel Cunningham");
403 MODULE_DESCRIPTION("Image Pruning Support for TuxOnIce");
405 late_initcall(toi_prune_load
);