Skip to content

Commit

Permalink
zlib: slim down zlib_deflate() workspace when possible
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jim Keniston authored and torvalds committed Mar 23, 2011
1 parent b12d125 commit 565d76c
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 23 deletions.
3 changes: 2 additions & 1 deletion crypto/deflate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
18 changes: 11 additions & 7 deletions crypto/zlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/ppp_deflate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion fs/btrfs/zlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 ||
Expand Down
7 changes: 4 additions & 3 deletions fs/jffs2/compr_zlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
2 changes: 1 addition & 1 deletion fs/logfs/compr.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ int logfs_uncompress(void *in, void *out, size_t inlen, size_t outlen)

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)
Expand Down
11 changes: 8 additions & 3 deletions include/linux/zlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/

/*
Expand Down
31 changes: 29 additions & 2 deletions lib/zlib_deflate/deflate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
17 changes: 13 additions & 4 deletions lib/zlib_deflate/defutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down

0 comments on commit 565d76c

Please sign in to comment.