2 * kernel/power/compression.c
4 * Copyright (C) 2003-2010 Nigel Cunningham (nigel at tuxonice net)
6 * This file is released under the GPLv2.
8 * This file contains data compression routines for TuxOnIce,
12 #include <linux/suspend.h>
13 #include <linux/highmem.h>
14 #include <linux/vmalloc.h>
15 #include <linux/crypto.h>
17 #include "tuxonice_builtin.h"
19 #include "tuxonice_modules.h"
20 #include "tuxonice_sysfs.h"
21 #include "tuxonice_io.h"
22 #include "tuxonice_ui.h"
23 #include "tuxonice_alloc.h"
25 static int toi_expected_compression
;
26 #ifdef CONFIG_TOI_ENHANCE
27 static int toi_actual_compression
;
30 static struct toi_module_ops toi_compression_ops
;
31 static struct toi_module_ops
*next_driver
;
33 static char toi_compressor_name
[32] = "lzo";
35 static DEFINE_MUTEX(stats_lock
);
37 struct toi_cpu_context
{
39 struct crypto_comp
*transform
;
45 #define OUT_BUF_SIZE (2 * PAGE_SIZE)
47 static DEFINE_PER_CPU(struct toi_cpu_context
, contexts
);
52 * Prepare to do some work by allocating buffers and transforms.
54 static int toi_compress_crypto_prepare(void)
58 if (!*toi_compressor_name
) {
59 printk(KERN_INFO
"TuxOnIce: Compression enabled but no " "compressor name set.\n");
63 for_each_online_cpu(cpu
) {
64 struct toi_cpu_context
*this = &per_cpu(contexts
, cpu
);
65 this->transform
= crypto_alloc_comp(toi_compressor_name
, 0, 0);
66 if (IS_ERR(this->transform
)) {
67 printk(KERN_INFO
"TuxOnIce: Failed to initialise the "
68 "%s compression transform.\n", toi_compressor_name
);
69 this->transform
= NULL
;
73 this->page_buffer
= (char *)toi_get_zeroed_page(16, TOI_ATOMIC_GFP
);
75 if (!this->page_buffer
) {
77 "Failed to allocate a page buffer for TuxOnIce "
78 "compression driver.\n");
82 this->output_buffer
= (char *)vmalloc_32(OUT_BUF_SIZE
);
84 if (!this->output_buffer
) {
86 "Failed to allocate a output buffer for TuxOnIce "
87 "compression driver.\n");
95 static int toi_compress_rw_cleanup(int writing
)
99 for_each_online_cpu(cpu
) {
100 struct toi_cpu_context
*this = &per_cpu(contexts
, cpu
);
101 if (this->transform
) {
102 crypto_free_comp(this->transform
);
103 this->transform
= NULL
;
106 if (this->page_buffer
)
107 toi_free_page(16, (unsigned long)this->page_buffer
);
109 this->page_buffer
= NULL
;
111 if (this->output_buffer
)
112 vfree(this->output_buffer
);
114 this->output_buffer
= NULL
;
124 static int toi_compress_init(int toi_or_resume
)
129 toi_compress_bytes_in
= 0;
130 toi_compress_bytes_out
= 0;
132 next_driver
= toi_get_next_filter(&toi_compression_ops
);
134 return next_driver
? 0 : -ECHILD
;
138 * toi_compress_rw_init()
141 static int toi_compress_rw_init(int rw
, int stream_number
)
143 if (toi_compress_crypto_prepare()) {
144 printk(KERN_ERR
"Failed to initialise compression " "algorithm.\n");
146 printk(KERN_INFO
"Unable to read the image.\n");
149 printk(KERN_INFO
"Continuing without " "compressing the image.\n");
150 toi_compression_ops
.enabled
= 0;
158 * toi_compress_write_page()
160 * Compress a page of data, buffering output and passing on filled
161 * pages to the next module in the pipeline.
163 * Buffer_page: Pointer to a buffer of size PAGE_SIZE, containing
164 * data to be compressed.
166 * Returns: 0 on success. Otherwise the error is that returned by later
167 * modules, -ECHILD if we have a broken pipeline or -EIO if
170 static int toi_compress_write_page(unsigned long index
, int buf_type
,
171 void *buffer_page
, unsigned int buf_size
)
173 int ret
= 0, cpu
= smp_processor_id();
174 struct toi_cpu_context
*ctx
= &per_cpu(contexts
, cpu
);
175 u8
*output_buffer
= buffer_page
;
176 int output_len
= buf_size
;
177 int out_buf_type
= buf_type
;
179 if (ctx
->transform
) {
181 ctx
->buffer_start
= TOI_MAP(buf_type
, buffer_page
);
182 ctx
->len
= OUT_BUF_SIZE
;
184 ret
= crypto_comp_compress(ctx
->transform
,
185 ctx
->buffer_start
, buf_size
,
186 ctx
->output_buffer
, &ctx
->len
);
188 TOI_UNMAP(buf_type
, buffer_page
);
190 toi_message(TOI_COMPRESS
, TOI_VERBOSE
, 0,
191 "CPU %d, index %lu: %d bytes", cpu
, index
, ctx
->len
);
193 if (!ret
&& ctx
->len
< buf_size
) { /* some compression */
194 output_buffer
= ctx
->output_buffer
;
195 output_len
= ctx
->len
;
196 out_buf_type
= TOI_VIRT
;
201 mutex_lock(&stats_lock
);
203 toi_compress_bytes_in
+= buf_size
;
204 toi_compress_bytes_out
+= output_len
;
206 mutex_unlock(&stats_lock
);
209 ret
= next_driver
->write_page(index
, out_buf_type
, output_buffer
, output_len
);
215 * toi_compress_read_page()
216 * @buffer_page: struct page *. Pointer to a buffer of size PAGE_SIZE.
218 * Retrieve data from later modules and decompress it until the input buffer
220 * Zero if successful. Error condition from me or from downstream on failure.
222 static int toi_compress_read_page(unsigned long *index
, int buf_type
,
223 void *buffer_page
, unsigned int *buf_size
)
225 int ret
, cpu
= smp_processor_id();
227 unsigned int outlen
= PAGE_SIZE
;
229 struct toi_cpu_context
*ctx
= &per_cpu(contexts
, cpu
);
232 return next_driver
->read_page(index
, TOI_PAGE
, buffer_page
, buf_size
);
235 * All our reads must be synchronous - we can't decompress
236 * data that hasn't been read yet.
239 ret
= next_driver
->read_page(index
, TOI_VIRT
, ctx
->page_buffer
, &len
);
241 buffer_start
= kmap(buffer_page
);
243 /* Error or uncompressed data */
244 if (ret
|| len
== PAGE_SIZE
) {
245 memcpy(buffer_start
, ctx
->page_buffer
, len
);
249 ret
= crypto_comp_decompress(ctx
->transform
, ctx
->page_buffer
, len
, buffer_start
, &outlen
);
251 toi_message(TOI_COMPRESS
, TOI_VERBOSE
, 0,
252 "CPU %d, index %lu: %d=>%d (%d).", cpu
, *index
, len
, outlen
, ret
);
255 abort_hibernate(TOI_FAILED_IO
, "Compress_read returned %d.\n", ret
);
256 else if (outlen
!= PAGE_SIZE
) {
257 abort_hibernate(TOI_FAILED_IO
,
258 "Decompression yielded %d bytes instead of %ld.\n",
260 printk(KERN_ERR
"Decompression yielded %d bytes instead of "
261 "%ld.\n", outlen
, PAGE_SIZE
);
266 TOI_UNMAP(buf_type
, buffer_page
);
271 * toi_compress_print_debug_stats
272 * @buffer: Pointer to a buffer into which the debug info will be printed.
273 * @size: Size of the buffer.
275 * Print information to be recorded for debugging purposes into a buffer.
276 * Returns: Number of characters written to the buffer.
279 static int toi_compress_print_debug_stats(char *buffer
, int size
)
281 unsigned long pages_in
= toi_compress_bytes_in
>> PAGE_SHIFT
,
282 pages_out
= toi_compress_bytes_out
>> PAGE_SHIFT
;
285 /* Output the compression ratio achieved. */
286 if (*toi_compressor_name
)
287 len
= scnprintf(buffer
, size
, "- Compressor is '%s'.\n", toi_compressor_name
);
289 len
= scnprintf(buffer
, size
, "- Compressor is not set.\n");
292 len
+= scnprintf(buffer
+ len
, size
- len
, " Compressed "
293 "%lu bytes into %lu (%ld percent compression).\n",
294 toi_compress_bytes_in
,
295 toi_compress_bytes_out
, (pages_in
- pages_out
) * 100 / pages_in
);
300 * toi_compress_compression_memory_needed
302 * Tell the caller how much memory we need to operate during hibernate/resume.
303 * Returns: Unsigned long. Maximum number of bytes of memory required for
306 static int toi_compress_memory_needed(void)
308 return 2 * PAGE_SIZE
;
311 static int toi_compress_storage_needed(void)
313 return 2 * sizeof(unsigned long) + 2 * sizeof(int) + strlen(toi_compressor_name
) + 1;
317 * toi_compress_save_config_info
318 * @buffer: Pointer to a buffer of size PAGE_SIZE.
320 * Save informaton needed when reloading the image at resume time.
321 * Returns: Number of bytes used for saving our data.
323 static int toi_compress_save_config_info(char *buffer
)
325 int len
= strlen(toi_compressor_name
) + 1, offset
= 0;
327 *((unsigned long *)buffer
) = toi_compress_bytes_in
;
328 offset
+= sizeof(unsigned long);
329 *((unsigned long *)(buffer
+ offset
)) = toi_compress_bytes_out
;
330 offset
+= sizeof(unsigned long);
331 *((int *)(buffer
+ offset
)) = toi_expected_compression
;
332 offset
+= sizeof(int);
333 *((int *)(buffer
+ offset
)) = len
;
334 offset
+= sizeof(int);
335 strncpy(buffer
+ offset
, toi_compressor_name
, len
);
339 /* toi_compress_load_config_info
340 * @buffer: Pointer to the start of the data.
341 * @size: Number of bytes that were saved.
343 * Description: Reload information needed for decompressing the image at
346 static void toi_compress_load_config_info(char *buffer
, int size
)
350 toi_compress_bytes_in
= *((unsigned long *)buffer
);
351 offset
+= sizeof(unsigned long);
352 toi_compress_bytes_out
= *((unsigned long *)(buffer
+ offset
));
353 offset
+= sizeof(unsigned long);
354 toi_expected_compression
= *((int *)(buffer
+ offset
));
355 offset
+= sizeof(int);
356 len
= *((int *)(buffer
+ offset
));
357 offset
+= sizeof(int);
358 strncpy(toi_compressor_name
, buffer
+ offset
, len
);
361 static void toi_compress_pre_atomic_restore(struct toi_boot_kernel_data
*bkd
)
363 bkd
->compress_bytes_in
= toi_compress_bytes_in
;
364 bkd
->compress_bytes_out
= toi_compress_bytes_out
;
367 static void toi_compress_post_atomic_restore(struct toi_boot_kernel_data
*bkd
)
369 toi_compress_bytes_in
= bkd
->compress_bytes_in
;
370 toi_compress_bytes_out
= bkd
->compress_bytes_out
;
374 * toi_expected_compression_ratio
376 * Description: Returns the expected ratio between data passed into this module
377 * and the amount of data output when writing.
378 * Returns: 100 if the module is disabled. Otherwise the value set by the
379 * user via our sysfs entry.
382 static int toi_compress_expected_ratio(void)
384 if (!toi_compression_ops
.enabled
)
387 return 100 - toi_expected_compression
;
390 #ifdef CONFIG_TOI_ENHANCE
392 * toi_actual_compression_ratio
394 * Description: Returns the actual ratio of the lastest compression result.
395 * Returns: 0 if the module is disabled.
397 static int toi_compress_actual_ratio(void)
399 unsigned long pages_in
= toi_compress_bytes_in
>> PAGE_SHIFT
,
400 pages_out
= toi_compress_bytes_out
>> PAGE_SHIFT
;
402 toi_actual_compression
= 0;
403 if (!toi_compression_ops
.enabled
)
404 toi_actual_compression
= 0;
405 else if (pages_in
> 0 && (pages_in
- pages_out
>= 0))
406 toi_actual_compression
= (pages_in
- pages_out
) * 100 / pages_in
;
408 pr_warn("[%s] actual compressed ratio %d (%lu/%lu)\n", __func__
,
409 toi_actual_compression
, pages_in
, pages_out
);
410 return toi_actual_compression
;
415 * data for our sysfs entries.
417 static struct toi_sysfs_data sysfs_params
[] = {
418 SYSFS_INT("expected_compression", SYSFS_RW
, &toi_expected_compression
,
420 SYSFS_INT("enabled", SYSFS_RW
, &toi_compression_ops
.enabled
, 0, 1, 0,
422 SYSFS_STRING("algorithm", SYSFS_RW
, toi_compressor_name
, 31, 0, NULL
),
423 #ifdef CONFIG_TOI_ENHANCE
424 SYSFS_INT("actual_compression", SYSFS_READONLY
, &toi_actual_compression
,
432 static struct toi_module_ops toi_compression_ops
= {
433 .type
= FILTER_MODULE
,
434 .name
= "compression",
435 .directory
= "compression",
436 .module
= THIS_MODULE
,
437 .initialise
= toi_compress_init
,
438 .memory_needed
= toi_compress_memory_needed
,
439 .print_debug_info
= toi_compress_print_debug_stats
,
440 .save_config_info
= toi_compress_save_config_info
,
441 .load_config_info
= toi_compress_load_config_info
,
442 .storage_needed
= toi_compress_storage_needed
,
443 .expected_compression
= toi_compress_expected_ratio
,
444 #ifdef CONFIG_TOI_ENHANCE
445 .actual_compression
= toi_compress_actual_ratio
,
448 .pre_atomic_restore
= toi_compress_pre_atomic_restore
,
449 .post_atomic_restore
= toi_compress_post_atomic_restore
,
451 .rw_init
= toi_compress_rw_init
,
452 .rw_cleanup
= toi_compress_rw_cleanup
,
454 .write_page
= toi_compress_write_page
,
455 .read_page
= toi_compress_read_page
,
457 .sysfs_data
= sysfs_params
,
458 .num_sysfs_entries
= sizeof(sysfs_params
) / sizeof(struct toi_sysfs_data
),
461 /* ---- Registration ---- */
463 static __init
int toi_compress_load(void)
465 return toi_register_module(&toi_compression_ops
);
469 static __exit
void toi_compress_unload(void)
471 toi_unregister_module(&toi_compression_ops
);
473 module_init(toi_compress_load
);
474 module_exit(toi_compress_unload
);
475 MODULE_LICENSE("GPL");
476 MODULE_AUTHOR("Nigel Cunningham");
477 MODULE_DESCRIPTION("Compression Support for TuxOnIce");
479 late_initcall(toi_compress_load
);