2.2.9.17 release.
[linux-2.6/suspend2-2.6.18.git] / kernel / power / suspend_compress.c
blob4c94b62890fdec6eb1447d7baf714d2300ec7018
1 /*
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,
9 * using cryptoapi.
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"
19 #include "suspend.h"
20 #include "modules.h"
21 #include "sysfs.h"
22 #include "io.h"
23 #include "ui.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);
34 struct cpu_context {
35 u8 * page_buffer;
36 struct crypto_tfm *transform;
37 unsigned int len;
38 char *buffer_start;
41 static DEFINE_PER_CPU(struct cpu_context, contexts);
43 static int suspend_compress_prepare_result;
45 /*
46 * suspend_compress_cleanup
48 * Frees memory allocated for our labours.
50 static void suspend_compress_cleanup(int suspend_or_resume)
52 int cpu;
54 if (!suspend_or_resume)
55 return;
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;
71 /*
72 * suspend_crypto_prepare
74 * Prepare to do some work by allocating buffers and transforms.
76 static int suspend_compress_crypto_prepare(void)
78 int cpu;
80 if (!*suspend_compressor_name) {
81 printk("Suspend2: Compression enabled but no compressor name set.\n");
82 return 1;
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;
93 return 1;
96 this->page_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
98 if (!this->page_buffer) {
99 printk(KERN_ERR
100 "Failed to allocate a page buffer for suspend2 "
101 "encryption driver.\n");
102 return -ENOMEM;
106 return 0;
110 * suspend_compress_init
113 static int suspend_compress_init(int suspend_or_resume)
115 if (!suspend_or_resume)
116 return 0;
118 suspend_compress_bytes_in = suspend_compress_bytes_out = 0;
120 next_driver = suspend_get_next_filter(&suspend_compression_ops);
122 if (!next_driver) {
123 printk("Compression Driver: Argh! Nothing follows me in"
124 " the pipeline!\n");
125 return -ECHILD;
128 suspend_compress_prepare_result = suspend_compress_crypto_prepare();
130 return 0;
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");
141 if (rw == READ)
142 return -ENODEV;
143 else
144 suspend_compression_ops.enabled = 0;
147 return 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
161 * zlib errs.
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);
169 if (!ctx->transform)
170 return next_driver->write_chunk(index, buffer_page, buf_size);
172 ctx->buffer_start = kmap(buffer_page);
174 ctx->len = buf_size;
176 ret = crypto_comp_compress(ctx->transform,
177 ctx->buffer_start, buf_size,
178 ctx->page_buffer, &ctx->len);
180 kunmap(buffer_page);
182 if (ret) {
183 printk("Compression failed.\n");
184 goto failure;
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),
195 ctx->len);
196 else
197 ret = next_driver->write_chunk(index, buffer_page, buf_size);
199 failure:
200 return ret;
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
207 * synchronously.
209 * Retrieve data from later modules and decompress it until the input buffer
210 * is filled.
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();
217 unsigned int len;
218 unsigned int outlen = PAGE_SIZE;
219 char *buffer_start;
220 struct cpu_context *ctx = &per_cpu(contexts, cpu);
222 if (!ctx->transform)
223 return next_driver->read_chunk(index, buffer_page, buf_size,
224 sync);
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)
237 return ret;
239 buffer_start = kmap(buffer_page);
240 memcpy(ctx->page_buffer, buffer_start, len);
241 ret = crypto_comp_decompress(
242 ctx->transform,
243 ctx->page_buffer,
244 len, buffer_start, &outlen);
245 if (ret)
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",
251 outlen, PAGE_SIZE);
252 ret = -EIO;
253 *buf_size = outlen;
255 kunmap(buffer_page);
256 return ret;
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;
272 int len;
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);
278 else
279 len = snprintf_used(buffer, size, "- Compressor is not set.\n");
281 if (pages_in)
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);
287 return len;
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
295 * operation.
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;
317 int total_len;
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,
326 namelen);
327 total_len = 4 * sizeof(unsigned long) + namelen;
328 return total_len;
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
336 * resume time.
338 static void suspend_compress_load_config_info(char *buffer, int size)
340 int namelen;
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),
348 namelen);
349 return;
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)
364 return 100;
365 else
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)
390 * Ops structure.
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);
422 #ifdef MODULE
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");
433 #else
434 late_initcall(suspend_compress_load);
435 #endif