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
48
Documentation/devicetree/bindings/misc/ramoops.txt
Normal file
48
Documentation/devicetree/bindings/misc/ramoops.txt
Normal file
@ -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
|
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
|
1. Use the module parameters (which have the names of the variables described
|
||||||
as before).
|
as before).
|
||||||
For quick debugging, you can also reserve parts of memory during boot
|
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
|
kernel to use only the first 128 MB of memory, and place ECC-protected ramoops
|
||||||
region at 128 MB boundary:
|
region at 128 MB boundary:
|
||||||
"mem=128M ramoops.mem_address=0x8000000 ramoops.ecc=1"
|
"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:
|
be set through that platform data. An example of doing that is:
|
||||||
|
|
||||||
#include <linux/pstore_ram.h>
|
#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,
|
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,
|
||||||
bool *compressed, struct pstore_info *psi)
|
bool *compressed, ssize_t *ecc_notice_size,
|
||||||
|
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;
|
||||||
@ -545,6 +546,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
kfree(buff);
|
kfree(buff);
|
||||||
|
|
||||||
|
*ecc_notice_size = 0;
|
||||||
if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
|
if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
|
||||||
*compressed = true;
|
*compressed = true;
|
||||||
else
|
else
|
||||||
|
@ -927,7 +927,8 @@ 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,
|
||||||
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,
|
static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
|
||||||
u64 *id, unsigned int part, int count, bool compressed,
|
u64 *id, unsigned int part, int count, bool compressed,
|
||||||
size_t size, struct pstore_info *psi);
|
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,
|
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
|
||||||
struct timespec *time, char **buf,
|
struct timespec *time, char **buf,
|
||||||
bool *compressed, struct pstore_info *psi)
|
bool *compressed, ssize_t *ecc_notice_size,
|
||||||
|
struct pstore_info *psi)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
ssize_t len = 0;
|
ssize_t len = 0;
|
||||||
@ -1033,6 +1035,7 @@ skip:
|
|||||||
memcpy(*buf, rcd->data, len - sizeof(*rcd));
|
memcpy(*buf, rcd->data, len - sizeof(*rcd));
|
||||||
*id = record_id;
|
*id = record_id;
|
||||||
*compressed = false;
|
*compressed = false;
|
||||||
|
*ecc_notice_size = 0;
|
||||||
if (uuid_le_cmp(rcd->sec_hdr.section_type,
|
if (uuid_le_cmp(rcd->sec_hdr.section_type,
|
||||||
CPER_SECTION_TYPE_DMESG_Z) == 0) {
|
CPER_SECTION_TYPE_DMESG_Z) == 0) {
|
||||||
*type = PSTORE_TYPE_DMESG;
|
*type = PSTORE_TYPE_DMESG;
|
||||||
|
@ -34,6 +34,7 @@ struct pstore_read_data {
|
|||||||
int *count;
|
int *count;
|
||||||
struct timespec *timespec;
|
struct timespec *timespec;
|
||||||
bool *compressed;
|
bool *compressed;
|
||||||
|
ssize_t *ecc_notice_size;
|
||||||
char **buf;
|
char **buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,6 +70,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
|
|||||||
*cb_data->compressed = true;
|
*cb_data->compressed = true;
|
||||||
else
|
else
|
||||||
*cb_data->compressed = false;
|
*cb_data->compressed = false;
|
||||||
|
*cb_data->ecc_notice_size = 0;
|
||||||
} else if (sscanf(name, "dump-type%u-%u-%d-%lu",
|
} 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 = generic_id(time, part, cnt);
|
*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_sec = time;
|
||||||
cb_data->timespec->tv_nsec = 0;
|
cb_data->timespec->tv_nsec = 0;
|
||||||
*cb_data->compressed = false;
|
*cb_data->compressed = false;
|
||||||
|
*cb_data->ecc_notice_size = 0;
|
||||||
} 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) {
|
||||||
/*
|
/*
|
||||||
@ -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_sec = time;
|
||||||
cb_data->timespec->tv_nsec = 0;
|
cb_data->timespec->tv_nsec = 0;
|
||||||
*cb_data->compressed = false;
|
*cb_data->compressed = false;
|
||||||
|
*cb_data->ecc_notice_size = 0;
|
||||||
} else
|
} else
|
||||||
return 0;
|
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,
|
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, bool *compressed,
|
char **buf, bool *compressed,
|
||||||
|
ssize_t *ecc_notice_size,
|
||||||
struct pstore_info *psi)
|
struct pstore_info *psi)
|
||||||
{
|
{
|
||||||
struct pstore_read_data data;
|
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.count = count;
|
||||||
data.timespec = timespec;
|
data.timespec = timespec;
|
||||||
data.compressed = compressed;
|
data.compressed = compressed;
|
||||||
|
data.ecc_notice_size = ecc_notice_size;
|
||||||
data.buf = buf;
|
data.buf = buf;
|
||||||
|
|
||||||
*data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
|
*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)
|
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);
|
module_init(efivars_pstore_init);
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
config PSTORE
|
config PSTORE
|
||||||
tristate "Persistent store support"
|
tristate "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
|
||||||
@ -14,6 +12,35 @@ config PSTORE
|
|||||||
If you don't have a platform persistent store driver,
|
If you don't have a platform persistent store driver,
|
||||||
say N.
|
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
|
config PSTORE_CONSOLE
|
||||||
bool "Log kernel console messages"
|
bool "Log kernel console messages"
|
||||||
depends on PSTORE
|
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 = {
|
static const struct file_operations pstore_file_operations = {
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.open = pstore_file_open,
|
.open = pstore_file_open,
|
||||||
.read = pstore_file_read,
|
.read = pstore_file_read,
|
||||||
.llseek = pstore_file_llseek,
|
.llseek = pstore_file_llseek,
|
||||||
|
@ -28,7 +28,15 @@
|
|||||||
#include <linux/console.h>
|
#include <linux/console.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pstore.h>
|
#include <linux/pstore.h>
|
||||||
|
#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
|
||||||
#include <linux/zlib.h>
|
#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/string.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@ -69,10 +77,23 @@ struct pstore_info *psinfo;
|
|||||||
static char *backend;
|
static char *backend;
|
||||||
|
|
||||||
/* Compression parameters */
|
/* Compression parameters */
|
||||||
|
#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
|
||||||
#define COMPR_LEVEL 6
|
#define COMPR_LEVEL 6
|
||||||
#define WINDOW_BITS 12
|
#define WINDOW_BITS 12
|
||||||
#define MEM_LEVEL 4
|
#define MEM_LEVEL 4
|
||||||
static struct z_stream_s stream;
|
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 char *big_oops_buf;
|
||||||
static size_t big_oops_buf_sz;
|
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);
|
EXPORT_SYMBOL_GPL(pstore_cannot_block_path);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
|
||||||
/* Derived from logfs_compress() */
|
/* Derived from logfs_compress() */
|
||||||
static int pstore_compress(const void *in, void *out, size_t inlen,
|
static int compress_zlib(const void *in, void *out, size_t inlen, size_t outlen)
|
||||||
size_t outlen)
|
|
||||||
{
|
{
|
||||||
int err, ret;
|
int err, ret;
|
||||||
|
|
||||||
@ -165,7 +186,7 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Derived from logfs_uncompress */
|
/* 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;
|
int err, ret;
|
||||||
|
|
||||||
@ -194,7 +215,7 @@ error:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void allocate_buf_for_compression(void)
|
static void allocate_zlib(void)
|
||||||
{
|
{
|
||||||
size_t size;
|
size_t size;
|
||||||
size_t cmpr;
|
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);
|
kfree(stream.workspace);
|
||||||
stream.workspace = NULL;
|
stream.workspace = NULL;
|
||||||
kfree(big_oops_buf);
|
kfree(big_oops_buf);
|
||||||
big_oops_buf = NULL;
|
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;
|
u64 id;
|
||||||
unsigned int part = 1;
|
unsigned int part = 1;
|
||||||
unsigned long flags = 0;
|
unsigned long flags = 0;
|
||||||
int is_locked = 0;
|
int is_locked;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
why = get_reason_str(reason);
|
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"
|
pr_err("pstore dump routine blocked in %s path, may corrupt error record\n"
|
||||||
, in_nmi() ? "NMI" : why);
|
, in_nmi() ? "NMI" : why);
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
spin_lock_irqsave(&psinfo->buf_lock, flags);
|
spin_lock_irqsave(&psinfo->buf_lock, flags);
|
||||||
|
is_locked = 1;
|
||||||
|
}
|
||||||
oopscount++;
|
oopscount++;
|
||||||
while (total < kmsg_bytes) {
|
while (total < kmsg_bytes) {
|
||||||
char *dst;
|
char *dst;
|
||||||
@ -304,19 +505,25 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||||||
int hsize;
|
int hsize;
|
||||||
int zipped_len = -1;
|
int zipped_len = -1;
|
||||||
size_t len;
|
size_t len;
|
||||||
bool compressed;
|
bool compressed = false;
|
||||||
size_t total_len;
|
size_t total_len;
|
||||||
|
|
||||||
if (big_oops_buf && is_locked) {
|
if (big_oops_buf && is_locked) {
|
||||||
dst = big_oops_buf;
|
dst = big_oops_buf;
|
||||||
hsize = sprintf(dst, "%s#%d Part%u\n", why,
|
size = big_oops_buf_sz;
|
||||||
oopscount, part);
|
} else {
|
||||||
size = big_oops_buf_sz - hsize;
|
dst = psinfo->buf;
|
||||||
|
size = psinfo->bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
if (!kmsg_dump_get_buffer(dumper, true, dst + hsize,
|
hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part);
|
||||||
size, &len))
|
size -= hsize;
|
||||||
break;
|
|
||||||
|
|
||||||
|
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,
|
zipped_len = pstore_compress(dst, psinfo->buf,
|
||||||
hsize + len, psinfo->bufsize);
|
hsize + len, psinfo->bufsize);
|
||||||
|
|
||||||
@ -324,21 +531,9 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||||||
compressed = true;
|
compressed = true;
|
||||||
total_len = zipped_len;
|
total_len = zipped_len;
|
||||||
} else {
|
} else {
|
||||||
compressed = false;
|
|
||||||
total_len = copy_kmsg_to_buffer(hsize, len);
|
total_len = copy_kmsg_to_buffer(hsize, len);
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
total_len = hsize + len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,10 +545,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||||||
total += total_len;
|
total += total_len;
|
||||||
part++;
|
part++;
|
||||||
}
|
}
|
||||||
if (pstore_cannot_block_path(reason)) {
|
if (is_locked)
|
||||||
if (is_locked)
|
|
||||||
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
|
|
||||||
} else
|
|
||||||
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
|
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,9 +689,11 @@ EXPORT_SYMBOL_GPL(pstore_register);
|
|||||||
|
|
||||||
void pstore_unregister(struct pstore_info *psi)
|
void pstore_unregister(struct pstore_info *psi)
|
||||||
{
|
{
|
||||||
pstore_unregister_pmsg();
|
if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) {
|
||||||
pstore_unregister_ftrace();
|
pstore_unregister_pmsg();
|
||||||
pstore_unregister_console();
|
pstore_unregister_ftrace();
|
||||||
|
pstore_unregister_console();
|
||||||
|
}
|
||||||
pstore_unregister_kmsg();
|
pstore_unregister_kmsg();
|
||||||
|
|
||||||
free_buf_for_compression();
|
free_buf_for_compression();
|
||||||
@ -527,6 +721,7 @@ void pstore_get_records(int quiet)
|
|||||||
int failed = 0, rc;
|
int failed = 0, rc;
|
||||||
bool compressed;
|
bool compressed;
|
||||||
int unzipped_len = -1;
|
int unzipped_len = -1;
|
||||||
|
ssize_t ecc_notice_size = 0;
|
||||||
|
|
||||||
if (!psi)
|
if (!psi)
|
||||||
return;
|
return;
|
||||||
@ -536,7 +731,7 @@ void pstore_get_records(int quiet)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed,
|
while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed,
|
||||||
psi)) > 0) {
|
&ecc_notice_size, psi)) > 0) {
|
||||||
if (compressed && (type == PSTORE_TYPE_DMESG)) {
|
if (compressed && (type == PSTORE_TYPE_DMESG)) {
|
||||||
if (big_oops_buf)
|
if (big_oops_buf)
|
||||||
unzipped_len = pstore_decompress(buf,
|
unzipped_len = pstore_decompress(buf,
|
||||||
@ -544,6 +739,9 @@ void pstore_get_records(int quiet)
|
|||||||
big_oops_buf_sz);
|
big_oops_buf_sz);
|
||||||
|
|
||||||
if (unzipped_len > 0) {
|
if (unzipped_len > 0) {
|
||||||
|
if (ecc_notice_size)
|
||||||
|
memcpy(big_oops_buf + unzipped_len,
|
||||||
|
buf + size, ecc_notice_size);
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
buf = big_oops_buf;
|
buf = big_oops_buf;
|
||||||
size = unzipped_len;
|
size = unzipped_len;
|
||||||
@ -555,7 +753,8 @@ void pstore_get_records(int quiet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
rc = pstore_mkfile(type, psi->name, id, count, buf,
|
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) {
|
if (unzipped_len < 0) {
|
||||||
/* Free buffer other than big oops */
|
/* Free buffer other than big oops */
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
|
105
fs/pstore/ram.c
105
fs/pstore/ram.c
@ -34,6 +34,8 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/pstore_ram.h>
|
#include <linux/pstore_ram.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
|
||||||
#define RAMOOPS_KERNMSG_HDR "===="
|
#define RAMOOPS_KERNMSG_HDR "===="
|
||||||
#define MIN_MEM_SIZE 4096UL
|
#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,
|
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, bool *compressed,
|
char **buf, bool *compressed,
|
||||||
|
ssize_t *ecc_notice_size,
|
||||||
struct pstore_info *psi)
|
struct pstore_info *psi)
|
||||||
{
|
{
|
||||||
ssize_t size;
|
ssize_t size;
|
||||||
ssize_t ecc_notice_size;
|
|
||||||
struct ramoops_context *cxt = psi->data;
|
struct ramoops_context *cxt = psi->data;
|
||||||
struct persistent_ram_zone *prz = NULL;
|
struct persistent_ram_zone *prz = NULL;
|
||||||
int header_length = 0;
|
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;
|
size = persistent_ram_old_size(prz) - header_length;
|
||||||
|
|
||||||
/* ECC correction notice */
|
/* 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)
|
if (*buf == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
memcpy(*buf, (char *)persistent_ram_old(prz) + header_length, size);
|
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,
|
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;
|
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)
|
static int ramoops_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
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;
|
struct ramoops_context *cxt = &oops_cxt;
|
||||||
size_t dump_mem_sz;
|
size_t dump_mem_sz;
|
||||||
phys_addr_t paddr;
|
phys_addr_t paddr;
|
||||||
int err = -EINVAL;
|
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
|
/* Only a single ramoops area allowed at a time, so fail extra
|
||||||
* probes.
|
* probes.
|
||||||
*/
|
*/
|
||||||
@ -596,11 +681,17 @@ static int ramoops_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id dt_match[] = {
|
||||||
|
{ .compatible = "ramoops" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver ramoops_driver = {
|
static struct platform_driver ramoops_driver = {
|
||||||
.probe = ramoops_probe,
|
.probe = ramoops_probe,
|
||||||
.remove = ramoops_remove,
|
.remove = ramoops_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ramoops",
|
.name = "ramoops",
|
||||||
|
.of_match_table = dt_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,7 +58,8 @@ 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,
|
||||||
bool *compressed, struct pstore_info *psi);
|
bool *compressed, ssize_t *ecc_notice_size,
|
||||||
|
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, bool compressed,
|
unsigned int part, int count, bool compressed,
|
||||||
|
Loading…
Reference in New Issue
Block a user