diff options
-rw-r--r-- | Documentation/devicetree/bindings/misc/ramoops.txt | 48 | ||||
-rw-r--r-- | Documentation/ramoops.txt | 6 | ||||
-rw-r--r-- | arch/powerpc/kernel/nvram_64.c | 4 | ||||
-rw-r--r-- | drivers/acpi/apei/erst.c | 7 | ||||
-rw-r--r-- | drivers/firmware/efi/efi-pstore.c | 13 | ||||
-rw-r--r-- | fs/pstore/Kconfig | 31 | ||||
-rw-r--r-- | fs/pstore/inode.c | 1 | ||||
-rw-r--r-- | fs/pstore/platform.c | 269 | ||||
-rw-r--r-- | fs/pstore/ram.c | 105 | ||||
-rw-r--r-- | include/linux/pstore.h | 3 |
10 files changed, 436 insertions, 51 deletions
diff --git a/Documentation/devicetree/bindings/misc/ramoops.txt b/Documentation/devicetree/bindings/misc/ramoops.txt new file mode 100644 index 000000000000..cd02cec67d38 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/ramoops.txt | |||
@@ -0,0 +1,48 @@ | |||
1 | Ramoops oops/panic logger | ||
2 | ========================= | ||
3 | |||
4 | ramoops provides persistent RAM storage for oops and panics, so they can be | ||
5 | recovered after a reboot. It is a backend to pstore, so this node is named | ||
6 | "ramoops" after the backend, rather than "pstore" which is the subsystem. | ||
7 | |||
8 | Parts of this storage may be set aside for other persistent log buffers, such | ||
9 | as kernel log messages, or for optional ECC error-correction data. The total | ||
10 | size of these optional buffers must fit in the reserved region. | ||
11 | |||
12 | Any remaining space will be used for a circular buffer of oops and panic | ||
13 | records. These records have a configurable size, with a size of 0 indicating | ||
14 | that they should be disabled. | ||
15 | |||
16 | At least one of "record-size", "console-size", "ftrace-size", or "pmsg-size" | ||
17 | must be set non-zero, but are otherwise optional as listed below. | ||
18 | |||
19 | |||
20 | Required properties: | ||
21 | |||
22 | - compatible: must be "ramoops" | ||
23 | |||
24 | - memory-region: phandle to a region of memory that is preserved between | ||
25 | reboots | ||
26 | |||
27 | |||
28 | Optional properties: | ||
29 | |||
30 | - ecc-size: enables ECC support and specifies ECC buffer size in bytes | ||
31 | (defaults to 0: no ECC) | ||
32 | |||
33 | - record-size: maximum size in bytes of each dump done on oops/panic | ||
34 | (defaults to 0: disabled) | ||
35 | |||
36 | - console-size: size in bytes of log buffer reserved for kernel messages | ||
37 | (defaults to 0: disabled) | ||
38 | |||
39 | - ftrace-size: size in bytes of log buffer reserved for function tracing and | ||
40 | profiling (defaults to 0: disabled) | ||
41 | |||
42 | - pmsg-size: size in bytes of log buffer reserved for userspace messages | ||
43 | (defaults to 0: disabled) | ||
44 | |||
45 | - unbuffered: if present, use unbuffered mappings to map the reserved region | ||
46 | (defaults to buffered mappings) | ||
47 | |||
48 | - no-dump-oops: if present, only dump panics (defaults to panics and oops) | ||
diff --git a/Documentation/ramoops.txt b/Documentation/ramoops.txt index 5d8675615e59..9264bcab4099 100644 --- a/Documentation/ramoops.txt +++ b/Documentation/ramoops.txt | |||
@@ -45,7 +45,7 @@ corrupt, but usually it is restorable. | |||
45 | 45 | ||
46 | 2. Setting the parameters | 46 | 2. Setting the parameters |
47 | 47 | ||
48 | Setting the ramoops parameters can be done in 2 different manners: | 48 | Setting the ramoops parameters can be done in 3 different manners: |
49 | 1. Use the module parameters (which have the names of the variables described | 49 | 1. Use the module parameters (which have the names of the variables described |
50 | as before). | 50 | as before). |
51 | For quick debugging, you can also reserve parts of memory during boot | 51 | 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: | |||
54 | kernel to use only the first 128 MB of memory, and place ECC-protected ramoops | 54 | kernel to use only the first 128 MB of memory, and place ECC-protected ramoops |
55 | region at 128 MB boundary: | 55 | region at 128 MB boundary: |
56 | "mem=128M ramoops.mem_address=0x8000000 ramoops.ecc=1" | 56 | "mem=128M ramoops.mem_address=0x8000000 ramoops.ecc=1" |
57 | 2. Use a platform device and set the platform data. The parameters can then | 57 | 2. Use Device Tree bindings, as described in |
58 | Documentation/device-tree/bindings/misc/ramoops.txt. | ||
59 | 3. Use a platform device and set the platform data. The parameters can then | ||
58 | be set through that platform data. An example of doing that is: | 60 | be set through that platform data. An example of doing that is: |
59 | 61 | ||
60 | #include <linux/pstore_ram.h> | 62 | #include <linux/pstore_ram.h> |
diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c index 856f9a7944cd..64174bf95611 100644 --- a/arch/powerpc/kernel/nvram_64.c +++ b/arch/powerpc/kernel/nvram_64.c | |||
@@ -444,7 +444,8 @@ static int nvram_pstore_write(enum pstore_type_id type, | |||
444 | */ | 444 | */ |
445 | static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, | 445 | static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, |
446 | int *count, struct timespec *time, char **buf, | 446 | int *count, struct timespec *time, char **buf, |
447 | bool *compressed, struct pstore_info *psi) | 447 | bool *compressed, ssize_t *ecc_notice_size, |
448 | struct pstore_info *psi) | ||
448 | { | 449 | { |
449 | struct oops_log_info *oops_hdr; | 450 | struct oops_log_info *oops_hdr; |
450 | unsigned int err_type, id_no, size = 0; | 451 | 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, | |||
545 | return -ENOMEM; | 546 | return -ENOMEM; |
546 | kfree(buff); | 547 | kfree(buff); |
547 | 548 | ||
549 | *ecc_notice_size = 0; | ||
548 | if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) | 550 | if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) |
549 | *compressed = true; | 551 | *compressed = true; |
550 | else | 552 | else |
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 006c3894c6ea..f096ab3cb54d 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c | |||
@@ -927,7 +927,8 @@ static int erst_open_pstore(struct pstore_info *psi); | |||
927 | static int erst_close_pstore(struct pstore_info *psi); | 927 | static int erst_close_pstore(struct pstore_info *psi); |
928 | static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, | 928 | static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, |
929 | struct timespec *time, char **buf, | 929 | struct timespec *time, char **buf, |
930 | bool *compressed, struct pstore_info *psi); | 930 | bool *compressed, ssize_t *ecc_notice_size, |
931 | struct pstore_info *psi); | ||
931 | static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, | 932 | static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, |
932 | u64 *id, unsigned int part, int count, bool compressed, | 933 | u64 *id, unsigned int part, int count, bool compressed, |
933 | size_t size, struct pstore_info *psi); | 934 | size_t size, struct pstore_info *psi); |
@@ -987,7 +988,8 @@ static int erst_close_pstore(struct pstore_info *psi) | |||
987 | 988 | ||
988 | static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, | 989 | static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, |
989 | struct timespec *time, char **buf, | 990 | struct timespec *time, char **buf, |
990 | bool *compressed, struct pstore_info *psi) | 991 | bool *compressed, ssize_t *ecc_notice_size, |
992 | struct pstore_info *psi) | ||
991 | { | 993 | { |
992 | int rc; | 994 | int rc; |
993 | ssize_t len = 0; | 995 | ssize_t len = 0; |
@@ -1033,6 +1035,7 @@ skip: | |||
1033 | memcpy(*buf, rcd->data, len - sizeof(*rcd)); | 1035 | memcpy(*buf, rcd->data, len - sizeof(*rcd)); |
1034 | *id = record_id; | 1036 | *id = record_id; |
1035 | *compressed = false; | 1037 | *compressed = false; |
1038 | *ecc_notice_size = 0; | ||
1036 | if (uuid_le_cmp(rcd->sec_hdr.section_type, | 1039 | if (uuid_le_cmp(rcd->sec_hdr.section_type, |
1037 | CPER_SECTION_TYPE_DMESG_Z) == 0) { | 1040 | CPER_SECTION_TYPE_DMESG_Z) == 0) { |
1038 | *type = PSTORE_TYPE_DMESG; | 1041 | *type = PSTORE_TYPE_DMESG; |
diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index eac76a79a880..30a24d09ea6c 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c | |||
@@ -34,6 +34,7 @@ struct pstore_read_data { | |||
34 | int *count; | 34 | int *count; |
35 | struct timespec *timespec; | 35 | struct timespec *timespec; |
36 | bool *compressed; | 36 | bool *compressed; |
37 | ssize_t *ecc_notice_size; | ||
37 | char **buf; | 38 | char **buf; |
38 | }; | 39 | }; |
39 | 40 | ||
@@ -69,6 +70,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) | |||
69 | *cb_data->compressed = true; | 70 | *cb_data->compressed = true; |
70 | else | 71 | else |
71 | *cb_data->compressed = false; | 72 | *cb_data->compressed = false; |
73 | *cb_data->ecc_notice_size = 0; | ||
72 | } else if (sscanf(name, "dump-type%u-%u-%d-%lu", | 74 | } else if (sscanf(name, "dump-type%u-%u-%d-%lu", |
73 | cb_data->type, &part, &cnt, &time) == 4) { | 75 | cb_data->type, &part, &cnt, &time) == 4) { |
74 | *cb_data->id = generic_id(time, part, cnt); | 76 | *cb_data->id = generic_id(time, part, cnt); |
@@ -76,6 +78,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) | |||
76 | cb_data->timespec->tv_sec = time; | 78 | cb_data->timespec->tv_sec = time; |
77 | cb_data->timespec->tv_nsec = 0; | 79 | cb_data->timespec->tv_nsec = 0; |
78 | *cb_data->compressed = false; | 80 | *cb_data->compressed = false; |
81 | *cb_data->ecc_notice_size = 0; | ||
79 | } else if (sscanf(name, "dump-type%u-%u-%lu", | 82 | } else if (sscanf(name, "dump-type%u-%u-%lu", |
80 | cb_data->type, &part, &time) == 3) { | 83 | cb_data->type, &part, &time) == 3) { |
81 | /* | 84 | /* |
@@ -88,6 +91,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) | |||
88 | cb_data->timespec->tv_sec = time; | 91 | cb_data->timespec->tv_sec = time; |
89 | cb_data->timespec->tv_nsec = 0; | 92 | cb_data->timespec->tv_nsec = 0; |
90 | *cb_data->compressed = false; | 93 | *cb_data->compressed = false; |
94 | *cb_data->ecc_notice_size = 0; | ||
91 | } else | 95 | } else |
92 | return 0; | 96 | return 0; |
93 | 97 | ||
@@ -210,6 +214,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) | |||
210 | static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, | 214 | static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, |
211 | int *count, struct timespec *timespec, | 215 | int *count, struct timespec *timespec, |
212 | char **buf, bool *compressed, | 216 | char **buf, bool *compressed, |
217 | ssize_t *ecc_notice_size, | ||
213 | struct pstore_info *psi) | 218 | struct pstore_info *psi) |
214 | { | 219 | { |
215 | struct pstore_read_data data; | 220 | struct pstore_read_data data; |
@@ -220,6 +225,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, | |||
220 | data.count = count; | 225 | data.count = count; |
221 | data.timespec = timespec; | 226 | data.timespec = timespec; |
222 | data.compressed = compressed; | 227 | data.compressed = compressed; |
228 | data.ecc_notice_size = ecc_notice_size; | ||
223 | data.buf = buf; | 229 | data.buf = buf; |
224 | 230 | ||
225 | *data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); | 231 | *data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); |
@@ -393,6 +399,13 @@ static __init int efivars_pstore_init(void) | |||
393 | 399 | ||
394 | static __exit void efivars_pstore_exit(void) | 400 | static __exit void efivars_pstore_exit(void) |
395 | { | 401 | { |
402 | if (!efi_pstore_info.bufsize) | ||
403 | return; | ||
404 | |||
405 | pstore_unregister(&efi_pstore_info); | ||
406 | kfree(efi_pstore_info.buf); | ||
407 | efi_pstore_info.buf = NULL; | ||
408 | efi_pstore_info.bufsize = 0; | ||
396 | } | 409 | } |
397 | 410 | ||
398 | module_init(efivars_pstore_init); | 411 | module_init(efivars_pstore_init); |
diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 360ae43f590c..be40813eff52 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig | |||
@@ -1,8 +1,6 @@ | |||
1 | config PSTORE | 1 | config PSTORE |
2 | tristate "Persistent store support" | 2 | tristate "Persistent store support" |
3 | default n | 3 | default n |
4 | select ZLIB_DEFLATE | ||
5 | select ZLIB_INFLATE | ||
6 | help | 4 | help |
7 | This option enables generic access to platform level | 5 | This option enables generic access to platform level |
8 | persistent storage via "pstore" filesystem that can | 6 | persistent storage via "pstore" filesystem that can |
@@ -14,6 +12,35 @@ config PSTORE | |||
14 | If you don't have a platform persistent store driver, | 12 | If you don't have a platform persistent store driver, |
15 | say N. | 13 | say N. |
16 | 14 | ||
15 | choice | ||
16 | prompt "Choose compression algorithm" | ||
17 | depends on PSTORE | ||
18 | default PSTORE_ZLIB_COMPRESS | ||
19 | help | ||
20 | This option chooses compression algorithm. | ||
21 | |||
22 | config PSTORE_ZLIB_COMPRESS | ||
23 | bool "ZLIB" | ||
24 | select ZLIB_DEFLATE | ||
25 | select ZLIB_INFLATE | ||
26 | help | ||
27 | This option enables ZLIB compression algorithm support. | ||
28 | |||
29 | config PSTORE_LZO_COMPRESS | ||
30 | bool "LZO" | ||
31 | select LZO_COMPRESS | ||
32 | select LZO_DECOMPRESS | ||
33 | help | ||
34 | This option enables LZO compression algorithm support. | ||
35 | |||
36 | config PSTORE_LZ4_COMPRESS | ||
37 | bool "LZ4" | ||
38 | select LZ4_COMPRESS | ||
39 | select LZ4_DECOMPRESS | ||
40 | help | ||
41 | This option enables LZ4 compression algorithm support. | ||
42 | endchoice | ||
43 | |||
17 | config PSTORE_CONSOLE | 44 | config PSTORE_CONSOLE |
18 | bool "Log kernel console messages" | 45 | bool "Log kernel console messages" |
19 | depends on PSTORE | 46 | depends on PSTORE |
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 45d6110744cb..ec9ddef5ae75 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c | |||
@@ -178,7 +178,6 @@ static loff_t pstore_file_llseek(struct file *file, loff_t off, int whence) | |||
178 | } | 178 | } |
179 | 179 | ||
180 | static const struct file_operations pstore_file_operations = { | 180 | static const struct file_operations pstore_file_operations = { |
181 | .owner = THIS_MODULE, | ||
182 | .open = pstore_file_open, | 181 | .open = pstore_file_open, |
183 | .read = pstore_file_read, | 182 | .read = pstore_file_read, |
184 | .llseek = pstore_file_llseek, | 183 | .llseek = pstore_file_llseek, |
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 588461bb2dd4..16ecca5b72d8 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c | |||
@@ -28,7 +28,15 @@ | |||
28 | #include <linux/console.h> | 28 | #include <linux/console.h> |
29 | #include <linux/module.h> | 29 | #include <linux/module.h> |
30 | #include <linux/pstore.h> | 30 | #include <linux/pstore.h> |
31 | #ifdef CONFIG_PSTORE_ZLIB_COMPRESS | ||
31 | #include <linux/zlib.h> | 32 | #include <linux/zlib.h> |
33 | #endif | ||
34 | #ifdef CONFIG_PSTORE_LZO_COMPRESS | ||
35 | #include <linux/lzo.h> | ||
36 | #endif | ||
37 | #ifdef CONFIG_PSTORE_LZ4_COMPRESS | ||
38 | #include <linux/lz4.h> | ||
39 | #endif | ||
32 | #include <linux/string.h> | 40 | #include <linux/string.h> |
33 | #include <linux/timer.h> | 41 | #include <linux/timer.h> |
34 | #include <linux/slab.h> | 42 | #include <linux/slab.h> |
@@ -69,10 +77,23 @@ struct pstore_info *psinfo; | |||
69 | static char *backend; | 77 | static char *backend; |
70 | 78 | ||
71 | /* Compression parameters */ | 79 | /* Compression parameters */ |
80 | #ifdef CONFIG_PSTORE_ZLIB_COMPRESS | ||
72 | #define COMPR_LEVEL 6 | 81 | #define COMPR_LEVEL 6 |
73 | #define WINDOW_BITS 12 | 82 | #define WINDOW_BITS 12 |
74 | #define MEM_LEVEL 4 | 83 | #define MEM_LEVEL 4 |
75 | static struct z_stream_s stream; | 84 | static struct z_stream_s stream; |
85 | #else | ||
86 | static unsigned char *workspace; | ||
87 | #endif | ||
88 | |||
89 | struct pstore_zbackend { | ||
90 | int (*compress)(const void *in, void *out, size_t inlen, size_t outlen); | ||
91 | int (*decompress)(void *in, void *out, size_t inlen, size_t outlen); | ||
92 | void (*allocate)(void); | ||
93 | void (*free)(void); | ||
94 | |||
95 | const char *name; | ||
96 | }; | ||
76 | 97 | ||
77 | static char *big_oops_buf; | 98 | static char *big_oops_buf; |
78 | static size_t big_oops_buf_sz; | 99 | static size_t big_oops_buf_sz; |
@@ -129,9 +150,9 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason) | |||
129 | } | 150 | } |
130 | EXPORT_SYMBOL_GPL(pstore_cannot_block_path); | 151 | EXPORT_SYMBOL_GPL(pstore_cannot_block_path); |
131 | 152 | ||
153 | #ifdef CONFIG_PSTORE_ZLIB_COMPRESS | ||
132 | /* Derived from logfs_compress() */ | 154 | /* Derived from logfs_compress() */ |
133 | static int pstore_compress(const void *in, void *out, size_t inlen, | 155 | static int compress_zlib(const void *in, void *out, size_t inlen, size_t outlen) |
134 | size_t outlen) | ||
135 | { | 156 | { |
136 | int err, ret; | 157 | int err, ret; |
137 | 158 | ||
@@ -165,7 +186,7 @@ error: | |||
165 | } | 186 | } |
166 | 187 | ||
167 | /* Derived from logfs_uncompress */ | 188 | /* Derived from logfs_uncompress */ |
168 | static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen) | 189 | static int decompress_zlib(void *in, void *out, size_t inlen, size_t outlen) |
169 | { | 190 | { |
170 | int err, ret; | 191 | int err, ret; |
171 | 192 | ||
@@ -194,7 +215,7 @@ error: | |||
194 | return ret; | 215 | return ret; |
195 | } | 216 | } |
196 | 217 | ||
197 | static void allocate_buf_for_compression(void) | 218 | static void allocate_zlib(void) |
198 | { | 219 | { |
199 | size_t size; | 220 | size_t size; |
200 | size_t cmpr; | 221 | size_t cmpr; |
@@ -237,12 +258,190 @@ static void allocate_buf_for_compression(void) | |||
237 | 258 | ||
238 | } | 259 | } |
239 | 260 | ||
240 | static void free_buf_for_compression(void) | 261 | static void free_zlib(void) |
241 | { | 262 | { |
242 | kfree(stream.workspace); | 263 | kfree(stream.workspace); |
243 | stream.workspace = NULL; | 264 | stream.workspace = NULL; |
244 | kfree(big_oops_buf); | 265 | kfree(big_oops_buf); |
245 | big_oops_buf = NULL; | 266 | big_oops_buf = NULL; |
267 | big_oops_buf_sz = 0; | ||
268 | } | ||
269 | |||
270 | static struct pstore_zbackend backend_zlib = { | ||
271 | .compress = compress_zlib, | ||
272 | .decompress = decompress_zlib, | ||
273 | .allocate = allocate_zlib, | ||
274 | .free = free_zlib, | ||
275 | .name = "zlib", | ||
276 | }; | ||
277 | #endif | ||
278 | |||
279 | #ifdef CONFIG_PSTORE_LZO_COMPRESS | ||
280 | static int compress_lzo(const void *in, void *out, size_t inlen, size_t outlen) | ||
281 | { | ||
282 | int ret; | ||
283 | |||
284 | ret = lzo1x_1_compress(in, inlen, out, &outlen, workspace); | ||
285 | if (ret != LZO_E_OK) { | ||
286 | pr_err("lzo_compress error, ret = %d!\n", ret); | ||
287 | return -EIO; | ||
288 | } | ||
289 | |||
290 | return outlen; | ||
291 | } | ||
292 | |||
293 | static int decompress_lzo(void *in, void *out, size_t inlen, size_t outlen) | ||
294 | { | ||
295 | int ret; | ||
296 | |||
297 | ret = lzo1x_decompress_safe(in, inlen, out, &outlen); | ||
298 | if (ret != LZO_E_OK) { | ||
299 | pr_err("lzo_decompress error, ret = %d!\n", ret); | ||
300 | return -EIO; | ||
301 | } | ||
302 | |||
303 | return outlen; | ||
304 | } | ||
305 | |||
306 | static void allocate_lzo(void) | ||
307 | { | ||
308 | big_oops_buf_sz = lzo1x_worst_compress(psinfo->bufsize); | ||
309 | big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); | ||
310 | if (big_oops_buf) { | ||
311 | workspace = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); | ||
312 | if (!workspace) { | ||
313 | pr_err("No memory for compression workspace; skipping compression\n"); | ||
314 | kfree(big_oops_buf); | ||
315 | big_oops_buf = NULL; | ||
316 | } | ||
317 | } else { | ||
318 | pr_err("No memory for uncompressed data; skipping compression\n"); | ||
319 | workspace = NULL; | ||
320 | } | ||
321 | } | ||
322 | |||
323 | static void free_lzo(void) | ||
324 | { | ||
325 | kfree(workspace); | ||
326 | kfree(big_oops_buf); | ||
327 | big_oops_buf = NULL; | ||
328 | big_oops_buf_sz = 0; | ||
329 | } | ||
330 | |||
331 | static struct pstore_zbackend backend_lzo = { | ||
332 | .compress = compress_lzo, | ||
333 | .decompress = decompress_lzo, | ||
334 | .allocate = allocate_lzo, | ||
335 | .free = free_lzo, | ||
336 | .name = "lzo", | ||
337 | }; | ||
338 | #endif | ||
339 | |||
340 | #ifdef CONFIG_PSTORE_LZ4_COMPRESS | ||
341 | static int compress_lz4(const void *in, void *out, size_t inlen, size_t outlen) | ||
342 | { | ||
343 | int ret; | ||
344 | |||
345 | ret = lz4_compress(in, inlen, out, &outlen, workspace); | ||
346 | if (ret) { | ||
347 | pr_err("lz4_compress error, ret = %d!\n", ret); | ||
348 | return -EIO; | ||
349 | } | ||
350 | |||
351 | return outlen; | ||
352 | } | ||
353 | |||
354 | static int decompress_lz4(void *in, void *out, size_t inlen, size_t outlen) | ||
355 | { | ||
356 | int ret; | ||
357 | |||
358 | ret = lz4_decompress_unknownoutputsize(in, inlen, out, &outlen); | ||
359 | if (ret) { | ||
360 | pr_err("lz4_decompress error, ret = %d!\n", ret); | ||
361 | return -EIO; | ||
362 | } | ||
363 | |||
364 | return outlen; | ||
365 | } | ||
366 | |||
367 | static void allocate_lz4(void) | ||
368 | { | ||
369 | big_oops_buf_sz = lz4_compressbound(psinfo->bufsize); | ||
370 | big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); | ||
371 | if (big_oops_buf) { | ||
372 | workspace = kmalloc(LZ4_MEM_COMPRESS, GFP_KERNEL); | ||
373 | if (!workspace) { | ||
374 | pr_err("No memory for compression workspace; skipping compression\n"); | ||
375 | kfree(big_oops_buf); | ||
376 | big_oops_buf = NULL; | ||
377 | } | ||
378 | } else { | ||
379 | pr_err("No memory for uncompressed data; skipping compression\n"); | ||
380 | workspace = NULL; | ||
381 | } | ||
382 | } | ||
383 | |||
384 | static void free_lz4(void) | ||
385 | { | ||
386 | kfree(workspace); | ||
387 | kfree(big_oops_buf); | ||
388 | big_oops_buf = NULL; | ||
389 | big_oops_buf_sz = 0; | ||
390 | } | ||
391 | |||
392 | static struct pstore_zbackend backend_lz4 = { | ||
393 | .compress = compress_lz4, | ||
394 | .decompress = decompress_lz4, | ||
395 | .allocate = allocate_lz4, | ||
396 | .free = free_lz4, | ||
397 | .name = "lz4", | ||
398 | }; | ||
399 | #endif | ||
400 | |||
401 | static struct pstore_zbackend *zbackend = | ||
402 | #if defined(CONFIG_PSTORE_ZLIB_COMPRESS) | ||
403 | &backend_zlib; | ||
404 | #elif defined(CONFIG_PSTORE_LZO_COMPRESS) | ||
405 | &backend_lzo; | ||
406 | #elif defined(CONFIG_PSTORE_LZ4_COMPRESS) | ||
407 | &backend_lz4; | ||
408 | #else | ||
409 | NULL; | ||
410 | #endif | ||
411 | |||
412 | static int pstore_compress(const void *in, void *out, | ||
413 | size_t inlen, size_t outlen) | ||
414 | { | ||
415 | if (zbackend) | ||
416 | return zbackend->compress(in, out, inlen, outlen); | ||
417 | else | ||
418 | return -EIO; | ||
419 | } | ||
420 | |||
421 | static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen) | ||
422 | { | ||
423 | if (zbackend) | ||
424 | return zbackend->decompress(in, out, inlen, outlen); | ||
425 | else | ||
426 | return -EIO; | ||
427 | } | ||
428 | |||
429 | static void allocate_buf_for_compression(void) | ||
430 | { | ||
431 | if (zbackend) { | ||
432 | pr_info("using %s compression\n", zbackend->name); | ||
433 | zbackend->allocate(); | ||
434 | } else { | ||
435 | pr_err("allocate compression buffer error!\n"); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | static void free_buf_for_compression(void) | ||
440 | { | ||
441 | if (zbackend) | ||
442 | zbackend->free(); | ||
443 | else | ||
444 | pr_err("free compression buffer error!\n"); | ||
246 | } | 445 | } |
247 | 446 | ||
248 | /* | 447 | /* |
@@ -284,7 +483,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
284 | u64 id; | 483 | u64 id; |
285 | unsigned int part = 1; | 484 | unsigned int part = 1; |
286 | unsigned long flags = 0; | 485 | unsigned long flags = 0; |
287 | int is_locked = 0; | 486 | int is_locked; |
288 | int ret; | 487 | int ret; |
289 | 488 | ||
290 | why = get_reason_str(reason); | 489 | why = get_reason_str(reason); |
@@ -295,8 +494,10 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
295 | pr_err("pstore dump routine blocked in %s path, may corrupt error record\n" | 494 | pr_err("pstore dump routine blocked in %s path, may corrupt error record\n" |
296 | , in_nmi() ? "NMI" : why); | 495 | , in_nmi() ? "NMI" : why); |
297 | } | 496 | } |
298 | } else | 497 | } else { |
299 | spin_lock_irqsave(&psinfo->buf_lock, flags); | 498 | spin_lock_irqsave(&psinfo->buf_lock, flags); |
499 | is_locked = 1; | ||
500 | } | ||
300 | oopscount++; | 501 | oopscount++; |
301 | while (total < kmsg_bytes) { | 502 | while (total < kmsg_bytes) { |
302 | char *dst; | 503 | char *dst; |
@@ -304,19 +505,25 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
304 | int hsize; | 505 | int hsize; |
305 | int zipped_len = -1; | 506 | int zipped_len = -1; |
306 | size_t len; | 507 | size_t len; |
307 | bool compressed; | 508 | bool compressed = false; |
308 | size_t total_len; | 509 | size_t total_len; |
309 | 510 | ||
310 | if (big_oops_buf && is_locked) { | 511 | if (big_oops_buf && is_locked) { |
311 | dst = big_oops_buf; | 512 | dst = big_oops_buf; |
312 | hsize = sprintf(dst, "%s#%d Part%u\n", why, | 513 | size = big_oops_buf_sz; |
313 | oopscount, part); | 514 | } else { |
314 | size = big_oops_buf_sz - hsize; | 515 | dst = psinfo->buf; |
516 | size = psinfo->bufsize; | ||
517 | } | ||
315 | 518 | ||
316 | if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, | 519 | hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part); |
317 | size, &len)) | 520 | size -= hsize; |
318 | break; | 521 | |
522 | if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, | ||
523 | size, &len)) | ||
524 | break; | ||
319 | 525 | ||
526 | if (big_oops_buf && is_locked) { | ||
320 | zipped_len = pstore_compress(dst, psinfo->buf, | 527 | zipped_len = pstore_compress(dst, psinfo->buf, |
321 | hsize + len, psinfo->bufsize); | 528 | hsize + len, psinfo->bufsize); |
322 | 529 | ||
@@ -324,21 +531,9 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
324 | compressed = true; | 531 | compressed = true; |
325 | total_len = zipped_len; | 532 | total_len = zipped_len; |
326 | } else { | 533 | } else { |
327 | compressed = false; | ||
328 | total_len = copy_kmsg_to_buffer(hsize, len); | 534 | total_len = copy_kmsg_to_buffer(hsize, len); |
329 | } | 535 | } |
330 | } else { | 536 | } else { |
331 | dst = psinfo->buf; | ||
332 | hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, | ||
333 | part); | ||
334 | size = psinfo->bufsize - hsize; | ||
335 | dst += hsize; | ||
336 | |||
337 | if (!kmsg_dump_get_buffer(dumper, true, dst, | ||
338 | size, &len)) | ||
339 | break; | ||
340 | |||
341 | compressed = false; | ||
342 | total_len = hsize + len; | 537 | total_len = hsize + len; |
343 | } | 538 | } |
344 | 539 | ||
@@ -350,10 +545,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
350 | total += total_len; | 545 | total += total_len; |
351 | part++; | 546 | part++; |
352 | } | 547 | } |
353 | if (pstore_cannot_block_path(reason)) { | 548 | if (is_locked) |
354 | if (is_locked) | ||
355 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); | ||
356 | } else | ||
357 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); | 549 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); |
358 | } | 550 | } |
359 | 551 | ||
@@ -497,9 +689,11 @@ EXPORT_SYMBOL_GPL(pstore_register); | |||
497 | 689 | ||
498 | void pstore_unregister(struct pstore_info *psi) | 690 | void pstore_unregister(struct pstore_info *psi) |
499 | { | 691 | { |
500 | pstore_unregister_pmsg(); | 692 | if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) { |
501 | pstore_unregister_ftrace(); | 693 | pstore_unregister_pmsg(); |
502 | pstore_unregister_console(); | 694 | pstore_unregister_ftrace(); |
695 | pstore_unregister_console(); | ||
696 | } | ||
503 | pstore_unregister_kmsg(); | 697 | pstore_unregister_kmsg(); |
504 | 698 | ||
505 | free_buf_for_compression(); | 699 | free_buf_for_compression(); |
@@ -527,6 +721,7 @@ void pstore_get_records(int quiet) | |||
527 | int failed = 0, rc; | 721 | int failed = 0, rc; |
528 | bool compressed; | 722 | bool compressed; |
529 | int unzipped_len = -1; | 723 | int unzipped_len = -1; |
724 | ssize_t ecc_notice_size = 0; | ||
530 | 725 | ||
531 | if (!psi) | 726 | if (!psi) |
532 | return; | 727 | return; |
@@ -536,7 +731,7 @@ void pstore_get_records(int quiet) | |||
536 | goto out; | 731 | goto out; |
537 | 732 | ||
538 | while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed, | 733 | while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed, |
539 | psi)) > 0) { | 734 | &ecc_notice_size, psi)) > 0) { |
540 | if (compressed && (type == PSTORE_TYPE_DMESG)) { | 735 | if (compressed && (type == PSTORE_TYPE_DMESG)) { |
541 | if (big_oops_buf) | 736 | if (big_oops_buf) |
542 | unzipped_len = pstore_decompress(buf, | 737 | unzipped_len = pstore_decompress(buf, |
@@ -544,6 +739,9 @@ void pstore_get_records(int quiet) | |||
544 | big_oops_buf_sz); | 739 | big_oops_buf_sz); |
545 | 740 | ||
546 | if (unzipped_len > 0) { | 741 | if (unzipped_len > 0) { |
742 | if (ecc_notice_size) | ||
743 | memcpy(big_oops_buf + unzipped_len, | ||
744 | buf + size, ecc_notice_size); | ||
547 | kfree(buf); | 745 | kfree(buf); |
548 | buf = big_oops_buf; | 746 | buf = big_oops_buf; |
549 | size = unzipped_len; | 747 | size = unzipped_len; |
@@ -555,7 +753,8 @@ void pstore_get_records(int quiet) | |||
555 | } | 753 | } |
556 | } | 754 | } |
557 | rc = pstore_mkfile(type, psi->name, id, count, buf, | 755 | rc = pstore_mkfile(type, psi->name, id, count, buf, |
558 | compressed, (size_t)size, time, psi); | 756 | compressed, size + ecc_notice_size, |
757 | time, psi); | ||
559 | if (unzipped_len < 0) { | 758 | if (unzipped_len < 0) { |
560 | /* Free buffer other than big oops */ | 759 | /* Free buffer other than big oops */ |
561 | kfree(buf); | 760 | kfree(buf); |
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index bd9812e83461..47516a794011 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c | |||
@@ -34,6 +34,8 @@ | |||
34 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
35 | #include <linux/compiler.h> | 35 | #include <linux/compiler.h> |
36 | #include <linux/pstore_ram.h> | 36 | #include <linux/pstore_ram.h> |
37 | #include <linux/of.h> | ||
38 | #include <linux/of_address.h> | ||
37 | 39 | ||
38 | #define RAMOOPS_KERNMSG_HDR "====" | 40 | #define RAMOOPS_KERNMSG_HDR "====" |
39 | #define MIN_MEM_SIZE 4096UL | 41 | #define MIN_MEM_SIZE 4096UL |
@@ -181,10 +183,10 @@ static bool prz_ok(struct persistent_ram_zone *prz) | |||
181 | static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | 183 | static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, |
182 | int *count, struct timespec *time, | 184 | int *count, struct timespec *time, |
183 | char **buf, bool *compressed, | 185 | char **buf, bool *compressed, |
186 | ssize_t *ecc_notice_size, | ||
184 | struct pstore_info *psi) | 187 | struct pstore_info *psi) |
185 | { | 188 | { |
186 | ssize_t size; | 189 | ssize_t size; |
187 | ssize_t ecc_notice_size; | ||
188 | struct ramoops_context *cxt = psi->data; | 190 | struct ramoops_context *cxt = psi->data; |
189 | struct persistent_ram_zone *prz = NULL; | 191 | struct persistent_ram_zone *prz = NULL; |
190 | int header_length = 0; | 192 | int header_length = 0; |
@@ -229,16 +231,16 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | |||
229 | size = persistent_ram_old_size(prz) - header_length; | 231 | size = persistent_ram_old_size(prz) - header_length; |
230 | 232 | ||
231 | /* ECC correction notice */ | 233 | /* ECC correction notice */ |
232 | ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0); | 234 | *ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0); |
233 | 235 | ||
234 | *buf = kmalloc(size + ecc_notice_size + 1, GFP_KERNEL); | 236 | *buf = kmalloc(size + *ecc_notice_size + 1, GFP_KERNEL); |
235 | if (*buf == NULL) | 237 | if (*buf == NULL) |
236 | return -ENOMEM; | 238 | return -ENOMEM; |
237 | 239 | ||
238 | memcpy(*buf, (char *)persistent_ram_old(prz) + header_length, size); | 240 | memcpy(*buf, (char *)persistent_ram_old(prz) + header_length, size); |
239 | persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1); | 241 | persistent_ram_ecc_string(prz, *buf + size, *ecc_notice_size + 1); |
240 | 242 | ||
241 | return size + ecc_notice_size; | 243 | return size; |
242 | } | 244 | } |
243 | 245 | ||
244 | static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz, | 246 | 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, | |||
458 | return 0; | 460 | return 0; |
459 | } | 461 | } |
460 | 462 | ||
463 | static int ramoops_parse_dt_size(struct platform_device *pdev, | ||
464 | const char *propname, u32 *value) | ||
465 | { | ||
466 | u32 val32 = 0; | ||
467 | int ret; | ||
468 | |||
469 | ret = of_property_read_u32(pdev->dev.of_node, propname, &val32); | ||
470 | if (ret < 0 && ret != -EINVAL) { | ||
471 | dev_err(&pdev->dev, "failed to parse property %s: %d\n", | ||
472 | propname, ret); | ||
473 | return ret; | ||
474 | } | ||
475 | |||
476 | if (val32 > INT_MAX) { | ||
477 | dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32); | ||
478 | return -EOVERFLOW; | ||
479 | } | ||
480 | |||
481 | *value = val32; | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int ramoops_parse_dt(struct platform_device *pdev, | ||
486 | struct ramoops_platform_data *pdata) | ||
487 | { | ||
488 | struct device_node *of_node = pdev->dev.of_node; | ||
489 | struct device_node *mem_region; | ||
490 | struct resource res; | ||
491 | u32 value; | ||
492 | int ret; | ||
493 | |||
494 | dev_dbg(&pdev->dev, "using Device Tree\n"); | ||
495 | |||
496 | mem_region = of_parse_phandle(of_node, "memory-region", 0); | ||
497 | if (!mem_region) { | ||
498 | dev_err(&pdev->dev, "no memory-region phandle\n"); | ||
499 | return -ENODEV; | ||
500 | } | ||
501 | |||
502 | ret = of_address_to_resource(mem_region, 0, &res); | ||
503 | of_node_put(mem_region); | ||
504 | if (ret) { | ||
505 | dev_err(&pdev->dev, | ||
506 | "failed to translate memory-region to resource: %d\n", | ||
507 | ret); | ||
508 | return ret; | ||
509 | } | ||
510 | |||
511 | pdata->mem_size = resource_size(&res); | ||
512 | pdata->mem_address = res.start; | ||
513 | pdata->mem_type = of_property_read_bool(of_node, "unbuffered"); | ||
514 | pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops"); | ||
515 | |||
516 | #define parse_size(name, field) { \ | ||
517 | ret = ramoops_parse_dt_size(pdev, name, &value); \ | ||
518 | if (ret < 0) \ | ||
519 | return ret; \ | ||
520 | field = value; \ | ||
521 | } | ||
522 | |||
523 | parse_size("record-size", pdata->record_size); | ||
524 | parse_size("console-size", pdata->console_size); | ||
525 | parse_size("ftrace-size", pdata->ftrace_size); | ||
526 | parse_size("pmsg-size", pdata->pmsg_size); | ||
527 | parse_size("ecc-size", pdata->ecc_info.ecc_size); | ||
528 | |||
529 | #undef parse_size | ||
530 | |||
531 | return 0; | ||
532 | } | ||
533 | |||
461 | static int ramoops_probe(struct platform_device *pdev) | 534 | static int ramoops_probe(struct platform_device *pdev) |
462 | { | 535 | { |
463 | struct device *dev = &pdev->dev; | 536 | struct device *dev = &pdev->dev; |
464 | struct ramoops_platform_data *pdata = pdev->dev.platform_data; | 537 | struct ramoops_platform_data *pdata = dev->platform_data; |
465 | struct ramoops_context *cxt = &oops_cxt; | 538 | struct ramoops_context *cxt = &oops_cxt; |
466 | size_t dump_mem_sz; | 539 | size_t dump_mem_sz; |
467 | phys_addr_t paddr; | 540 | phys_addr_t paddr; |
468 | int err = -EINVAL; | 541 | int err = -EINVAL; |
469 | 542 | ||
543 | if (dev_of_node(dev) && !pdata) { | ||
544 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | ||
545 | if (!pdata) { | ||
546 | err = -ENOMEM; | ||
547 | goto fail_out; | ||
548 | } | ||
549 | |||
550 | err = ramoops_parse_dt(pdev, pdata); | ||
551 | if (err < 0) | ||
552 | goto fail_out; | ||
553 | } | ||
554 | |||
470 | /* Only a single ramoops area allowed at a time, so fail extra | 555 | /* Only a single ramoops area allowed at a time, so fail extra |
471 | * probes. | 556 | * probes. |
472 | */ | 557 | */ |
@@ -596,11 +681,17 @@ static int ramoops_remove(struct platform_device *pdev) | |||
596 | return 0; | 681 | return 0; |
597 | } | 682 | } |
598 | 683 | ||
684 | static const struct of_device_id dt_match[] = { | ||
685 | { .compatible = "ramoops" }, | ||
686 | {} | ||
687 | }; | ||
688 | |||
599 | static struct platform_driver ramoops_driver = { | 689 | static struct platform_driver ramoops_driver = { |
600 | .probe = ramoops_probe, | 690 | .probe = ramoops_probe, |
601 | .remove = ramoops_remove, | 691 | .remove = ramoops_remove, |
602 | .driver = { | 692 | .driver = { |
603 | .name = "ramoops", | 693 | .name = "ramoops", |
694 | .of_match_table = dt_match, | ||
604 | }, | 695 | }, |
605 | }; | 696 | }; |
606 | 697 | ||
diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 831479f8df8f..899e95e84400 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h | |||
@@ -58,7 +58,8 @@ struct pstore_info { | |||
58 | int (*close)(struct pstore_info *psi); | 58 | int (*close)(struct pstore_info *psi); |
59 | ssize_t (*read)(u64 *id, enum pstore_type_id *type, | 59 | ssize_t (*read)(u64 *id, enum pstore_type_id *type, |
60 | int *count, struct timespec *time, char **buf, | 60 | int *count, struct timespec *time, char **buf, |
61 | bool *compressed, struct pstore_info *psi); | 61 | bool *compressed, ssize_t *ecc_notice_size, |
62 | struct pstore_info *psi); | ||
62 | int (*write)(enum pstore_type_id type, | 63 | int (*write)(enum pstore_type_id type, |
63 | enum kmsg_dump_reason reason, u64 *id, | 64 | enum kmsg_dump_reason reason, u64 *id, |
64 | unsigned int part, int count, bool compressed, | 65 | unsigned int part, int count, bool compressed, |