mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-05 20:04:19 +08:00
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJSJhMJAAoJEKurIx+X31iBcpsP/1DfQDD9IHEt7E+tTObIdKOx 1yygmQu3IL2pvdUIkKRfblDP0PJRuwJmRPdp48Rb5CV+8i4WQuTCueOvNomIMZ4G 1DkAzJPD2W4C+dSBD3rvWk4totXCsqLwCkaWp/FzWM7OW+cJN3Kj3Vdh3k01CVxn PqfCoZB5y8CVVd6EQiUmAU8z/LoGE0psxxO/CL7lBC1IsAlVqSO93hqHwE9cm/f4 7EN069sHbKV5zOT1QsV7agn9TfHAHuj0fr3t8aDs/cC0rgz19VAsxg0sRkMHzKJD MGLqGCTHyQvYmo2xfHZLb1NjhL6EmHsQqhM9KQnkdDyQ3QgYOCSsglcYygSdZcN1 ZjynLbD/OSkLABefyw/ZjW1GGzIqU6jWzj+0fmbkUa0Pe1B1aDuf/OiAcYMRJdmg clWE7RbnisSlrLYl3MI36T4XvyzzHVfumOVET6foWcnVb8/t/gHhC6Yf96FSUWVj jISE8ECfjkk7SQTYHE0ThvBmqv8M/IwUEg6wA9tnsQByLMAO+VqGoXcBMKOxz+hD 7TPoQpa04Sqnplwx7HefbhErKH9TCQWUOgT5kjB7+bzbPpEG1cB3pb7izg3VFd7k l7d6vfXtAukeuYmU8PgY/ea6/Nnw7iMcDZCegzv+A4Ms/kWM9oH2ctvV6V/edYyb 0x7BgSOu69HztW5IveB6 =6H8r -----END PGP SIGNATURE----- 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()
This commit is contained in:
commit
f83b0a4e4c
@ -539,36 +539,6 @@ static int zip_oops(size_t text_len)
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
/* Reset the iterator to start reading partitions again */
|
||||
@ -584,7 +554,7 @@ static int nvram_pstore_open(struct pstore_info *psi)
|
||||
* @part: pstore writes data to registered buffer in parts,
|
||||
* part number will indicate the same.
|
||||
* @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
|
||||
* @psi: registered pstore_info structure
|
||||
*
|
||||
@ -595,7 +565,7 @@ static int nvram_pstore_open(struct pstore_info *psi)
|
||||
static int nvram_pstore_write(enum pstore_type_id type,
|
||||
enum kmsg_dump_reason reason,
|
||||
u64 *id, unsigned int part, int count,
|
||||
size_t hsize, size_t size,
|
||||
bool compressed, size_t size,
|
||||
struct pstore_info *psi)
|
||||
{
|
||||
int rc;
|
||||
@ -611,30 +581,11 @@ static int nvram_pstore_write(enum pstore_type_id type,
|
||||
oops_hdr->report_length = (u16) size;
|
||||
oops_hdr->timestamp = get_seconds();
|
||||
|
||||
if (big_oops_buf) {
|
||||
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;
|
||||
}
|
||||
if (compressed)
|
||||
err_type = ERR_TYPE_KERNEL_PANIC_GZ;
|
||||
|
||||
rc = nvram_write_os_partition(&oops_log_partition, oops_buf,
|
||||
(int) (sizeof(*oops_hdr) + oops_hdr->report_length), err_type,
|
||||
count);
|
||||
(int) (sizeof(*oops_hdr) + size), err_type, count);
|
||||
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
@ -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,
|
||||
int *count, struct timespec *time, char **buf,
|
||||
struct pstore_info *psi)
|
||||
bool *compressed, struct pstore_info *psi)
|
||||
{
|
||||
struct oops_log_info *oops_hdr;
|
||||
unsigned int err_type, id_no, size = 0;
|
||||
struct nvram_os_partition *part = NULL;
|
||||
char *buff = NULL, *big_buff = NULL;
|
||||
char *buff = NULL;
|
||||
int sig = 0;
|
||||
loff_t p;
|
||||
|
||||
@ -719,8 +670,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
|
||||
*id = id_no;
|
||||
|
||||
if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) {
|
||||
int length, unzipped_len;
|
||||
size_t hdr_size;
|
||||
size_t length, hdr_size;
|
||||
|
||||
oops_hdr = (struct oops_log_info *)buff;
|
||||
if (oops_hdr->version < OOPS_HDR_VERSION) {
|
||||
@ -741,23 +691,10 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
|
||||
memcpy(*buf, buff + hdr_size, length);
|
||||
kfree(buff);
|
||||
|
||||
if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) {
|
||||
big_buff = kmalloc(big_oops_buf_sz, GFP_KERNEL);
|
||||
if (!big_buff)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
|
||||
*compressed = true;
|
||||
else
|
||||
*compressed = false;
|
||||
return length;
|
||||
}
|
||||
|
||||
@ -777,13 +714,8 @@ static int nvram_pstore_init(void)
|
||||
{
|
||||
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.bufsize = oops_data_sz;
|
||||
}
|
||||
nvram_pstore_info.buf = oops_data;
|
||||
nvram_pstore_info.bufsize = oops_data_sz;
|
||||
|
||||
rc = pstore_register(&nvram_pstore_info);
|
||||
if (rc != 0)
|
||||
@ -802,7 +734,6 @@ static int nvram_pstore_init(void)
|
||||
static void __init nvram_init_oops_partition(int rtas_partition_exists)
|
||||
{
|
||||
int rc;
|
||||
size_t size;
|
||||
|
||||
rc = pseries_nvram_init_os_partition(&oops_log_partition);
|
||||
if (rc != 0) {
|
||||
@ -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_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>
|
||||
* 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)
|
||||
big_oops_buf_sz = (oops_data_sz * 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);
|
||||
stream.workspace = kmalloc(zlib_deflate_workspacesize(
|
||||
WINDOW_BITS, MEM_LEVEL), GFP_KERNEL);
|
||||
if (!stream.workspace) {
|
||||
pr_err("nvram: No memory for compression workspace; "
|
||||
"skipping compression of %s partition data\n",
|
||||
@ -847,11 +782,6 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
|
||||
stream.workspace = NULL;
|
||||
}
|
||||
|
||||
rc = nvram_pstore_init();
|
||||
|
||||
if (!rc)
|
||||
return;
|
||||
|
||||
rc = kmsg_dump_register(&nvram_kmsg_dumper);
|
||||
if (rc != 0) {
|
||||
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,
|
||||
if (!src)
|
||||
return -ENOMEM;
|
||||
dst = ioremap(ctx->dst_base + offset, ctx->var2);
|
||||
if (!dst)
|
||||
if (!dst) {
|
||||
iounmap(src);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memmove(dst, src, ctx->var2);
|
||||
|
||||
@ -933,9 +935,9 @@ static int erst_open_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,
|
||||
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,
|
||||
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);
|
||||
static int erst_clearer(enum pstore_type_id type, u64 id, int count,
|
||||
struct timespec time, struct pstore_info *psi);
|
||||
@ -956,6 +958,9 @@ static struct pstore_info erst_info = {
|
||||
#define CPER_SECTION_TYPE_DMESG \
|
||||
UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \
|
||||
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 \
|
||||
UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \
|
||||
0x04, 0x4a, 0x38, 0xfc)
|
||||
@ -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,
|
||||
struct timespec *time, char **buf,
|
||||
struct pstore_info *psi)
|
||||
bool *compressed, struct pstore_info *psi)
|
||||
{
|
||||
int rc;
|
||||
ssize_t len = 0;
|
||||
@ -1034,7 +1039,12 @@ skip:
|
||||
}
|
||||
memcpy(*buf, rcd->data, len - sizeof(*rcd));
|
||||
*id = record_id;
|
||||
*compressed = false;
|
||||
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)
|
||||
*type = PSTORE_TYPE_DMESG;
|
||||
else if (uuid_le_cmp(rcd->sec_hdr.section_type,
|
||||
@ -1055,7 +1065,7 @@ out:
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
|
||||
@ -1085,7 +1095,10 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
|
||||
rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
|
||||
switch (type) {
|
||||
case PSTORE_TYPE_DMESG:
|
||||
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
|
||||
if (compressed)
|
||||
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z;
|
||||
else
|
||||
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
|
||||
break;
|
||||
case PSTORE_TYPE_MCE:
|
||||
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE;
|
||||
|
@ -35,6 +35,7 @@ struct pstore_read_data {
|
||||
enum pstore_type_id *type;
|
||||
int *count;
|
||||
struct timespec *timespec;
|
||||
bool *compressed;
|
||||
char **buf;
|
||||
};
|
||||
|
||||
@ -42,7 +43,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
|
||||
{
|
||||
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
|
||||
struct pstore_read_data *cb_data = data;
|
||||
char name[DUMP_NAME_LEN];
|
||||
char name[DUMP_NAME_LEN], data_type;
|
||||
int i;
|
||||
int cnt;
|
||||
unsigned int part;
|
||||
@ -54,12 +55,23 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
|
||||
for (i = 0; i < DUMP_NAME_LEN; 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->id = part;
|
||||
*cb_data->count = cnt;
|
||||
cb_data->timespec->tv_sec = time;
|
||||
cb_data->timespec->tv_nsec = 0;
|
||||
*cb_data->compressed = false;
|
||||
} else if (sscanf(name, "dump-type%u-%u-%lu",
|
||||
cb_data->type, &part, &time) == 3) {
|
||||
/*
|
||||
@ -71,6 +83,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
|
||||
*cb_data->count = 0;
|
||||
cb_data->timespec->tv_sec = time;
|
||||
cb_data->timespec->tv_nsec = 0;
|
||||
*cb_data->compressed = false;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
@ -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,
|
||||
int *count, struct timespec *timespec,
|
||||
char **buf, struct pstore_info *psi)
|
||||
char **buf, bool *compressed,
|
||||
struct pstore_info *psi)
|
||||
{
|
||||
struct pstore_read_data data;
|
||||
|
||||
@ -95,6 +109,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
|
||||
data.type = type;
|
||||
data.count = count;
|
||||
data.timespec = timespec;
|
||||
data.compressed = compressed;
|
||||
data.buf = buf;
|
||||
|
||||
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,
|
||||
|
||||
static int efi_pstore_write(enum pstore_type_id type,
|
||||
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)
|
||||
{
|
||||
char name[DUMP_NAME_LEN];
|
||||
@ -111,8 +126,8 @@ static int efi_pstore_write(enum pstore_type_id type,
|
||||
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
|
||||
int i, ret = 0;
|
||||
|
||||
sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count,
|
||||
get_seconds());
|
||||
sprintf(name, "dump-type%u-%u-%d-%lu-%c", type, part, count,
|
||||
get_seconds(), compressed ? 'C' : 'D');
|
||||
|
||||
for (i = 0; i < DUMP_NAME_LEN; i++)
|
||||
efi_name[i] = name[i];
|
||||
|
@ -1,6 +1,8 @@
|
||||
config PSTORE
|
||||
bool "Persistent store support"
|
||||
default n
|
||||
select ZLIB_DEFLATE
|
||||
select ZLIB_INFLATE
|
||||
help
|
||||
This option enables generic access to platform level
|
||||
persistent storage via "pstore" filesystem that can
|
||||
|
@ -275,8 +275,8 @@ int pstore_is_mounted(void)
|
||||
* 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,
|
||||
char *data, size_t size, struct timespec time,
|
||||
struct pstore_info *psi)
|
||||
char *data, bool compressed, size_t size,
|
||||
struct timespec time, struct pstore_info *psi)
|
||||
{
|
||||
struct dentry *root = pstore_sb->s_root;
|
||||
struct dentry *dentry;
|
||||
@ -315,7 +315,8 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
|
||||
|
||||
switch (type) {
|
||||
case PSTORE_TYPE_DMESG:
|
||||
sprintf(name, "dmesg-%s-%lld", psname, id);
|
||||
sprintf(name, "dmesg-%s-%lld%s", psname, id,
|
||||
compressed ? ".enc.z" : "");
|
||||
break;
|
||||
case PSTORE_TYPE_CONSOLE:
|
||||
sprintf(name, "console-%s", psname);
|
||||
@ -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);
|
||||
|
||||
rc = -ENOSPC;
|
||||
dentry = d_alloc_name(root, name);
|
||||
if (IS_ERR(dentry))
|
||||
if (!dentry)
|
||||
goto fail_lockedalloc;
|
||||
|
||||
memcpy(private->data, data, size);
|
||||
|
@ -50,8 +50,9 @@ extern struct pstore_info *psinfo;
|
||||
extern void pstore_set_kmsg_bytes(int);
|
||||
extern void pstore_get_records(int);
|
||||
extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
|
||||
int count, char *data, size_t size,
|
||||
struct timespec time, struct pstore_info *psi);
|
||||
int count, char *data, bool compressed,
|
||||
size_t size, struct timespec time,
|
||||
struct pstore_info *psi);
|
||||
extern int pstore_is_mounted(void);
|
||||
|
||||
#endif
|
||||
|
@ -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>
|
||||
@ -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;
|
||||
|
||||
@ -117,6 +127,121 @@ 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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
* written bytes, older bytes are in (s1,l1). Save as much
|
||||
@ -148,22 +273,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;
|
||||
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, hsize, 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)) {
|
||||
@ -221,10 +380,10 @@ static void pstore_register_console(void) {}
|
||||
static int pstore_write_compat(enum pstore_type_id type,
|
||||
enum kmsg_dump_reason reason,
|
||||
u64 *id, unsigned int part, int count,
|
||||
size_t hsize, size_t size,
|
||||
bool compressed, size_t size,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -261,6 +420,8 @@ int pstore_register(struct pstore_info *psi)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
allocate_buf_for_compression();
|
||||
|
||||
if (pstore_is_mounted())
|
||||
pstore_get_records(0);
|
||||
|
||||
@ -297,6 +458,8 @@ void pstore_get_records(int quiet)
|
||||
enum pstore_type_id type;
|
||||
struct timespec time;
|
||||
int failed = 0, rc;
|
||||
bool compressed;
|
||||
int unzipped_len = -1;
|
||||
|
||||
if (!psi)
|
||||
return;
|
||||
@ -305,11 +468,32 @@ void pstore_get_records(int quiet)
|
||||
if (psi->open && psi->open(psi))
|
||||
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,
|
||||
(size_t)size, time, psi);
|
||||
kfree(buf);
|
||||
buf = NULL;
|
||||
compressed, (size_t)size, time, psi);
|
||||
if (unzipped_len < 0) {
|
||||
/* Free buffer other than big oops */
|
||||
kfree(buf);
|
||||
buf = NULL;
|
||||
} else
|
||||
unzipped_len = -1;
|
||||
if (rc && (rc != -EEXIST || !quiet))
|
||||
failed++;
|
||||
}
|
||||
|
@ -131,9 +131,31 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max,
|
||||
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,
|
||||
int *count, struct timespec *time,
|
||||
char **buf, struct pstore_info *psi)
|
||||
char **buf, bool *compressed,
|
||||
struct pstore_info *psi)
|
||||
{
|
||||
ssize_t size;
|
||||
ssize_t ecc_notice_size;
|
||||
@ -152,10 +174,6 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
|
||||
if (!prz)
|
||||
return 0;
|
||||
|
||||
/* TODO(kees): Bogus time for the moment. */
|
||||
time->tv_sec = 0;
|
||||
time->tv_nsec = 0;
|
||||
|
||||
size = persistent_ram_old_size(prz);
|
||||
|
||||
/* ECC correction notice */
|
||||
@ -166,12 +184,14 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
|
||||
return -ENOMEM;
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
struct timespec timestamp;
|
||||
@ -182,8 +202,9 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz)
|
||||
timestamp.tv_sec = 0;
|
||||
timestamp.tv_nsec = 0;
|
||||
}
|
||||
hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
|
||||
(long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000));
|
||||
hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu-%c\n",
|
||||
(long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000),
|
||||
compressed ? 'C' : 'D');
|
||||
WARN_ON_ONCE(!hdr);
|
||||
len = hdr ? strlen(hdr) : 0;
|
||||
persistent_ram_write(prz, hdr, len);
|
||||
@ -196,7 +217,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
|
||||
enum kmsg_dump_reason reason,
|
||||
u64 *id, unsigned int part,
|
||||
const char *buf,
|
||||
size_t hsize, size_t size,
|
||||
bool compressed, size_t size,
|
||||
struct pstore_info *psi)
|
||||
{
|
||||
struct ramoops_context *cxt = psi->data;
|
||||
@ -242,7 +263,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
|
||||
|
||||
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)
|
||||
size = prz->buffer_size - hlen;
|
||||
persistent_ram_write(prz, buf, size);
|
||||
@ -400,11 +421,11 @@ static int ramoops_probe(struct platform_device *pdev)
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
|
||||
cxt->dump_read_cnt = 0;
|
||||
|
@ -55,14 +55,14 @@ struct pstore_info {
|
||||
int (*close)(struct pstore_info *psi);
|
||||
ssize_t (*read)(u64 *id, enum pstore_type_id *type,
|
||||
int *count, struct timespec *time, char **buf,
|
||||
struct pstore_info *psi);
|
||||
bool *compressed, struct pstore_info *psi);
|
||||
int (*write)(enum pstore_type_id type,
|
||||
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);
|
||||
int (*write_buf)(enum pstore_type_id type,
|
||||
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);
|
||||
int (*erase)(enum pstore_type_id type, u64 id,
|
||||
int count, struct timespec time,
|
||||
|
Loading…
Reference in New Issue
Block a user