zlib: slim down zlib_deflate() workspace when possible
authorJim Keniston <jkenisto@linux.vnet.ibm.com>
Tue, 22 Mar 2011 23:35:12 +0000 (16:35 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 Mar 2011 00:44:17 +0000 (17:44 -0700)
Instead of always creating a huge (268K) deflate_workspace with the
maximum compression parameters (windowBits=15, memLevel=8), allow the
caller to obtain a smaller workspace by specifying smaller parameter
values.

For example, when capturing oops and panic reports to a medium with
limited capacity, such as NVRAM, compression may be the only way to
capture the whole report.  In this case, a small workspace (24K works
fine) is a win, whether you allocate the workspace when you need it (i.e.,
during an oops or panic) or at boot time.

I've verified that this patch works with all accepted values of windowBits
(positive and negative), memLevel, and compression level.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: David Miller <davem@davemloft.net>
Cc: Chris Mason <chris.mason@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
crypto/deflate.c
crypto/zlib.c
drivers/net/ppp_deflate.c
fs/btrfs/zlib.c
fs/jffs2/compr_zlib.c
fs/logfs/compr.c
include/linux/zlib.h
lib/zlib_deflate/deflate.c
lib/zlib_deflate/defutil.h

index cbc7a33a9600c2b1cb9e9bdc161f3586229b85d1..b5ccae29be7491571301b81beae5b135fa064628 100644 (file)
@@ -48,7 +48,8 @@ static int deflate_comp_init(struct deflate_ctx *ctx)
        int ret = 0;
        struct z_stream_s *stream = &ctx->comp_stream;
 
-       stream->workspace = vzalloc(zlib_deflate_workspacesize());
+       stream->workspace = vzalloc(zlib_deflate_workspacesize(
+                               -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL));
        if (!stream->workspace) {
                ret = -ENOMEM;
                goto out;
index 739b8fca4cea518777f569372ded2e0b751dcd49..d11d761a5e418191c29a987f923ac3e2be0f20a6 100644 (file)
@@ -85,6 +85,7 @@ static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
        struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
        struct z_stream_s *stream = &ctx->comp_stream;
        struct nlattr *tb[ZLIB_COMP_MAX + 1];
+       int window_bits, mem_level;
        size_t workspacesize;
        int ret;
 
@@ -94,7 +95,14 @@ static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
 
        zlib_comp_exit(ctx);
 
-       workspacesize = zlib_deflate_workspacesize();
+       window_bits = tb[ZLIB_COMP_WINDOWBITS]
+                                       ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
+                                       : MAX_WBITS;
+       mem_level = tb[ZLIB_COMP_MEMLEVEL]
+                                       ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
+                                       : DEF_MEM_LEVEL;
+
+       workspacesize = zlib_deflate_workspacesize(window_bits, mem_level);
        stream->workspace = vzalloc(workspacesize);
        if (!stream->workspace)
                return -ENOMEM;
@@ -106,12 +114,8 @@ static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
                                tb[ZLIB_COMP_METHOD]
                                        ? nla_get_u32(tb[ZLIB_COMP_METHOD])
                                        : Z_DEFLATED,
-                               tb[ZLIB_COMP_WINDOWBITS]
-                                       ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
-                                       : MAX_WBITS,
-                               tb[ZLIB_COMP_MEMLEVEL]
-                                       ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
-                                       : DEF_MEM_LEVEL,
+                               window_bits,
+                               mem_level,
                                tb[ZLIB_COMP_STRATEGY]
                                        ? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
                                        : Z_DEFAULT_STRATEGY);
index 43583309a65db6ec7053c182b479cb4e13c4471d..31e9407a07394e6bdeaf976afe59b012160e2c9b 100644 (file)
@@ -129,7 +129,7 @@ static void *z_comp_alloc(unsigned char *options, int opt_len)
 
        state->strm.next_in   = NULL;
        state->w_size         = w_size;
-       state->strm.workspace = vmalloc(zlib_deflate_workspacesize());
+       state->strm.workspace = vmalloc(zlib_deflate_workspacesize(-w_size, 8));
        if (state->strm.workspace == NULL)
                goto out_free;
 
index f5ec2d44150df2a484d6f6b88f856ba9ba8b6464..faccd47c6c468fb7534f43ecf8a4f7dd691def77 100644 (file)
@@ -57,7 +57,8 @@ static struct list_head *zlib_alloc_workspace(void)
        if (!workspace)
                return ERR_PTR(-ENOMEM);
 
-       workspace->def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
+       workspace->def_strm.workspace = vmalloc(zlib_deflate_workspacesize(
+                                               MAX_WBITS, MAX_MEM_LEVEL));
        workspace->inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
        workspace->buf = kmalloc(PAGE_CACHE_SIZE, GFP_NOFS);
        if (!workspace->def_strm.workspace ||
index fd05a0b9431df3e2bb37ed3b4bed29a131c37ffd..5a001020c542a652726e18521429d2d44b6c31d5 100644 (file)
@@ -40,12 +40,13 @@ static z_stream inf_strm, def_strm;
 
 static int __init alloc_workspaces(void)
 {
-       def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
+       def_strm.workspace = vmalloc(zlib_deflate_workspacesize(MAX_WBITS,
+                                                       MAX_MEM_LEVEL));
        if (!def_strm.workspace) {
-               printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
+               printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL));
                return -ENOMEM;
        }
-       D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
+       D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL)));
        inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
        if (!inf_strm.workspace) {
                printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
index 44bbfd249abc129ac7544be77b9cbd7a62b04e3e..961f02b86d97e22f595e235e96c8876bff08214e 100644 (file)
@@ -81,7 +81,7 @@ error:
 
 int __init logfs_compr_init(void)
 {
-       size_t size = max(zlib_deflate_workspacesize(),
+       size_t size = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
                        zlib_inflate_workspacesize());
        stream.workspace = vmalloc(size);
        if (!stream.workspace)
index 40c49cb3eb518355f14d05ec8d043e00f1bb45c3..9c5a6b4de0a30d716b2ec72470813be3a2dd82a8 100644 (file)
@@ -179,11 +179,16 @@ typedef z_stream *z_streamp;
 
                         /* basic functions */
 
-extern int zlib_deflate_workspacesize (void);
+extern int zlib_deflate_workspacesize (int windowBits, int memLevel);
 /*
    Returns the number of bytes that needs to be allocated for a per-
-   stream workspace.  A pointer to this number of bytes should be
-   returned in stream->workspace before calling zlib_deflateInit().
+   stream workspace with the specified parameters.  A pointer to this
+   number of bytes should be returned in stream->workspace before
+   you call zlib_deflateInit() or zlib_deflateInit2().  If you call
+   zlib_deflateInit(), specify windowBits = MAX_WBITS and memLevel =
+   MAX_MEM_LEVEL here.  If you call zlib_deflateInit2(), the windowBits
+   and memLevel parameters passed to zlib_deflateInit2() must not
+   exceed those passed here.
 */
 
 /* 
index 46a31e5f49c3ef44ad5e7d2ecd557476b7f466f1..d63381e8e3331064463283420d872842e27c8a9a 100644 (file)
@@ -176,6 +176,7 @@ int zlib_deflateInit2(
     deflate_state *s;
     int noheader = 0;
     deflate_workspace *mem;
+    char *next;
 
     ush *overlay;
     /* We overlay pending_buf and d_buf+l_buf. This works since the average
@@ -199,6 +200,21 @@ int zlib_deflateInit2(
        strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
         return Z_STREAM_ERROR;
     }
+
+    /*
+     * Direct the workspace's pointers to the chunks that were allocated
+     * along with the deflate_workspace struct.
+     */
+    next = (char *) mem;
+    next += sizeof(*mem);
+    mem->window_memory = (Byte *) next;
+    next += zlib_deflate_window_memsize(windowBits);
+    mem->prev_memory = (Pos *) next;
+    next += zlib_deflate_prev_memsize(windowBits);
+    mem->head_memory = (Pos *) next;
+    next += zlib_deflate_head_memsize(memLevel);
+    mem->overlay_memory = next;
+
     s = (deflate_state *) &(mem->deflate_memory);
     strm->state = (struct internal_state *)s;
     s->strm = strm;
@@ -1247,7 +1263,18 @@ static block_state deflate_slow(
     return flush == Z_FINISH ? finish_done : block_done;
 }
 
-int zlib_deflate_workspacesize(void)
+int zlib_deflate_workspacesize(int windowBits, int memLevel)
 {
-    return sizeof(deflate_workspace);
+    if (windowBits < 0) /* undocumented feature: suppress zlib header */
+        windowBits = -windowBits;
+
+    /* Since the return value is typically passed to vmalloc() unchecked... */
+    BUG_ON(memLevel < 1 || memLevel > MAX_MEM_LEVEL || windowBits < 9 ||
+                                                       windowBits > 15);
+
+    return sizeof(deflate_workspace)
+        + zlib_deflate_window_memsize(windowBits)
+        + zlib_deflate_prev_memsize(windowBits)
+        + zlib_deflate_head_memsize(memLevel)
+        + zlib_deflate_overlay_memsize(memLevel);
 }
index 6b15a909ca3f0cfc163b4cdcd8a1613884643784..b640b6402e99e3a74db809012a4138c4cef48457 100644 (file)
@@ -241,12 +241,21 @@ typedef struct deflate_state {
 typedef struct deflate_workspace {
     /* State memory for the deflator */
     deflate_state deflate_memory;
-    Byte window_memory[2 * (1 << MAX_WBITS)];
-    Pos prev_memory[1 << MAX_WBITS];
-    Pos head_memory[1 << (MAX_MEM_LEVEL + 7)];
-    char overlay_memory[(1 << (MAX_MEM_LEVEL + 6)) * (sizeof(ush)+2)];
+    Byte *window_memory;
+    Pos *prev_memory;
+    Pos *head_memory;
+    char *overlay_memory;
 } deflate_workspace;
 
+#define zlib_deflate_window_memsize(windowBits) \
+       (2 * (1 << (windowBits)) * sizeof(Byte))
+#define zlib_deflate_prev_memsize(windowBits) \
+       ((1 << (windowBits)) * sizeof(Pos))
+#define zlib_deflate_head_memsize(memLevel) \
+       ((1 << ((memLevel)+7)) * sizeof(Pos))
+#define zlib_deflate_overlay_memsize(memLevel) \
+       ((1 << ((memLevel)+6)) * (sizeof(ush)+2))
+
 /* Output a byte on the stream.
  * IN assertion: there is enough room in pending_buf.
  */