Suspend 2 merge: 47/51: GZIP support.

Nigel Cunningham
2004-11-24T13:36:33Z

The original compressor. Slow. I've tried to drop it, but for reasons I
simply don't understand, some users still want it.

diff -ruN 853-gzip-old/kernel/power/suspend_gzip.c 853-gzip-new/kernel/power/suspend_gzip.c
--- 853-gzip-old/kernel/power/suspend_gzip.c 1970-01-01 10:00:00.000000000 +1000
+++ 853-gzip-new/kernel/power/suspend_gzip.c 2004-11-11 08:46:25.000000000 +1100
@@ -0,0 +1,560 @@
+/*
+ * kernel/power/gzip_compression.c
+ *
+ * Copyright (C) 2003,2004 Nigel Cunningham <ncunningham@linuxmail.org>
+ *
+ * This file is released under the GPLv2.
+ *
+ * This file contains data compression routines for suspend.
+ * Compression is implemented using the zlib library.
+ *
+ */
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/zlib.h>
+
+#include "plugins.h"
+#include "proc.h"
+#include "suspend.h"
+
+/* Forward declaration for the ops structure we export */
+struct suspend_plugin_ops gzip_compression_ops;
+
+/* The next driver in the pipeline */
+static struct suspend_plugin_ops * next_driver;
+
+/* Zlib routines we use to compress/decompress the data */
+extern int zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
+ u32 *sourcelen, u32 *dstlen);
+extern void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ u32 srclen, u32 destlen);
+
+/* Buffers */
+static void *compression_workspace = NULL;
+static char *local_buffer = NULL;
+
+/* Configuration data we pass to zlib */
+static z_stream strm;
+
+/* Stats we save */
+static __nosavedata unsigned long bytes_in = 0, bytes_out = 0;
+
+/* Expected compression is used to reduce the amount of storage allocated */
+static int expected_gzip_compression = 0;
+
+/* ---- Zlib memory management ---- */
+
+/* allocate_zlib_compression_space
+ *
+ * Description: Allocate space for zlib to use in compressing our data.
+ * Each call must have a matching call to free_zlib_memory.
+ * Returns: Int: Zero if successful, -ENONEM otherwise.
+ */
+static inline int allocate_zlib_compression_space(void)
+{
+ BUG_ON(compression_workspace);
+
+ compression_workspace = vmalloc_32(zlib_deflate_workspacesize());
+ if (!compression_workspace) {
+ printk(KERN_WARNING
+ "Failed to allocate %d bytes for deflate workspace\n",
+ zlib_deflate_workspacesize());
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* allocate_zlib_decompression_space
+ *
+ * Description: Allocate space for zlib to use in decompressing our data.
+ * Each call must have a matching call to free_zlib_memory.
+ * Returns: Int: Zero if successful, -ENONEM otherwise.
+ */
+static inline int allocate_zlib_decompression_space(void)
+{
+ BUG_ON(compression_workspace);
+
+ compression_workspace = vmalloc_32(zlib_inflate_workspacesize());
+ if (!compression_workspace) {
+ printk(KERN_WARNING
+ "Failed to allocate %d bytes for inflate workspace\n",
+ zlib_inflate_workspacesize());
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* free_zlib_memory
+ *
+ * Description: Frees memory allocated by either allocation routine (above).
+ */
+static inline void free_zlib_memory(void)
+{
+ if (!compression_workspace)
+ return;
+
+ vfree(compression_workspace);
+ compression_workspace = NULL;
+}
+
+/* ---- Local buffer management ---- */
+
+/* allocate_local_buffer
+ *
+ * Description: Allocates a page of memory for buffering output.
+ * Returns: Int: Zero if successful, -ENONEM otherwise.
+ */
+static int allocate_local_buffer(void)
+{
+ if (local_buffer)
+ return 0;
+
+ local_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+
+ if (!local_buffer) {
+ printk(KERN_ERR
+ "Failed to allocate a page for compression "
+ "driver's buffer.\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* free_local_buffer
+ *
+ * Description: Frees memory allocated for buffering output.
+ */
+static inline void free_local_buffer(void)
+{
+ if (local_buffer)
+ free_pages((unsigned long) local_buffer, 0);
+ local_buffer = NULL;
+}
+
+/* ---- Functions exported via operations struct ---- */
+
+/* gzip_write_init
+ *
+ * Description: Allocate buffers and prepares zlib for deflating a new stream
+ * of data.
+ * Arguments: Stream_number: Ignored.
+ * Returns: Int. Zero if successful, otherwise an appropriate error number.
+ */
+
+static int gzip_write_init(int stream_number)
+{
+ int result;
+
+ next_driver = get_next_filter(&gzip_compression_ops);
+
+ if (!next_driver) {
+ printk("GZip Compression Driver: Argh! No one wants my output!");
+ return -ECHILD;
+ }
+
+ if ((result = allocate_zlib_compression_space()))
+ return result;
+
+ if ((result = allocate_local_buffer()))
+ return result;
+
+ strm.total_in = 0;
+ strm.total_out = 0;
+ strm.workspace = compression_workspace;
+ strm.next_out = (char *) local_buffer;
+ strm.avail_out = PAGE_SIZE;
+ result = zlib_deflateInit(&strm, Z_BEST_SPEED);
+
+ if (Z_OK != result) {
+ printk(KERN_ERR name_suspend "Failed to initialise zlib.\n");
+ return -EPERM;
+ }
+
+ /*
+ * Reset the statistics iif we are about to write the first part of
+ * the image
+ */
+ if (stream_number == 2)
+ bytes_in = bytes_out = 0;
+
+ return 0;
+}
+
+/* gzip_write_chunk()
+ *
+ * Description: Compress a page of data, buffering output and passing on
+ * filled pages to the next plugin in the pipeline.
+ * Arguments: Buffer_start: Pointer to a buffer of size PAGE_SIZE,
+ * containing data to be compressed.
+ * Returns: 0 on success. Otherwise the error is that returned by later
+ * plugins, -ECHILD if we have a broken pipeline or -EPERM if
+ * zlib errs.
+ */
+
+static int gzip_write_chunk(struct page * buffer_page)
+{
+ int ret;
+ char * buffer_start = kmap(buffer_page);
+
+ /* Work to do */
+ strm.next_in = buffer_start;
+ strm.avail_in = PAGE_SIZE;
+ while (strm.avail_in) {
+ ret = zlib_deflate(&strm, Z_PARTIAL_FLUSH);
+ if (ret != Z_OK) {
+ printk("Zlib failed to compress our data. "
+ "Result code was %d.\n", ret);
+ kunmap(buffer_page);
+ return -EPERM;
+ }
+
+ if (!strm.avail_out) {
+
+ if ((ret = next_driver->ops.filter.write_chunk(
+ virt_to_page(local_buffer)))) {
+ kunmap(buffer_page);
+ return ret;
+ }
+ strm.next_out = local_buffer;
+ strm.avail_out = PAGE_SIZE;
+ }
+ }
+ kunmap(buffer_page);
+
+ return 0;
+}
+
+/* gzip_write_cleanup()
+ *
+ * Description: Flush remaining data, update statistics and free allocated
+ * space.
+ * Returns: Zero. Never fails. Okay. Zlib might fail... but it shouldn't.
+ */
+
+static int gzip_write_cleanup(void)
+{
+ int ret = 0, finished = 0;
+
+ while (!finished) {
+ if (strm.avail_out) {
+ ret = zlib_deflate(&strm, Z_FINISH);
+
+ if (ret == Z_STREAM_END) {
+ ret = zlib_deflateEnd(&strm);
+ finished = 1;
+ }
+
+ if ((ret != Z_OK) && (ret != Z_STREAM_END)) {
+ zlib_deflateEnd(&strm);
+ printk("Failed to finish compressing data. "
+ "Result %d received.\n", ret);
+ return -EPERM;
+ }
+ }
+
+ if ((!strm.avail_out) || (finished)) {
+ if ((ret = next_driver->ops.filter.write_chunk(
+ virt_to_page(local_buffer))))
+ return ret;
+ strm.next_out = local_buffer;
+ strm.avail_out = PAGE_SIZE;
+ }
+ }
+
+ bytes_in+= strm.total_in;
+ bytes_out+= strm.total_out;
+
+ free_zlib_memory();
+ free_local_buffer();
+
+ return 0;
+}
+
+/* gzip_read_init
+ *
+ * Description: Prepare to read a new stream of data.
+ * Arguments: Stream_number: Not used.
+ * Returns: Int. Zero if successful, otherwise an appropriate error number.
+ */
+
+static int gzip_read_init(int stream_number)
+{
+ int result;
+
+ next_driver = get_next_filter(&gzip_compression_ops);
+
+ if (!next_driver) {
+ printk("GZip Compression Driver: Argh! "
+ "No one wants to feed me data!");
+ return -ECHILD;
+ }
+
+ if ((result = allocate_zlib_decompression_space()))
+ return result;
+
+ if ((result = allocate_local_buffer()))
+ return result;
+
+ strm.total_in = 0;
+ strm.total_out = 0;
+ strm.workspace = compression_workspace;
+ strm.avail_in = 0;
+ if ((result = zlib_inflateInit(&strm)) != Z_OK) {
+ printk(KERN_ERR name_suspend "Failed to initialise zlib.\n");
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/* gzip_read_chunk()
+ *
+ * Description: Retrieve data from later plugins and decompress it until the
+ * input buffer is filled.
+ * Arguments: Buffer_start: Pointer to a buffer of size PAGE_SIZE.
+ * Sync: Whether the previous plugin (or core) wants its
+ * data synchronously.
+ * Returns: Zero if successful. Error condition from me or from downstream
+ * on failure.
+ */
+
+static int gzip_read_chunk(struct page * buffer_page, int sync)
+{
+ int ret;
+ char * buffer_start = kmap(buffer_page);
+
+ /*
+ * All our reads must be synchronous - we can't decompress
+ * data that hasn't been read yet.
+ */
+
+ /* Work to do */
+ strm.next_out = buffer_start;
+ strm.avail_out = PAGE_SIZE;
+ while (strm.avail_out) {
+ if (!strm.avail_in) {
+ if ((ret = next_driver->ops.filter.read_chunk(
+ virt_to_page(local_buffer),
+ SUSPEND_SYNC)) < 0) {
+ kunmap(buffer_page);
+ return ret;
+ }
+ strm.next_in = local_buffer;
+ strm.avail_in = PAGE_SIZE;
+ }
+
+ ret = zlib_inflate(&strm, Z_PARTIAL_FLUSH);
+
+ if ((ret == Z_BUF_ERROR) && (!strm.avail_in)) {
+ continue;
+ }
+
+ if ((ret != Z_OK) && (ret != Z_STREAM_END)) {
+ printk("Zlib failed to decompress our data. "
+ "Result code was %d.\n", ret);
+ kunmap(buffer_page);
+ return -EPERM;
+ }
+ }
+ kunmap(buffer_page);
+
+ return 0;
+}
+
+/* read_cleanup()
+ *
+ * Description: Clean up after reading part or all of a stream of data.
+ * Returns: int: Always zero. Never fails.
+ */
+
+static int gzip_read_cleanup(void)
+{
+ zlib_inflateEnd(&strm);
+
+ free_zlib_memory();
+ free_local_buffer();
+ return 0;
+}
+
+/* gzip_print_debug_stats
+ *
+ * Description: Print information to be recorded for debugging purposes into a
+ * buffer.
+ * Arguments: buffer: Pointer to a buffer into which the debug info will be
+ * printed.
+ * size: Size of the buffer.
+ * Returns: Number of characters written to the buffer.
+ */
+
+static int gzip_print_debug_stats(char * buffer, int size)
+{
+ int pages_in = bytes_in >> PAGE_SHIFT;
+ int pages_out = bytes_out >> PAGE_SHIFT;
+ int len;
+
+ //Output the compression ratio achieved.
+ len = suspend_snprintf(buffer, size, "- GZIP compressor enabled.\n");
+ if (pages_in)
+ len+= suspend_snprintf(buffer+len, size - len,
+ " Compressed %ld bytes into %ld.\n "
+ "Image compressed by %d percent.\n",
+ bytes_in, bytes_out, (pages_in - pages_out) * 100 / pages_in);
+ return len;
+}
+
+/* compression_memory_needed
+ *
+ * Description: Tell the caller how much memory we need to operate during
+ * suspend/resume.
+ * Returns: Unsigned long. Maximum number of bytes of memory required for
+ * operation.
+ */
+
+static unsigned long gzip_memory_needed(void)
+{
+ return PAGE_SIZE + max( zlib_deflate_workspacesize(),
+ zlib_inflate_workspacesize());
+}
+
+static unsigned long gzip_storage_needed(void)
+{
+ return 2 * sizeof(unsigned long);
+}
+
+/* gzip_save_config_info
+ *
+ * Description: Save information needed when reloading the image at resume time.
+ * Arguments: Buffer: Pointer to a buffer of size PAGE_SIZE.
+ * Returns: Number of bytes used for saving our data.
+ */
+
+static int gzip_save_config_info(char * buffer)
+{
+ *((unsigned long *) buffer) = bytes_in;
+ *((unsigned long *) (buffer + sizeof(unsigned long))) = bytes_out;
+ *((int *) (buffer + 2 * sizeof(unsigned long))) = expected_gzip_compression;
+ return 2 * sizeof(unsigned long) + sizeof(int);
+}
+
+/* gzip_load_config_info
+ *
+ * Description: Reload information needed for decompressing the image at
+ * resume time.
+ * Arguments: Buffer: Pointer to the start of the data.
+ * Size: Number of bytes that were saved.
+ */
+
+static void gzip_load_config_info(char * buffer, int size)
+{
+ if(size == 2 * sizeof(unsigned long) + sizeof(int)) {
+ bytes_in = *((unsigned long *) buffer);
+ bytes_out = *((unsigned long *) (buffer + sizeof(unsigned long)));
+ expected_gzip_compression = *((int *) (buffer + 2 * sizeof(unsigned long)));
+ } else
+ printk("Suspend GZIP config info size mismatch: settings ignored.\n");
+ return;
+}
+
+/* gzip_get_expected_compression
+ *
+ * Description: Returns the expected ratio between data passed into this plugin
+ * and the amount of data output when writing.
+ * Returns: The value set by the user via our proc entry.
+ */
+
+static int gzip_get_expected_compression(void)
+{
+ return 100 - expected_gzip_compression;
+}
+
+/*
+ * data for our proc entries.
+ */
+
+struct suspend_proc_data expected_compression_proc_data = {
+ .filename = "expected_gzip_compression",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_INTEGER,
+ .data = {
+ .integer = {
+ .variable = &expected_gzip_compression,
+ .minimum = 0,
+ .maximum = 99,
+ }
+ }
+};
+
+struct suspend_proc_data disable_compression_proc_data = {
+ .filename = "disable_gzip_compression",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_INTEGER,
+ .data = {
+ .integer = {
+ .variable = &gzip_compression_ops.disabled,
+ .minimum = 0,
+ .maximum = 1,
+ }
+ }
+};
+
+/*
+ * Ops structure.
+ */
+
+struct suspend_plugin_ops gzip_compression_ops = {
+ .type = FILTER_PLUGIN,
+ .name = "Zlib Page Compressor",
+ .memory_needed = gzip_memory_needed,
+ .print_debug_info = gzip_print_debug_stats,
+ .save_config_info = gzip_save_config_info,
+ .load_config_info = gzip_load_config_info,
+ .storage_needed = gzip_storage_needed,
+ .ops = {
+ .filter = {
+ .write_init = gzip_write_init,
+ .write_chunk = gzip_write_chunk,
+ .write_cleanup = gzip_write_cleanup,
+ .read_init = gzip_read_init,
+ .read_chunk = gzip_read_chunk,
+ .read_cleanup = gzip_read_cleanup,
+ .expected_compression = gzip_get_expected_compression,
+ }
+ }
+};
+
+/* ---- Registration ---- */
+
+static __init int gzip_load(void)
+{
+ int result;
+
+ if (!(result = suspend_register_plugin(&gzip_compression_ops))) {
+ printk("Software Suspend Gzip Compression Driver registered.\n");
+ suspend_register_procfile(&expected_compression_proc_data);
+ suspend_register_procfile(&disable_compression_proc_data);
+ }
+ return result;
+}
+
+#ifdef MODULE
+static __exit void gzip_unload(void)
+{
+ printk("Software Suspend Gzip Compression Driver unloading.\n");
+ suspend_unregister_procfile(&expected_compression_proc_data);
+ suspend_unregister_procfile(&disable_compression_proc_data);
+ suspend_unregister_plugin(&gzip_compression_ops);
+}
+
+module_init(gzip_load);
+module_exit(gzip_unload);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nigel Cunningham");
+MODULE_DESCRIPTION("Gzip Compression support for Suspend2");
+#else
+late_initcall(gzip_load);
+#endif