pstore subsystem updates for v4.8
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 Comment: Kees Cook <kees@outflux.net> iQIcBAABCgAGBQJXloQYAAoJEIly9N/cbcAmGC0QAIpyoiqEuDiJq/XpRg1ux+PC Vyr15Pub9yQYwcrWffMH1Zr0GhlFmXb1iP9rp36zdtMhjBEfq7wegvblLVMlzl6G 7nYt8hJjDh/h8iw1lElgDL2kwUbTym43HoczJNvY/lOmFuUMK8AoDIRYjFTLAKfQ S4KA9MFJe3kDh4OUoQVfQNrC2VReLD4uvXk4EUF0wDYoqjVKyU3WBHOMgEmggKTR cb+fwhg3Lj4cuMMtZqy8wCqZ/hqhaH8giHC9YbIZQyre3ylncH9xUZyfiqS6nQGc eLc03qxqDNsmZvcY6cJgXldLQ3tXM4o96Moakzn2n4sQcW9vh/3oZzDPd7gC8Ei1 GfIXmRBXFhj5JaeHNGJxL6oCywK+JaqxG8nqD7cEcXTzJiHzjn5kKKSFlr3GmI7w 47htXv9t07SMgQW0IlBws5yApfeB62dQXmhZc1kMtbonhGdAZCCUg2Nrv34VxrjX Dp+LCmD5bg/fBrnAt8f+IIQEd3pElngay+SmSEB9XFUejf3pKw8SvcoDbmE3LD7M zGh5bEkptHll2GMInVAt4b4tiC44e7u+0H1Rsi/ttA1cktXZ9hOtFewhXNvfOS2I hAMGSngOdpzR5v9Mof5hJAWrr1CLkoh757UoYMsb8u2V9aQw7oVZ5JRMzjZBU6iC qXqHm4h5P1bpAeit3DLF =fiRI -----END PGP SIGNATURE----- Merge tag 'pstore-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux Pull pstore subsystem updates from Kees Cook: "This expands the supported compressors, fixes some bugs, and finally adds DT bindings" * tag 'pstore-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: pstore/ram: add Device Tree bindings efi-pstore: implement efivars_pstore_exit() pstore: drop file opened reference count pstore: add lzo/lz4 compression support pstore: Cleanup pstore_dump() pstore: Enable compression on normal path (again) ramoops: Only unregister when registered
This commit is contained in:
commit
1b3fc0bef8
|
@ -0,0 +1,48 @@
|
|||
Ramoops oops/panic logger
|
||||
=========================
|
||||
|
||||
ramoops provides persistent RAM storage for oops and panics, so they can be
|
||||
recovered after a reboot. It is a backend to pstore, so this node is named
|
||||
"ramoops" after the backend, rather than "pstore" which is the subsystem.
|
||||
|
||||
Parts of this storage may be set aside for other persistent log buffers, such
|
||||
as kernel log messages, or for optional ECC error-correction data. The total
|
||||
size of these optional buffers must fit in the reserved region.
|
||||
|
||||
Any remaining space will be used for a circular buffer of oops and panic
|
||||
records. These records have a configurable size, with a size of 0 indicating
|
||||
that they should be disabled.
|
||||
|
||||
At least one of "record-size", "console-size", "ftrace-size", or "pmsg-size"
|
||||
must be set non-zero, but are otherwise optional as listed below.
|
||||
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "ramoops"
|
||||
|
||||
- memory-region: phandle to a region of memory that is preserved between
|
||||
reboots
|
||||
|
||||
|
||||
Optional properties:
|
||||
|
||||
- ecc-size: enables ECC support and specifies ECC buffer size in bytes
|
||||
(defaults to 0: no ECC)
|
||||
|
||||
- record-size: maximum size in bytes of each dump done on oops/panic
|
||||
(defaults to 0: disabled)
|
||||
|
||||
- console-size: size in bytes of log buffer reserved for kernel messages
|
||||
(defaults to 0: disabled)
|
||||
|
||||
- ftrace-size: size in bytes of log buffer reserved for function tracing and
|
||||
profiling (defaults to 0: disabled)
|
||||
|
||||
- pmsg-size: size in bytes of log buffer reserved for userspace messages
|
||||
(defaults to 0: disabled)
|
||||
|
||||
- unbuffered: if present, use unbuffered mappings to map the reserved region
|
||||
(defaults to buffered mappings)
|
||||
|
||||
- no-dump-oops: if present, only dump panics (defaults to panics and oops)
|
|
@ -45,7 +45,7 @@ corrupt, but usually it is restorable.
|
|||
|
||||
2. Setting the parameters
|
||||
|
||||
Setting the ramoops parameters can be done in 2 different manners:
|
||||
Setting the ramoops parameters can be done in 3 different manners:
|
||||
1. Use the module parameters (which have the names of the variables described
|
||||
as before).
|
||||
For quick debugging, you can also reserve parts of memory during boot
|
||||
|
@ -54,7 +54,9 @@ Setting the ramoops parameters can be done in 2 different manners:
|
|||
kernel to use only the first 128 MB of memory, and place ECC-protected ramoops
|
||||
region at 128 MB boundary:
|
||||
"mem=128M ramoops.mem_address=0x8000000 ramoops.ecc=1"
|
||||
2. Use a platform device and set the platform data. The parameters can then
|
||||
2. Use Device Tree bindings, as described in
|
||||
Documentation/device-tree/bindings/misc/ramoops.txt.
|
||||
3. Use a platform device and set the platform data. The parameters can then
|
||||
be set through that platform data. An example of doing that is:
|
||||
|
||||
#include <linux/pstore_ram.h>
|
||||
|
|
|
@ -444,7 +444,8 @@ 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,
|
||||
bool *compressed, struct pstore_info *psi)
|
||||
bool *compressed, ssize_t *ecc_notice_size,
|
||||
struct pstore_info *psi)
|
||||
{
|
||||
struct oops_log_info *oops_hdr;
|
||||
unsigned int err_type, id_no, size = 0;
|
||||
|
@ -545,6 +546,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
|
|||
return -ENOMEM;
|
||||
kfree(buff);
|
||||
|
||||
*ecc_notice_size = 0;
|
||||
if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
|
||||
*compressed = true;
|
||||
else
|
||||
|
|
|
@ -927,7 +927,8 @@ 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,
|
||||
bool *compressed, struct pstore_info *psi);
|
||||
bool *compressed, ssize_t *ecc_notice_size,
|
||||
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, bool compressed,
|
||||
size_t size, struct pstore_info *psi);
|
||||
|
@ -987,7 +988,8 @@ 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,
|
||||
bool *compressed, struct pstore_info *psi)
|
||||
bool *compressed, ssize_t *ecc_notice_size,
|
||||
struct pstore_info *psi)
|
||||
{
|
||||
int rc;
|
||||
ssize_t len = 0;
|
||||
|
@ -1033,6 +1035,7 @@ skip:
|
|||
memcpy(*buf, rcd->data, len - sizeof(*rcd));
|
||||
*id = record_id;
|
||||
*compressed = false;
|
||||
*ecc_notice_size = 0;
|
||||
if (uuid_le_cmp(rcd->sec_hdr.section_type,
|
||||
CPER_SECTION_TYPE_DMESG_Z) == 0) {
|
||||
*type = PSTORE_TYPE_DMESG;
|
||||
|
|
|
@ -34,6 +34,7 @@ struct pstore_read_data {
|
|||
int *count;
|
||||
struct timespec *timespec;
|
||||
bool *compressed;
|
||||
ssize_t *ecc_notice_size;
|
||||
char **buf;
|
||||
};
|
||||
|
||||
|
@ -69,6 +70,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
|
|||
*cb_data->compressed = true;
|
||||
else
|
||||
*cb_data->compressed = false;
|
||||
*cb_data->ecc_notice_size = 0;
|
||||
} else if (sscanf(name, "dump-type%u-%u-%d-%lu",
|
||||
cb_data->type, &part, &cnt, &time) == 4) {
|
||||
*cb_data->id = generic_id(time, part, cnt);
|
||||
|
@ -76,6 +78,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
|
|||
cb_data->timespec->tv_sec = time;
|
||||
cb_data->timespec->tv_nsec = 0;
|
||||
*cb_data->compressed = false;
|
||||
*cb_data->ecc_notice_size = 0;
|
||||
} else if (sscanf(name, "dump-type%u-%u-%lu",
|
||||
cb_data->type, &part, &time) == 3) {
|
||||
/*
|
||||
|
@ -88,6 +91,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
|
|||
cb_data->timespec->tv_sec = time;
|
||||
cb_data->timespec->tv_nsec = 0;
|
||||
*cb_data->compressed = false;
|
||||
*cb_data->ecc_notice_size = 0;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
|
@ -210,6 +214,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
|
|||
static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
|
||||
int *count, struct timespec *timespec,
|
||||
char **buf, bool *compressed,
|
||||
ssize_t *ecc_notice_size,
|
||||
struct pstore_info *psi)
|
||||
{
|
||||
struct pstore_read_data data;
|
||||
|
@ -220,6 +225,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
|
|||
data.count = count;
|
||||
data.timespec = timespec;
|
||||
data.compressed = compressed;
|
||||
data.ecc_notice_size = ecc_notice_size;
|
||||
data.buf = buf;
|
||||
|
||||
*data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
|
||||
|
@ -393,6 +399,13 @@ static __init int efivars_pstore_init(void)
|
|||
|
||||
static __exit void efivars_pstore_exit(void)
|
||||
{
|
||||
if (!efi_pstore_info.bufsize)
|
||||
return;
|
||||
|
||||
pstore_unregister(&efi_pstore_info);
|
||||
kfree(efi_pstore_info.buf);
|
||||
efi_pstore_info.buf = NULL;
|
||||
efi_pstore_info.bufsize = 0;
|
||||
}
|
||||
|
||||
module_init(efivars_pstore_init);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
config PSTORE
|
||||
tristate "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
|
||||
|
@ -14,6 +12,35 @@ config PSTORE
|
|||
If you don't have a platform persistent store driver,
|
||||
say N.
|
||||
|
||||
choice
|
||||
prompt "Choose compression algorithm"
|
||||
depends on PSTORE
|
||||
default PSTORE_ZLIB_COMPRESS
|
||||
help
|
||||
This option chooses compression algorithm.
|
||||
|
||||
config PSTORE_ZLIB_COMPRESS
|
||||
bool "ZLIB"
|
||||
select ZLIB_DEFLATE
|
||||
select ZLIB_INFLATE
|
||||
help
|
||||
This option enables ZLIB compression algorithm support.
|
||||
|
||||
config PSTORE_LZO_COMPRESS
|
||||
bool "LZO"
|
||||
select LZO_COMPRESS
|
||||
select LZO_DECOMPRESS
|
||||
help
|
||||
This option enables LZO compression algorithm support.
|
||||
|
||||
config PSTORE_LZ4_COMPRESS
|
||||
bool "LZ4"
|
||||
select LZ4_COMPRESS
|
||||
select LZ4_DECOMPRESS
|
||||
help
|
||||
This option enables LZ4 compression algorithm support.
|
||||
endchoice
|
||||
|
||||
config PSTORE_CONSOLE
|
||||
bool "Log kernel console messages"
|
||||
depends on PSTORE
|
||||
|
|
|
@ -178,7 +178,6 @@ static loff_t pstore_file_llseek(struct file *file, loff_t off, int whence)
|
|||
}
|
||||
|
||||
static const struct file_operations pstore_file_operations = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pstore_file_open,
|
||||
.read = pstore_file_read,
|
||||
.llseek = pstore_file_llseek,
|
||||
|
|
|
@ -28,7 +28,15 @@
|
|||
#include <linux/console.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pstore.h>
|
||||
#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
|
||||
#include <linux/zlib.h>
|
||||
#endif
|
||||
#ifdef CONFIG_PSTORE_LZO_COMPRESS
|
||||
#include <linux/lzo.h>
|
||||
#endif
|
||||
#ifdef CONFIG_PSTORE_LZ4_COMPRESS
|
||||
#include <linux/lz4.h>
|
||||
#endif
|
||||
#include <linux/string.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -69,10 +77,23 @@ struct pstore_info *psinfo;
|
|||
static char *backend;
|
||||
|
||||
/* Compression parameters */
|
||||
#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
|
||||
#define COMPR_LEVEL 6
|
||||
#define WINDOW_BITS 12
|
||||
#define MEM_LEVEL 4
|
||||
static struct z_stream_s stream;
|
||||
#else
|
||||
static unsigned char *workspace;
|
||||
#endif
|
||||
|
||||
struct pstore_zbackend {
|
||||
int (*compress)(const void *in, void *out, size_t inlen, size_t outlen);
|
||||
int (*decompress)(void *in, void *out, size_t inlen, size_t outlen);
|
||||
void (*allocate)(void);
|
||||
void (*free)(void);
|
||||
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static char *big_oops_buf;
|
||||
static size_t big_oops_buf_sz;
|
||||
|
@ -129,9 +150,9 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pstore_cannot_block_path);
|
||||
|
||||
#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
|
||||
/* Derived from logfs_compress() */
|
||||
static int pstore_compress(const void *in, void *out, size_t inlen,
|
||||
size_t outlen)
|
||||
static int compress_zlib(const void *in, void *out, size_t inlen, size_t outlen)
|
||||
{
|
||||
int err, ret;
|
||||
|
||||
|
@ -165,7 +186,7 @@ error:
|
|||
}
|
||||
|
||||
/* Derived from logfs_uncompress */
|
||||
static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
|
||||
static int decompress_zlib(void *in, void *out, size_t inlen, size_t outlen)
|
||||
{
|
||||
int err, ret;
|
||||
|
||||
|
@ -194,7 +215,7 @@ error:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void allocate_buf_for_compression(void)
|
||||
static void allocate_zlib(void)
|
||||
{
|
||||
size_t size;
|
||||
size_t cmpr;
|
||||
|
@ -237,12 +258,190 @@ static void allocate_buf_for_compression(void)
|
|||
|
||||
}
|
||||
|
||||
static void free_buf_for_compression(void)
|
||||
static void free_zlib(void)
|
||||
{
|
||||
kfree(stream.workspace);
|
||||
stream.workspace = NULL;
|
||||
kfree(big_oops_buf);
|
||||
big_oops_buf = NULL;
|
||||
big_oops_buf_sz = 0;
|
||||
}
|
||||
|
||||
static struct pstore_zbackend backend_zlib = {
|
||||
.compress = compress_zlib,
|
||||
.decompress = decompress_zlib,
|
||||
.allocate = allocate_zlib,
|
||||
.free = free_zlib,
|
||||
.name = "zlib",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PSTORE_LZO_COMPRESS
|
||||
static int compress_lzo(const void *in, void *out, size_t inlen, size_t outlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lzo1x_1_compress(in, inlen, out, &outlen, workspace);
|
||||
if (ret != LZO_E_OK) {
|
||||
pr_err("lzo_compress error, ret = %d!\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
static int decompress_lzo(void *in, void *out, size_t inlen, size_t outlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lzo1x_decompress_safe(in, inlen, out, &outlen);
|
||||
if (ret != LZO_E_OK) {
|
||||
pr_err("lzo_decompress error, ret = %d!\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
static void allocate_lzo(void)
|
||||
{
|
||||
big_oops_buf_sz = lzo1x_worst_compress(psinfo->bufsize);
|
||||
big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
|
||||
if (big_oops_buf) {
|
||||
workspace = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
|
||||
if (!workspace) {
|
||||
pr_err("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");
|
||||
workspace = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_lzo(void)
|
||||
{
|
||||
kfree(workspace);
|
||||
kfree(big_oops_buf);
|
||||
big_oops_buf = NULL;
|
||||
big_oops_buf_sz = 0;
|
||||
}
|
||||
|
||||
static struct pstore_zbackend backend_lzo = {
|
||||
.compress = compress_lzo,
|
||||
.decompress = decompress_lzo,
|
||||
.allocate = allocate_lzo,
|
||||
.free = free_lzo,
|
||||
.name = "lzo",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PSTORE_LZ4_COMPRESS
|
||||
static int compress_lz4(const void *in, void *out, size_t inlen, size_t outlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lz4_compress(in, inlen, out, &outlen, workspace);
|
||||
if (ret) {
|
||||
pr_err("lz4_compress error, ret = %d!\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
static int decompress_lz4(void *in, void *out, size_t inlen, size_t outlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lz4_decompress_unknownoutputsize(in, inlen, out, &outlen);
|
||||
if (ret) {
|
||||
pr_err("lz4_decompress error, ret = %d!\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
static void allocate_lz4(void)
|
||||
{
|
||||
big_oops_buf_sz = lz4_compressbound(psinfo->bufsize);
|
||||
big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
|
||||
if (big_oops_buf) {
|
||||
workspace = kmalloc(LZ4_MEM_COMPRESS, GFP_KERNEL);
|
||||
if (!workspace) {
|
||||
pr_err("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");
|
||||
workspace = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_lz4(void)
|
||||
{
|
||||
kfree(workspace);
|
||||
kfree(big_oops_buf);
|
||||
big_oops_buf = NULL;
|
||||
big_oops_buf_sz = 0;
|
||||
}
|
||||
|
||||
static struct pstore_zbackend backend_lz4 = {
|
||||
.compress = compress_lz4,
|
||||
.decompress = decompress_lz4,
|
||||
.allocate = allocate_lz4,
|
||||
.free = free_lz4,
|
||||
.name = "lz4",
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct pstore_zbackend *zbackend =
|
||||
#if defined(CONFIG_PSTORE_ZLIB_COMPRESS)
|
||||
&backend_zlib;
|
||||
#elif defined(CONFIG_PSTORE_LZO_COMPRESS)
|
||||
&backend_lzo;
|
||||
#elif defined(CONFIG_PSTORE_LZ4_COMPRESS)
|
||||
&backend_lz4;
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
|
||||
static int pstore_compress(const void *in, void *out,
|
||||
size_t inlen, size_t outlen)
|
||||
{
|
||||
if (zbackend)
|
||||
return zbackend->compress(in, out, inlen, outlen);
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
|
||||
{
|
||||
if (zbackend)
|
||||
return zbackend->decompress(in, out, inlen, outlen);
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void allocate_buf_for_compression(void)
|
||||
{
|
||||
if (zbackend) {
|
||||
pr_info("using %s compression\n", zbackend->name);
|
||||
zbackend->allocate();
|
||||
} else {
|
||||
pr_err("allocate compression buffer error!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void free_buf_for_compression(void)
|
||||
{
|
||||
if (zbackend)
|
||||
zbackend->free();
|
||||
else
|
||||
pr_err("free compression buffer error!\n");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -284,7 +483,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||
u64 id;
|
||||
unsigned int part = 1;
|
||||
unsigned long flags = 0;
|
||||
int is_locked = 0;
|
||||
int is_locked;
|
||||
int ret;
|
||||
|
||||
why = get_reason_str(reason);
|
||||
|
@ -295,8 +494,10 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||
pr_err("pstore dump routine blocked in %s path, may corrupt error record\n"
|
||||
, in_nmi() ? "NMI" : why);
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
spin_lock_irqsave(&psinfo->buf_lock, flags);
|
||||
is_locked = 1;
|
||||
}
|
||||
oopscount++;
|
||||
while (total < kmsg_bytes) {
|
||||
char *dst;
|
||||
|
@ -304,19 +505,25 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||
int hsize;
|
||||
int zipped_len = -1;
|
||||
size_t len;
|
||||
bool compressed;
|
||||
bool compressed = false;
|
||||
size_t total_len;
|
||||
|
||||
if (big_oops_buf && is_locked) {
|
||||
dst = big_oops_buf;
|
||||
hsize = sprintf(dst, "%s#%d Part%u\n", why,
|
||||
oopscount, part);
|
||||
size = big_oops_buf_sz - hsize;
|
||||
size = big_oops_buf_sz;
|
||||
} else {
|
||||
dst = psinfo->buf;
|
||||
size = psinfo->bufsize;
|
||||
}
|
||||
|
||||
if (!kmsg_dump_get_buffer(dumper, true, dst + hsize,
|
||||
size, &len))
|
||||
break;
|
||||
hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part);
|
||||
size -= hsize;
|
||||
|
||||
if (!kmsg_dump_get_buffer(dumper, true, dst + hsize,
|
||||
size, &len))
|
||||
break;
|
||||
|
||||
if (big_oops_buf && is_locked) {
|
||||
zipped_len = pstore_compress(dst, psinfo->buf,
|
||||
hsize + len, psinfo->bufsize);
|
||||
|
||||
|
@ -324,21 +531,9 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||
compressed = true;
|
||||
total_len = zipped_len;
|
||||
} else {
|
||||
compressed = false;
|
||||
total_len = copy_kmsg_to_buffer(hsize, len);
|
||||
}
|
||||
} else {
|
||||
dst = psinfo->buf;
|
||||
hsize = sprintf(dst, "%s#%d Part%u\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;
|
||||
}
|
||||
|
||||
|
@ -350,10 +545,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||
total += total_len;
|
||||
part++;
|
||||
}
|
||||
if (pstore_cannot_block_path(reason)) {
|
||||
if (is_locked)
|
||||
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
|
||||
} else
|
||||
if (is_locked)
|
||||
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -497,9 +689,11 @@ EXPORT_SYMBOL_GPL(pstore_register);
|
|||
|
||||
void pstore_unregister(struct pstore_info *psi)
|
||||
{
|
||||
pstore_unregister_pmsg();
|
||||
pstore_unregister_ftrace();
|
||||
pstore_unregister_console();
|
||||
if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) {
|
||||
pstore_unregister_pmsg();
|
||||
pstore_unregister_ftrace();
|
||||
pstore_unregister_console();
|
||||
}
|
||||
pstore_unregister_kmsg();
|
||||
|
||||
free_buf_for_compression();
|
||||
|
@ -527,6 +721,7 @@ void pstore_get_records(int quiet)
|
|||
int failed = 0, rc;
|
||||
bool compressed;
|
||||
int unzipped_len = -1;
|
||||
ssize_t ecc_notice_size = 0;
|
||||
|
||||
if (!psi)
|
||||
return;
|
||||
|
@ -536,7 +731,7 @@ void pstore_get_records(int quiet)
|
|||
goto out;
|
||||
|
||||
while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed,
|
||||
psi)) > 0) {
|
||||
&ecc_notice_size, psi)) > 0) {
|
||||
if (compressed && (type == PSTORE_TYPE_DMESG)) {
|
||||
if (big_oops_buf)
|
||||
unzipped_len = pstore_decompress(buf,
|
||||
|
@ -544,6 +739,9 @@ void pstore_get_records(int quiet)
|
|||
big_oops_buf_sz);
|
||||
|
||||
if (unzipped_len > 0) {
|
||||
if (ecc_notice_size)
|
||||
memcpy(big_oops_buf + unzipped_len,
|
||||
buf + size, ecc_notice_size);
|
||||
kfree(buf);
|
||||
buf = big_oops_buf;
|
||||
size = unzipped_len;
|
||||
|
@ -555,7 +753,8 @@ void pstore_get_records(int quiet)
|
|||
}
|
||||
}
|
||||
rc = pstore_mkfile(type, psi->name, id, count, buf,
|
||||
compressed, (size_t)size, time, psi);
|
||||
compressed, size + ecc_notice_size,
|
||||
time, psi);
|
||||
if (unzipped_len < 0) {
|
||||
/* Free buffer other than big oops */
|
||||
kfree(buf);
|
||||
|
|
105
fs/pstore/ram.c
105
fs/pstore/ram.c
|
@ -34,6 +34,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/pstore_ram.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#define RAMOOPS_KERNMSG_HDR "===="
|
||||
#define MIN_MEM_SIZE 4096UL
|
||||
|
@ -181,10 +183,10 @@ static bool prz_ok(struct persistent_ram_zone *prz)
|
|||
static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
|
||||
int *count, struct timespec *time,
|
||||
char **buf, bool *compressed,
|
||||
ssize_t *ecc_notice_size,
|
||||
struct pstore_info *psi)
|
||||
{
|
||||
ssize_t size;
|
||||
ssize_t ecc_notice_size;
|
||||
struct ramoops_context *cxt = psi->data;
|
||||
struct persistent_ram_zone *prz = NULL;
|
||||
int header_length = 0;
|
||||
|
@ -229,16 +231,16 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
|
|||
size = persistent_ram_old_size(prz) - header_length;
|
||||
|
||||
/* ECC correction notice */
|
||||
ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
|
||||
*ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
|
||||
|
||||
*buf = kmalloc(size + ecc_notice_size + 1, GFP_KERNEL);
|
||||
*buf = kmalloc(size + *ecc_notice_size + 1, GFP_KERNEL);
|
||||
if (*buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(*buf, (char *)persistent_ram_old(prz) + header_length, size);
|
||||
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;
|
||||
}
|
||||
|
||||
static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
|
||||
|
@ -458,15 +460,98 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ramoops_parse_dt_size(struct platform_device *pdev,
|
||||
const char *propname, u32 *value)
|
||||
{
|
||||
u32 val32 = 0;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, propname, &val32);
|
||||
if (ret < 0 && ret != -EINVAL) {
|
||||
dev_err(&pdev->dev, "failed to parse property %s: %d\n",
|
||||
propname, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val32 > INT_MAX) {
|
||||
dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
*value = val32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ramoops_parse_dt(struct platform_device *pdev,
|
||||
struct ramoops_platform_data *pdata)
|
||||
{
|
||||
struct device_node *of_node = pdev->dev.of_node;
|
||||
struct device_node *mem_region;
|
||||
struct resource res;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "using Device Tree\n");
|
||||
|
||||
mem_region = of_parse_phandle(of_node, "memory-region", 0);
|
||||
if (!mem_region) {
|
||||
dev_err(&pdev->dev, "no memory-region phandle\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(mem_region, 0, &res);
|
||||
of_node_put(mem_region);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to translate memory-region to resource: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pdata->mem_size = resource_size(&res);
|
||||
pdata->mem_address = res.start;
|
||||
pdata->mem_type = of_property_read_bool(of_node, "unbuffered");
|
||||
pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops");
|
||||
|
||||
#define parse_size(name, field) { \
|
||||
ret = ramoops_parse_dt_size(pdev, name, &value); \
|
||||
if (ret < 0) \
|
||||
return ret; \
|
||||
field = value; \
|
||||
}
|
||||
|
||||
parse_size("record-size", pdata->record_size);
|
||||
parse_size("console-size", pdata->console_size);
|
||||
parse_size("ftrace-size", pdata->ftrace_size);
|
||||
parse_size("pmsg-size", pdata->pmsg_size);
|
||||
parse_size("ecc-size", pdata->ecc_info.ecc_size);
|
||||
|
||||
#undef parse_size
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ramoops_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ramoops_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct ramoops_platform_data *pdata = dev->platform_data;
|
||||
struct ramoops_context *cxt = &oops_cxt;
|
||||
size_t dump_mem_sz;
|
||||
phys_addr_t paddr;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (dev_of_node(dev) && !pdata) {
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
err = -ENOMEM;
|
||||
goto fail_out;
|
||||
}
|
||||
|
||||
err = ramoops_parse_dt(pdev, pdata);
|
||||
if (err < 0)
|
||||
goto fail_out;
|
||||
}
|
||||
|
||||
/* Only a single ramoops area allowed at a time, so fail extra
|
||||
* probes.
|
||||
*/
|
||||
|
@ -596,11 +681,17 @@ static int ramoops_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dt_match[] = {
|
||||
{ .compatible = "ramoops" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ramoops_driver = {
|
||||
.probe = ramoops_probe,
|
||||
.remove = ramoops_remove,
|
||||
.driver = {
|
||||
.name = "ramoops",
|
||||
.name = "ramoops",
|
||||
.of_match_table = dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -58,7 +58,8 @@ 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,
|
||||
bool *compressed, struct pstore_info *psi);
|
||||
bool *compressed, ssize_t *ecc_notice_size,
|
||||
struct pstore_info *psi);
|
||||
int (*write)(enum pstore_type_id type,
|
||||
enum kmsg_dump_reason reason, u64 *id,
|
||||
unsigned int part, int count, bool compressed,
|
||||
|
|
Loading…
Reference in New Issue