2 * kernel/power/compression.c
4 * Copyright (C) 2003-2007 Nigel Cunningham (nigel at suspend2 net)
6 * This file is released under the GPLv2.
8 * This file contains data compression routines for suspend,
12 #include <linux/module.h>
13 #include <linux/suspend.h>
14 #include <linux/highmem.h>
15 #include <linux/vmalloc.h>
16 #include <linux/crypto.h>
18 #include "suspend2_builtin.h"
25 static int suspend_expected_compression
= 0;
27 static struct suspend_module_ops suspend_compression_ops
;
28 static struct suspend_module_ops
*next_driver
;
30 static char suspend_compressor_name
[32] = "lzf";
32 static DEFINE_MUTEX(stats_lock
);
36 struct crypto_tfm
*transform
;
41 static DEFINE_PER_CPU(struct cpu_context
, contexts
);
43 static int suspend_compress_prepare_result
;
46 * suspend_compress_cleanup
48 * Frees memory allocated for our labours.
50 static void suspend_compress_cleanup(int suspend_or_resume
)
54 if (!suspend_or_resume
)
57 for_each_online_cpu(cpu
) {
58 struct cpu_context
*this = &per_cpu(contexts
, cpu
);
59 if (this->transform
) {
60 crypto_free_tfm(this->transform
);
61 this->transform
= NULL
;
64 if (this->page_buffer
)
65 free_page((unsigned long) this->page_buffer
);
67 this->page_buffer
= NULL
;
72 * suspend_crypto_prepare
74 * Prepare to do some work by allocating buffers and transforms.
76 static int suspend_compress_crypto_prepare(void)
80 if (!*suspend_compressor_name
) {
81 printk("Suspend2: Compression enabled but no compressor name set.\n");
85 for_each_online_cpu(cpu
) {
86 struct cpu_context
*this = &per_cpu(contexts
, cpu
);
87 this->transform
= crypto_alloc_tfm(suspend_compressor_name
, 0);
88 if (IS_ERR(this->transform
)) {
89 printk("Suspend2: Failed to initialise the %s "
90 "compression transform.\n",
91 suspend_compressor_name
);
92 this->transform
= NULL
;
96 this->page_buffer
= (char *) get_zeroed_page(GFP_ATOMIC
);
98 if (!this->page_buffer
) {
100 "Failed to allocate a page buffer for suspend2 "
101 "encryption driver.\n");
110 * suspend_compress_init
113 static int suspend_compress_init(int suspend_or_resume
)
115 if (!suspend_or_resume
)
118 suspend_compress_bytes_in
= suspend_compress_bytes_out
= 0;
120 next_driver
= suspend_get_next_filter(&suspend_compression_ops
);
123 printk("Compression Driver: Argh! Nothing follows me in"
128 suspend_compress_prepare_result
= suspend_compress_crypto_prepare();
134 * suspend_compress_rw_init()
137 int suspend_compress_rw_init(int rw
, int stream_number
)
139 if (suspend_compress_prepare_result
) {
140 printk("Failed to initialise compression algorithm.\n");
144 suspend_compression_ops
.enabled
= 0;
151 * suspend_compress_write_chunk()
153 * Compress a page of data, buffering output and passing on filled
154 * pages to the next module in the pipeline.
156 * Buffer_page: Pointer to a buffer of size PAGE_SIZE, containing
157 * data to be compressed.
159 * Returns: 0 on success. Otherwise the error is that returned by later
160 * modules, -ECHILD if we have a broken pipeline or -EIO if
163 static int suspend_compress_write_chunk(unsigned long index
,
164 struct page
*buffer_page
, unsigned int buf_size
)
166 int ret
, cpu
= smp_processor_id();
167 struct cpu_context
*ctx
= &per_cpu(contexts
, cpu
);
170 return next_driver
->write_chunk(index
, buffer_page
, buf_size
);
172 ctx
->buffer_start
= kmap(buffer_page
);
176 ret
= crypto_comp_compress(ctx
->transform
,
177 ctx
->buffer_start
, buf_size
,
178 ctx
->page_buffer
, &ctx
->len
);
183 printk("Compression failed.\n");
187 mutex_lock(&stats_lock
);
188 suspend_compress_bytes_in
+= buf_size
;
189 suspend_compress_bytes_out
+= ctx
->len
;
190 mutex_unlock(&stats_lock
);
192 if (ctx
->len
< buf_size
) /* some compression */
193 ret
= next_driver
->write_chunk(index
,
194 virt_to_page(ctx
->page_buffer
),
197 ret
= next_driver
->write_chunk(index
, buffer_page
, buf_size
);
204 * suspend_compress_read_chunk()
205 * @buffer_page: struct page *. Pointer to a buffer of size PAGE_SIZE.
206 * @sync: int. Whether the previous module (or core) wants its data
209 * Retrieve data from later modules and decompress it until the input buffer
211 * Zero if successful. Error condition from me or from downstream on failure.
213 static int suspend_compress_read_chunk(unsigned long *index
,
214 struct page
*buffer_page
, unsigned int *buf_size
, int sync
)
216 int ret
, cpu
= smp_processor_id();
218 unsigned int outlen
= PAGE_SIZE
;
220 struct cpu_context
*ctx
= &per_cpu(contexts
, cpu
);
223 return next_driver
->read_chunk(index
, buffer_page
, buf_size
,
227 * All our reads must be synchronous - we can't decompress
228 * data that hasn't been read yet.
231 *buf_size
= PAGE_SIZE
;
233 ret
= next_driver
->read_chunk(index
, buffer_page
, &len
, SUSPEND_SYNC
);
235 /* Error or uncompressed data */
236 if (ret
|| len
== PAGE_SIZE
)
239 buffer_start
= kmap(buffer_page
);
240 memcpy(ctx
->page_buffer
, buffer_start
, len
);
241 ret
= crypto_comp_decompress(
244 len
, buffer_start
, &outlen
);
246 abort_suspend(SUSPEND_FAILED_IO
,
247 "Compress_read returned %d.\n", ret
);
248 else if (outlen
!= PAGE_SIZE
) {
249 abort_suspend(SUSPEND_FAILED_IO
,
250 "Decompression yielded %d bytes instead of %ld.\n",
260 * suspend_compress_print_debug_stats
261 * @buffer: Pointer to a buffer into which the debug info will be printed.
262 * @size: Size of the buffer.
264 * Print information to be recorded for debugging purposes into a buffer.
265 * Returns: Number of characters written to the buffer.
268 static int suspend_compress_print_debug_stats(char *buffer
, int size
)
270 int pages_in
= suspend_compress_bytes_in
>> PAGE_SHIFT
,
271 pages_out
= suspend_compress_bytes_out
>> PAGE_SHIFT
;
274 /* Output the compression ratio achieved. */
275 if (*suspend_compressor_name
)
276 len
= snprintf_used(buffer
, size
, "- Compressor is '%s'.\n",
277 suspend_compressor_name
);
279 len
= snprintf_used(buffer
, size
, "- Compressor is not set.\n");
282 len
+= snprintf_used(buffer
+len
, size
- len
,
283 " Compressed %ld bytes into %ld (%d percent compression).\n",
284 suspend_compress_bytes_in
,
285 suspend_compress_bytes_out
,
286 (pages_in
- pages_out
) * 100 / pages_in
);
291 * suspend_compress_compression_memory_needed
293 * Tell the caller how much memory we need to operate during suspend/resume.
294 * Returns: Unsigned long. Maximum number of bytes of memory required for
297 static int suspend_compress_memory_needed(void)
299 return 2 * PAGE_SIZE
;
302 static int suspend_compress_storage_needed(void)
304 return 4 * sizeof(unsigned long) + strlen(suspend_compressor_name
) + 1;
308 * suspend_compress_save_config_info
309 * @buffer: Pointer to a buffer of size PAGE_SIZE.
311 * Save informaton needed when reloading the image at resume time.
312 * Returns: Number of bytes used for saving our data.
314 static int suspend_compress_save_config_info(char *buffer
)
316 int namelen
= strlen(suspend_compressor_name
) + 1;
319 *((unsigned long *) buffer
) = suspend_compress_bytes_in
;
320 *((unsigned long *) (buffer
+ 1 * sizeof(unsigned long))) =
321 suspend_compress_bytes_out
;
322 *((unsigned long *) (buffer
+ 2 * sizeof(unsigned long))) =
323 suspend_expected_compression
;
324 *((unsigned long *) (buffer
+ 3 * sizeof(unsigned long))) = namelen
;
325 strncpy(buffer
+ 4 * sizeof(unsigned long), suspend_compressor_name
,
327 total_len
= 4 * sizeof(unsigned long) + namelen
;
331 /* suspend_compress_load_config_info
332 * @buffer: Pointer to the start of the data.
333 * @size: Number of bytes that were saved.
335 * Description: Reload information needed for decompressing the image at
338 static void suspend_compress_load_config_info(char *buffer
, int size
)
342 suspend_compress_bytes_in
= *((unsigned long *) buffer
);
343 suspend_compress_bytes_out
= *((unsigned long *) (buffer
+ 1 * sizeof(unsigned long)));
344 suspend_expected_compression
= *((unsigned long *) (buffer
+ 2 *
345 sizeof(unsigned long)));
346 namelen
= *((unsigned long *) (buffer
+ 3 * sizeof(unsigned long)));
347 strncpy(suspend_compressor_name
, buffer
+ 4 * sizeof(unsigned long),
353 * suspend_expected_compression_ratio
355 * Description: Returns the expected ratio between data passed into this module
356 * and the amount of data output when writing.
357 * Returns: 100 if the module is disabled. Otherwise the value set by the
358 * user via our sysfs entry.
361 static int suspend_compress_expected_ratio(void)
363 if (!suspend_compression_ops
.enabled
)
366 return 100 - suspend_expected_compression
;
370 * data for our sysfs entries.
372 static struct suspend_sysfs_data sysfs_params
[] = {
374 SUSPEND2_ATTR("expected_compression", SYSFS_RW
),
375 SYSFS_INT(&suspend_expected_compression
, 0, 99, 0)
379 SUSPEND2_ATTR("enabled", SYSFS_RW
),
380 SYSFS_INT(&suspend_compression_ops
.enabled
, 0, 1, 0)
384 SUSPEND2_ATTR("algorithm", SYSFS_RW
),
385 SYSFS_STRING(suspend_compressor_name
, 31, 0)
392 static struct suspend_module_ops suspend_compression_ops
= {
393 .type
= FILTER_MODULE
,
394 .name
= "Compressor",
395 .directory
= "compression",
396 .module
= THIS_MODULE
,
397 .initialise
= suspend_compress_init
,
398 .cleanup
= suspend_compress_cleanup
,
399 .memory_needed
= suspend_compress_memory_needed
,
400 .print_debug_info
= suspend_compress_print_debug_stats
,
401 .save_config_info
= suspend_compress_save_config_info
,
402 .load_config_info
= suspend_compress_load_config_info
,
403 .storage_needed
= suspend_compress_storage_needed
,
404 .expected_compression
= suspend_compress_expected_ratio
,
406 .rw_init
= suspend_compress_rw_init
,
408 .write_chunk
= suspend_compress_write_chunk
,
409 .read_chunk
= suspend_compress_read_chunk
,
411 .sysfs_data
= sysfs_params
,
412 .num_sysfs_entries
= sizeof(sysfs_params
) / sizeof(struct suspend_sysfs_data
),
415 /* ---- Registration ---- */
417 static __init
int suspend_compress_load(void)
419 return suspend_register_module(&suspend_compression_ops
);
423 static __exit
void suspend_compress_unload(void)
425 suspend_unregister_module(&suspend_compression_ops
);
428 module_init(suspend_compress_load
);
429 module_exit(suspend_compress_unload
);
430 MODULE_LICENSE("GPL");
431 MODULE_AUTHOR("Nigel Cunningham");
432 MODULE_DESCRIPTION("Compression Support for Suspend2");
434 late_initcall(suspend_compress_load
);