lkml.org 
[lkml]   [2004]   [Nov]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    SubjectSuspend 2 merge: 47/51: GZIP support.
    From
    Date
    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

    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2005-03-22 14:08    [W:0.045 / U:189.780 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site