Skip to content

Commit

Permalink
pstore: Add compression support to pstore
Browse files Browse the repository at this point in the history
Add compression support to pstore which will help in capturing more data.
Initially, pstore will make a call to kmsg_dump with a bigger buffer
and will pass the size of bigger buffer to kmsg_dump and then compress
the data to registered buffer of registered size.

In case compression fails, pstore will capture the uncompressed
data by making a call again to kmsg_dump with registered_buffer
of registered size.

Pstore will indicate the data is compressed or not with a flag
in the write callback.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Tony Luck <tony.luck@intel.com>
  • Loading branch information
arunabal authored and aegl committed Aug 19, 2013
1 parent 90ce4ca commit b0aad7a
Showing 1 changed file with 139 additions and 9 deletions.
148 changes: 139 additions & 9 deletions fs/pstore/platform.c
Expand Up @@ -26,6 +26,7 @@
#include <linux/console.h>
#include <linux/module.h>
#include <linux/pstore.h>
#include <linux/zlib.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/slab.h>
Expand Down Expand Up @@ -65,6 +66,15 @@ struct pstore_info *psinfo;

static char *backend;

/* Compression parameters */
#define COMPR_LEVEL 6
#define WINDOW_BITS 12
#define MEM_LEVEL 4
static struct z_stream_s stream;

static char *big_oops_buf;
static size_t big_oops_buf_sz;

/* How much of the console log to snapshot */
static unsigned long kmsg_bytes = 10240;

Expand Down Expand Up @@ -117,6 +127,91 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
}
EXPORT_SYMBOL_GPL(pstore_cannot_block_path);

/* Derived from logfs_compress() */
static int pstore_compress(const void *in, void *out, size_t inlen,
size_t outlen)
{
int err, ret;

ret = -EIO;
err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
MEM_LEVEL, Z_DEFAULT_STRATEGY);
if (err != Z_OK)
goto error;

stream.next_in = in;
stream.avail_in = inlen;
stream.total_in = 0;
stream.next_out = out;
stream.avail_out = outlen;
stream.total_out = 0;

err = zlib_deflate(&stream, Z_FINISH);
if (err != Z_STREAM_END)
goto error;

err = zlib_deflateEnd(&stream);
if (err != Z_OK)
goto error;

if (stream.total_out >= stream.total_in)
goto error;

ret = stream.total_out;
error:
return ret;
}

static void allocate_buf_for_compression(void)
{
size_t size;

big_oops_buf_sz = (psinfo->bufsize * 100) / 45;
big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
if (big_oops_buf) {
size = max(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
zlib_inflate_workspacesize());
stream.workspace = kmalloc(size, GFP_KERNEL);
if (!stream.workspace) {
pr_err("pstore: No memory for compression workspace; "
"skipping compression\n");
kfree(big_oops_buf);
big_oops_buf = NULL;
}
} else {
pr_err("No memory for uncompressed data; "
"skipping compression\n");
stream.workspace = NULL;
}

}

/*
* Called when compression fails, since the printk buffer
* would be fetched for compression calling it again when
* compression fails would have moved the iterator of
* printk buffer which results in fetching old contents.
* Copy the recent messages from big_oops_buf to psinfo->buf
*/
static size_t copy_kmsg_to_buffer(int hsize, size_t len)
{
size_t total_len;
size_t diff;

total_len = hsize + len;

if (total_len > psinfo->bufsize) {
diff = total_len - psinfo->bufsize + hsize;
memcpy(psinfo->buf, big_oops_buf, hsize);
memcpy(psinfo->buf + hsize, big_oops_buf + diff,
psinfo->bufsize - hsize);
total_len = psinfo->bufsize;
} else
memcpy(psinfo->buf, big_oops_buf, total_len);

return total_len;
}

/*
* callback from kmsg_dump. (s2,l2) has the most recently
* written bytes, older bytes are in (s1,l1). Save as much
Expand Down Expand Up @@ -148,23 +243,56 @@ static void pstore_dump(struct kmsg_dumper *dumper,
char *dst;
unsigned long size;
int hsize;
int zipped_len = -1;
size_t len;
bool compressed = false;
bool compressed;
size_t total_len;

dst = psinfo->buf;
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
size = psinfo->bufsize - hsize;
dst += hsize;
if (big_oops_buf) {
dst = big_oops_buf;
hsize = sprintf(dst, "%s#%d Part%d\n", why,
oopscount, part);
size = big_oops_buf_sz - hsize;

if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len))
break;
if (!kmsg_dump_get_buffer(dumper, true, dst + hsize,
size, &len))
break;

zipped_len = pstore_compress(dst, psinfo->buf,
hsize + len, psinfo->bufsize);

if (zipped_len > 0) {
compressed = true;
total_len = zipped_len;
} else {
pr_err("pstore: compression failed for Part %d"
" returned %d\n", part, zipped_len);
pr_err("pstore: Capture uncompressed"
" oops/panic report of Part %d\n", part);
compressed = false;
total_len = copy_kmsg_to_buffer(hsize, len);
}
} else {
dst = psinfo->buf;
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount,
part);
size = psinfo->bufsize - hsize;
dst += hsize;

if (!kmsg_dump_get_buffer(dumper, true, dst,
size, &len))
break;

compressed = false;
total_len = hsize + len;
}

ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
oopscount, compressed, hsize + len, psinfo);
oopscount, compressed, total_len, psinfo);
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_new_entry = 1;

total += hsize + len;
total += total_len;
part++;
}
if (pstore_cannot_block_path(reason)) {
Expand Down Expand Up @@ -262,6 +390,8 @@ int pstore_register(struct pstore_info *psi)
return -EINVAL;
}

allocate_buf_for_compression();

if (pstore_is_mounted())
pstore_get_records(0);

Expand Down

0 comments on commit b0aad7a

Please sign in to comment.