Commit f83b0a4e authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'please-pull-pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux

Pull pstore changes from Tony Luck:
 "A big part of this is the addition of compression to the generic
  pstore layer so that all backends can use the pitiful amounts of
  storage they control more effectively.  Three other small
  fixes/cleanups too.

* tag 'please-pull-pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux:
  pstore/ram: (really) fix undefined usage of rounddown_pow_of_two
  pstore/ram: Read and write to the 'compressed' flag of pstore
  efi-pstore: Read and write to the 'compressed' flag of pstore
  erst: Read and write to the 'compressed' flag of pstore
  powerpc/pseries: Read and write to the 'compressed' flag of pstore
  pstore: Add file extension to pstore file if compressed
  pstore: Add decompression support to pstore
  pstore: Introduce new argument 'compressed' in the read callback
  pstore: Add compression support to pstore
  pstore/Kconfig: Select ZLIB_DEFLATE and ZLIB_INFLATE when PSTORE is selected
  pstore: Add new argument 'compressed' in pstore write callback
  powerpc/pseries: Remove (de)compression in nvram with pstore enabled
  pstore: d_alloc_name() doesn't return an ERR_PTR
  acpi/apei/erst: Add missing iounmap() on error in erst_exec_move_data()
parents 32dad03d 3bd11cf5
...@@ -539,36 +539,6 @@ static int zip_oops(size_t text_len) ...@@ -539,36 +539,6 @@ static int zip_oops(size_t text_len)
} }
#ifdef CONFIG_PSTORE #ifdef CONFIG_PSTORE
/* Derived from logfs_uncompress */
int nvram_decompress(void *in, void *out, size_t inlen, size_t outlen)
{
int err, ret;
ret = -EIO;
err = zlib_inflateInit(&stream);
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_inflate(&stream, Z_FINISH);
if (err != Z_STREAM_END)
goto error;
err = zlib_inflateEnd(&stream);
if (err != Z_OK)
goto error;
ret = stream.total_out;
error:
return ret;
}
static int nvram_pstore_open(struct pstore_info *psi) static int nvram_pstore_open(struct pstore_info *psi)
{ {
/* Reset the iterator to start reading partitions again */ /* Reset the iterator to start reading partitions again */
...@@ -584,7 +554,7 @@ static int nvram_pstore_open(struct pstore_info *psi) ...@@ -584,7 +554,7 @@ static int nvram_pstore_open(struct pstore_info *psi)
* @part: pstore writes data to registered buffer in parts, * @part: pstore writes data to registered buffer in parts,
* part number will indicate the same. * part number will indicate the same.
* @count: Indicates oops count * @count: Indicates oops count
* @hsize: Size of header added by pstore * @compressed: Flag to indicate the log is compressed
* @size: number of bytes written to the registered buffer * @size: number of bytes written to the registered buffer
* @psi: registered pstore_info structure * @psi: registered pstore_info structure
* *
...@@ -595,7 +565,7 @@ static int nvram_pstore_open(struct pstore_info *psi) ...@@ -595,7 +565,7 @@ static int nvram_pstore_open(struct pstore_info *psi)
static int nvram_pstore_write(enum pstore_type_id type, static int nvram_pstore_write(enum pstore_type_id type,
enum kmsg_dump_reason reason, enum kmsg_dump_reason reason,
u64 *id, unsigned int part, int count, u64 *id, unsigned int part, int count,
size_t hsize, size_t size, bool compressed, size_t size,
struct pstore_info *psi) struct pstore_info *psi)
{ {
int rc; int rc;
...@@ -611,30 +581,11 @@ static int nvram_pstore_write(enum pstore_type_id type, ...@@ -611,30 +581,11 @@ static int nvram_pstore_write(enum pstore_type_id type,
oops_hdr->report_length = (u16) size; oops_hdr->report_length = (u16) size;
oops_hdr->timestamp = get_seconds(); oops_hdr->timestamp = get_seconds();
if (big_oops_buf) { if (compressed)
rc = zip_oops(size);
/*
* If compression fails copy recent log messages from
* big_oops_buf to oops_data.
*/
if (rc != 0) {
size_t diff = size - oops_data_sz + hsize;
if (size > oops_data_sz) {
memcpy(oops_data, big_oops_buf, hsize);
memcpy(oops_data + hsize, big_oops_buf + diff,
oops_data_sz - hsize);
oops_hdr->report_length = (u16) oops_data_sz;
} else
memcpy(oops_data, big_oops_buf, size);
} else
err_type = ERR_TYPE_KERNEL_PANIC_GZ; err_type = ERR_TYPE_KERNEL_PANIC_GZ;
}
rc = nvram_write_os_partition(&oops_log_partition, oops_buf, rc = nvram_write_os_partition(&oops_log_partition, oops_buf,
(int) (sizeof(*oops_hdr) + oops_hdr->report_length), err_type, (int) (sizeof(*oops_hdr) + size), err_type, count);
count);
if (rc != 0) if (rc != 0)
return rc; return rc;
...@@ -650,12 +601,12 @@ static int nvram_pstore_write(enum pstore_type_id type, ...@@ -650,12 +601,12 @@ static int nvram_pstore_write(enum pstore_type_id type,
*/ */
static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *time, char **buf, int *count, struct timespec *time, char **buf,
struct pstore_info *psi) bool *compressed, struct pstore_info *psi)
{ {
struct oops_log_info *oops_hdr; struct oops_log_info *oops_hdr;
unsigned int err_type, id_no, size = 0; unsigned int err_type, id_no, size = 0;
struct nvram_os_partition *part = NULL; struct nvram_os_partition *part = NULL;
char *buff = NULL, *big_buff = NULL; char *buff = NULL;
int sig = 0; int sig = 0;
loff_t p; loff_t p;
...@@ -719,8 +670,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -719,8 +670,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
*id = id_no; *id = id_no;
if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) {
int length, unzipped_len; size_t length, hdr_size;
size_t hdr_size;
oops_hdr = (struct oops_log_info *)buff; oops_hdr = (struct oops_log_info *)buff;
if (oops_hdr->version < OOPS_HDR_VERSION) { if (oops_hdr->version < OOPS_HDR_VERSION) {
...@@ -741,23 +691,10 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -741,23 +691,10 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
memcpy(*buf, buff + hdr_size, length); memcpy(*buf, buff + hdr_size, length);
kfree(buff); kfree(buff);
if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) { if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
big_buff = kmalloc(big_oops_buf_sz, GFP_KERNEL); *compressed = true;
if (!big_buff) else
return -ENOMEM; *compressed = false;
unzipped_len = nvram_decompress(*buf, big_buff,
length, big_oops_buf_sz);
if (unzipped_len < 0) {
pr_err("nvram: decompression failed, returned "
"rc %d\n", unzipped_len);
kfree(big_buff);
} else {
*buf = big_buff;
length = unzipped_len;
}
}
return length; return length;
} }
...@@ -777,13 +714,8 @@ static int nvram_pstore_init(void) ...@@ -777,13 +714,8 @@ static int nvram_pstore_init(void)
{ {
int rc = 0; int rc = 0;
if (big_oops_buf) {
nvram_pstore_info.buf = big_oops_buf;
nvram_pstore_info.bufsize = big_oops_buf_sz;
} else {
nvram_pstore_info.buf = oops_data; nvram_pstore_info.buf = oops_data;
nvram_pstore_info.bufsize = oops_data_sz; nvram_pstore_info.bufsize = oops_data_sz;
}
rc = pstore_register(&nvram_pstore_info); rc = pstore_register(&nvram_pstore_info);
if (rc != 0) if (rc != 0)
...@@ -802,7 +734,6 @@ static int nvram_pstore_init(void) ...@@ -802,7 +734,6 @@ static int nvram_pstore_init(void)
static void __init nvram_init_oops_partition(int rtas_partition_exists) static void __init nvram_init_oops_partition(int rtas_partition_exists)
{ {
int rc; int rc;
size_t size;
rc = pseries_nvram_init_os_partition(&oops_log_partition); rc = pseries_nvram_init_os_partition(&oops_log_partition);
if (rc != 0) { if (rc != 0) {
...@@ -823,6 +754,11 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) ...@@ -823,6 +754,11 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
oops_data = oops_buf + sizeof(struct oops_log_info); oops_data = oops_buf + sizeof(struct oops_log_info);
oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info);
rc = nvram_pstore_init();
if (!rc)
return;
/* /*
* Figure compression (preceded by elimination of each line's <n> * Figure compression (preceded by elimination of each line's <n>
* severity prefix) will reduce the oops/panic report to at most * severity prefix) will reduce the oops/panic report to at most
...@@ -831,9 +767,8 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) ...@@ -831,9 +767,8 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
big_oops_buf_sz = (oops_data_sz * 100) / 45; big_oops_buf_sz = (oops_data_sz * 100) / 45;
big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
if (big_oops_buf) { if (big_oops_buf) {
size = max(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL), stream.workspace = kmalloc(zlib_deflate_workspacesize(
zlib_inflate_workspacesize()); WINDOW_BITS, MEM_LEVEL), GFP_KERNEL);
stream.workspace = kmalloc(size, GFP_KERNEL);
if (!stream.workspace) { if (!stream.workspace) {
pr_err("nvram: No memory for compression workspace; " pr_err("nvram: No memory for compression workspace; "
"skipping compression of %s partition data\n", "skipping compression of %s partition data\n",
...@@ -847,11 +782,6 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) ...@@ -847,11 +782,6 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
stream.workspace = NULL; stream.workspace = NULL;
} }
rc = nvram_pstore_init();
if (!rc)
return;
rc = kmsg_dump_register(&nvram_kmsg_dumper); rc = kmsg_dump_register(&nvram_kmsg_dumper);
if (rc != 0) { if (rc != 0) {
pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
......
...@@ -284,8 +284,10 @@ static int erst_exec_move_data(struct apei_exec_context *ctx, ...@@ -284,8 +284,10 @@ static int erst_exec_move_data(struct apei_exec_context *ctx,
if (!src) if (!src)
return -ENOMEM; return -ENOMEM;
dst = ioremap(ctx->dst_base + offset, ctx->var2); dst = ioremap(ctx->dst_base + offset, ctx->var2);
if (!dst) if (!dst) {
iounmap(src);
return -ENOMEM; return -ENOMEM;
}
memmove(dst, src, ctx->var2); memmove(dst, src, ctx->var2);
...@@ -933,9 +935,9 @@ static int erst_open_pstore(struct pstore_info *psi); ...@@ -933,9 +935,9 @@ static int erst_open_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi); static int erst_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
struct timespec *time, char **buf, struct timespec *time, char **buf,
struct pstore_info *psi); bool *compressed, struct pstore_info *psi);
static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
u64 *id, unsigned int part, int count, size_t hsize, u64 *id, unsigned int part, int count, bool compressed,
size_t size, struct pstore_info *psi); size_t size, struct pstore_info *psi);
static int erst_clearer(enum pstore_type_id type, u64 id, int count, static int erst_clearer(enum pstore_type_id type, u64 id, int count,
struct timespec time, struct pstore_info *psi); struct timespec time, struct pstore_info *psi);
...@@ -956,6 +958,9 @@ static struct pstore_info erst_info = { ...@@ -956,6 +958,9 @@ static struct pstore_info erst_info = {
#define CPER_SECTION_TYPE_DMESG \ #define CPER_SECTION_TYPE_DMESG \
UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \ UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \
0x94, 0x19, 0xeb, 0x12) 0x94, 0x19, 0xeb, 0x12)
#define CPER_SECTION_TYPE_DMESG_Z \
UUID_LE(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d, \
0x34, 0xdd, 0xfa, 0xc6)
#define CPER_SECTION_TYPE_MCE \ #define CPER_SECTION_TYPE_MCE \
UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \
0x04, 0x4a, 0x38, 0xfc) 0x04, 0x4a, 0x38, 0xfc)
...@@ -989,7 +994,7 @@ static int erst_close_pstore(struct pstore_info *psi) ...@@ -989,7 +994,7 @@ static int erst_close_pstore(struct pstore_info *psi)
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
struct timespec *time, char **buf, struct timespec *time, char **buf,
struct pstore_info *psi) bool *compressed, struct pstore_info *psi)
{ {
int rc; int rc;
ssize_t len = 0; ssize_t len = 0;
...@@ -1034,7 +1039,12 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, ...@@ -1034,7 +1039,12 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
} }
memcpy(*buf, rcd->data, len - sizeof(*rcd)); memcpy(*buf, rcd->data, len - sizeof(*rcd));
*id = record_id; *id = record_id;
*compressed = false;
if (uuid_le_cmp(rcd->sec_hdr.section_type, if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_DMESG_Z) == 0) {
*type = PSTORE_TYPE_DMESG;
*compressed = true;
} else if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_DMESG) == 0) CPER_SECTION_TYPE_DMESG) == 0)
*type = PSTORE_TYPE_DMESG; *type = PSTORE_TYPE_DMESG;
else if (uuid_le_cmp(rcd->sec_hdr.section_type, else if (uuid_le_cmp(rcd->sec_hdr.section_type,
...@@ -1055,7 +1065,7 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, ...@@ -1055,7 +1065,7 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
} }
static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
u64 *id, unsigned int part, int count, size_t hsize, u64 *id, unsigned int part, int count, bool compressed,
size_t size, struct pstore_info *psi) size_t size, struct pstore_info *psi)
{ {
struct cper_pstore_record *rcd = (struct cper_pstore_record *) struct cper_pstore_record *rcd = (struct cper_pstore_record *)
...@@ -1085,6 +1095,9 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, ...@@ -1085,6 +1095,9 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
rcd->sec_hdr.flags = CPER_SEC_PRIMARY; rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
switch (type) { switch (type) {
case PSTORE_TYPE_DMESG: case PSTORE_TYPE_DMESG:
if (compressed)
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z;
else
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
break; break;
case PSTORE_TYPE_MCE: case PSTORE_TYPE_MCE:
......
...@@ -35,6 +35,7 @@ struct pstore_read_data { ...@@ -35,6 +35,7 @@ struct pstore_read_data {
enum pstore_type_id *type; enum pstore_type_id *type;
int *count; int *count;
struct timespec *timespec; struct timespec *timespec;
bool *compressed;
char **buf; char **buf;
}; };
...@@ -42,7 +43,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) ...@@ -42,7 +43,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
{ {
efi_guid_t vendor = LINUX_EFI_CRASH_GUID; efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
struct pstore_read_data *cb_data = data; struct pstore_read_data *cb_data = data;
char name[DUMP_NAME_LEN]; char name[DUMP_NAME_LEN], data_type;
int i; int i;
int cnt; int cnt;
unsigned int part; unsigned int part;
...@@ -54,12 +55,23 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) ...@@ -54,12 +55,23 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
for (i = 0; i < DUMP_NAME_LEN; i++) for (i = 0; i < DUMP_NAME_LEN; i++)
name[i] = entry->var.VariableName[i]; name[i] = entry->var.VariableName[i];
if (sscanf(name, "dump-type%u-%u-%d-%lu", if (sscanf(name, "dump-type%u-%u-%d-%lu-%c",
cb_data->type, &part, &cnt, &time, &data_type) == 5) {
*cb_data->id = part;
*cb_data->count = cnt;
cb_data->timespec->tv_sec = time;
cb_data->timespec->tv_nsec = 0;
if (data_type == 'C')
*cb_data->compressed = true;
else
*cb_data->compressed = false;
} else if (sscanf(name, "dump-type%u-%u-%d-%lu",
cb_data->type, &part, &cnt, &time) == 4) { cb_data->type, &part, &cnt, &time) == 4) {
*cb_data->id = part; *cb_data->id = part;
*cb_data->count = cnt; *cb_data->count = cnt;
cb_data->timespec->tv_sec = time; cb_data->timespec->tv_sec = time;
cb_data->timespec->tv_nsec = 0; cb_data->timespec->tv_nsec = 0;
*cb_data->compressed = false;
} else if (sscanf(name, "dump-type%u-%u-%lu", } else if (sscanf(name, "dump-type%u-%u-%lu",
cb_data->type, &part, &time) == 3) { cb_data->type, &part, &time) == 3) {
/* /*
...@@ -71,6 +83,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) ...@@ -71,6 +83,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
*cb_data->count = 0; *cb_data->count = 0;
cb_data->timespec->tv_sec = time; cb_data->timespec->tv_sec = time;
cb_data->timespec->tv_nsec = 0; cb_data->timespec->tv_nsec = 0;
*cb_data->compressed = false;
} else } else
return 0; return 0;
...@@ -87,7 +100,8 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) ...@@ -87,7 +100,8 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *timespec, int *count, struct timespec *timespec,
char **buf, struct pstore_info *psi) char **buf, bool *compressed,
struct pstore_info *psi)
{ {
struct pstore_read_data data; struct pstore_read_data data;
...@@ -95,6 +109,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -95,6 +109,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
data.type = type; data.type = type;
data.count = count; data.count = count;
data.timespec = timespec; data.timespec = timespec;
data.compressed = compressed;
data.buf = buf; data.buf = buf;
return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data, return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
...@@ -103,7 +118,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -103,7 +118,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
static int efi_pstore_write(enum pstore_type_id type, static int efi_pstore_write(enum pstore_type_id type,
enum kmsg_dump_reason reason, u64 *id, enum kmsg_dump_reason reason, u64 *id,
unsigned int part, int count, size_t hsize, size_t size, unsigned int part, int count, bool compressed, size_t size,
struct pstore_info *psi) struct pstore_info *psi)
{ {
char name[DUMP_NAME_LEN]; char name[DUMP_NAME_LEN];
...@@ -111,8 +126,8 @@ static int efi_pstore_write(enum pstore_type_id type, ...@@ -111,8 +126,8 @@ static int efi_pstore_write(enum pstore_type_id type,
efi_guid_t vendor = LINUX_EFI_CRASH_GUID; efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
int i, ret = 0; int i, ret = 0;
sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count, sprintf(name, "dump-type%u-%u-%d-%lu-%c", type, part, count,
get_seconds()); get_seconds(), compressed ? 'C' : 'D');
for (i = 0; i < DUMP_NAME_LEN; i++) for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i]; efi_name[i] = name[i];
......
config PSTORE config PSTORE
bool "Persistent store support" bool "Persistent store support"
default n default n
select ZLIB_DEFLATE
select ZLIB_INFLATE
help help
This option enables generic access to platform level This option enables generic access to platform level
persistent storage via "pstore" filesystem that can persistent storage via "pstore" filesystem that can
......
...@@ -275,8 +275,8 @@ int pstore_is_mounted(void) ...@@ -275,8 +275,8 @@ int pstore_is_mounted(void)
* Set the mtime & ctime to the date that this record was originally stored. * Set the mtime & ctime to the date that this record was originally stored.
*/ */
int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
char *data, size_t size, struct timespec time, char *data, bool compressed, size_t size,
struct pstore_info *psi) struct timespec time, struct pstore_info *psi)
{ {
struct dentry *root = pstore_sb->s_root; struct dentry *root = pstore_sb->s_root;
struct dentry *dentry; struct dentry *dentry;
...@@ -315,7 +315,8 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, ...@@ -315,7 +315,8 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
switch (type) { switch (type) {
case PSTORE_TYPE_DMESG: case PSTORE_TYPE_DMESG:
sprintf(name, "dmesg-%s-%lld", psname, id); sprintf(name, "dmesg-%s-%lld%s", psname, id,
compressed ? ".enc.z" : "");
break; break;
case PSTORE_TYPE_CONSOLE: case PSTORE_TYPE_CONSOLE:
sprintf(name, "console-%s", psname); sprintf(name, "console-%s", psname);
...@@ -345,9 +346,8 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, ...@@ -345,9 +346,8 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
mutex_lock(&root->d_inode->i_mutex); mutex_lock(&root->d_inode->i_mutex);
rc = -ENOSPC;
dentry = d_alloc_name(root, name); dentry = d_alloc_name(root, name);
if (IS_ERR(dentry)) if (!dentry)
goto fail_lockedalloc; goto fail_lockedalloc;
memcpy(private->data, data, size); memcpy(private->data, data, size);
......
...@@ -50,8 +50,9 @@ extern struct pstore_info *psinfo; ...@@ -50,8 +50,9 @@ extern struct pstore_info *psinfo;
extern void pstore_set_kmsg_bytes(int); extern void pstore_set_kmsg_bytes(int);
extern void pstore_get_records(int); extern void pstore_get_records(int);
extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
int count, char *data, size_t size, int count, char *data, bool compressed,
struct timespec time, struct pstore_info *psi); size_t size, struct timespec time,
struct pstore_info *psi);
extern int pstore_is_mounted(void); extern int pstore_is_mounted(void);
#endif #endif
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/console.h> #include <linux/console.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pstore.h> #include <linux/pstore.h>
#include <linux/zlib.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -65,6 +66,15 @@ struct pstore_info *psinfo; ...@@ -65,6 +66,15 @@ struct pstore_info *psinfo;
static char *backend; 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 */ /* How much of the console log to snapshot */
static unsigned long kmsg_bytes = 10240; static unsigned long kmsg_bytes = 10240;
...@@ -117,6 +127,121 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason) ...@@ -117,6 +127,121 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
} }
EXPORT_SYMBOL_GPL(pstore_cannot_block_path); 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;
}
/* Derived from logfs_uncompress */
static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
{
int err, ret;
ret = -EIO;
err = zlib_inflateInit(&stream);
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_inflate(&stream, Z_FINISH);
if (err != Z_STREAM_END)
goto error;
err = zlib_inflateEnd(&stream);
if (err != Z_OK)
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 * callback from kmsg_dump. (s2,l2) has the most recently
* written bytes, older bytes are in (s1,l1). Save as much * written bytes, older bytes are in (s1,l1). Save as much
...@@ -148,22 +273,56 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -148,22 +273,56 @@ static void pstore_dump(struct kmsg_dumper *dumper,
char *dst; char *dst;
unsigned long size; unsigned long size;
int hsize; int hsize;
int zipped_len = -1;
size_t len; size_t len;
bool compressed;
size_t total_len;
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 + 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; dst = psinfo->buf;
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount,
part);
size = psinfo->bufsize - hsize; size = psinfo->bufsize - hsize;
dst += hsize; dst += hsize;
if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len)) if (!kmsg_dump_get_buffer(dumper, true, dst,
size, &len))
break; break;
compressed = false;
total_len = hsize + len;
}
ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
oopscount, hsize, hsize + len, psinfo); oopscount, compressed, total_len, psinfo);
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_new_entry = 1; pstore_new_entry = 1;
total += hsize + len; total += total_len;
part++; part++;
} }
if (pstore_cannot_block_path(reason)) { if (pstore_cannot_block_path(reason)) {
...@@ -221,10 +380,10 @@ static void pstore_register_console(void) {} ...@@ -221,10 +380,10 @@ static void pstore_register_console(void) {}
static int pstore_write_compat(enum pstore_type_id type, static int pstore_write_compat(enum pstore_type_id type,
enum kmsg_dump_reason reason, enum kmsg_dump_reason reason,
u64 *id, unsigned int part, int count, u64 *id, unsigned int part, int count,
size_t hsize, size_t size, bool compressed, size_t size,
struct pstore_info *psi) struct pstore_info *psi)
{ {
return psi->write_buf(type, reason, id, part, psinfo->buf, hsize, return psi->write_buf(type, reason, id, part, psinfo->buf, compressed,
size, psi); size, psi);
} }
...@@ -261,6 +420,8 @@ int pstore_register(struct pstore_info *psi) ...@@ -261,6 +420,8 @@ int pstore_register(struct pstore_info *psi)
return -EINVAL; return -EINVAL;
} }
allocate_buf_for_compression();
if (pstore_is_mounted()) if (pstore_is_mounted())
pstore_get_records(0); pstore_get_records(0);
...@@ -297,6 +458,8 @@ void pstore_get_records(int quiet) ...@@ -297,6 +458,8 @@ void pstore_get_records(int quiet)
enum pstore_type_id type; enum pstore_type_id type;
struct timespec time; struct timespec time;
int failed = 0, rc; int failed = 0, rc;
bool compressed;
int unzipped_len = -1;
if (!psi) if (!psi)
return; return;
...@@ -305,11 +468,32 @@ void pstore_get_records(int quiet) ...@@ -305,11 +468,32 @@ void pstore_get_records(int quiet)
if (psi->open && psi->open(psi)) if (psi->open && psi->open(psi))
goto out; goto out;
while ((size = psi->read(&id, &type, &count, &time, &buf, psi)) > 0) { while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed,
psi)) > 0) {
if (compressed && (type == PSTORE_TYPE_DMESG)) {
if (big_oops_buf)
unzipped_len = pstore_decompress(buf,
big_oops_buf, size,
big_oops_buf_sz);
if (unzipped_len > 0) {
buf = big_oops_buf;
size = unzipped_len;
compressed = false;
} else {
pr_err("pstore: decompression failed;"
"returned %d\n", unzipped_len);
compressed = true;
}
}
rc = pstore_mkfile(type, psi->name, id, count, buf, rc = pstore_mkfile(type, psi->name, id, count, buf,
(size_t)size, time, psi); compressed, (size_t)size, time, psi);
if (unzipped_len < 0) {
/* Free buffer other than big oops */
kfree(buf); kfree(buf);
buf = NULL; buf = NULL;
} else
unzipped_len = -1;
if (rc && (rc != -EEXIST || !quiet)) if (rc && (rc != -EEXIST || !quiet))
failed++; failed++;
} }
......
...@@ -131,9 +131,31 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max, ...@@ -131,9 +131,31 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max,
return prz; return prz;
} }
static void ramoops_read_kmsg_hdr(char *buffer, struct timespec *time,
bool *compressed)
{
char data_type;
if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lu.%lu-%c\n",
&time->tv_sec, &time->tv_nsec, &data_type) == 3) {
if (data_type == 'C')
*compressed = true;
else
*compressed = false;
} else if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
&time->tv_sec, &time->tv_nsec) == 2) {
*compressed = false;
} else {
time->tv_sec = 0;
time->tv_nsec = 0;
*compressed = false;
}
}
static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *time, int *count, struct timespec *time,
char **buf, struct pstore_info *psi) char **buf, bool *compressed,
struct pstore_info *psi)
{ {
ssize_t size; ssize_t size;
ssize_t ecc_notice_size; ssize_t ecc_notice_size;
...@@ -152,10 +174,6 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -152,10 +174,6 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
if (!prz) if (!prz)
return 0; return 0;
/* TODO(kees): Bogus time for the moment. */
time->tv_sec = 0;
time->tv_nsec = 0;
size = persistent_ram_old_size(prz); size = persistent_ram_old_size(prz);
/* ECC correction notice */ /* ECC correction notice */
...@@ -166,12 +184,14 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -166,12 +184,14 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
return -ENOMEM; return -ENOMEM;
memcpy(*buf, persistent_ram_old(prz), size); memcpy(*buf, persistent_ram_old(prz), size);
ramoops_read_kmsg_hdr(*buf, time, compressed);
persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1); persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1);
return size + ecc_notice_size; return size + ecc_notice_size;
} }
static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz) static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
bool compressed)
{ {
char *hdr; char *hdr;
struct timespec timestamp; struct timespec timestamp;
...@@ -182,8 +202,9 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz) ...@@ -182,8 +202,9 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz)
timestamp.tv_sec = 0; timestamp.tv_sec = 0;
timestamp.tv_nsec = 0; timestamp.tv_nsec = 0;
} }
hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n", hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu-%c\n",
(long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000)); (long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000),
compressed ? 'C' : 'D');
WARN_ON_ONCE(!hdr); WARN_ON_ONCE(!hdr);
len = hdr ? strlen(hdr) : 0; len = hdr ? strlen(hdr) : 0;
persistent_ram_write(prz, hdr, len); persistent_ram_write(prz, hdr, len);
...@@ -196,7 +217,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, ...@@ -196,7 +217,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
enum kmsg_dump_reason reason, enum kmsg_dump_reason reason,
u64 *id, unsigned int part, u64 *id, unsigned int part,
const char *buf, const char *buf,
size_t hsize, size_t size, bool compressed, size_t size,
struct pstore_info *psi) struct pstore_info *psi)
{ {
struct ramoops_context *cxt = psi->data; struct ramoops_context *cxt = psi->data;
...@@ -242,7 +263,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, ...@@ -242,7 +263,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
prz = cxt->przs[cxt->dump_write_cnt]; prz = cxt->przs[cxt->dump_write_cnt];
hlen = ramoops_write_kmsg_hdr(prz); hlen = ramoops_write_kmsg_hdr(prz, compressed);
if (size + hlen > prz->buffer_size) if (size + hlen > prz->buffer_size)
size = prz->buffer_size - hlen; size = prz->buffer_size - hlen;
persistent_ram_write(prz, buf, size); persistent_ram_write(prz, buf, size);
...@@ -400,11 +421,11 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -400,11 +421,11 @@ static int ramoops_probe(struct platform_device *pdev)
goto fail_out; goto fail_out;
} }
if (!is_power_of_2(pdata->record_size)) if (pdata->record_size && !is_power_of_2(pdata->record_size))
pdata->record_size = rounddown_pow_of_two(pdata->record_size); pdata->record_size = rounddown_pow_of_two(pdata->record_size);
if (!is_power_of_2(pdata->console_size)) if (pdata->console_size && !is_power_of_2(pdata->console_size))
pdata->console_size = rounddown_pow_of_two(pdata->console_size); pdata->console_size = rounddown_pow_of_two(pdata->console_size);
if (!is_power_of_2(pdata->ftrace_size)) if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size))
pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
cxt->dump_read_cnt = 0; cxt->dump_read_cnt = 0;
......
...@@ -55,14 +55,14 @@ struct pstore_info { ...@@ -55,14 +55,14 @@ struct pstore_info {
int (*close)(struct pstore_info *psi); int (*close)(struct pstore_info *psi);
ssize_t (*read)(u64 *id, enum pstore_type_id *type, ssize_t (*read)(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *time, char **buf, int *count, struct timespec *time, char **buf,
struct pstore_info *psi); bool *compressed, struct pstore_info *psi);
int (*write)(enum pstore_type_id type, int (*write)(enum pstore_type_id type,
enum kmsg_dump_reason reason, u64 *id, enum kmsg_dump_reason reason, u64 *id,
unsigned int part, int count, size_t hsize, unsigned int part, int count, bool compressed,
size_t size, struct pstore_info *psi); size_t size, struct pstore_info *psi);
int (*write_buf)(enum pstore_type_id type, int (*write_buf)(enum pstore_type_id type,
enum kmsg_dump_reason reason, u64 *id, enum kmsg_dump_reason reason, u64 *id,
unsigned int part, const char *buf, size_t hsize, unsigned int part, const char *buf, bool compressed,
size_t size, struct pstore_info *psi); size_t size, struct pstore_info *psi);
int (*erase)(enum pstore_type_id type, u64 id, int (*erase)(enum pstore_type_id type, u64 id,
int count, struct timespec time, int count, struct timespec time,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment